diff --git a/#!inf.json b/#!inf.json
new file mode 100644
index 0000000..1f869d1
--- /dev/null
+++ b/#!inf.json
@@ -0,0 +1,218 @@
+#!/usr/bin/env inf
+{
+    "#": "gi0.PINF.it/core/v0",
+
+    "#": {
+        "/dist/firephp.build": "to.pinf.org.mozilla.web-ext",
+        "runner": "it.pinf.org.mozilla.web-ext"
+    },
+    ":builder:": "/dist/firephp.build @ builder/v1",
+    ":runner:": "runner @ runner/v0",
+
+    "gi0.PINF.it/build/v0 @ # /dist/firephp.build": (inf () >>>
+        {
+            "gi0.PINF.it/build/v0 @ # :builder: build() /": {
+                "port": 9000,
+                "manifest": {
+                    "name": "FirePHP (Official)",
+                    "version": "${version}",
+                    "description": "Log from PHP to a devtools panel.",
+                    "applications": {
+                        "gecko": {
+                            "id": "FirePHPExtension-Build@firephp.org",
+                            "strict_min_version": "56.0"
+                        }
+                    },
+                    "icons": {
+                        "48": "./src/skin/Logo.png"
+                    },
+                    "permissions": [
+                        "activeTab",
+                        "storage",
+                        "webRequest",
+                        "webNavigation",
+                        "webRequestBlocking",            
+                        "<all_urls>"
+                    ],
+                    "content_security_policy": "script-src 'self'; style-src 'self'; object-src 'self'; img-src 'self'",
+                    "background": {
+                        "scripts": [
+                            {
+                                "background.js": {
+                                    "gi0.PINF.it/build/v0 # /scripts # /background.js": {
+                                        "@it.pinf.org.browserify # router/v1": {
+                                            "src": "./src/background.js",
+                                            "format": "pinf",
+                                            "babel": {
+                                                "presets": {
+                                                    "@babel/preset-env": {
+                                                        "targets": "last 1 Firefox versions"
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        ]
+                    },
+                    "devtools": {
+                        "panels": [
+                            {
+                                "devtools/dist/index.js": {
+                                    "label": "FirePHP",
+                                    "icon": "./src/skin/Logo.png",
+                                    "include": {
+                                        "codemirror.js": "codemirror/lib/codemirror.js",
+                                        "codemirror.css": "codemirror/lib/codemirror.css",
+                                        "codemirror/addon/selection/active-line.js": "codemirror/addon/selection/active-line.js",
+                                        "codemirror/mode/xml/xml.js": "codemirror/mode/xml/xml.js",
+                                        "codemirror/mode/javascript/javascript.js": "codemirror/mode/javascript/javascript.js",
+                                        "codemirror/mode/css/css.js": "codemirror/mode/css/css.js",
+                                        "codemirror/mode/htmlmixed/htmlmixed.js": "codemirror/mode/htmlmixed/htmlmixed.js",
+                                        "codemirror/mode/clike/clike.js": "codemirror/mode/clike/clike.js",
+                                        "codemirror/mode/php/php.js": "codemirror/mode/php/php.js"
+                                    },
+                                    "code": {
+                                        "gi0.PINF.it/build/v0 # /scripts/devtools # /": {
+                                            "@fireconsole.rep.js # builder/v1": {
+                                                "externalizeCss": true,
+                                                "indexFilename": "index",
+                                                "indexFormat": "js",
+                                                "include": {
+                                                    "jquery": false,
+                                                    "regenerator-runtime": false,
+                                                    "riot.csp.js": true,
+                                                    "riot.js": false,
+                                                    "riot.min.js": false
+                                                },
+                                                "page": {
+                                                    "@layout": {
+                                                        "console": {
+                                                            "@console": {
+                                                                "@fireconsole": {
+                                                                }
+                                                            }
+                                                        },
+                                                        "menu": {
+                                                            "@menu": {}
+                                                        },
+                                                        "settings": {
+                                                            "@enabler": {}
+                                                        },
+                                                        "inspector": {
+                                                            "@inspector": {}
+                                                        },
+                                                        "editor": {
+                                                            "@editor": {}
+                                                        },
+                                                        "manage": {
+                                                            "@manage": {
+                                                                "settings": {
+                                                                    "@settings": {}
+                                                                }
+                                                            }
+                                                        }
+                                                    }
+                                                },
+                                                "reps": {
+                                                    "layout": "./src/layout.rep.js",
+                                                    "menu": "./src/menu.rep.js",
+                                                    "summary": "./src/summary.rep.js",
+                                                    "settings": "./src/settings.rep.js",
+                                                    "manage": "./src/manage.rep.js",
+                                                    "inspector": "./src/inspector.rep.js",
+                                                    "editor": "./src/editor.rep.js",
+                                                    "console": "./src/console.rep.js",
+                                                    "enabler": "./src/enabler.rep.js"
+                                                },
+                                                "babel": {
+                                                    "presets": {
+                                                        "@babel/preset-env": {
+                                                            "targets": "last 1 Firefox versions"
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                },
+                "routes": {
+                    "^/$": (javascript (API) >>>
+
+                        return function (req, res, next) {
+            
+                            if (
+                                req.headers["x-firephp-version"] ||
+                                /\sFirePHP\/([\.|\d]*)\s?/.test(req.headers["user-agent"])
+                            ) {
+            
+                                function wrap (message) {
+                                    return message.length + '|' + message + '|';
+                                }
+            
+                                res.writeHead(200, {
+                                    'X-Wf-Protocol-1': 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2',
+                                    'X-Wf-1-Plugin-1': 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.0.0master1106021548',
+                                    'X-Wf-1-Structure-1': 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1',
+            
+                                    // @see https://github.com/firephp/firephp/issues/16
+                                    'X-Wf-1-1-1-1': wrap('[{"Type":"LOG","File":"/path/to/file","Line":10},"Hello World"]'),
+                                    'X-Wf-1-1-1-2': wrap('[{"Type":"INFO","File":"\/christoph\/projects\/gi0.FireConsole.org\/rep.js\/examples\/03-FirePHPCore\/index.php","Line":75},"\\u0427\\u0442\\u043e-\\u0442\\u043e"]'),
+                                    'X-Wf-1-1-1-3': wrap('[{"Type":"INFO","File":"\/christoph\/projects\/gi0.FireConsole.org\/rep.js\/examples\/03-FirePHPCore\/index.php","Line":76},"Od\\u00f3metro"]'),
+            
+                                    'X-Wf-1-Index': '3'
+                                });
+            
+            
+                                res.end("FirePHP Core formatted messages sent in HTTP response headers.");
+                            } else {
+            
+                                res.end([
+                                    "<p>No FirePHP HTTP request headers found.</p>",
+                                    "<p><a href=\"http://127.0.0.1:8080/FirePHP.php\">http://127.0.0.1:8080/FirePHP.php</a></p>"
+                                ].join("\\n"));
+                            }
+
+
+                            // if (process.env.BO_TEST_FLAG_DEV) return;
+
+                            // setTimeout(function () {
+                            //     API.SERVER.stop();
+                            // }, 1000);
+                        };
+                    <<<)
+                },
+                "postbuild": (javascript (LIB) >>>
+
+                    return async function () {
+                        // // Remove files that we do not need.
+                        // await Promise.all([
+                        //     'domplate-eval.browser.js',
+                        //     'domplate.browser.js',
+                        //     'pinf-loader-core.browser.js',
+                        //     'babel-regenerator-runtime.js'
+                        // ].map(async function (filename) {
+                        //     const paths = LIB.GLOB.sync('**/' + filename);
+                        //     await Promise.all(paths.map(async function (path) {
+                        //         await LIB.FS_EXTRA.removeSync(path);
+                        //     }));
+                        // }));
+                    };
+                <<<)
+            }
+        }
+    <<<),
+
+    "gi0.PINF.it/build/v0 @ # onDone()": (inf () >>>
+        {
+            "gi0.PINF.it/core/v0 @ # :runner: set() extensionPath": "dist/firephp.build",
+
+            "gi0.PINF.it/core/v0 @ # :runner: run()": ""
+        }
+    <<<)
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index fc49b2c..d2d05e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,5 @@ node_modules/
 /dist/firephp.zip
 /dist/firephp.build/.web-extension-id
 .~*
-/dist/firephp.build.zip
\ No newline at end of file
+/dist/firephp.build.zip
+/tests/*/.extension.built
\ No newline at end of file
diff --git a/README.md b/README.md
index 8956fed..cdddb33 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,15 @@ This project holds the [FirePHP Tool](http://firephp.org) in the form of a Brows
 
   * Mozilla Firefox - https://developer.mozilla.org/en-US/Add-ons/WebExtensions
   * Google Chrome - https://developer.chrome.com/extensions
+  * Brave - (runs Google Chrome extensions)
 
 > Log from PHP to Browser Developer Tools via HTTP Response Headers.
 
+Supported protocols:
+
+  * FirePHP (Wildfire based)
+  * [Chrome Logger](https://craig.is/writing/chrome-logger/techspecs)
+
 For a list of supported server libraries see [http://firephp.org](http://firephp.org).
 
 
@@ -25,7 +31,7 @@ Install
 ### Published
 
   * Firefox: [addons.mozilla.org/en-US/firefox/addon/firephp](https://addons.mozilla.org/en-US/firefox/addon/firephp/)
-  * Chrome: *TBD*
+  * Chrome & Brave: [chrome.google.com/webstore/detail/firephp/ikfbpappjhegehjflebknjbhdocbgkdi](https://chrome.google.com/webstore/detail/firephp/ikfbpappjhegehjflebknjbhdocbgkdi)
 
 ### Local
 
@@ -43,14 +49,16 @@ Requirements (tested on macOS):
 
 Build from source:
 
+**Status: DEV - Commands will currently not run for you!**
+
     rm -Rf dist/        # Optional to verify build
-    nvm use 10
+    nvm use 14
     npm install
-    npm run build       # Append '--ignore-dirty' if you removed 'dist/'
+    npm run build
     git diff            # Optional to verify build
         # Only the `version` property in `dist/firephp.build/manifest.json` should have changed.
 
-*Load Temporary Addon* at `dist/firephp.build/` into Firefox or Chrome.
+*Load Addon* from `dist/firephp.build/` into Firefox, Chrome or Brave.
 
 ### Notes for build reviewers
 
@@ -68,6 +76,8 @@ Build from source:
       * https://github.com/pinf/pinf-loader-js
     * `./lib/eventemitter3.js`
       * https://github.com/primus/EventEmitter3
+    * `./lib/codemirror.*`
+      * https://github.com/codemirror/codemirror
   * The following `./dist/firephp.build` files are generated by the build tool by processing source files from `./src` using instructions from `./workspace.sh`:
     * `./skin`
     * `./scripts/background.js`
diff --git a/dist/firephp.build/devtools-page.js b/dist/firephp.build/devtools-page.js
index 8adb1df..e930f72 100644
--- a/dist/firephp.build/devtools-page.js
+++ b/dist/firephp.build/devtools-page.js
@@ -22,7 +22,7 @@ panels.on("set", function (name, value) {
 
 });
 
-[{"label":"FirePHP","icon":"skin/__icon_devtools_panel_Logo.png","filename":"devtools/index.html"}].forEach(function (panel) {
+[{"label":"FirePHP","icon":"skin/__icon_devtools_panel_Logo.png","filename":"devtools/dist/index.html"}].forEach(function (panel) {
 
     const url = "scripts/" + panel.filename;
 
diff --git a/dist/firephp.build/lib.js b/dist/firephp.build/lib.js
index 0de96c5..490fe28 100644
--- a/dist/firephp.build/lib.js
+++ b/dist/firephp.build/lib.js
@@ -41,23 +41,6 @@
     }
 
     WINDOW.crossbrowser = {
-        storage: {
-            onChanged: BROWSER.storage.onChanged,
-            local: {
-                get: async function () {
-                    if (IS_FIREFOX) {
-                        return BROWSER.storage.local.get.apply(BROWSER.storage.local, arguments);
-                    }
-                    return promisifyNoErr(BROWSER.storage.local.get, BROWSER.storage.local)(arguments);
-                },
-                set: async function () {
-                    if (IS_FIREFOX) {
-                        return BROWSER.storage.local.set.apply(BROWSER.storage.local, arguments);
-                    }
-                    return promisifyNoErr(BROWSER.storage.local.set, BROWSER.storage.local)(arguments);
-                }
-            }
-        },
         runtime: {
             getURL: BROWSER.runtime.getURL,
             onMessage: BROWSER.runtime.onMessage,
@@ -74,8 +57,37 @@
             getManifest: function () {
                 return BROWSER.runtime.getManifest();
             }            
-        },
-        tabs: {
+        }
+    };
+
+    if (BROWSER.webRequest) {
+        WINDOW.crossbrowser.webRequest = {
+            onBeforeSendHeaders: BROWSER.webRequest.onBeforeSendHeaders,
+            onHeadersReceived: BROWSER.webRequest.onHeadersReceived
+        };
+    }
+
+    if (BROWSER.devtools) {
+        WINDOW.crossbrowser.devtools = {
+            inspectedWindow: {},
+            panels: {
+                create: async function () {
+                    if (IS_FIREFOX) {
+                        return BROWSER.devtools.panels.create.apply(BROWSER.devtools.panels, arguments);
+                    }
+                    return promisifyNoErr(BROWSER.devtools.panels.create, BROWSER.devtools.panels)(arguments);
+                }
+            }
+        };
+        Object.defineProperty(WINDOW.crossbrowser.devtools.inspectedWindow, 'tabId', {
+            get: function() {
+                return BROWSER.devtools.inspectedWindow.tabId;
+            }
+        });
+    }
+
+    if (BROWSER.tabs) {
+        WINDOW.crossbrowser.tabs = {
             query: async function () {
                 if (IS_FIREFOX) {
                     return BROWSER.tabs.query.apply(BROWSER.tabs, arguments);
@@ -94,24 +106,27 @@
                 }
                 return promisifyNoErr(BROWSER.tabs.reload, BROWSER.tabs)(arguments);
             }
-        },
-        devtools: {
-            inspectedWindow: {},
-            panels: {
-                create: async function () {
+        };
+    }
+
+    if (BROWSER.storage) {
+        WINDOW.crossbrowser.storage = {
+            onChanged: BROWSER.storage.onChanged,
+            local: {
+                get: async function () {
                     if (IS_FIREFOX) {
-                        return BROWSER.devtools.panels.create.apply(BROWSER.devtools.panels, arguments);
+                        return BROWSER.storage.local.get.apply(BROWSER.storage.local, arguments);
                     }
-                    return promisifyNoErr(BROWSER.devtools.panels.create, BROWSER.devtools.panels)(arguments);
+                    return promisifyNoErr(BROWSER.storage.local.get, BROWSER.storage.local)(arguments);
+                },
+                set: async function () {
+                    if (IS_FIREFOX) {
+                        return BROWSER.storage.local.set.apply(BROWSER.storage.local, arguments);
+                    }
+                    return promisifyNoErr(BROWSER.storage.local.set, BROWSER.storage.local)(arguments);
                 }
             }
-        }
+        };
     };
 
-    Object.defineProperty(WINDOW.crossbrowser.devtools.inspectedWindow, 'tabId', {
-        get: function() {
-            return BROWSER.devtools.inspectedWindow.tabId;
-        }
-    });
-
 })(window);
diff --git a/dist/firephp.build/lib/codemirror.css b/dist/firephp.build/lib/codemirror.css
new file mode 100644
index 0000000..bc910fb
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror.css
@@ -0,0 +1,349 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height: 300px;
+  color: black;
+  direction: ltr;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+  white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+  white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+  border-left: 1px solid black;
+  border-right: none;
+  width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+  width: auto;
+  border: 0 !important;
+  background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+  z-index: 1;
+}
+.cm-fat-cursor-mark {
+  background-color: rgba(20, 255, 20, 0.5);
+  -webkit-animation: blink 1.06s steps(1) infinite;
+  -moz-animation: blink 1.06s steps(1) infinite;
+  animation: blink 1.06s steps(1) infinite;
+}
+.cm-animate-fat-cursor {
+  width: auto;
+  border: 0;
+  -webkit-animation: blink 1.06s steps(1) infinite;
+  -moz-animation: blink 1.06s steps(1) infinite;
+  animation: blink 1.06s steps(1) infinite;
+  background-color: #7e7;
+}
+@-moz-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@-webkit-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-rulers {
+  position: absolute;
+  left: 0; right: 0; top: -50px; bottom: 0;
+  overflow: hidden;
+}
+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  top: 0; bottom: 0;
+  position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  position: relative;
+  overflow: hidden;
+  background: white;
+}
+
+.CodeMirror-scroll {
+  overflow: scroll !important; /* Things will break if this is overridden */
+  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror */
+  margin-bottom: -30px; margin-right: -30px;
+  padding-bottom: 30px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+}
+.CodeMirror-sizer {
+  position: relative;
+  border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actual scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+  left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  min-height: 100%;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  white-space: normal;
+  height: 100%;
+  display: inline-block;
+  vertical-align: top;
+  margin-bottom: -30px;
+}
+.CodeMirror-gutter-wrapper {
+  position: absolute;
+  z-index: 4;
+  background: none !important;
+  border: none !important;
+}
+.CodeMirror-gutter-background {
+  position: absolute;
+  top: 0; bottom: 0;
+  z-index: 4;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
+.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
+
+.CodeMirror-lines {
+  cursor: text;
+  min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+  -webkit-tap-highlight-color: transparent;
+  -webkit-font-variant-ligatures: contextual;
+  font-variant-ligatures: contextual;
+}
+.CodeMirror-wrap pre.CodeMirror-line,
+.CodeMirror-wrap pre.CodeMirror-line-like {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+  padding: 0.1px; /* Force widget margins to stay inside of the container */
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-rtl pre { direction: rtl; }
+
+.CodeMirror-code {
+  outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+  visibility: hidden;
+}
+
+.CodeMirror-cursor {
+  position: absolute;
+  pointer-events: none;
+}
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 3;
+}
+div.CodeMirror-dragcursors {
+  visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+  background-color: #ffa;
+  background-color: rgba(255, 255, 0, .4);
+}
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror div.CodeMirror-cursors {
+    visibility: hidden;
+  }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
diff --git a/dist/firephp.build/lib/codemirror.js b/dist/firephp.build/lib/codemirror.js
new file mode 100644
index 0000000..f23842f
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror.js
@@ -0,0 +1,9765 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// This is CodeMirror (https://codemirror.net), a code editor
+// implemented in JavaScript on top of the browser's DOM.
+//
+// You can find some technical background for some of the code below
+// at http://marijnhaverbeke.nl/blog/#cm-internals .
+
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+  typeof define === 'function' && define.amd ? define(factory) :
+  (global.CodeMirror = factory());
+}(this, (function () { 'use strict';
+
+  // Kludges for bugs and behavior differences that can't be feature
+  // detected are enabled based on userAgent etc sniffing.
+  var userAgent = navigator.userAgent;
+  var platform = navigator.platform;
+
+  var gecko = /gecko\/\d/i.test(userAgent);
+  var ie_upto10 = /MSIE \d/.test(userAgent);
+  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
+  var edge = /Edge\/(\d+)/.exec(userAgent);
+  var ie = ie_upto10 || ie_11up || edge;
+  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
+  var webkit = !edge && /WebKit\//.test(userAgent);
+  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
+  var chrome = !edge && /Chrome\//.test(userAgent);
+  var presto = /Opera\//.test(userAgent);
+  var safari = /Apple Computer/.test(navigator.vendor);
+  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
+  var phantom = /PhantomJS/.test(userAgent);
+
+  var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
+  var android = /Android/.test(userAgent);
+  // This is woefully incomplete. Suggestions for alternative methods welcome.
+  var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
+  var mac = ios || /Mac/.test(platform);
+  var chromeOS = /\bCrOS\b/.test(userAgent);
+  var windows = /win/i.test(platform);
+
+  var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
+  if (presto_version) { presto_version = Number(presto_version[1]); }
+  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
+  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
+  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
+  var captureRightClick = gecko || (ie && ie_version >= 9);
+
+  function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
+
+  var rmClass = function(node, cls) {
+    var current = node.className;
+    var match = classTest(cls).exec(current);
+    if (match) {
+      var after = current.slice(match.index + match[0].length);
+      node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
+    }
+  };
+
+  function removeChildren(e) {
+    for (var count = e.childNodes.length; count > 0; --count)
+      { e.removeChild(e.firstChild); }
+    return e
+  }
+
+  function removeChildrenAndAdd(parent, e) {
+    return removeChildren(parent).appendChild(e)
+  }
+
+  function elt(tag, content, className, style) {
+    var e = document.createElement(tag);
+    if (className) { e.className = className; }
+    if (style) { e.style.cssText = style; }
+    if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
+    else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
+    return e
+  }
+  // wrapper for elt, which removes the elt from the accessibility tree
+  function eltP(tag, content, className, style) {
+    var e = elt(tag, content, className, style);
+    e.setAttribute("role", "presentation");
+    return e
+  }
+
+  var range;
+  if (document.createRange) { range = function(node, start, end, endNode) {
+    var r = document.createRange();
+    r.setEnd(endNode || node, end);
+    r.setStart(node, start);
+    return r
+  }; }
+  else { range = function(node, start, end) {
+    var r = document.body.createTextRange();
+    try { r.moveToElementText(node.parentNode); }
+    catch(e) { return r }
+    r.collapse(true);
+    r.moveEnd("character", end);
+    r.moveStart("character", start);
+    return r
+  }; }
+
+  function contains(parent, child) {
+    if (child.nodeType == 3) // Android browser always returns false when child is a textnode
+      { child = child.parentNode; }
+    if (parent.contains)
+      { return parent.contains(child) }
+    do {
+      if (child.nodeType == 11) { child = child.host; }
+      if (child == parent) { return true }
+    } while (child = child.parentNode)
+  }
+
+  function activeElt() {
+    // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
+    // IE < 10 will throw when accessed while the page is loading or in an iframe.
+    // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
+    var activeElement;
+    try {
+      activeElement = document.activeElement;
+    } catch(e) {
+      activeElement = document.body || null;
+    }
+    while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
+      { activeElement = activeElement.shadowRoot.activeElement; }
+    return activeElement
+  }
+
+  function addClass(node, cls) {
+    var current = node.className;
+    if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
+  }
+  function joinClasses(a, b) {
+    var as = a.split(" ");
+    for (var i = 0; i < as.length; i++)
+      { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
+    return b
+  }
+
+  var selectInput = function(node) { node.select(); };
+  if (ios) // Mobile Safari apparently has a bug where select() is broken.
+    { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
+  else if (ie) // Suppress mysterious IE10 errors
+    { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
+
+  function bind(f) {
+    var args = Array.prototype.slice.call(arguments, 1);
+    return function(){return f.apply(null, args)}
+  }
+
+  function copyObj(obj, target, overwrite) {
+    if (!target) { target = {}; }
+    for (var prop in obj)
+      { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
+        { target[prop] = obj[prop]; } }
+    return target
+  }
+
+  // Counts the column offset in a string, taking tabs into account.
+  // Used mostly to find indentation.
+  function countColumn(string, end, tabSize, startIndex, startValue) {
+    if (end == null) {
+      end = string.search(/[^\s\u00a0]/);
+      if (end == -1) { end = string.length; }
+    }
+    for (var i = startIndex || 0, n = startValue || 0;;) {
+      var nextTab = string.indexOf("\t", i);
+      if (nextTab < 0 || nextTab >= end)
+        { return n + (end - i) }
+      n += nextTab - i;
+      n += tabSize - (n % tabSize);
+      i = nextTab + 1;
+    }
+  }
+
+  var Delayed = function() {this.id = null;};
+  Delayed.prototype.set = function (ms, f) {
+    clearTimeout(this.id);
+    this.id = setTimeout(f, ms);
+  };
+
+  function indexOf(array, elt) {
+    for (var i = 0; i < array.length; ++i)
+      { if (array[i] == elt) { return i } }
+    return -1
+  }
+
+  // Number of pixels added to scroller and sizer to hide scrollbar
+  var scrollerGap = 30;
+
+  // Returned or thrown by various protocols to signal 'I'm not
+  // handling this'.
+  var Pass = {toString: function(){return "CodeMirror.Pass"}};
+
+  // Reused option objects for setSelection & friends
+  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
+
+  // The inverse of countColumn -- find the offset that corresponds to
+  // a particular column.
+  function findColumn(string, goal, tabSize) {
+    for (var pos = 0, col = 0;;) {
+      var nextTab = string.indexOf("\t", pos);
+      if (nextTab == -1) { nextTab = string.length; }
+      var skipped = nextTab - pos;
+      if (nextTab == string.length || col + skipped >= goal)
+        { return pos + Math.min(skipped, goal - col) }
+      col += nextTab - pos;
+      col += tabSize - (col % tabSize);
+      pos = nextTab + 1;
+      if (col >= goal) { return pos }
+    }
+  }
+
+  var spaceStrs = [""];
+  function spaceStr(n) {
+    while (spaceStrs.length <= n)
+      { spaceStrs.push(lst(spaceStrs) + " "); }
+    return spaceStrs[n]
+  }
+
+  function lst(arr) { return arr[arr.length-1] }
+
+  function map(array, f) {
+    var out = [];
+    for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
+    return out
+  }
+
+  function insertSorted(array, value, score) {
+    var pos = 0, priority = score(value);
+    while (pos < array.length && score(array[pos]) <= priority) { pos++; }
+    array.splice(pos, 0, value);
+  }
+
+  function nothing() {}
+
+  function createObj(base, props) {
+    var inst;
+    if (Object.create) {
+      inst = Object.create(base);
+    } else {
+      nothing.prototype = base;
+      inst = new nothing();
+    }
+    if (props) { copyObj(props, inst); }
+    return inst
+  }
+
+  var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+  function isWordCharBasic(ch) {
+    return /\w/.test(ch) || ch > "\x80" &&
+      (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
+  }
+  function isWordChar(ch, helper) {
+    if (!helper) { return isWordCharBasic(ch) }
+    if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
+    return helper.test(ch)
+  }
+
+  function isEmpty(obj) {
+    for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
+    return true
+  }
+
+  // Extending unicode characters. A series of a non-extending char +
+  // any number of extending chars is treated as a single unit as far
+  // as editing and measuring is concerned. This is not fully correct,
+  // since some scripts/fonts/browsers also treat other configurations
+  // of code points as a group.
+  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
+  function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
+
+  // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
+  function skipExtendingChars(str, pos, dir) {
+    while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
+    return pos
+  }
+
+  // Returns the value from the range [`from`; `to`] that satisfies
+  // `pred` and is closest to `from`. Assumes that at least `to`
+  // satisfies `pred`. Supports `from` being greater than `to`.
+  function findFirst(pred, from, to) {
+    // At any point we are certain `to` satisfies `pred`, don't know
+    // whether `from` does.
+    var dir = from > to ? -1 : 1;
+    for (;;) {
+      if (from == to) { return from }
+      var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
+      if (mid == from) { return pred(mid) ? from : to }
+      if (pred(mid)) { to = mid; }
+      else { from = mid + dir; }
+    }
+  }
+
+  // BIDI HELPERS
+
+  function iterateBidiSections(order, from, to, f) {
+    if (!order) { return f(from, to, "ltr", 0) }
+    var found = false;
+    for (var i = 0; i < order.length; ++i) {
+      var part = order[i];
+      if (part.from < to && part.to > from || from == to && part.to == from) {
+        f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
+        found = true;
+      }
+    }
+    if (!found) { f(from, to, "ltr"); }
+  }
+
+  var bidiOther = null;
+  function getBidiPartAt(order, ch, sticky) {
+    var found;
+    bidiOther = null;
+    for (var i = 0; i < order.length; ++i) {
+      var cur = order[i];
+      if (cur.from < ch && cur.to > ch) { return i }
+      if (cur.to == ch) {
+        if (cur.from != cur.to && sticky == "before") { found = i; }
+        else { bidiOther = i; }
+      }
+      if (cur.from == ch) {
+        if (cur.from != cur.to && sticky != "before") { found = i; }
+        else { bidiOther = i; }
+      }
+    }
+    return found != null ? found : bidiOther
+  }
+
+  // Bidirectional ordering algorithm
+  // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
+  // that this (partially) implements.
+
+  // One-char codes used for character types:
+  // L (L):   Left-to-Right
+  // R (R):   Right-to-Left
+  // r (AL):  Right-to-Left Arabic
+  // 1 (EN):  European Number
+  // + (ES):  European Number Separator
+  // % (ET):  European Number Terminator
+  // n (AN):  Arabic Number
+  // , (CS):  Common Number Separator
+  // m (NSM): Non-Spacing Mark
+  // b (BN):  Boundary Neutral
+  // s (B):   Paragraph Separator
+  // t (S):   Segment Separator
+  // w (WS):  Whitespace
+  // N (ON):  Other Neutrals
+
+  // Returns null if characters are ordered as they appear
+  // (left-to-right), or an array of sections ({from, to, level}
+  // objects) in the order in which they occur visually.
+  var bidiOrdering = (function() {
+    // Character types for codepoints 0 to 0xff
+    var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
+    // Character types for codepoints 0x600 to 0x6f9
+    var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
+    function charType(code) {
+      if (code <= 0xf7) { return lowTypes.charAt(code) }
+      else if (0x590 <= code && code <= 0x5f4) { return "R" }
+      else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
+      else if (0x6ee <= code && code <= 0x8ac) { return "r" }
+      else if (0x2000 <= code && code <= 0x200b) { return "w" }
+      else if (code == 0x200c) { return "b" }
+      else { return "L" }
+    }
+
+    var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
+    var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
+
+    function BidiSpan(level, from, to) {
+      this.level = level;
+      this.from = from; this.to = to;
+    }
+
+    return function(str, direction) {
+      var outerType = direction == "ltr" ? "L" : "R";
+
+      if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
+      var len = str.length, types = [];
+      for (var i = 0; i < len; ++i)
+        { types.push(charType(str.charCodeAt(i))); }
+
+      // W1. Examine each non-spacing mark (NSM) in the level run, and
+      // change the type of the NSM to the type of the previous
+      // character. If the NSM is at the start of the level run, it will
+      // get the type of sor.
+      for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
+        var type = types[i$1];
+        if (type == "m") { types[i$1] = prev; }
+        else { prev = type; }
+      }
+
+      // W2. Search backwards from each instance of a European number
+      // until the first strong type (R, L, AL, or sor) is found. If an
+      // AL is found, change the type of the European number to Arabic
+      // number.
+      // W3. Change all ALs to R.
+      for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
+        var type$1 = types[i$2];
+        if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
+        else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
+      }
+
+      // W4. A single European separator between two European numbers
+      // changes to a European number. A single common separator between
+      // two numbers of the same type changes to that type.
+      for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
+        var type$2 = types[i$3];
+        if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
+        else if (type$2 == "," && prev$1 == types[i$3+1] &&
+                 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
+        prev$1 = type$2;
+      }
+
+      // W5. A sequence of European terminators adjacent to European
+      // numbers changes to all European numbers.
+      // W6. Otherwise, separators and terminators change to Other
+      // Neutral.
+      for (var i$4 = 0; i$4 < len; ++i$4) {
+        var type$3 = types[i$4];
+        if (type$3 == ",") { types[i$4] = "N"; }
+        else if (type$3 == "%") {
+          var end = (void 0);
+          for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
+          var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
+          for (var j = i$4; j < end; ++j) { types[j] = replace; }
+          i$4 = end - 1;
+        }
+      }
+
+      // W7. Search backwards from each instance of a European number
+      // until the first strong type (R, L, or sor) is found. If an L is
+      // found, then change the type of the European number to L.
+      for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
+        var type$4 = types[i$5];
+        if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
+        else if (isStrong.test(type$4)) { cur$1 = type$4; }
+      }
+
+      // N1. A sequence of neutrals takes the direction of the
+      // surrounding strong text if the text on both sides has the same
+      // direction. European and Arabic numbers act as if they were R in
+      // terms of their influence on neutrals. Start-of-level-run (sor)
+      // and end-of-level-run (eor) are used at level run boundaries.
+      // N2. Any remaining neutrals take the embedding direction.
+      for (var i$6 = 0; i$6 < len; ++i$6) {
+        if (isNeutral.test(types[i$6])) {
+          var end$1 = (void 0);
+          for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
+          var before = (i$6 ? types[i$6-1] : outerType) == "L";
+          var after = (end$1 < len ? types[end$1] : outerType) == "L";
+          var replace$1 = before == after ? (before ? "L" : "R") : outerType;
+          for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
+          i$6 = end$1 - 1;
+        }
+      }
+
+      // Here we depart from the documented algorithm, in order to avoid
+      // building up an actual levels array. Since there are only three
+      // levels (0, 1, 2) in an implementation that doesn't take
+      // explicit embedding into account, we can build up the order on
+      // the fly, without following the level-based algorithm.
+      var order = [], m;
+      for (var i$7 = 0; i$7 < len;) {
+        if (countsAsLeft.test(types[i$7])) {
+          var start = i$7;
+          for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
+          order.push(new BidiSpan(0, start, i$7));
+        } else {
+          var pos = i$7, at = order.length;
+          for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
+          for (var j$2 = pos; j$2 < i$7;) {
+            if (countsAsNum.test(types[j$2])) {
+              if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
+              var nstart = j$2;
+              for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
+              order.splice(at, 0, new BidiSpan(2, nstart, j$2));
+              pos = j$2;
+            } else { ++j$2; }
+          }
+          if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
+        }
+      }
+      if (direction == "ltr") {
+        if (order[0].level == 1 && (m = str.match(/^\s+/))) {
+          order[0].from = m[0].length;
+          order.unshift(new BidiSpan(0, 0, m[0].length));
+        }
+        if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
+          lst(order).to -= m[0].length;
+          order.push(new BidiSpan(0, len - m[0].length, len));
+        }
+      }
+
+      return direction == "rtl" ? order.reverse() : order
+    }
+  })();
+
+  // Get the bidi ordering for the given line (and cache it). Returns
+  // false for lines that are fully left-to-right, and an array of
+  // BidiSpan objects otherwise.
+  function getOrder(line, direction) {
+    var order = line.order;
+    if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
+    return order
+  }
+
+  // EVENT HANDLING
+
+  // Lightweight event framework. on/off also work on DOM nodes,
+  // registering native DOM handlers.
+
+  var noHandlers = [];
+
+  var on = function(emitter, type, f) {
+    if (emitter.addEventListener) {
+      emitter.addEventListener(type, f, false);
+    } else if (emitter.attachEvent) {
+      emitter.attachEvent("on" + type, f);
+    } else {
+      var map$$1 = emitter._handlers || (emitter._handlers = {});
+      map$$1[type] = (map$$1[type] || noHandlers).concat(f);
+    }
+  };
+
+  function getHandlers(emitter, type) {
+    return emitter._handlers && emitter._handlers[type] || noHandlers
+  }
+
+  function off(emitter, type, f) {
+    if (emitter.removeEventListener) {
+      emitter.removeEventListener(type, f, false);
+    } else if (emitter.detachEvent) {
+      emitter.detachEvent("on" + type, f);
+    } else {
+      var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
+      if (arr) {
+        var index = indexOf(arr, f);
+        if (index > -1)
+          { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
+      }
+    }
+  }
+
+  function signal(emitter, type /*, values...*/) {
+    var handlers = getHandlers(emitter, type);
+    if (!handlers.length) { return }
+    var args = Array.prototype.slice.call(arguments, 2);
+    for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
+  }
+
+  // The DOM events that CodeMirror handles can be overridden by
+  // registering a (non-DOM) handler on the editor for the event name,
+  // and preventDefault-ing the event in that handler.
+  function signalDOMEvent(cm, e, override) {
+    if (typeof e == "string")
+      { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
+    signal(cm, override || e.type, cm, e);
+    return e_defaultPrevented(e) || e.codemirrorIgnore
+  }
+
+  function signalCursorActivity(cm) {
+    var arr = cm._handlers && cm._handlers.cursorActivity;
+    if (!arr) { return }
+    var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
+    for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
+      { set.push(arr[i]); } }
+  }
+
+  function hasHandler(emitter, type) {
+    return getHandlers(emitter, type).length > 0
+  }
+
+  // Add on and off methods to a constructor's prototype, to make
+  // registering events on such objects more convenient.
+  function eventMixin(ctor) {
+    ctor.prototype.on = function(type, f) {on(this, type, f);};
+    ctor.prototype.off = function(type, f) {off(this, type, f);};
+  }
+
+  // Due to the fact that we still support jurassic IE versions, some
+  // compatibility wrappers are needed.
+
+  function e_preventDefault(e) {
+    if (e.preventDefault) { e.preventDefault(); }
+    else { e.returnValue = false; }
+  }
+  function e_stopPropagation(e) {
+    if (e.stopPropagation) { e.stopPropagation(); }
+    else { e.cancelBubble = true; }
+  }
+  function e_defaultPrevented(e) {
+    return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
+  }
+  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
+
+  function e_target(e) {return e.target || e.srcElement}
+  function e_button(e) {
+    var b = e.which;
+    if (b == null) {
+      if (e.button & 1) { b = 1; }
+      else if (e.button & 2) { b = 3; }
+      else if (e.button & 4) { b = 2; }
+    }
+    if (mac && e.ctrlKey && b == 1) { b = 3; }
+    return b
+  }
+
+  // Detect drag-and-drop
+  var dragAndDrop = function() {
+    // There is *some* kind of drag-and-drop support in IE6-8, but I
+    // couldn't get it to work yet.
+    if (ie && ie_version < 9) { return false }
+    var div = elt('div');
+    return "draggable" in div || "dragDrop" in div
+  }();
+
+  var zwspSupported;
+  function zeroWidthElement(measure) {
+    if (zwspSupported == null) {
+      var test = elt("span", "\u200b");
+      removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
+      if (measure.firstChild.offsetHeight != 0)
+        { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
+    }
+    var node = zwspSupported ? elt("span", "\u200b") :
+      elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
+    node.setAttribute("cm-text", "");
+    return node
+  }
+
+  // Feature-detect IE's crummy client rect reporting for bidi text
+  var badBidiRects;
+  function hasBadBidiRects(measure) {
+    if (badBidiRects != null) { return badBidiRects }
+    var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
+    var r0 = range(txt, 0, 1).getBoundingClientRect();
+    var r1 = range(txt, 1, 2).getBoundingClientRect();
+    removeChildren(measure);
+    if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
+    return badBidiRects = (r1.right - r0.right < 3)
+  }
+
+  // See if "".split is the broken IE version, if so, provide an
+  // alternative way to split lines.
+  var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
+    var pos = 0, result = [], l = string.length;
+    while (pos <= l) {
+      var nl = string.indexOf("\n", pos);
+      if (nl == -1) { nl = string.length; }
+      var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
+      var rt = line.indexOf("\r");
+      if (rt != -1) {
+        result.push(line.slice(0, rt));
+        pos += rt + 1;
+      } else {
+        result.push(line);
+        pos = nl + 1;
+      }
+    }
+    return result
+  } : function (string) { return string.split(/\r\n?|\n/); };
+
+  var hasSelection = window.getSelection ? function (te) {
+    try { return te.selectionStart != te.selectionEnd }
+    catch(e) { return false }
+  } : function (te) {
+    var range$$1;
+    try {range$$1 = te.ownerDocument.selection.createRange();}
+    catch(e) {}
+    if (!range$$1 || range$$1.parentElement() != te) { return false }
+    return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
+  };
+
+  var hasCopyEvent = (function () {
+    var e = elt("div");
+    if ("oncopy" in e) { return true }
+    e.setAttribute("oncopy", "return;");
+    return typeof e.oncopy == "function"
+  })();
+
+  var badZoomedRects = null;
+  function hasBadZoomedRects(measure) {
+    if (badZoomedRects != null) { return badZoomedRects }
+    var node = removeChildrenAndAdd(measure, elt("span", "x"));
+    var normal = node.getBoundingClientRect();
+    var fromRange = range(node, 0, 1).getBoundingClientRect();
+    return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
+  }
+
+  // Known modes, by name and by MIME
+  var modes = {}, mimeModes = {};
+
+  // Extra arguments are stored as the mode's dependencies, which is
+  // used by (legacy) mechanisms like loadmode.js to automatically
+  // load a mode. (Preferred mechanism is the require/define calls.)
+  function defineMode(name, mode) {
+    if (arguments.length > 2)
+      { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
+    modes[name] = mode;
+  }
+
+  function defineMIME(mime, spec) {
+    mimeModes[mime] = spec;
+  }
+
+  // Given a MIME type, a {name, ...options} config object, or a name
+  // string, return a mode config object.
+  function resolveMode(spec) {
+    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
+      spec = mimeModes[spec];
+    } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+      var found = mimeModes[spec.name];
+      if (typeof found == "string") { found = {name: found}; }
+      spec = createObj(found, spec);
+      spec.name = found.name;
+    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+      return resolveMode("application/xml")
+    } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
+      return resolveMode("application/json")
+    }
+    if (typeof spec == "string") { return {name: spec} }
+    else { return spec || {name: "null"} }
+  }
+
+  // Given a mode spec (anything that resolveMode accepts), find and
+  // initialize an actual mode object.
+  function getMode(options, spec) {
+    spec = resolveMode(spec);
+    var mfactory = modes[spec.name];
+    if (!mfactory) { return getMode(options, "text/plain") }
+    var modeObj = mfactory(options, spec);
+    if (modeExtensions.hasOwnProperty(spec.name)) {
+      var exts = modeExtensions[spec.name];
+      for (var prop in exts) {
+        if (!exts.hasOwnProperty(prop)) { continue }
+        if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
+        modeObj[prop] = exts[prop];
+      }
+    }
+    modeObj.name = spec.name;
+    if (spec.helperType) { modeObj.helperType = spec.helperType; }
+    if (spec.modeProps) { for (var prop$1 in spec.modeProps)
+      { modeObj[prop$1] = spec.modeProps[prop$1]; } }
+
+    return modeObj
+  }
+
+  // This can be used to attach properties to mode objects from
+  // outside the actual mode definition.
+  var modeExtensions = {};
+  function extendMode(mode, properties) {
+    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+    copyObj(properties, exts);
+  }
+
+  function copyState(mode, state) {
+    if (state === true) { return state }
+    if (mode.copyState) { return mode.copyState(state) }
+    var nstate = {};
+    for (var n in state) {
+      var val = state[n];
+      if (val instanceof Array) { val = val.concat([]); }
+      nstate[n] = val;
+    }
+    return nstate
+  }
+
+  // Given a mode and a state (for that mode), find the inner mode and
+  // state at the position that the state refers to.
+  function innerMode(mode, state) {
+    var info;
+    while (mode.innerMode) {
+      info = mode.innerMode(state);
+      if (!info || info.mode == mode) { break }
+      state = info.state;
+      mode = info.mode;
+    }
+    return info || {mode: mode, state: state}
+  }
+
+  function startState(mode, a1, a2) {
+    return mode.startState ? mode.startState(a1, a2) : true
+  }
+
+  // STRING STREAM
+
+  // Fed to the mode parsers, provides helper functions to make
+  // parsers more succinct.
+
+  var StringStream = function(string, tabSize, lineOracle) {
+    this.pos = this.start = 0;
+    this.string = string;
+    this.tabSize = tabSize || 8;
+    this.lastColumnPos = this.lastColumnValue = 0;
+    this.lineStart = 0;
+    this.lineOracle = lineOracle;
+  };
+
+  StringStream.prototype.eol = function () {return this.pos >= this.string.length};
+  StringStream.prototype.sol = function () {return this.pos == this.lineStart};
+  StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
+  StringStream.prototype.next = function () {
+    if (this.pos < this.string.length)
+      { return this.string.charAt(this.pos++) }
+  };
+  StringStream.prototype.eat = function (match) {
+    var ch = this.string.charAt(this.pos);
+    var ok;
+    if (typeof match == "string") { ok = ch == match; }
+    else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
+    if (ok) {++this.pos; return ch}
+  };
+  StringStream.prototype.eatWhile = function (match) {
+    var start = this.pos;
+    while (this.eat(match)){}
+    return this.pos > start
+  };
+  StringStream.prototype.eatSpace = function () {
+      var this$1 = this;
+
+    var start = this.pos;
+    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
+    return this.pos > start
+  };
+  StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
+  StringStream.prototype.skipTo = function (ch) {
+    var found = this.string.indexOf(ch, this.pos);
+    if (found > -1) {this.pos = found; return true}
+  };
+  StringStream.prototype.backUp = function (n) {this.pos -= n;};
+  StringStream.prototype.column = function () {
+    if (this.lastColumnPos < this.start) {
+      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
+      this.lastColumnPos = this.start;
+    }
+    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+  };
+  StringStream.prototype.indentation = function () {
+    return countColumn(this.string, null, this.tabSize) -
+      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+  };
+  StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
+    if (typeof pattern == "string") {
+      var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
+      var substr = this.string.substr(this.pos, pattern.length);
+      if (cased(substr) == cased(pattern)) {
+        if (consume !== false) { this.pos += pattern.length; }
+        return true
+      }
+    } else {
+      var match = this.string.slice(this.pos).match(pattern);
+      if (match && match.index > 0) { return null }
+      if (match && consume !== false) { this.pos += match[0].length; }
+      return match
+    }
+  };
+  StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
+  StringStream.prototype.hideFirstChars = function (n, inner) {
+    this.lineStart += n;
+    try { return inner() }
+    finally { this.lineStart -= n; }
+  };
+  StringStream.prototype.lookAhead = function (n) {
+    var oracle = this.lineOracle;
+    return oracle && oracle.lookAhead(n)
+  };
+  StringStream.prototype.baseToken = function () {
+    var oracle = this.lineOracle;
+    return oracle && oracle.baseToken(this.pos)
+  };
+
+  // Find the line object corresponding to the given line number.
+  function getLine(doc, n) {
+    n -= doc.first;
+    if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
+    var chunk = doc;
+    while (!chunk.lines) {
+      for (var i = 0;; ++i) {
+        var child = chunk.children[i], sz = child.chunkSize();
+        if (n < sz) { chunk = child; break }
+        n -= sz;
+      }
+    }
+    return chunk.lines[n]
+  }
+
+  // Get the part of a document between two positions, as an array of
+  // strings.
+  function getBetween(doc, start, end) {
+    var out = [], n = start.line;
+    doc.iter(start.line, end.line + 1, function (line) {
+      var text = line.text;
+      if (n == end.line) { text = text.slice(0, end.ch); }
+      if (n == start.line) { text = text.slice(start.ch); }
+      out.push(text);
+      ++n;
+    });
+    return out
+  }
+  // Get the lines between from and to, as array of strings.
+  function getLines(doc, from, to) {
+    var out = [];
+    doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
+    return out
+  }
+
+  // Update the height of a line, propagating the height change
+  // upwards to parent nodes.
+  function updateLineHeight(line, height) {
+    var diff = height - line.height;
+    if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
+  }
+
+  // Given a line object, find its line number by walking up through
+  // its parent links.
+  function lineNo(line) {
+    if (line.parent == null) { return null }
+    var cur = line.parent, no = indexOf(cur.lines, line);
+    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
+      for (var i = 0;; ++i) {
+        if (chunk.children[i] == cur) { break }
+        no += chunk.children[i].chunkSize();
+      }
+    }
+    return no + cur.first
+  }
+
+  // Find the line at the given vertical position, using the height
+  // information in the document tree.
+  function lineAtHeight(chunk, h) {
+    var n = chunk.first;
+    outer: do {
+      for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
+        var child = chunk.children[i$1], ch = child.height;
+        if (h < ch) { chunk = child; continue outer }
+        h -= ch;
+        n += child.chunkSize();
+      }
+      return n
+    } while (!chunk.lines)
+    var i = 0;
+    for (; i < chunk.lines.length; ++i) {
+      var line = chunk.lines[i], lh = line.height;
+      if (h < lh) { break }
+      h -= lh;
+    }
+    return n + i
+  }
+
+  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
+
+  function lineNumberFor(options, i) {
+    return String(options.lineNumberFormatter(i + options.firstLineNumber))
+  }
+
+  // A Pos instance represents a position within the text.
+  function Pos(line, ch, sticky) {
+    if ( sticky === void 0 ) sticky = null;
+
+    if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
+    this.line = line;
+    this.ch = ch;
+    this.sticky = sticky;
+  }
+
+  // Compare two positions, return 0 if they are the same, a negative
+  // number when a is less, and a positive number otherwise.
+  function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
+
+  function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
+
+  function copyPos(x) {return Pos(x.line, x.ch)}
+  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
+  function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
+
+  // Most of the external API clips given positions to make sure they
+  // actually exist within the document.
+  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
+  function clipPos(doc, pos) {
+    if (pos.line < doc.first) { return Pos(doc.first, 0) }
+    var last = doc.first + doc.size - 1;
+    if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
+    return clipToLen(pos, getLine(doc, pos.line).text.length)
+  }
+  function clipToLen(pos, linelen) {
+    var ch = pos.ch;
+    if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
+    else if (ch < 0) { return Pos(pos.line, 0) }
+    else { return pos }
+  }
+  function clipPosArray(doc, array) {
+    var out = [];
+    for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
+    return out
+  }
+
+  var SavedContext = function(state, lookAhead) {
+    this.state = state;
+    this.lookAhead = lookAhead;
+  };
+
+  var Context = function(doc, state, line, lookAhead) {
+    this.state = state;
+    this.doc = doc;
+    this.line = line;
+    this.maxLookAhead = lookAhead || 0;
+    this.baseTokens = null;
+    this.baseTokenPos = 1;
+  };
+
+  Context.prototype.lookAhead = function (n) {
+    var line = this.doc.getLine(this.line + n);
+    if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
+    return line
+  };
+
+  Context.prototype.baseToken = function (n) {
+      var this$1 = this;
+
+    if (!this.baseTokens) { return null }
+    while (this.baseTokens[this.baseTokenPos] <= n)
+      { this$1.baseTokenPos += 2; }
+    var type = this.baseTokens[this.baseTokenPos + 1];
+    return {type: type && type.replace(/( |^)overlay .*/, ""),
+            size: this.baseTokens[this.baseTokenPos] - n}
+  };
+
+  Context.prototype.nextLine = function () {
+    this.line++;
+    if (this.maxLookAhead > 0) { this.maxLookAhead--; }
+  };
+
+  Context.fromSaved = function (doc, saved, line) {
+    if (saved instanceof SavedContext)
+      { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
+    else
+      { return new Context(doc, copyState(doc.mode, saved), line) }
+  };
+
+  Context.prototype.save = function (copy) {
+    var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
+    return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
+  };
+
+
+  // Compute a style array (an array starting with a mode generation
+  // -- for invalidation -- followed by pairs of end positions and
+  // style strings), which is used to highlight the tokens on the
+  // line.
+  function highlightLine(cm, line, context, forceToEnd) {
+    // A styles array always starts with a number identifying the
+    // mode/overlays that it is based on (for easy invalidation).
+    var st = [cm.state.modeGen], lineClasses = {};
+    // Compute the base array of styles
+    runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
+            lineClasses, forceToEnd);
+    var state = context.state;
+
+    // Run overlays, adjust style array.
+    var loop = function ( o ) {
+      context.baseTokens = st;
+      var overlay = cm.state.overlays[o], i = 1, at = 0;
+      context.state = true;
+      runMode(cm, line.text, overlay.mode, context, function (end, style) {
+        var start = i;
+        // Ensure there's a token end at the current position, and that i points at it
+        while (at < end) {
+          var i_end = st[i];
+          if (i_end > end)
+            { st.splice(i, 1, end, st[i+1], i_end); }
+          i += 2;
+          at = Math.min(end, i_end);
+        }
+        if (!style) { return }
+        if (overlay.opaque) {
+          st.splice(start, i - start, end, "overlay " + style);
+          i = start + 2;
+        } else {
+          for (; start < i; start += 2) {
+            var cur = st[start+1];
+            st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
+          }
+        }
+      }, lineClasses);
+      context.state = state;
+      context.baseTokens = null;
+      context.baseTokenPos = 1;
+    };
+
+    for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
+
+    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
+  }
+
+  function getLineStyles(cm, line, updateFrontier) {
+    if (!line.styles || line.styles[0] != cm.state.modeGen) {
+      var context = getContextBefore(cm, lineNo(line));
+      var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
+      var result = highlightLine(cm, line, context);
+      if (resetState) { context.state = resetState; }
+      line.stateAfter = context.save(!resetState);
+      line.styles = result.styles;
+      if (result.classes) { line.styleClasses = result.classes; }
+      else if (line.styleClasses) { line.styleClasses = null; }
+      if (updateFrontier === cm.doc.highlightFrontier)
+        { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
+    }
+    return line.styles
+  }
+
+  function getContextBefore(cm, n, precise) {
+    var doc = cm.doc, display = cm.display;
+    if (!doc.mode.startState) { return new Context(doc, true, n) }
+    var start = findStartLine(cm, n, precise);
+    var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
+    var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
+
+    doc.iter(start, n, function (line) {
+      processLine(cm, line.text, context);
+      var pos = context.line;
+      line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
+      context.nextLine();
+    });
+    if (precise) { doc.modeFrontier = context.line; }
+    return context
+  }
+
+  // Lightweight form of highlight -- proceed over this line and
+  // update state, but don't save a style array. Used for lines that
+  // aren't currently visible.
+  function processLine(cm, text, context, startAt) {
+    var mode = cm.doc.mode;
+    var stream = new StringStream(text, cm.options.tabSize, context);
+    stream.start = stream.pos = startAt || 0;
+    if (text == "") { callBlankLine(mode, context.state); }
+    while (!stream.eol()) {
+      readToken(mode, stream, context.state);
+      stream.start = stream.pos;
+    }
+  }
+
+  function callBlankLine(mode, state) {
+    if (mode.blankLine) { return mode.blankLine(state) }
+    if (!mode.innerMode) { return }
+    var inner = innerMode(mode, state);
+    if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
+  }
+
+  function readToken(mode, stream, state, inner) {
+    for (var i = 0; i < 10; i++) {
+      if (inner) { inner[0] = innerMode(mode, state).mode; }
+      var style = mode.token(stream, state);
+      if (stream.pos > stream.start) { return style }
+    }
+    throw new Error("Mode " + mode.name + " failed to advance stream.")
+  }
+
+  var Token = function(stream, type, state) {
+    this.start = stream.start; this.end = stream.pos;
+    this.string = stream.current();
+    this.type = type || null;
+    this.state = state;
+  };
+
+  // Utility for getTokenAt and getLineTokens
+  function takeToken(cm, pos, precise, asArray) {
+    var doc = cm.doc, mode = doc.mode, style;
+    pos = clipPos(doc, pos);
+    var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
+    var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
+    if (asArray) { tokens = []; }
+    while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
+      stream.start = stream.pos;
+      style = readToken(mode, stream, context.state);
+      if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
+    }
+    return asArray ? tokens : new Token(stream, style, context.state)
+  }
+
+  function extractLineClasses(type, output) {
+    if (type) { for (;;) {
+      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
+      if (!lineClass) { break }
+      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
+      var prop = lineClass[1] ? "bgClass" : "textClass";
+      if (output[prop] == null)
+        { output[prop] = lineClass[2]; }
+      else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
+        { output[prop] += " " + lineClass[2]; }
+    } }
+    return type
+  }
+
+  // Run the given mode's parser over a line, calling f for each token.
+  function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
+    var flattenSpans = mode.flattenSpans;
+    if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
+    var curStart = 0, curStyle = null;
+    var stream = new StringStream(text, cm.options.tabSize, context), style;
+    var inner = cm.options.addModeClass && [null];
+    if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
+    while (!stream.eol()) {
+      if (stream.pos > cm.options.maxHighlightLength) {
+        flattenSpans = false;
+        if (forceToEnd) { processLine(cm, text, context, stream.pos); }
+        stream.pos = text.length;
+        style = null;
+      } else {
+        style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
+      }
+      if (inner) {
+        var mName = inner[0].name;
+        if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
+      }
+      if (!flattenSpans || curStyle != style) {
+        while (curStart < stream.start) {
+          curStart = Math.min(stream.start, curStart + 5000);
+          f(curStart, curStyle);
+        }
+        curStyle = style;
+      }
+      stream.start = stream.pos;
+    }
+    while (curStart < stream.pos) {
+      // Webkit seems to refuse to render text nodes longer than 57444
+      // characters, and returns inaccurate measurements in nodes
+      // starting around 5000 chars.
+      var pos = Math.min(stream.pos, curStart + 5000);
+      f(pos, curStyle);
+      curStart = pos;
+    }
+  }
+
+  // Finds the line to start with when starting a parse. Tries to
+  // find a line with a stateAfter, so that it can start with a
+  // valid state. If that fails, it returns the line with the
+  // smallest indentation, which tends to need the least context to
+  // parse correctly.
+  function findStartLine(cm, n, precise) {
+    var minindent, minline, doc = cm.doc;
+    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
+    for (var search = n; search > lim; --search) {
+      if (search <= doc.first) { return doc.first }
+      var line = getLine(doc, search - 1), after = line.stateAfter;
+      if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
+        { return search }
+      var indented = countColumn(line.text, null, cm.options.tabSize);
+      if (minline == null || minindent > indented) {
+        minline = search - 1;
+        minindent = indented;
+      }
+    }
+    return minline
+  }
+
+  function retreatFrontier(doc, n) {
+    doc.modeFrontier = Math.min(doc.modeFrontier, n);
+    if (doc.highlightFrontier < n - 10) { return }
+    var start = doc.first;
+    for (var line = n - 1; line > start; line--) {
+      var saved = getLine(doc, line).stateAfter;
+      // change is on 3
+      // state on line 1 looked ahead 2 -- so saw 3
+      // test 1 + 2 < 3 should cover this
+      if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
+        start = line + 1;
+        break
+      }
+    }
+    doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
+  }
+
+  // Optimize some code when these features are not used.
+  var sawReadOnlySpans = false, sawCollapsedSpans = false;
+
+  function seeReadOnlySpans() {
+    sawReadOnlySpans = true;
+  }
+
+  function seeCollapsedSpans() {
+    sawCollapsedSpans = true;
+  }
+
+  // TEXTMARKER SPANS
+
+  function MarkedSpan(marker, from, to) {
+    this.marker = marker;
+    this.from = from; this.to = to;
+  }
+
+  // Search an array of spans for a span matching the given marker.
+  function getMarkedSpanFor(spans, marker) {
+    if (spans) { for (var i = 0; i < spans.length; ++i) {
+      var span = spans[i];
+      if (span.marker == marker) { return span }
+    } }
+  }
+  // Remove a span from an array, returning undefined if no spans are
+  // left (we don't store arrays for lines without spans).
+  function removeMarkedSpan(spans, span) {
+    var r;
+    for (var i = 0; i < spans.length; ++i)
+      { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
+    return r
+  }
+  // Add a span to a line.
+  function addMarkedSpan(line, span) {
+    line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
+    span.marker.attachLine(line);
+  }
+
+  // Used for the algorithm that adjusts markers for a change in the
+  // document. These functions cut an array of spans at a given
+  // character position, returning an array of remaining chunks (or
+  // undefined if nothing remains).
+  function markedSpansBefore(old, startCh, isInsert) {
+    var nw;
+    if (old) { for (var i = 0; i < old.length; ++i) {
+      var span = old[i], marker = span.marker;
+      var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
+      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
+        var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
+        ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
+      }
+    } }
+    return nw
+  }
+  function markedSpansAfter(old, endCh, isInsert) {
+    var nw;
+    if (old) { for (var i = 0; i < old.length; ++i) {
+      var span = old[i], marker = span.marker;
+      var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
+      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
+        var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
+        ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
+                                              span.to == null ? null : span.to - endCh));
+      }
+    } }
+    return nw
+  }
+
+  // Given a change object, compute the new set of marker spans that
+  // cover the line in which the change took place. Removes spans
+  // entirely within the change, reconnects spans belonging to the
+  // same marker that appear on both sides of the change, and cuts off
+  // spans partially within the change. Returns an array of span
+  // arrays with one element for each line in (after) the change.
+  function stretchSpansOverChange(doc, change) {
+    if (change.full) { return null }
+    var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
+    var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
+    if (!oldFirst && !oldLast) { return null }
+
+    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
+    // Get the spans that 'stick out' on both sides
+    var first = markedSpansBefore(oldFirst, startCh, isInsert);
+    var last = markedSpansAfter(oldLast, endCh, isInsert);
+
+    // Next, merge those two ends
+    var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
+    if (first) {
+      // Fix up .to properties of first
+      for (var i = 0; i < first.length; ++i) {
+        var span = first[i];
+        if (span.to == null) {
+          var found = getMarkedSpanFor(last, span.marker);
+          if (!found) { span.to = startCh; }
+          else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
+        }
+      }
+    }
+    if (last) {
+      // Fix up .from in last (or move them into first in case of sameLine)
+      for (var i$1 = 0; i$1 < last.length; ++i$1) {
+        var span$1 = last[i$1];
+        if (span$1.to != null) { span$1.to += offset; }
+        if (span$1.from == null) {
+          var found$1 = getMarkedSpanFor(first, span$1.marker);
+          if (!found$1) {
+            span$1.from = offset;
+            if (sameLine) { (first || (first = [])).push(span$1); }
+          }
+        } else {
+          span$1.from += offset;
+          if (sameLine) { (first || (first = [])).push(span$1); }
+        }
+      }
+    }
+    // Make sure we didn't create any zero-length spans
+    if (first) { first = clearEmptySpans(first); }
+    if (last && last != first) { last = clearEmptySpans(last); }
+
+    var newMarkers = [first];
+    if (!sameLine) {
+      // Fill gap with whole-line-spans
+      var gap = change.text.length - 2, gapMarkers;
+      if (gap > 0 && first)
+        { for (var i$2 = 0; i$2 < first.length; ++i$2)
+          { if (first[i$2].to == null)
+            { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
+      for (var i$3 = 0; i$3 < gap; ++i$3)
+        { newMarkers.push(gapMarkers); }
+      newMarkers.push(last);
+    }
+    return newMarkers
+  }
+
+  // Remove spans that are empty and don't have a clearWhenEmpty
+  // option of false.
+  function clearEmptySpans(spans) {
+    for (var i = 0; i < spans.length; ++i) {
+      var span = spans[i];
+      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
+        { spans.splice(i--, 1); }
+    }
+    if (!spans.length) { return null }
+    return spans
+  }
+
+  // Used to 'clip' out readOnly ranges when making a change.
+  function removeReadOnlyRanges(doc, from, to) {
+    var markers = null;
+    doc.iter(from.line, to.line + 1, function (line) {
+      if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
+        var mark = line.markedSpans[i].marker;
+        if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
+          { (markers || (markers = [])).push(mark); }
+      } }
+    });
+    if (!markers) { return null }
+    var parts = [{from: from, to: to}];
+    for (var i = 0; i < markers.length; ++i) {
+      var mk = markers[i], m = mk.find(0);
+      for (var j = 0; j < parts.length; ++j) {
+        var p = parts[j];
+        if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
+        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
+        if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
+          { newParts.push({from: p.from, to: m.from}); }
+        if (dto > 0 || !mk.inclusiveRight && !dto)
+          { newParts.push({from: m.to, to: p.to}); }
+        parts.splice.apply(parts, newParts);
+        j += newParts.length - 3;
+      }
+    }
+    return parts
+  }
+
+  // Connect or disconnect spans from a line.
+  function detachMarkedSpans(line) {
+    var spans = line.markedSpans;
+    if (!spans) { return }
+    for (var i = 0; i < spans.length; ++i)
+      { spans[i].marker.detachLine(line); }
+    line.markedSpans = null;
+  }
+  function attachMarkedSpans(line, spans) {
+    if (!spans) { return }
+    for (var i = 0; i < spans.length; ++i)
+      { spans[i].marker.attachLine(line); }
+    line.markedSpans = spans;
+  }
+
+  // Helpers used when computing which overlapping collapsed span
+  // counts as the larger one.
+  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
+  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
+
+  // Returns a number indicating which of two overlapping collapsed
+  // spans is larger (and thus includes the other). Falls back to
+  // comparing ids when the spans cover exactly the same range.
+  function compareCollapsedMarkers(a, b) {
+    var lenDiff = a.lines.length - b.lines.length;
+    if (lenDiff != 0) { return lenDiff }
+    var aPos = a.find(), bPos = b.find();
+    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
+    if (fromCmp) { return -fromCmp }
+    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
+    if (toCmp) { return toCmp }
+    return b.id - a.id
+  }
+
+  // Find out whether a line ends or starts in a collapsed span. If
+  // so, return the marker for that span.
+  function collapsedSpanAtSide(line, start) {
+    var sps = sawCollapsedSpans && line.markedSpans, found;
+    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
+      sp = sps[i];
+      if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
+          (!found || compareCollapsedMarkers(found, sp.marker) < 0))
+        { found = sp.marker; }
+    } }
+    return found
+  }
+  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
+  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
+
+  function collapsedSpanAround(line, ch) {
+    var sps = sawCollapsedSpans && line.markedSpans, found;
+    if (sps) { for (var i = 0; i < sps.length; ++i) {
+      var sp = sps[i];
+      if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
+          (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
+    } }
+    return found
+  }
+
+  // Test whether there exists a collapsed span that partially
+  // overlaps (covers the start or end, but not both) of a new span.
+  // Such overlap is not allowed.
+  function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
+    var line = getLine(doc, lineNo$$1);
+    var sps = sawCollapsedSpans && line.markedSpans;
+    if (sps) { for (var i = 0; i < sps.length; ++i) {
+      var sp = sps[i];
+      if (!sp.marker.collapsed) { continue }
+      var found = sp.marker.find(0);
+      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
+      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
+      if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
+      if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
+          fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
+        { return true }
+    } }
+  }
+
+  // A visual line is a line as drawn on the screen. Folding, for
+  // example, can cause multiple logical lines to appear on the same
+  // visual line. This finds the start of the visual line that the
+  // given line is part of (usually that is the line itself).
+  function visualLine(line) {
+    var merged;
+    while (merged = collapsedSpanAtStart(line))
+      { line = merged.find(-1, true).line; }
+    return line
+  }
+
+  function visualLineEnd(line) {
+    var merged;
+    while (merged = collapsedSpanAtEnd(line))
+      { line = merged.find(1, true).line; }
+    return line
+  }
+
+  // Returns an array of logical lines that continue the visual line
+  // started by the argument, or undefined if there are no such lines.
+  function visualLineContinued(line) {
+    var merged, lines;
+    while (merged = collapsedSpanAtEnd(line)) {
+      line = merged.find(1, true).line
+      ;(lines || (lines = [])).push(line);
+    }
+    return lines
+  }
+
+  // Get the line number of the start of the visual line that the
+  // given line number is part of.
+  function visualLineNo(doc, lineN) {
+    var line = getLine(doc, lineN), vis = visualLine(line);
+    if (line == vis) { return lineN }
+    return lineNo(vis)
+  }
+
+  // Get the line number of the start of the next visual line after
+  // the given line.
+  function visualLineEndNo(doc, lineN) {
+    if (lineN > doc.lastLine()) { return lineN }
+    var line = getLine(doc, lineN), merged;
+    if (!lineIsHidden(doc, line)) { return lineN }
+    while (merged = collapsedSpanAtEnd(line))
+      { line = merged.find(1, true).line; }
+    return lineNo(line) + 1
+  }
+
+  // Compute whether a line is hidden. Lines count as hidden when they
+  // are part of a visual line that starts with another line, or when
+  // they are entirely covered by collapsed, non-widget span.
+  function lineIsHidden(doc, line) {
+    var sps = sawCollapsedSpans && line.markedSpans;
+    if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
+      sp = sps[i];
+      if (!sp.marker.collapsed) { continue }
+      if (sp.from == null) { return true }
+      if (sp.marker.widgetNode) { continue }
+      if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
+        { return true }
+    } }
+  }
+  function lineIsHiddenInner(doc, line, span) {
+    if (span.to == null) {
+      var end = span.marker.find(1, true);
+      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
+    }
+    if (span.marker.inclusiveRight && span.to == line.text.length)
+      { return true }
+    for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
+      sp = line.markedSpans[i];
+      if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
+          (sp.to == null || sp.to != span.from) &&
+          (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
+          lineIsHiddenInner(doc, line, sp)) { return true }
+    }
+  }
+
+  // Find the height above the given line.
+  function heightAtLine(lineObj) {
+    lineObj = visualLine(lineObj);
+
+    var h = 0, chunk = lineObj.parent;
+    for (var i = 0; i < chunk.lines.length; ++i) {
+      var line = chunk.lines[i];
+      if (line == lineObj) { break }
+      else { h += line.height; }
+    }
+    for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
+      for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
+        var cur = p.children[i$1];
+        if (cur == chunk) { break }
+        else { h += cur.height; }
+      }
+    }
+    return h
+  }
+
+  // Compute the character length of a line, taking into account
+  // collapsed ranges (see markText) that might hide parts, and join
+  // other lines onto it.
+  function lineLength(line) {
+    if (line.height == 0) { return 0 }
+    var len = line.text.length, merged, cur = line;
+    while (merged = collapsedSpanAtStart(cur)) {
+      var found = merged.find(0, true);
+      cur = found.from.line;
+      len += found.from.ch - found.to.ch;
+    }
+    cur = line;
+    while (merged = collapsedSpanAtEnd(cur)) {
+      var found$1 = merged.find(0, true);
+      len -= cur.text.length - found$1.from.ch;
+      cur = found$1.to.line;
+      len += cur.text.length - found$1.to.ch;
+    }
+    return len
+  }
+
+  // Find the longest line in the document.
+  function findMaxLine(cm) {
+    var d = cm.display, doc = cm.doc;
+    d.maxLine = getLine(doc, doc.first);
+    d.maxLineLength = lineLength(d.maxLine);
+    d.maxLineChanged = true;
+    doc.iter(function (line) {
+      var len = lineLength(line);
+      if (len > d.maxLineLength) {
+        d.maxLineLength = len;
+        d.maxLine = line;
+      }
+    });
+  }
+
+  // LINE DATA STRUCTURE
+
+  // Line objects. These hold state related to a line, including
+  // highlighting info (the styles array).
+  var Line = function(text, markedSpans, estimateHeight) {
+    this.text = text;
+    attachMarkedSpans(this, markedSpans);
+    this.height = estimateHeight ? estimateHeight(this) : 1;
+  };
+
+  Line.prototype.lineNo = function () { return lineNo(this) };
+  eventMixin(Line);
+
+  // Change the content (text, markers) of a line. Automatically
+  // invalidates cached information and tries to re-estimate the
+  // line's height.
+  function updateLine(line, text, markedSpans, estimateHeight) {
+    line.text = text;
+    if (line.stateAfter) { line.stateAfter = null; }
+    if (line.styles) { line.styles = null; }
+    if (line.order != null) { line.order = null; }
+    detachMarkedSpans(line);
+    attachMarkedSpans(line, markedSpans);
+    var estHeight = estimateHeight ? estimateHeight(line) : 1;
+    if (estHeight != line.height) { updateLineHeight(line, estHeight); }
+  }
+
+  // Detach a line from the document tree and its markers.
+  function cleanUpLine(line) {
+    line.parent = null;
+    detachMarkedSpans(line);
+  }
+
+  // Convert a style as returned by a mode (either null, or a string
+  // containing one or more styles) to a CSS style. This is cached,
+  // and also looks for line-wide styles.
+  var styleToClassCache = {}, styleToClassCacheWithMode = {};
+  function interpretTokenStyle(style, options) {
+    if (!style || /^\s*$/.test(style)) { return null }
+    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
+    return cache[style] ||
+      (cache[style] = style.replace(/\S+/g, "cm-$&"))
+  }
+
+  // Render the DOM representation of the text of a line. Also builds
+  // up a 'line map', which points at the DOM nodes that represent
+  // specific stretches of text, and is used by the measuring code.
+  // The returned object contains the DOM node, this map, and
+  // information about line-wide styles that were set by the mode.
+  function buildLineContent(cm, lineView) {
+    // The padding-right forces the element to have a 'border', which
+    // is needed on Webkit to be able to get line-level bounding
+    // rectangles for it (in measureChar).
+    var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
+    var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
+                   col: 0, pos: 0, cm: cm,
+                   trailingSpace: false,
+                   splitSpaces: cm.getOption("lineWrapping")};
+    lineView.measure = {};
+
+    // Iterate over the logical lines that make up this visual line.
+    for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
+      var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
+      builder.pos = 0;
+      builder.addToken = buildToken;
+      // Optionally wire in some hacks into the token-rendering
+      // algorithm, to deal with browser quirks.
+      if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
+        { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
+      builder.map = [];
+      var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
+      insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
+      if (line.styleClasses) {
+        if (line.styleClasses.bgClass)
+          { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
+        if (line.styleClasses.textClass)
+          { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
+      }
+
+      // Ensure at least a single node is present, for measuring.
+      if (builder.map.length == 0)
+        { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
+
+      // Store the map and a cache object for the current logical line
+      if (i == 0) {
+        lineView.measure.map = builder.map;
+        lineView.measure.cache = {};
+      } else {
+  (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
+        ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
+      }
+    }
+
+    // See issue #2901
+    if (webkit) {
+      var last = builder.content.lastChild;
+      if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
+        { builder.content.className = "cm-tab-wrap-hack"; }
+    }
+
+    signal(cm, "renderLine", cm, lineView.line, builder.pre);
+    if (builder.pre.className)
+      { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
+
+    return builder
+  }
+
+  function defaultSpecialCharPlaceholder(ch) {
+    var token = elt("span", "\u2022", "cm-invalidchar");
+    token.title = "\\u" + ch.charCodeAt(0).toString(16);
+    token.setAttribute("aria-label", token.title);
+    return token
+  }
+
+  // Build up the DOM representation for a single token, and add it to
+  // the line map. Takes care to render special characters separately.
+  function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
+    if (!text) { return }
+    var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
+    var special = builder.cm.state.specialChars, mustWrap = false;
+    var content;
+    if (!special.test(text)) {
+      builder.col += text.length;
+      content = document.createTextNode(displayText);
+      builder.map.push(builder.pos, builder.pos + text.length, content);
+      if (ie && ie_version < 9) { mustWrap = true; }
+      builder.pos += text.length;
+    } else {
+      content = document.createDocumentFragment();
+      var pos = 0;
+      while (true) {
+        special.lastIndex = pos;
+        var m = special.exec(text);
+        var skipped = m ? m.index - pos : text.length - pos;
+        if (skipped) {
+          var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
+          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
+          else { content.appendChild(txt); }
+          builder.map.push(builder.pos, builder.pos + skipped, txt);
+          builder.col += skipped;
+          builder.pos += skipped;
+        }
+        if (!m) { break }
+        pos += skipped + 1;
+        var txt$1 = (void 0);
+        if (m[0] == "\t") {
+          var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
+          txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
+          txt$1.setAttribute("role", "presentation");
+          txt$1.setAttribute("cm-text", "\t");
+          builder.col += tabWidth;
+        } else if (m[0] == "\r" || m[0] == "\n") {
+          txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
+          txt$1.setAttribute("cm-text", m[0]);
+          builder.col += 1;
+        } else {
+          txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
+          txt$1.setAttribute("cm-text", m[0]);
+          if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
+          else { content.appendChild(txt$1); }
+          builder.col += 1;
+        }
+        builder.map.push(builder.pos, builder.pos + 1, txt$1);
+        builder.pos++;
+      }
+    }
+    builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
+    if (style || startStyle || endStyle || mustWrap || css) {
+      var fullStyle = style || "";
+      if (startStyle) { fullStyle += startStyle; }
+      if (endStyle) { fullStyle += endStyle; }
+      var token = elt("span", [content], fullStyle, css);
+      if (attributes) {
+        for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
+          { token.setAttribute(attr, attributes[attr]); } }
+      }
+      return builder.content.appendChild(token)
+    }
+    builder.content.appendChild(content);
+  }
+
+  // Change some spaces to NBSP to prevent the browser from collapsing
+  // trailing spaces at the end of a line when rendering text (issue #1362).
+  function splitSpaces(text, trailingBefore) {
+    if (text.length > 1 && !/  /.test(text)) { return text }
+    var spaceBefore = trailingBefore, result = "";
+    for (var i = 0; i < text.length; i++) {
+      var ch = text.charAt(i);
+      if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
+        { ch = "\u00a0"; }
+      result += ch;
+      spaceBefore = ch == " ";
+    }
+    return result
+  }
+
+  // Work around nonsense dimensions being reported for stretches of
+  // right-to-left text.
+  function buildTokenBadBidi(inner, order) {
+    return function (builder, text, style, startStyle, endStyle, css, attributes) {
+      style = style ? style + " cm-force-border" : "cm-force-border";
+      var start = builder.pos, end = start + text.length;
+      for (;;) {
+        // Find the part that overlaps with the start of this text
+        var part = (void 0);
+        for (var i = 0; i < order.length; i++) {
+          part = order[i];
+          if (part.to > start && part.from <= start) { break }
+        }
+        if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
+        inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
+        startStyle = null;
+        text = text.slice(part.to - start);
+        start = part.to;
+      }
+    }
+  }
+
+  function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
+    var widget = !ignoreWidget && marker.widgetNode;
+    if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
+    if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
+      if (!widget)
+        { widget = builder.content.appendChild(document.createElement("span")); }
+      widget.setAttribute("cm-marker", marker.id);
+    }
+    if (widget) {
+      builder.cm.display.input.setUneditable(widget);
+      builder.content.appendChild(widget);
+    }
+    builder.pos += size;
+    builder.trailingSpace = false;
+  }
+
+  // Outputs a number of spans to make up a line, taking highlighting
+  // and marked text into account.
+  function insertLineContent(line, builder, styles) {
+    var spans = line.markedSpans, allText = line.text, at = 0;
+    if (!spans) {
+      for (var i$1 = 1; i$1 < styles.length; i$1+=2)
+        { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
+      return
+    }
+
+    var len = allText.length, pos = 0, i = 1, text = "", style, css;
+    var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
+    for (;;) {
+      if (nextChange == pos) { // Update current marker set
+        spanStyle = spanEndStyle = spanStartStyle = css = "";
+        attributes = null;
+        collapsed = null; nextChange = Infinity;
+        var foundBookmarks = [], endStyles = (void 0);
+        for (var j = 0; j < spans.length; ++j) {
+          var sp = spans[j], m = sp.marker;
+          if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
+            foundBookmarks.push(m);
+          } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
+            if (sp.to != null && sp.to != pos && nextChange > sp.to) {
+              nextChange = sp.to;
+              spanEndStyle = "";
+            }
+            if (m.className) { spanStyle += " " + m.className; }
+            if (m.css) { css = (css ? css + ";" : "") + m.css; }
+            if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
+            if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
+            // support for the old title property
+            // https://github.com/codemirror/CodeMirror/pull/5673
+            if (m.title) { (attributes || (attributes = {})).title = m.title; }
+            if (m.attributes) {
+              for (var attr in m.attributes)
+                { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
+            }
+            if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
+              { collapsed = sp; }
+          } else if (sp.from > pos && nextChange > sp.from) {
+            nextChange = sp.from;
+          }
+        }
+        if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
+          { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
+
+        if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
+          { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
+        if (collapsed && (collapsed.from || 0) == pos) {
+          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
+                             collapsed.marker, collapsed.from == null);
+          if (collapsed.to == null) { return }
+          if (collapsed.to == pos) { collapsed = false; }
+        }
+      }
+      if (pos >= len) { break }
+
+      var upto = Math.min(len, nextChange);
+      while (true) {
+        if (text) {
+          var end = pos + text.length;
+          if (!collapsed) {
+            var tokenText = end > upto ? text.slice(0, upto - pos) : text;
+            builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
+                             spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
+          }
+          if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
+          pos = end;
+          spanStartStyle = "";
+        }
+        text = allText.slice(at, at = styles[i++]);
+        style = interpretTokenStyle(styles[i++], builder.cm.options);
+      }
+    }
+  }
+
+
+  // These objects are used to represent the visible (currently drawn)
+  // part of the document. A LineView may correspond to multiple
+  // logical lines, if those are connected by collapsed ranges.
+  function LineView(doc, line, lineN) {
+    // The starting line
+    this.line = line;
+    // Continuing lines, if any
+    this.rest = visualLineContinued(line);
+    // Number of logical lines in this visual line
+    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
+    this.node = this.text = null;
+    this.hidden = lineIsHidden(doc, line);
+  }
+
+  // Create a range of LineView objects for the given lines.
+  function buildViewArray(cm, from, to) {
+    var array = [], nextPos;
+    for (var pos = from; pos < to; pos = nextPos) {
+      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
+      nextPos = pos + view.size;
+      array.push(view);
+    }
+    return array
+  }
+
+  var operationGroup = null;
+
+  function pushOperation(op) {
+    if (operationGroup) {
+      operationGroup.ops.push(op);
+    } else {
+      op.ownsGroup = operationGroup = {
+        ops: [op],
+        delayedCallbacks: []
+      };
+    }
+  }
+
+  function fireCallbacksForOps(group) {
+    // Calls delayed callbacks and cursorActivity handlers until no
+    // new ones appear
+    var callbacks = group.delayedCallbacks, i = 0;
+    do {
+      for (; i < callbacks.length; i++)
+        { callbacks[i].call(null); }
+      for (var j = 0; j < group.ops.length; j++) {
+        var op = group.ops[j];
+        if (op.cursorActivityHandlers)
+          { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
+            { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
+      }
+    } while (i < callbacks.length)
+  }
+
+  function finishOperation(op, endCb) {
+    var group = op.ownsGroup;
+    if (!group) { return }
+
+    try { fireCallbacksForOps(group); }
+    finally {
+      operationGroup = null;
+      endCb(group);
+    }
+  }
+
+  var orphanDelayedCallbacks = null;
+
+  // Often, we want to signal events at a point where we are in the
+  // middle of some work, but don't want the handler to start calling
+  // other methods on the editor, which might be in an inconsistent
+  // state or simply not expect any other events to happen.
+  // signalLater looks whether there are any handlers, and schedules
+  // them to be executed when the last operation ends, or, if no
+  // operation is active, when a timeout fires.
+  function signalLater(emitter, type /*, values...*/) {
+    var arr = getHandlers(emitter, type);
+    if (!arr.length) { return }
+    var args = Array.prototype.slice.call(arguments, 2), list;
+    if (operationGroup) {
+      list = operationGroup.delayedCallbacks;
+    } else if (orphanDelayedCallbacks) {
+      list = orphanDelayedCallbacks;
+    } else {
+      list = orphanDelayedCallbacks = [];
+      setTimeout(fireOrphanDelayed, 0);
+    }
+    var loop = function ( i ) {
+      list.push(function () { return arr[i].apply(null, args); });
+    };
+
+    for (var i = 0; i < arr.length; ++i)
+      loop( i );
+  }
+
+  function fireOrphanDelayed() {
+    var delayed = orphanDelayedCallbacks;
+    orphanDelayedCallbacks = null;
+    for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
+  }
+
+  // When an aspect of a line changes, a string is added to
+  // lineView.changes. This updates the relevant part of the line's
+  // DOM structure.
+  function updateLineForChanges(cm, lineView, lineN, dims) {
+    for (var j = 0; j < lineView.changes.length; j++) {
+      var type = lineView.changes[j];
+      if (type == "text") { updateLineText(cm, lineView); }
+      else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
+      else if (type == "class") { updateLineClasses(cm, lineView); }
+      else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
+    }
+    lineView.changes = null;
+  }
+
+  // Lines with gutter elements, widgets or a background class need to
+  // be wrapped, and have the extra elements added to the wrapper div
+  function ensureLineWrapped(lineView) {
+    if (lineView.node == lineView.text) {
+      lineView.node = elt("div", null, null, "position: relative");
+      if (lineView.text.parentNode)
+        { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
+      lineView.node.appendChild(lineView.text);
+      if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
+    }
+    return lineView.node
+  }
+
+  function updateLineBackground(cm, lineView) {
+    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
+    if (cls) { cls += " CodeMirror-linebackground"; }
+    if (lineView.background) {
+      if (cls) { lineView.background.className = cls; }
+      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
+    } else if (cls) {
+      var wrap = ensureLineWrapped(lineView);
+      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
+      cm.display.input.setUneditable(lineView.background);
+    }
+  }
+
+  // Wrapper around buildLineContent which will reuse the structure
+  // in display.externalMeasured when possible.
+  function getLineContent(cm, lineView) {
+    var ext = cm.display.externalMeasured;
+    if (ext && ext.line == lineView.line) {
+      cm.display.externalMeasured = null;
+      lineView.measure = ext.measure;
+      return ext.built
+    }
+    return buildLineContent(cm, lineView)
+  }
+
+  // Redraw the line's text. Interacts with the background and text
+  // classes because the mode may output tokens that influence these
+  // classes.
+  function updateLineText(cm, lineView) {
+    var cls = lineView.text.className;
+    var built = getLineContent(cm, lineView);
+    if (lineView.text == lineView.node) { lineView.node = built.pre; }
+    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
+    lineView.text = built.pre;
+    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
+      lineView.bgClass = built.bgClass;
+      lineView.textClass = built.textClass;
+      updateLineClasses(cm, lineView);
+    } else if (cls) {
+      lineView.text.className = cls;
+    }
+  }
+
+  function updateLineClasses(cm, lineView) {
+    updateLineBackground(cm, lineView);
+    if (lineView.line.wrapClass)
+      { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
+    else if (lineView.node != lineView.text)
+      { lineView.node.className = ""; }
+    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
+    lineView.text.className = textClass || "";
+  }
+
+  function updateLineGutter(cm, lineView, lineN, dims) {
+    if (lineView.gutter) {
+      lineView.node.removeChild(lineView.gutter);
+      lineView.gutter = null;
+    }
+    if (lineView.gutterBackground) {
+      lineView.node.removeChild(lineView.gutterBackground);
+      lineView.gutterBackground = null;
+    }
+    if (lineView.line.gutterClass) {
+      var wrap = ensureLineWrapped(lineView);
+      lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
+                                      ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
+      cm.display.input.setUneditable(lineView.gutterBackground);
+      wrap.insertBefore(lineView.gutterBackground, lineView.text);
+    }
+    var markers = lineView.line.gutterMarkers;
+    if (cm.options.lineNumbers || markers) {
+      var wrap$1 = ensureLineWrapped(lineView);
+      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
+      cm.display.input.setUneditable(gutterWrap);
+      wrap$1.insertBefore(gutterWrap, lineView.text);
+      if (lineView.line.gutterClass)
+        { gutterWrap.className += " " + lineView.line.gutterClass; }
+      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
+        { lineView.lineNumber = gutterWrap.appendChild(
+          elt("div", lineNumberFor(cm.options, lineN),
+              "CodeMirror-linenumber CodeMirror-gutter-elt",
+              ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
+      if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
+        var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
+        if (found)
+          { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
+                                     ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
+      } }
+    }
+  }
+
+  function updateLineWidgets(cm, lineView, dims) {
+    if (lineView.alignable) { lineView.alignable = null; }
+    for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
+      next = node.nextSibling;
+      if (node.className == "CodeMirror-linewidget")
+        { lineView.node.removeChild(node); }
+    }
+    insertLineWidgets(cm, lineView, dims);
+  }
+
+  // Build a line's DOM representation from scratch
+  function buildLineElement(cm, lineView, lineN, dims) {
+    var built = getLineContent(cm, lineView);
+    lineView.text = lineView.node = built.pre;
+    if (built.bgClass) { lineView.bgClass = built.bgClass; }
+    if (built.textClass) { lineView.textClass = built.textClass; }
+
+    updateLineClasses(cm, lineView);
+    updateLineGutter(cm, lineView, lineN, dims);
+    insertLineWidgets(cm, lineView, dims);
+    return lineView.node
+  }
+
+  // A lineView may contain multiple logical lines (when merged by
+  // collapsed spans). The widgets for all of them need to be drawn.
+  function insertLineWidgets(cm, lineView, dims) {
+    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
+    if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
+      { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
+  }
+
+  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
+    if (!line.widgets) { return }
+    var wrap = ensureLineWrapped(lineView);
+    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
+      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
+      if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
+      positionLineWidget(widget, node, lineView, dims);
+      cm.display.input.setUneditable(node);
+      if (allowAbove && widget.above)
+        { wrap.insertBefore(node, lineView.gutter || lineView.text); }
+      else
+        { wrap.appendChild(node); }
+      signalLater(widget, "redraw");
+    }
+  }
+
+  function positionLineWidget(widget, node, lineView, dims) {
+    if (widget.noHScroll) {
+  (lineView.alignable || (lineView.alignable = [])).push(node);
+      var width = dims.wrapperWidth;
+      node.style.left = dims.fixedPos + "px";
+      if (!widget.coverGutter) {
+        width -= dims.gutterTotalWidth;
+        node.style.paddingLeft = dims.gutterTotalWidth + "px";
+      }
+      node.style.width = width + "px";
+    }
+    if (widget.coverGutter) {
+      node.style.zIndex = 5;
+      node.style.position = "relative";
+      if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
+    }
+  }
+
+  function widgetHeight(widget) {
+    if (widget.height != null) { return widget.height }
+    var cm = widget.doc.cm;
+    if (!cm) { return 0 }
+    if (!contains(document.body, widget.node)) {
+      var parentStyle = "position: relative;";
+      if (widget.coverGutter)
+        { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
+      if (widget.noHScroll)
+        { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
+      removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
+    }
+    return widget.height = widget.node.parentNode.offsetHeight
+  }
+
+  // Return true when the given mouse event happened in a widget
+  function eventInWidget(display, e) {
+    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
+      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
+          (n.parentNode == display.sizer && n != display.mover))
+        { return true }
+    }
+  }
+
+  // POSITION MEASUREMENT
+
+  function paddingTop(display) {return display.lineSpace.offsetTop}
+  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
+  function paddingH(display) {
+    if (display.cachedPaddingH) { return display.cachedPaddingH }
+    var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
+    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
+    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
+    if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
+    return data
+  }
+
+  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
+  function displayWidth(cm) {
+    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
+  }
+  function displayHeight(cm) {
+    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
+  }
+
+  // Ensure the lineView.wrapping.heights array is populated. This is
+  // an array of bottom offsets for the lines that make up a drawn
+  // line. When lineWrapping is on, there might be more than one
+  // height.
+  function ensureLineHeights(cm, lineView, rect) {
+    var wrapping = cm.options.lineWrapping;
+    var curWidth = wrapping && displayWidth(cm);
+    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
+      var heights = lineView.measure.heights = [];
+      if (wrapping) {
+        lineView.measure.width = curWidth;
+        var rects = lineView.text.firstChild.getClientRects();
+        for (var i = 0; i < rects.length - 1; i++) {
+          var cur = rects[i], next = rects[i + 1];
+          if (Math.abs(cur.bottom - next.bottom) > 2)
+            { heights.push((cur.bottom + next.top) / 2 - rect.top); }
+        }
+      }
+      heights.push(rect.bottom - rect.top);
+    }
+  }
+
+  // Find a line map (mapping character offsets to text nodes) and a
+  // measurement cache for the given line number. (A line view might
+  // contain multiple lines when collapsed ranges are present.)
+  function mapFromLineView(lineView, line, lineN) {
+    if (lineView.line == line)
+      { return {map: lineView.measure.map, cache: lineView.measure.cache} }
+    for (var i = 0; i < lineView.rest.length; i++)
+      { if (lineView.rest[i] == line)
+        { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
+    for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
+      { if (lineNo(lineView.rest[i$1]) > lineN)
+        { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
+  }
+
+  // Render a line into the hidden node display.externalMeasured. Used
+  // when measurement is needed for a line that's not in the viewport.
+  function updateExternalMeasurement(cm, line) {
+    line = visualLine(line);
+    var lineN = lineNo(line);
+    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
+    view.lineN = lineN;
+    var built = view.built = buildLineContent(cm, view);
+    view.text = built.pre;
+    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
+    return view
+  }
+
+  // Get a {top, bottom, left, right} box (in line-local coordinates)
+  // for a given character.
+  function measureChar(cm, line, ch, bias) {
+    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
+  }
+
+  // Find a line view that corresponds to the given line number.
+  function findViewForLine(cm, lineN) {
+    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
+      { return cm.display.view[findViewIndex(cm, lineN)] }
+    var ext = cm.display.externalMeasured;
+    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
+      { return ext }
+  }
+
+  // Measurement can be split in two steps, the set-up work that
+  // applies to the whole line, and the measurement of the actual
+  // character. Functions like coordsChar, that need to do a lot of
+  // measurements in a row, can thus ensure that the set-up work is
+  // only done once.
+  function prepareMeasureForLine(cm, line) {
+    var lineN = lineNo(line);
+    var view = findViewForLine(cm, lineN);
+    if (view && !view.text) {
+      view = null;
+    } else if (view && view.changes) {
+      updateLineForChanges(cm, view, lineN, getDimensions(cm));
+      cm.curOp.forceUpdate = true;
+    }
+    if (!view)
+      { view = updateExternalMeasurement(cm, line); }
+
+    var info = mapFromLineView(view, line, lineN);
+    return {
+      line: line, view: view, rect: null,
+      map: info.map, cache: info.cache, before: info.before,
+      hasHeights: false
+    }
+  }
+
+  // Given a prepared measurement object, measures the position of an
+  // actual character (or fetches it from the cache).
+  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
+    if (prepared.before) { ch = -1; }
+    var key = ch + (bias || ""), found;
+    if (prepared.cache.hasOwnProperty(key)) {
+      found = prepared.cache[key];
+    } else {
+      if (!prepared.rect)
+        { prepared.rect = prepared.view.text.getBoundingClientRect(); }
+      if (!prepared.hasHeights) {
+        ensureLineHeights(cm, prepared.view, prepared.rect);
+        prepared.hasHeights = true;
+      }
+      found = measureCharInner(cm, prepared, ch, bias);
+      if (!found.bogus) { prepared.cache[key] = found; }
+    }
+    return {left: found.left, right: found.right,
+            top: varHeight ? found.rtop : found.top,
+            bottom: varHeight ? found.rbottom : found.bottom}
+  }
+
+  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
+
+  function nodeAndOffsetInLineMap(map$$1, ch, bias) {
+    var node, start, end, collapse, mStart, mEnd;
+    // First, search the line map for the text node corresponding to,
+    // or closest to, the target character.
+    for (var i = 0; i < map$$1.length; i += 3) {
+      mStart = map$$1[i];
+      mEnd = map$$1[i + 1];
+      if (ch < mStart) {
+        start = 0; end = 1;
+        collapse = "left";
+      } else if (ch < mEnd) {
+        start = ch - mStart;
+        end = start + 1;
+      } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
+        end = mEnd - mStart;
+        start = end - 1;
+        if (ch >= mEnd) { collapse = "right"; }
+      }
+      if (start != null) {
+        node = map$$1[i + 2];
+        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
+          { collapse = bias; }
+        if (bias == "left" && start == 0)
+          { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
+            node = map$$1[(i -= 3) + 2];
+            collapse = "left";
+          } }
+        if (bias == "right" && start == mEnd - mStart)
+          { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
+            node = map$$1[(i += 3) + 2];
+            collapse = "right";
+          } }
+        break
+      }
+    }
+    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
+  }
+
+  function getUsefulRect(rects, bias) {
+    var rect = nullRect;
+    if (bias == "left") { for (var i = 0; i < rects.length; i++) {
+      if ((rect = rects[i]).left != rect.right) { break }
+    } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
+      if ((rect = rects[i$1]).left != rect.right) { break }
+    } }
+    return rect
+  }
+
+  function measureCharInner(cm, prepared, ch, bias) {
+    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
+    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
+
+    var rect;
+    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
+      for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
+        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
+        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
+        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
+          { rect = node.parentNode.getBoundingClientRect(); }
+        else
+          { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
+        if (rect.left || rect.right || start == 0) { break }
+        end = start;
+        start = start - 1;
+        collapse = "right";
+      }
+      if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
+    } else { // If it is a widget, simply get the box for the whole widget.
+      if (start > 0) { collapse = bias = "right"; }
+      var rects;
+      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
+        { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
+      else
+        { rect = node.getBoundingClientRect(); }
+    }
+    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
+      var rSpan = node.parentNode.getClientRects()[0];
+      if (rSpan)
+        { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
+      else
+        { rect = nullRect; }
+    }
+
+    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
+    var mid = (rtop + rbot) / 2;
+    var heights = prepared.view.measure.heights;
+    var i = 0;
+    for (; i < heights.length - 1; i++)
+      { if (mid < heights[i]) { break } }
+    var top = i ? heights[i - 1] : 0, bot = heights[i];
+    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
+                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
+                  top: top, bottom: bot};
+    if (!rect.left && !rect.right) { result.bogus = true; }
+    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
+
+    return result
+  }
+
+  // Work around problem with bounding client rects on ranges being
+  // returned incorrectly when zoomed on IE10 and below.
+  function maybeUpdateRectForZooming(measure, rect) {
+    if (!window.screen || screen.logicalXDPI == null ||
+        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
+      { return rect }
+    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
+    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
+    return {left: rect.left * scaleX, right: rect.right * scaleX,
+            top: rect.top * scaleY, bottom: rect.bottom * scaleY}
+  }
+
+  function clearLineMeasurementCacheFor(lineView) {
+    if (lineView.measure) {
+      lineView.measure.cache = {};
+      lineView.measure.heights = null;
+      if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
+        { lineView.measure.caches[i] = {}; } }
+    }
+  }
+
+  function clearLineMeasurementCache(cm) {
+    cm.display.externalMeasure = null;
+    removeChildren(cm.display.lineMeasure);
+    for (var i = 0; i < cm.display.view.length; i++)
+      { clearLineMeasurementCacheFor(cm.display.view[i]); }
+  }
+
+  function clearCaches(cm) {
+    clearLineMeasurementCache(cm);
+    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
+    if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
+    cm.display.lineNumChars = null;
+  }
+
+  function pageScrollX() {
+    // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
+    // which causes page_Offset and bounding client rects to use
+    // different reference viewports and invalidate our calculations.
+    if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
+    return window.pageXOffset || (document.documentElement || document.body).scrollLeft
+  }
+  function pageScrollY() {
+    if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
+    return window.pageYOffset || (document.documentElement || document.body).scrollTop
+  }
+
+  function widgetTopHeight(lineObj) {
+    var height = 0;
+    if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
+      { height += widgetHeight(lineObj.widgets[i]); } } }
+    return height
+  }
+
+  // Converts a {top, bottom, left, right} box from line-local
+  // coordinates into another coordinate system. Context may be one of
+  // "line", "div" (display.lineDiv), "local"./null (editor), "window",
+  // or "page".
+  function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
+    if (!includeWidgets) {
+      var height = widgetTopHeight(lineObj);
+      rect.top += height; rect.bottom += height;
+    }
+    if (context == "line") { return rect }
+    if (!context) { context = "local"; }
+    var yOff = heightAtLine(lineObj);
+    if (context == "local") { yOff += paddingTop(cm.display); }
+    else { yOff -= cm.display.viewOffset; }
+    if (context == "page" || context == "window") {
+      var lOff = cm.display.lineSpace.getBoundingClientRect();
+      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
+      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
+      rect.left += xOff; rect.right += xOff;
+    }
+    rect.top += yOff; rect.bottom += yOff;
+    return rect
+  }
+
+  // Coverts a box from "div" coords to another coordinate system.
+  // Context may be "window", "page", "div", or "local"./null.
+  function fromCoordSystem(cm, coords, context) {
+    if (context == "div") { return coords }
+    var left = coords.left, top = coords.top;
+    // First move into "page" coordinate system
+    if (context == "page") {
+      left -= pageScrollX();
+      top -= pageScrollY();
+    } else if (context == "local" || !context) {
+      var localBox = cm.display.sizer.getBoundingClientRect();
+      left += localBox.left;
+      top += localBox.top;
+    }
+
+    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
+    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
+  }
+
+  function charCoords(cm, pos, context, lineObj, bias) {
+    if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
+    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
+  }
+
+  // Returns a box for a given cursor position, which may have an
+  // 'other' property containing the position of the secondary cursor
+  // on a bidi boundary.
+  // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
+  // and after `char - 1` in writing order of `char - 1`
+  // A cursor Pos(line, char, "after") is on the same visual line as `char`
+  // and before `char` in writing order of `char`
+  // Examples (upper-case letters are RTL, lower-case are LTR):
+  //     Pos(0, 1, ...)
+  //     before   after
+  // ab     a|b     a|b
+  // aB     a|B     aB|
+  // Ab     |Ab     A|b
+  // AB     B|A     B|A
+  // Every position after the last character on a line is considered to stick
+  // to the last character on the line.
+  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
+    lineObj = lineObj || getLine(cm.doc, pos.line);
+    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
+    function get(ch, right) {
+      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
+      if (right) { m.left = m.right; } else { m.right = m.left; }
+      return intoCoordSystem(cm, lineObj, m, context)
+    }
+    var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
+    if (ch >= lineObj.text.length) {
+      ch = lineObj.text.length;
+      sticky = "before";
+    } else if (ch <= 0) {
+      ch = 0;
+      sticky = "after";
+    }
+    if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
+
+    function getBidi(ch, partPos, invert) {
+      var part = order[partPos], right = part.level == 1;
+      return get(invert ? ch - 1 : ch, right != invert)
+    }
+    var partPos = getBidiPartAt(order, ch, sticky);
+    var other = bidiOther;
+    var val = getBidi(ch, partPos, sticky == "before");
+    if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
+    return val
+  }
+
+  // Used to cheaply estimate the coordinates for a position. Used for
+  // intermediate scroll updates.
+  function estimateCoords(cm, pos) {
+    var left = 0;
+    pos = clipPos(cm.doc, pos);
+    if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
+    var lineObj = getLine(cm.doc, pos.line);
+    var top = heightAtLine(lineObj) + paddingTop(cm.display);
+    return {left: left, right: left, top: top, bottom: top + lineObj.height}
+  }
+
+  // Positions returned by coordsChar contain some extra information.
+  // xRel is the relative x position of the input coordinates compared
+  // to the found position (so xRel > 0 means the coordinates are to
+  // the right of the character position, for example). When outside
+  // is true, that means the coordinates lie outside the line's
+  // vertical range.
+  function PosWithInfo(line, ch, sticky, outside, xRel) {
+    var pos = Pos(line, ch, sticky);
+    pos.xRel = xRel;
+    if (outside) { pos.outside = outside; }
+    return pos
+  }
+
+  // Compute the character position closest to the given coordinates.
+  // Input must be lineSpace-local ("div" coordinate system).
+  function coordsChar(cm, x, y) {
+    var doc = cm.doc;
+    y += cm.display.viewOffset;
+    if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
+    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
+    if (lineN > last)
+      { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
+    if (x < 0) { x = 0; }
+
+    var lineObj = getLine(doc, lineN);
+    for (;;) {
+      var found = coordsCharInner(cm, lineObj, lineN, x, y);
+      var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
+      if (!collapsed) { return found }
+      var rangeEnd = collapsed.find(1);
+      if (rangeEnd.line == lineN) { return rangeEnd }
+      lineObj = getLine(doc, lineN = rangeEnd.line);
+    }
+  }
+
+  function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
+    y -= widgetTopHeight(lineObj);
+    var end = lineObj.text.length;
+    var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
+    end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
+    return {begin: begin, end: end}
+  }
+
+  function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
+    if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
+    var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
+    return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
+  }
+
+  // Returns true if the given side of a box is after the given
+  // coordinates, in top-to-bottom, left-to-right order.
+  function boxIsAfter(box, x, y, left) {
+    return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
+  }
+
+  function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
+    // Move y into line-local coordinate space
+    y -= heightAtLine(lineObj);
+    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
+    // When directly calling `measureCharPrepared`, we have to adjust
+    // for the widgets at this line.
+    var widgetHeight$$1 = widgetTopHeight(lineObj);
+    var begin = 0, end = lineObj.text.length, ltr = true;
+
+    var order = getOrder(lineObj, cm.doc.direction);
+    // If the line isn't plain left-to-right text, first figure out
+    // which bidi section the coordinates fall into.
+    if (order) {
+      var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
+                   (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
+      ltr = part.level != 1;
+      // The awkward -1 offsets are needed because findFirst (called
+      // on these below) will treat its first bound as inclusive,
+      // second as exclusive, but we want to actually address the
+      // characters in the part's range
+      begin = ltr ? part.from : part.to - 1;
+      end = ltr ? part.to : part.from - 1;
+    }
+
+    // A binary search to find the first character whose bounding box
+    // starts after the coordinates. If we run across any whose box wrap
+    // the coordinates, store that.
+    var chAround = null, boxAround = null;
+    var ch = findFirst(function (ch) {
+      var box = measureCharPrepared(cm, preparedMeasure, ch);
+      box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
+      if (!boxIsAfter(box, x, y, false)) { return false }
+      if (box.top <= y && box.left <= x) {
+        chAround = ch;
+        boxAround = box;
+      }
+      return true
+    }, begin, end);
+
+    var baseX, sticky, outside = false;
+    // If a box around the coordinates was found, use that
+    if (boxAround) {
+      // Distinguish coordinates nearer to the left or right side of the box
+      var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
+      ch = chAround + (atStart ? 0 : 1);
+      sticky = atStart ? "after" : "before";
+      baseX = atLeft ? boxAround.left : boxAround.right;
+    } else {
+      // (Adjust for extended bound, if necessary.)
+      if (!ltr && (ch == end || ch == begin)) { ch++; }
+      // To determine which side to associate with, get the box to the
+      // left of the character and compare it's vertical position to the
+      // coordinates
+      sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
+        (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
+        "after" : "before";
+      // Now get accurate coordinates for this place, in order to get a
+      // base X position
+      var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
+      baseX = coords.left;
+      outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
+    }
+
+    ch = skipExtendingChars(lineObj.text, ch, 1);
+    return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
+  }
+
+  function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
+    // Bidi parts are sorted left-to-right, and in a non-line-wrapping
+    // situation, we can take this ordering to correspond to the visual
+    // ordering. This finds the first part whose end is after the given
+    // coordinates.
+    var index = findFirst(function (i) {
+      var part = order[i], ltr = part.level != 1;
+      return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
+                                     "line", lineObj, preparedMeasure), x, y, true)
+    }, 0, order.length - 1);
+    var part = order[index];
+    // If this isn't the first part, the part's start is also after
+    // the coordinates, and the coordinates aren't on the same line as
+    // that start, move one part back.
+    if (index > 0) {
+      var ltr = part.level != 1;
+      var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
+                               "line", lineObj, preparedMeasure);
+      if (boxIsAfter(start, x, y, true) && start.top > y)
+        { part = order[index - 1]; }
+    }
+    return part
+  }
+
+  function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
+    // In a wrapped line, rtl text on wrapping boundaries can do things
+    // that don't correspond to the ordering in our `order` array at
+    // all, so a binary search doesn't work, and we want to return a
+    // part that only spans one line so that the binary search in
+    // coordsCharInner is safe. As such, we first find the extent of the
+    // wrapped line, and then do a flat search in which we discard any
+    // spans that aren't on the line.
+    var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
+    var begin = ref.begin;
+    var end = ref.end;
+    if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
+    var part = null, closestDist = null;
+    for (var i = 0; i < order.length; i++) {
+      var p = order[i];
+      if (p.from >= end || p.to <= begin) { continue }
+      var ltr = p.level != 1;
+      var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
+      // Weigh against spans ending before this, so that they are only
+      // picked if nothing ends after
+      var dist = endX < x ? x - endX + 1e9 : endX - x;
+      if (!part || closestDist > dist) {
+        part = p;
+        closestDist = dist;
+      }
+    }
+    if (!part) { part = order[order.length - 1]; }
+    // Clip the part to the wrapped line.
+    if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
+    if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
+    return part
+  }
+
+  var measureText;
+  // Compute the default text height.
+  function textHeight(display) {
+    if (display.cachedTextHeight != null) { return display.cachedTextHeight }
+    if (measureText == null) {
+      measureText = elt("pre", null, "CodeMirror-line-like");
+      // Measure a bunch of lines, for browsers that compute
+      // fractional heights.
+      for (var i = 0; i < 49; ++i) {
+        measureText.appendChild(document.createTextNode("x"));
+        measureText.appendChild(elt("br"));
+      }
+      measureText.appendChild(document.createTextNode("x"));
+    }
+    removeChildrenAndAdd(display.measure, measureText);
+    var height = measureText.offsetHeight / 50;
+    if (height > 3) { display.cachedTextHeight = height; }
+    removeChildren(display.measure);
+    return height || 1
+  }
+
+  // Compute the default character width.
+  function charWidth(display) {
+    if (display.cachedCharWidth != null) { return display.cachedCharWidth }
+    var anchor = elt("span", "xxxxxxxxxx");
+    var pre = elt("pre", [anchor], "CodeMirror-line-like");
+    removeChildrenAndAdd(display.measure, pre);
+    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
+    if (width > 2) { display.cachedCharWidth = width; }
+    return width || 10
+  }
+
+  // Do a bulk-read of the DOM positions and sizes needed to draw the
+  // view, so that we don't interleave reading and writing to the DOM.
+  function getDimensions(cm) {
+    var d = cm.display, left = {}, width = {};
+    var gutterLeft = d.gutters.clientLeft;
+    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
+      var id = cm.display.gutterSpecs[i].className;
+      left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
+      width[id] = n.clientWidth;
+    }
+    return {fixedPos: compensateForHScroll(d),
+            gutterTotalWidth: d.gutters.offsetWidth,
+            gutterLeft: left,
+            gutterWidth: width,
+            wrapperWidth: d.wrapper.clientWidth}
+  }
+
+  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
+  // but using getBoundingClientRect to get a sub-pixel-accurate
+  // result.
+  function compensateForHScroll(display) {
+    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
+  }
+
+  // Returns a function that estimates the height of a line, to use as
+  // first approximation until the line becomes visible (and is thus
+  // properly measurable).
+  function estimateHeight(cm) {
+    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
+    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
+    return function (line) {
+      if (lineIsHidden(cm.doc, line)) { return 0 }
+
+      var widgetsHeight = 0;
+      if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
+        if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
+      } }
+
+      if (wrapping)
+        { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
+      else
+        { return widgetsHeight + th }
+    }
+  }
+
+  function estimateLineHeights(cm) {
+    var doc = cm.doc, est = estimateHeight(cm);
+    doc.iter(function (line) {
+      var estHeight = est(line);
+      if (estHeight != line.height) { updateLineHeight(line, estHeight); }
+    });
+  }
+
+  // Given a mouse event, find the corresponding position. If liberal
+  // is false, it checks whether a gutter or scrollbar was clicked,
+  // and returns null if it was. forRect is used by rectangular
+  // selections, and tries to estimate a character position even for
+  // coordinates beyond the right of the text.
+  function posFromMouse(cm, e, liberal, forRect) {
+    var display = cm.display;
+    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
+
+    var x, y, space = display.lineSpace.getBoundingClientRect();
+    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
+    try { x = e.clientX - space.left; y = e.clientY - space.top; }
+    catch (e) { return null }
+    var coords = coordsChar(cm, x, y), line;
+    if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
+      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
+      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
+    }
+    return coords
+  }
+
+  // Find the view element corresponding to a given line. Return null
+  // when the line isn't visible.
+  function findViewIndex(cm, n) {
+    if (n >= cm.display.viewTo) { return null }
+    n -= cm.display.viewFrom;
+    if (n < 0) { return null }
+    var view = cm.display.view;
+    for (var i = 0; i < view.length; i++) {
+      n -= view[i].size;
+      if (n < 0) { return i }
+    }
+  }
+
+  // Updates the display.view data structure for a given change to the
+  // document. From and to are in pre-change coordinates. Lendiff is
+  // the amount of lines added or subtracted by the change. This is
+  // used for changes that span multiple lines, or change the way
+  // lines are divided into visual lines. regLineChange (below)
+  // registers single-line changes.
+  function regChange(cm, from, to, lendiff) {
+    if (from == null) { from = cm.doc.first; }
+    if (to == null) { to = cm.doc.first + cm.doc.size; }
+    if (!lendiff) { lendiff = 0; }
+
+    var display = cm.display;
+    if (lendiff && to < display.viewTo &&
+        (display.updateLineNumbers == null || display.updateLineNumbers > from))
+      { display.updateLineNumbers = from; }
+
+    cm.curOp.viewChanged = true;
+
+    if (from >= display.viewTo) { // Change after
+      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
+        { resetView(cm); }
+    } else if (to <= display.viewFrom) { // Change before
+      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
+        resetView(cm);
+      } else {
+        display.viewFrom += lendiff;
+        display.viewTo += lendiff;
+      }
+    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
+      resetView(cm);
+    } else if (from <= display.viewFrom) { // Top overlap
+      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
+      if (cut) {
+        display.view = display.view.slice(cut.index);
+        display.viewFrom = cut.lineN;
+        display.viewTo += lendiff;
+      } else {
+        resetView(cm);
+      }
+    } else if (to >= display.viewTo) { // Bottom overlap
+      var cut$1 = viewCuttingPoint(cm, from, from, -1);
+      if (cut$1) {
+        display.view = display.view.slice(0, cut$1.index);
+        display.viewTo = cut$1.lineN;
+      } else {
+        resetView(cm);
+      }
+    } else { // Gap in the middle
+      var cutTop = viewCuttingPoint(cm, from, from, -1);
+      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
+      if (cutTop && cutBot) {
+        display.view = display.view.slice(0, cutTop.index)
+          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
+          .concat(display.view.slice(cutBot.index));
+        display.viewTo += lendiff;
+      } else {
+        resetView(cm);
+      }
+    }
+
+    var ext = display.externalMeasured;
+    if (ext) {
+      if (to < ext.lineN)
+        { ext.lineN += lendiff; }
+      else if (from < ext.lineN + ext.size)
+        { display.externalMeasured = null; }
+    }
+  }
+
+  // Register a change to a single line. Type must be one of "text",
+  // "gutter", "class", "widget"
+  function regLineChange(cm, line, type) {
+    cm.curOp.viewChanged = true;
+    var display = cm.display, ext = cm.display.externalMeasured;
+    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
+      { display.externalMeasured = null; }
+
+    if (line < display.viewFrom || line >= display.viewTo) { return }
+    var lineView = display.view[findViewIndex(cm, line)];
+    if (lineView.node == null) { return }
+    var arr = lineView.changes || (lineView.changes = []);
+    if (indexOf(arr, type) == -1) { arr.push(type); }
+  }
+
+  // Clear the view.
+  function resetView(cm) {
+    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
+    cm.display.view = [];
+    cm.display.viewOffset = 0;
+  }
+
+  function viewCuttingPoint(cm, oldN, newN, dir) {
+    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
+    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
+      { return {index: index, lineN: newN} }
+    var n = cm.display.viewFrom;
+    for (var i = 0; i < index; i++)
+      { n += view[i].size; }
+    if (n != oldN) {
+      if (dir > 0) {
+        if (index == view.length - 1) { return null }
+        diff = (n + view[index].size) - oldN;
+        index++;
+      } else {
+        diff = n - oldN;
+      }
+      oldN += diff; newN += diff;
+    }
+    while (visualLineNo(cm.doc, newN) != newN) {
+      if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
+      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
+      index += dir;
+    }
+    return {index: index, lineN: newN}
+  }
+
+  // Force the view to cover a given range, adding empty view element
+  // or clipping off existing ones as needed.
+  function adjustView(cm, from, to) {
+    var display = cm.display, view = display.view;
+    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
+      display.view = buildViewArray(cm, from, to);
+      display.viewFrom = from;
+    } else {
+      if (display.viewFrom > from)
+        { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
+      else if (display.viewFrom < from)
+        { display.view = display.view.slice(findViewIndex(cm, from)); }
+      display.viewFrom = from;
+      if (display.viewTo < to)
+        { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
+      else if (display.viewTo > to)
+        { display.view = display.view.slice(0, findViewIndex(cm, to)); }
+    }
+    display.viewTo = to;
+  }
+
+  // Count the number of lines in the view whose DOM representation is
+  // out of date (or nonexistent).
+  function countDirtyView(cm) {
+    var view = cm.display.view, dirty = 0;
+    for (var i = 0; i < view.length; i++) {
+      var lineView = view[i];
+      if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
+    }
+    return dirty
+  }
+
+  function updateSelection(cm) {
+    cm.display.input.showSelection(cm.display.input.prepareSelection());
+  }
+
+  function prepareSelection(cm, primary) {
+    if ( primary === void 0 ) primary = true;
+
+    var doc = cm.doc, result = {};
+    var curFragment = result.cursors = document.createDocumentFragment();
+    var selFragment = result.selection = document.createDocumentFragment();
+
+    for (var i = 0; i < doc.sel.ranges.length; i++) {
+      if (!primary && i == doc.sel.primIndex) { continue }
+      var range$$1 = doc.sel.ranges[i];
+      if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
+      var collapsed = range$$1.empty();
+      if (collapsed || cm.options.showCursorWhenSelecting)
+        { drawSelectionCursor(cm, range$$1.head, curFragment); }
+      if (!collapsed)
+        { drawSelectionRange(cm, range$$1, selFragment); }
+    }
+    return result
+  }
+
+  // Draws a cursor for the given range
+  function drawSelectionCursor(cm, head, output) {
+    var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
+
+    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
+    cursor.style.left = pos.left + "px";
+    cursor.style.top = pos.top + "px";
+    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
+
+    if (pos.other) {
+      // Secondary cursor, shown when on a 'jump' in bi-directional text
+      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
+      otherCursor.style.display = "";
+      otherCursor.style.left = pos.other.left + "px";
+      otherCursor.style.top = pos.other.top + "px";
+      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
+    }
+  }
+
+  function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
+
+  // Draws the given range as a highlighted selection
+  function drawSelectionRange(cm, range$$1, output) {
+    var display = cm.display, doc = cm.doc;
+    var fragment = document.createDocumentFragment();
+    var padding = paddingH(cm.display), leftSide = padding.left;
+    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
+    var docLTR = doc.direction == "ltr";
+
+    function add(left, top, width, bottom) {
+      if (top < 0) { top = 0; }
+      top = Math.round(top);
+      bottom = Math.round(bottom);
+      fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n                             top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n                             height: " + (bottom - top) + "px")));
+    }
+
+    function drawForLine(line, fromArg, toArg) {
+      var lineObj = getLine(doc, line);
+      var lineLen = lineObj.text.length;
+      var start, end;
+      function coords(ch, bias) {
+        return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
+      }
+
+      function wrapX(pos, dir, side) {
+        var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
+        var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
+        var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
+        return coords(ch, prop)[prop]
+      }
+
+      var order = getOrder(lineObj, doc.direction);
+      iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
+        var ltr = dir == "ltr";
+        var fromPos = coords(from, ltr ? "left" : "right");
+        var toPos = coords(to - 1, ltr ? "right" : "left");
+
+        var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
+        var first = i == 0, last = !order || i == order.length - 1;
+        if (toPos.top - fromPos.top <= 3) { // Single line
+          var openLeft = (docLTR ? openStart : openEnd) && first;
+          var openRight = (docLTR ? openEnd : openStart) && last;
+          var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
+          var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
+          add(left, fromPos.top, right - left, fromPos.bottom);
+        } else { // Multiple lines
+          var topLeft, topRight, botLeft, botRight;
+          if (ltr) {
+            topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
+            topRight = docLTR ? rightSide : wrapX(from, dir, "before");
+            botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
+            botRight = docLTR && openEnd && last ? rightSide : toPos.right;
+          } else {
+            topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
+            topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
+            botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
+            botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
+          }
+          add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
+          if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
+          add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
+        }
+
+        if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
+        if (cmpCoords(toPos, start) < 0) { start = toPos; }
+        if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
+        if (cmpCoords(toPos, end) < 0) { end = toPos; }
+      });
+      return {start: start, end: end}
+    }
+
+    var sFrom = range$$1.from(), sTo = range$$1.to();
+    if (sFrom.line == sTo.line) {
+      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
+    } else {
+      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
+      var singleVLine = visualLine(fromLine) == visualLine(toLine);
+      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
+      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
+      if (singleVLine) {
+        if (leftEnd.top < rightStart.top - 2) {
+          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
+          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
+        } else {
+          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
+        }
+      }
+      if (leftEnd.bottom < rightStart.top)
+        { add(leftSide, leftEnd.bottom, null, rightStart.top); }
+    }
+
+    output.appendChild(fragment);
+  }
+
+  // Cursor-blinking
+  function restartBlink(cm) {
+    if (!cm.state.focused) { return }
+    var display = cm.display;
+    clearInterval(display.blinker);
+    var on = true;
+    display.cursorDiv.style.visibility = "";
+    if (cm.options.cursorBlinkRate > 0)
+      { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
+        cm.options.cursorBlinkRate); }
+    else if (cm.options.cursorBlinkRate < 0)
+      { display.cursorDiv.style.visibility = "hidden"; }
+  }
+
+  function ensureFocus(cm) {
+    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
+  }
+
+  function delayBlurEvent(cm) {
+    cm.state.delayingBlurEvent = true;
+    setTimeout(function () { if (cm.state.delayingBlurEvent) {
+      cm.state.delayingBlurEvent = false;
+      onBlur(cm);
+    } }, 100);
+  }
+
+  function onFocus(cm, e) {
+    if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
+
+    if (cm.options.readOnly == "nocursor") { return }
+    if (!cm.state.focused) {
+      signal(cm, "focus", cm, e);
+      cm.state.focused = true;
+      addClass(cm.display.wrapper, "CodeMirror-focused");
+      // This test prevents this from firing when a context
+      // menu is closed (since the input reset would kill the
+      // select-all detection hack)
+      if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
+        cm.display.input.reset();
+        if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
+      }
+      cm.display.input.receivedFocus();
+    }
+    restartBlink(cm);
+  }
+  function onBlur(cm, e) {
+    if (cm.state.delayingBlurEvent) { return }
+
+    if (cm.state.focused) {
+      signal(cm, "blur", cm, e);
+      cm.state.focused = false;
+      rmClass(cm.display.wrapper, "CodeMirror-focused");
+    }
+    clearInterval(cm.display.blinker);
+    setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
+  }
+
+  // Read the actual heights of the rendered lines, and update their
+  // stored heights to match.
+  function updateHeightsInViewport(cm) {
+    var display = cm.display;
+    var prevBottom = display.lineDiv.offsetTop;
+    for (var i = 0; i < display.view.length; i++) {
+      var cur = display.view[i], wrapping = cm.options.lineWrapping;
+      var height = (void 0), width = 0;
+      if (cur.hidden) { continue }
+      if (ie && ie_version < 8) {
+        var bot = cur.node.offsetTop + cur.node.offsetHeight;
+        height = bot - prevBottom;
+        prevBottom = bot;
+      } else {
+        var box = cur.node.getBoundingClientRect();
+        height = box.bottom - box.top;
+        // Check that lines don't extend past the right of the current
+        // editor width
+        if (!wrapping && cur.text.firstChild)
+          { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
+      }
+      var diff = cur.line.height - height;
+      if (diff > .005 || diff < -.005) {
+        updateLineHeight(cur.line, height);
+        updateWidgetHeight(cur.line);
+        if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
+          { updateWidgetHeight(cur.rest[j]); } }
+      }
+      if (width > cm.display.sizerWidth) {
+        var chWidth = Math.ceil(width / charWidth(cm.display));
+        if (chWidth > cm.display.maxLineLength) {
+          cm.display.maxLineLength = chWidth;
+          cm.display.maxLine = cur.line;
+          cm.display.maxLineChanged = true;
+        }
+      }
+    }
+  }
+
+  // Read and store the height of line widgets associated with the
+  // given line.
+  function updateWidgetHeight(line) {
+    if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
+      var w = line.widgets[i], parent = w.node.parentNode;
+      if (parent) { w.height = parent.offsetHeight; }
+    } }
+  }
+
+  // Compute the lines that are visible in a given viewport (defaults
+  // the the current scroll position). viewport may contain top,
+  // height, and ensure (see op.scrollToPos) properties.
+  function visibleLines(display, doc, viewport) {
+    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
+    top = Math.floor(top - paddingTop(display));
+    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
+
+    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
+    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
+    // forces those lines into the viewport (if possible).
+    if (viewport && viewport.ensure) {
+      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
+      if (ensureFrom < from) {
+        from = ensureFrom;
+        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
+      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
+        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
+        to = ensureTo;
+      }
+    }
+    return {from: from, to: Math.max(to, from + 1)}
+  }
+
+  // SCROLLING THINGS INTO VIEW
+
+  // If an editor sits on the top or bottom of the window, partially
+  // scrolled out of view, this ensures that the cursor is visible.
+  function maybeScrollWindow(cm, rect) {
+    if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
+
+    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
+    if (rect.top + box.top < 0) { doScroll = true; }
+    else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
+    if (doScroll != null && !phantom) {
+      var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n                         top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n                         height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n                         left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
+      cm.display.lineSpace.appendChild(scrollNode);
+      scrollNode.scrollIntoView(doScroll);
+      cm.display.lineSpace.removeChild(scrollNode);
+    }
+  }
+
+  // Scroll a given position into view (immediately), verifying that
+  // it actually became visible (as line heights are accurately
+  // measured, the position of something may 'drift' during drawing).
+  function scrollPosIntoView(cm, pos, end, margin) {
+    if (margin == null) { margin = 0; }
+    var rect;
+    if (!cm.options.lineWrapping && pos == end) {
+      // Set pos and end to the cursor positions around the character pos sticks to
+      // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
+      // If pos == Pos(_, 0, "before"), pos and end are unchanged
+      pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
+      end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
+    }
+    for (var limit = 0; limit < 5; limit++) {
+      var changed = false;
+      var coords = cursorCoords(cm, pos);
+      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
+      rect = {left: Math.min(coords.left, endCoords.left),
+              top: Math.min(coords.top, endCoords.top) - margin,
+              right: Math.max(coords.left, endCoords.left),
+              bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
+      var scrollPos = calculateScrollPos(cm, rect);
+      var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
+      if (scrollPos.scrollTop != null) {
+        updateScrollTop(cm, scrollPos.scrollTop);
+        if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
+      }
+      if (scrollPos.scrollLeft != null) {
+        setScrollLeft(cm, scrollPos.scrollLeft);
+        if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
+      }
+      if (!changed) { break }
+    }
+    return rect
+  }
+
+  // Scroll a given set of coordinates into view (immediately).
+  function scrollIntoView(cm, rect) {
+    var scrollPos = calculateScrollPos(cm, rect);
+    if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
+    if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
+  }
+
+  // Calculate a new scroll position needed to scroll the given
+  // rectangle into view. Returns an object with scrollTop and
+  // scrollLeft properties. When these are undefined, the
+  // vertical/horizontal position does not need to be adjusted.
+  function calculateScrollPos(cm, rect) {
+    var display = cm.display, snapMargin = textHeight(cm.display);
+    if (rect.top < 0) { rect.top = 0; }
+    var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
+    var screen = displayHeight(cm), result = {};
+    if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
+    var docBottom = cm.doc.height + paddingVert(display);
+    var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
+    if (rect.top < screentop) {
+      result.scrollTop = atTop ? 0 : rect.top;
+    } else if (rect.bottom > screentop + screen) {
+      var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
+      if (newTop != screentop) { result.scrollTop = newTop; }
+    }
+
+    var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
+    var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
+    var tooWide = rect.right - rect.left > screenw;
+    if (tooWide) { rect.right = rect.left + screenw; }
+    if (rect.left < 10)
+      { result.scrollLeft = 0; }
+    else if (rect.left < screenleft)
+      { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
+    else if (rect.right > screenw + screenleft - 3)
+      { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
+    return result
+  }
+
+  // Store a relative adjustment to the scroll position in the current
+  // operation (to be applied when the operation finishes).
+  function addToScrollTop(cm, top) {
+    if (top == null) { return }
+    resolveScrollToPos(cm);
+    cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
+  }
+
+  // Make sure that at the end of the operation the current cursor is
+  // shown.
+  function ensureCursorVisible(cm) {
+    resolveScrollToPos(cm);
+    var cur = cm.getCursor();
+    cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
+  }
+
+  function scrollToCoords(cm, x, y) {
+    if (x != null || y != null) { resolveScrollToPos(cm); }
+    if (x != null) { cm.curOp.scrollLeft = x; }
+    if (y != null) { cm.curOp.scrollTop = y; }
+  }
+
+  function scrollToRange(cm, range$$1) {
+    resolveScrollToPos(cm);
+    cm.curOp.scrollToPos = range$$1;
+  }
+
+  // When an operation has its scrollToPos property set, and another
+  // scroll action is applied before the end of the operation, this
+  // 'simulates' scrolling that position into view in a cheap way, so
+  // that the effect of intermediate scroll commands is not ignored.
+  function resolveScrollToPos(cm) {
+    var range$$1 = cm.curOp.scrollToPos;
+    if (range$$1) {
+      cm.curOp.scrollToPos = null;
+      var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
+      scrollToCoordsRange(cm, from, to, range$$1.margin);
+    }
+  }
+
+  function scrollToCoordsRange(cm, from, to, margin) {
+    var sPos = calculateScrollPos(cm, {
+      left: Math.min(from.left, to.left),
+      top: Math.min(from.top, to.top) - margin,
+      right: Math.max(from.right, to.right),
+      bottom: Math.max(from.bottom, to.bottom) + margin
+    });
+    scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
+  }
+
+  // Sync the scrollable area and scrollbars, ensure the viewport
+  // covers the visible area.
+  function updateScrollTop(cm, val) {
+    if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
+    if (!gecko) { updateDisplaySimple(cm, {top: val}); }
+    setScrollTop(cm, val, true);
+    if (gecko) { updateDisplaySimple(cm); }
+    startWorker(cm, 100);
+  }
+
+  function setScrollTop(cm, val, forceScroll) {
+    val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
+    if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
+    cm.doc.scrollTop = val;
+    cm.display.scrollbars.setScrollTop(val);
+    if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
+  }
+
+  // Sync scroller and scrollbar, ensure the gutter elements are
+  // aligned.
+  function setScrollLeft(cm, val, isScroller, forceScroll) {
+    val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
+    if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
+    cm.doc.scrollLeft = val;
+    alignHorizontally(cm);
+    if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
+    cm.display.scrollbars.setScrollLeft(val);
+  }
+
+  // SCROLLBARS
+
+  // Prepare DOM reads needed to update the scrollbars. Done in one
+  // shot to minimize update/measure roundtrips.
+  function measureForScrollbars(cm) {
+    var d = cm.display, gutterW = d.gutters.offsetWidth;
+    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
+    return {
+      clientHeight: d.scroller.clientHeight,
+      viewHeight: d.wrapper.clientHeight,
+      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
+      viewWidth: d.wrapper.clientWidth,
+      barLeft: cm.options.fixedGutter ? gutterW : 0,
+      docHeight: docH,
+      scrollHeight: docH + scrollGap(cm) + d.barHeight,
+      nativeBarWidth: d.nativeBarWidth,
+      gutterWidth: gutterW
+    }
+  }
+
+  var NativeScrollbars = function(place, scroll, cm) {
+    this.cm = cm;
+    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
+    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
+    vert.tabIndex = horiz.tabIndex = -1;
+    place(vert); place(horiz);
+
+    on(vert, "scroll", function () {
+      if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
+    });
+    on(horiz, "scroll", function () {
+      if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
+    });
+
+    this.checkedZeroWidth = false;
+    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
+    if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
+  };
+
+  NativeScrollbars.prototype.update = function (measure) {
+    var needsH = measure.scrollWidth > measure.clientWidth + 1;
+    var needsV = measure.scrollHeight > measure.clientHeight + 1;
+    var sWidth = measure.nativeBarWidth;
+
+    if (needsV) {
+      this.vert.style.display = "block";
+      this.vert.style.bottom = needsH ? sWidth + "px" : "0";
+      var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
+      // A bug in IE8 can cause this value to be negative, so guard it.
+      this.vert.firstChild.style.height =
+        Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
+    } else {
+      this.vert.style.display = "";
+      this.vert.firstChild.style.height = "0";
+    }
+
+    if (needsH) {
+      this.horiz.style.display = "block";
+      this.horiz.style.right = needsV ? sWidth + "px" : "0";
+      this.horiz.style.left = measure.barLeft + "px";
+      var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
+      this.horiz.firstChild.style.width =
+        Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
+    } else {
+      this.horiz.style.display = "";
+      this.horiz.firstChild.style.width = "0";
+    }
+
+    if (!this.checkedZeroWidth && measure.clientHeight > 0) {
+      if (sWidth == 0) { this.zeroWidthHack(); }
+      this.checkedZeroWidth = true;
+    }
+
+    return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
+  };
+
+  NativeScrollbars.prototype.setScrollLeft = function (pos) {
+    if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
+    if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
+  };
+
+  NativeScrollbars.prototype.setScrollTop = function (pos) {
+    if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
+    if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
+  };
+
+  NativeScrollbars.prototype.zeroWidthHack = function () {
+    var w = mac && !mac_geMountainLion ? "12px" : "18px";
+    this.horiz.style.height = this.vert.style.width = w;
+    this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
+    this.disableHoriz = new Delayed;
+    this.disableVert = new Delayed;
+  };
+
+  NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
+    bar.style.pointerEvents = "auto";
+    function maybeDisable() {
+      // To find out whether the scrollbar is still visible, we
+      // check whether the element under the pixel in the bottom
+      // right corner of the scrollbar box is the scrollbar box
+      // itself (when the bar is still visible) or its filler child
+      // (when the bar is hidden). If it is still visible, we keep
+      // it enabled, if it's hidden, we disable pointer events.
+      var box = bar.getBoundingClientRect();
+      var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
+          : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
+      if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
+      else { delay.set(1000, maybeDisable); }
+    }
+    delay.set(1000, maybeDisable);
+  };
+
+  NativeScrollbars.prototype.clear = function () {
+    var parent = this.horiz.parentNode;
+    parent.removeChild(this.horiz);
+    parent.removeChild(this.vert);
+  };
+
+  var NullScrollbars = function () {};
+
+  NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
+  NullScrollbars.prototype.setScrollLeft = function () {};
+  NullScrollbars.prototype.setScrollTop = function () {};
+  NullScrollbars.prototype.clear = function () {};
+
+  function updateScrollbars(cm, measure) {
+    if (!measure) { measure = measureForScrollbars(cm); }
+    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
+    updateScrollbarsInner(cm, measure);
+    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
+      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
+        { updateHeightsInViewport(cm); }
+      updateScrollbarsInner(cm, measureForScrollbars(cm));
+      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
+    }
+  }
+
+  // Re-synchronize the fake scrollbars with the actual size of the
+  // content.
+  function updateScrollbarsInner(cm, measure) {
+    var d = cm.display;
+    var sizes = d.scrollbars.update(measure);
+
+    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
+    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
+    d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
+
+    if (sizes.right && sizes.bottom) {
+      d.scrollbarFiller.style.display = "block";
+      d.scrollbarFiller.style.height = sizes.bottom + "px";
+      d.scrollbarFiller.style.width = sizes.right + "px";
+    } else { d.scrollbarFiller.style.display = ""; }
+    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
+      d.gutterFiller.style.display = "block";
+      d.gutterFiller.style.height = sizes.bottom + "px";
+      d.gutterFiller.style.width = measure.gutterWidth + "px";
+    } else { d.gutterFiller.style.display = ""; }
+  }
+
+  var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
+
+  function initScrollbars(cm) {
+    if (cm.display.scrollbars) {
+      cm.display.scrollbars.clear();
+      if (cm.display.scrollbars.addClass)
+        { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
+    }
+
+    cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
+      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
+      // Prevent clicks in the scrollbars from killing focus
+      on(node, "mousedown", function () {
+        if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
+      });
+      node.setAttribute("cm-not-content", "true");
+    }, function (pos, axis) {
+      if (axis == "horizontal") { setScrollLeft(cm, pos); }
+      else { updateScrollTop(cm, pos); }
+    }, cm);
+    if (cm.display.scrollbars.addClass)
+      { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
+  }
+
+  // Operations are used to wrap a series of changes to the editor
+  // state in such a way that each change won't have to update the
+  // cursor and display (which would be awkward, slow, and
+  // error-prone). Instead, display updates are batched and then all
+  // combined and executed at once.
+
+  var nextOpId = 0;
+  // Start a new operation.
+  function startOperation(cm) {
+    cm.curOp = {
+      cm: cm,
+      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
+      startHeight: cm.doc.height, // Used to detect need to update scrollbar
+      forceUpdate: false,      // Used to force a redraw
+      updateInput: 0,       // Whether to reset the input textarea
+      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
+      changeObjs: null,        // Accumulated changes, for firing change events
+      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
+      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
+      selectionChanged: false, // Whether the selection needs to be redrawn
+      updateMaxLine: false,    // Set when the widest line needs to be determined anew
+      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
+      scrollToPos: null,       // Used to scroll to a specific position
+      focus: false,
+      id: ++nextOpId           // Unique ID
+    };
+    pushOperation(cm.curOp);
+  }
+
+  // Finish an operation, updating the display and signalling delayed events
+  function endOperation(cm) {
+    var op = cm.curOp;
+    if (op) { finishOperation(op, function (group) {
+      for (var i = 0; i < group.ops.length; i++)
+        { group.ops[i].cm.curOp = null; }
+      endOperations(group);
+    }); }
+  }
+
+  // The DOM updates done when an operation finishes are batched so
+  // that the minimum number of relayouts are required.
+  function endOperations(group) {
+    var ops = group.ops;
+    for (var i = 0; i < ops.length; i++) // Read DOM
+      { endOperation_R1(ops[i]); }
+    for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
+      { endOperation_W1(ops[i$1]); }
+    for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
+      { endOperation_R2(ops[i$2]); }
+    for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
+      { endOperation_W2(ops[i$3]); }
+    for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
+      { endOperation_finish(ops[i$4]); }
+  }
+
+  function endOperation_R1(op) {
+    var cm = op.cm, display = cm.display;
+    maybeClipScrollbars(cm);
+    if (op.updateMaxLine) { findMaxLine(cm); }
+
+    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
+      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
+                         op.scrollToPos.to.line >= display.viewTo) ||
+      display.maxLineChanged && cm.options.lineWrapping;
+    op.update = op.mustUpdate &&
+      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
+  }
+
+  function endOperation_W1(op) {
+    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
+  }
+
+  function endOperation_R2(op) {
+    var cm = op.cm, display = cm.display;
+    if (op.updatedDisplay) { updateHeightsInViewport(cm); }
+
+    op.barMeasure = measureForScrollbars(cm);
+
+    // If the max line changed since it was last measured, measure it,
+    // and ensure the document's width matches it.
+    // updateDisplay_W2 will use these properties to do the actual resizing
+    if (display.maxLineChanged && !cm.options.lineWrapping) {
+      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
+      cm.display.sizerWidth = op.adjustWidthTo;
+      op.barMeasure.scrollWidth =
+        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
+      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
+    }
+
+    if (op.updatedDisplay || op.selectionChanged)
+      { op.preparedSelection = display.input.prepareSelection(); }
+  }
+
+  function endOperation_W2(op) {
+    var cm = op.cm;
+
+    if (op.adjustWidthTo != null) {
+      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
+      if (op.maxScrollLeft < cm.doc.scrollLeft)
+        { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
+      cm.display.maxLineChanged = false;
+    }
+
+    var takeFocus = op.focus && op.focus == activeElt();
+    if (op.preparedSelection)
+      { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
+    if (op.updatedDisplay || op.startHeight != cm.doc.height)
+      { updateScrollbars(cm, op.barMeasure); }
+    if (op.updatedDisplay)
+      { setDocumentHeight(cm, op.barMeasure); }
+
+    if (op.selectionChanged) { restartBlink(cm); }
+
+    if (cm.state.focused && op.updateInput)
+      { cm.display.input.reset(op.typing); }
+    if (takeFocus) { ensureFocus(op.cm); }
+  }
+
+  function endOperation_finish(op) {
+    var cm = op.cm, display = cm.display, doc = cm.doc;
+
+    if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
+
+    // Abort mouse wheel delta measurement, when scrolling explicitly
+    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
+      { display.wheelStartX = display.wheelStartY = null; }
+
+    // Propagate the scroll position to the actual DOM scroller
+    if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
+
+    if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
+    // If we need to scroll a specific position into view, do so.
+    if (op.scrollToPos) {
+      var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
+                                   clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
+      maybeScrollWindow(cm, rect);
+    }
+
+    // Fire events for markers that are hidden/unidden by editing or
+    // undoing
+    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
+    if (hidden) { for (var i = 0; i < hidden.length; ++i)
+      { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
+    if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
+      { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
+
+    if (display.wrapper.offsetHeight)
+      { doc.scrollTop = cm.display.scroller.scrollTop; }
+
+    // Fire change events, and delayed event handlers
+    if (op.changeObjs)
+      { signal(cm, "changes", cm, op.changeObjs); }
+    if (op.update)
+      { op.update.finish(); }
+  }
+
+  // Run the given function in an operation
+  function runInOp(cm, f) {
+    if (cm.curOp) { return f() }
+    startOperation(cm);
+    try { return f() }
+    finally { endOperation(cm); }
+  }
+  // Wraps a function in an operation. Returns the wrapped function.
+  function operation(cm, f) {
+    return function() {
+      if (cm.curOp) { return f.apply(cm, arguments) }
+      startOperation(cm);
+      try { return f.apply(cm, arguments) }
+      finally { endOperation(cm); }
+    }
+  }
+  // Used to add methods to editor and doc instances, wrapping them in
+  // operations.
+  function methodOp(f) {
+    return function() {
+      if (this.curOp) { return f.apply(this, arguments) }
+      startOperation(this);
+      try { return f.apply(this, arguments) }
+      finally { endOperation(this); }
+    }
+  }
+  function docMethodOp(f) {
+    return function() {
+      var cm = this.cm;
+      if (!cm || cm.curOp) { return f.apply(this, arguments) }
+      startOperation(cm);
+      try { return f.apply(this, arguments) }
+      finally { endOperation(cm); }
+    }
+  }
+
+  // HIGHLIGHT WORKER
+
+  function startWorker(cm, time) {
+    if (cm.doc.highlightFrontier < cm.display.viewTo)
+      { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
+  }
+
+  function highlightWorker(cm) {
+    var doc = cm.doc;
+    if (doc.highlightFrontier >= cm.display.viewTo) { return }
+    var end = +new Date + cm.options.workTime;
+    var context = getContextBefore(cm, doc.highlightFrontier);
+    var changedLines = [];
+
+    doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
+      if (context.line >= cm.display.viewFrom) { // Visible
+        var oldStyles = line.styles;
+        var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
+        var highlighted = highlightLine(cm, line, context, true);
+        if (resetState) { context.state = resetState; }
+        line.styles = highlighted.styles;
+        var oldCls = line.styleClasses, newCls = highlighted.classes;
+        if (newCls) { line.styleClasses = newCls; }
+        else if (oldCls) { line.styleClasses = null; }
+        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
+          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
+        for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
+        if (ischange) { changedLines.push(context.line); }
+        line.stateAfter = context.save();
+        context.nextLine();
+      } else {
+        if (line.text.length <= cm.options.maxHighlightLength)
+          { processLine(cm, line.text, context); }
+        line.stateAfter = context.line % 5 == 0 ? context.save() : null;
+        context.nextLine();
+      }
+      if (+new Date > end) {
+        startWorker(cm, cm.options.workDelay);
+        return true
+      }
+    });
+    doc.highlightFrontier = context.line;
+    doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
+    if (changedLines.length) { runInOp(cm, function () {
+      for (var i = 0; i < changedLines.length; i++)
+        { regLineChange(cm, changedLines[i], "text"); }
+    }); }
+  }
+
+  // DISPLAY DRAWING
+
+  var DisplayUpdate = function(cm, viewport, force) {
+    var display = cm.display;
+
+    this.viewport = viewport;
+    // Store some values that we'll need later (but don't want to force a relayout for)
+    this.visible = visibleLines(display, cm.doc, viewport);
+    this.editorIsHidden = !display.wrapper.offsetWidth;
+    this.wrapperHeight = display.wrapper.clientHeight;
+    this.wrapperWidth = display.wrapper.clientWidth;
+    this.oldDisplayWidth = displayWidth(cm);
+    this.force = force;
+    this.dims = getDimensions(cm);
+    this.events = [];
+  };
+
+  DisplayUpdate.prototype.signal = function (emitter, type) {
+    if (hasHandler(emitter, type))
+      { this.events.push(arguments); }
+  };
+  DisplayUpdate.prototype.finish = function () {
+      var this$1 = this;
+
+    for (var i = 0; i < this.events.length; i++)
+      { signal.apply(null, this$1.events[i]); }
+  };
+
+  function maybeClipScrollbars(cm) {
+    var display = cm.display;
+    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
+      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
+      display.heightForcer.style.height = scrollGap(cm) + "px";
+      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
+      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
+      display.scrollbarsClipped = true;
+    }
+  }
+
+  function selectionSnapshot(cm) {
+    if (cm.hasFocus()) { return null }
+    var active = activeElt();
+    if (!active || !contains(cm.display.lineDiv, active)) { return null }
+    var result = {activeElt: active};
+    if (window.getSelection) {
+      var sel = window.getSelection();
+      if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
+        result.anchorNode = sel.anchorNode;
+        result.anchorOffset = sel.anchorOffset;
+        result.focusNode = sel.focusNode;
+        result.focusOffset = sel.focusOffset;
+      }
+    }
+    return result
+  }
+
+  function restoreSelection(snapshot) {
+    if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
+    snapshot.activeElt.focus();
+    if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
+      var sel = window.getSelection(), range$$1 = document.createRange();
+      range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
+      range$$1.collapse(false);
+      sel.removeAllRanges();
+      sel.addRange(range$$1);
+      sel.extend(snapshot.focusNode, snapshot.focusOffset);
+    }
+  }
+
+  // Does the actual updating of the line display. Bails out
+  // (returning false) when there is nothing to be done and forced is
+  // false.
+  function updateDisplayIfNeeded(cm, update) {
+    var display = cm.display, doc = cm.doc;
+
+    if (update.editorIsHidden) {
+      resetView(cm);
+      return false
+    }
+
+    // Bail out if the visible area is already rendered and nothing changed.
+    if (!update.force &&
+        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
+        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
+        display.renderedView == display.view && countDirtyView(cm) == 0)
+      { return false }
+
+    if (maybeUpdateLineNumberWidth(cm)) {
+      resetView(cm);
+      update.dims = getDimensions(cm);
+    }
+
+    // Compute a suitable new viewport (from & to)
+    var end = doc.first + doc.size;
+    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
+    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
+    if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
+    if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
+    if (sawCollapsedSpans) {
+      from = visualLineNo(cm.doc, from);
+      to = visualLineEndNo(cm.doc, to);
+    }
+
+    var different = from != display.viewFrom || to != display.viewTo ||
+      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
+    adjustView(cm, from, to);
+
+    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
+    // Position the mover div to align with the current scroll position
+    cm.display.mover.style.top = display.viewOffset + "px";
+
+    var toUpdate = countDirtyView(cm);
+    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
+        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
+      { return false }
+
+    // For big changes, we hide the enclosing element during the
+    // update, since that speeds up the operations on most browsers.
+    var selSnapshot = selectionSnapshot(cm);
+    if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
+    patchDisplay(cm, display.updateLineNumbers, update.dims);
+    if (toUpdate > 4) { display.lineDiv.style.display = ""; }
+    display.renderedView = display.view;
+    // There might have been a widget with a focused element that got
+    // hidden or updated, if so re-focus it.
+    restoreSelection(selSnapshot);
+
+    // Prevent selection and cursors from interfering with the scroll
+    // width and height.
+    removeChildren(display.cursorDiv);
+    removeChildren(display.selectionDiv);
+    display.gutters.style.height = display.sizer.style.minHeight = 0;
+
+    if (different) {
+      display.lastWrapHeight = update.wrapperHeight;
+      display.lastWrapWidth = update.wrapperWidth;
+      startWorker(cm, 400);
+    }
+
+    display.updateLineNumbers = null;
+
+    return true
+  }
+
+  function postUpdateDisplay(cm, update) {
+    var viewport = update.viewport;
+
+    for (var first = true;; first = false) {
+      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
+        // Clip forced viewport to actual scrollable area.
+        if (viewport && viewport.top != null)
+          { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
+        // Updated line heights might result in the drawn area not
+        // actually covering the viewport. Keep looping until it does.
+        update.visible = visibleLines(cm.display, cm.doc, viewport);
+        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
+          { break }
+      }
+      if (!updateDisplayIfNeeded(cm, update)) { break }
+      updateHeightsInViewport(cm);
+      var barMeasure = measureForScrollbars(cm);
+      updateSelection(cm);
+      updateScrollbars(cm, barMeasure);
+      setDocumentHeight(cm, barMeasure);
+      update.force = false;
+    }
+
+    update.signal(cm, "update", cm);
+    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
+      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
+      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
+    }
+  }
+
+  function updateDisplaySimple(cm, viewport) {
+    var update = new DisplayUpdate(cm, viewport);
+    if (updateDisplayIfNeeded(cm, update)) {
+      updateHeightsInViewport(cm);
+      postUpdateDisplay(cm, update);
+      var barMeasure = measureForScrollbars(cm);
+      updateSelection(cm);
+      updateScrollbars(cm, barMeasure);
+      setDocumentHeight(cm, barMeasure);
+      update.finish();
+    }
+  }
+
+  // Sync the actual display DOM structure with display.view, removing
+  // nodes for lines that are no longer in view, and creating the ones
+  // that are not there yet, and updating the ones that are out of
+  // date.
+  function patchDisplay(cm, updateNumbersFrom, dims) {
+    var display = cm.display, lineNumbers = cm.options.lineNumbers;
+    var container = display.lineDiv, cur = container.firstChild;
+
+    function rm(node) {
+      var next = node.nextSibling;
+      // Works around a throw-scroll bug in OS X Webkit
+      if (webkit && mac && cm.display.currentWheelTarget == node)
+        { node.style.display = "none"; }
+      else
+        { node.parentNode.removeChild(node); }
+      return next
+    }
+
+    var view = display.view, lineN = display.viewFrom;
+    // Loop over the elements in the view, syncing cur (the DOM nodes
+    // in display.lineDiv) with the view as we go.
+    for (var i = 0; i < view.length; i++) {
+      var lineView = view[i];
+      if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
+        var node = buildLineElement(cm, lineView, lineN, dims);
+        container.insertBefore(node, cur);
+      } else { // Already drawn
+        while (cur != lineView.node) { cur = rm(cur); }
+        var updateNumber = lineNumbers && updateNumbersFrom != null &&
+          updateNumbersFrom <= lineN && lineView.lineNumber;
+        if (lineView.changes) {
+          if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
+          updateLineForChanges(cm, lineView, lineN, dims);
+        }
+        if (updateNumber) {
+          removeChildren(lineView.lineNumber);
+          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
+        }
+        cur = lineView.node.nextSibling;
+      }
+      lineN += lineView.size;
+    }
+    while (cur) { cur = rm(cur); }
+  }
+
+  function updateGutterSpace(display) {
+    var width = display.gutters.offsetWidth;
+    display.sizer.style.marginLeft = width + "px";
+  }
+
+  function setDocumentHeight(cm, measure) {
+    cm.display.sizer.style.minHeight = measure.docHeight + "px";
+    cm.display.heightForcer.style.top = measure.docHeight + "px";
+    cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
+  }
+
+  // Re-align line numbers and gutter marks to compensate for
+  // horizontal scrolling.
+  function alignHorizontally(cm) {
+    var display = cm.display, view = display.view;
+    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
+    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
+    var gutterW = display.gutters.offsetWidth, left = comp + "px";
+    for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
+      if (cm.options.fixedGutter) {
+        if (view[i].gutter)
+          { view[i].gutter.style.left = left; }
+        if (view[i].gutterBackground)
+          { view[i].gutterBackground.style.left = left; }
+      }
+      var align = view[i].alignable;
+      if (align) { for (var j = 0; j < align.length; j++)
+        { align[j].style.left = left; } }
+    } }
+    if (cm.options.fixedGutter)
+      { display.gutters.style.left = (comp + gutterW) + "px"; }
+  }
+
+  // Used to ensure that the line number gutter is still the right
+  // size for the current document size. Returns true when an update
+  // is needed.
+  function maybeUpdateLineNumberWidth(cm) {
+    if (!cm.options.lineNumbers) { return false }
+    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
+    if (last.length != display.lineNumChars) {
+      var test = display.measure.appendChild(elt("div", [elt("div", last)],
+                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
+      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
+      display.lineGutter.style.width = "";
+      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
+      display.lineNumWidth = display.lineNumInnerWidth + padding;
+      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
+      display.lineGutter.style.width = display.lineNumWidth + "px";
+      updateGutterSpace(cm.display);
+      return true
+    }
+    return false
+  }
+
+  function getGutters(gutters, lineNumbers) {
+    var result = [], sawLineNumbers = false;
+    for (var i = 0; i < gutters.length; i++) {
+      var name = gutters[i], style = null;
+      if (typeof name != "string") { style = name.style; name = name.className; }
+      if (name == "CodeMirror-linenumbers") {
+        if (!lineNumbers) { continue }
+        else { sawLineNumbers = true; }
+      }
+      result.push({className: name, style: style});
+    }
+    if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
+    return result
+  }
+
+  // Rebuild the gutter elements, ensure the margin to the left of the
+  // code matches their width.
+  function renderGutters(display) {
+    var gutters = display.gutters, specs = display.gutterSpecs;
+    removeChildren(gutters);
+    display.lineGutter = null;
+    for (var i = 0; i < specs.length; ++i) {
+      var ref = specs[i];
+      var className = ref.className;
+      var style = ref.style;
+      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
+      if (style) { gElt.style.cssText = style; }
+      if (className == "CodeMirror-linenumbers") {
+        display.lineGutter = gElt;
+        gElt.style.width = (display.lineNumWidth || 1) + "px";
+      }
+    }
+    gutters.style.display = specs.length ? "" : "none";
+    updateGutterSpace(display);
+  }
+
+  function updateGutters(cm) {
+    renderGutters(cm.display);
+    regChange(cm);
+    alignHorizontally(cm);
+  }
+
+  // The display handles the DOM integration, both for input reading
+  // and content drawing. It holds references to DOM nodes and
+  // display-related state.
+
+  function Display(place, doc, input, options) {
+    var d = this;
+    this.input = input;
+
+    // Covers bottom-right square when both scrollbars are present.
+    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
+    d.scrollbarFiller.setAttribute("cm-not-content", "true");
+    // Covers bottom of gutter when coverGutterNextToScrollbar is on
+    // and h scrollbar is present.
+    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
+    d.gutterFiller.setAttribute("cm-not-content", "true");
+    // Will contain the actual code, positioned to cover the viewport.
+    d.lineDiv = eltP("div", null, "CodeMirror-code");
+    // Elements are added to these to represent selection and cursors.
+    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
+    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
+    // A visibility: hidden element used to find the size of things.
+    d.measure = elt("div", null, "CodeMirror-measure");
+    // When lines outside of the viewport are measured, they are drawn in this.
+    d.lineMeasure = elt("div", null, "CodeMirror-measure");
+    // Wraps everything that needs to exist inside the vertically-padded coordinate system
+    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
+                      null, "position: relative; outline: none");
+    var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
+    // Moved around its parent to cover visible view.
+    d.mover = elt("div", [lines], null, "position: relative");
+    // Set to the height of the document, allowing scrolling.
+    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
+    d.sizerWidth = null;
+    // Behavior of elts with overflow: auto and padding is
+    // inconsistent across browsers. This is used to ensure the
+    // scrollable area is big enough.
+    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
+    // Will contain the gutters, if any.
+    d.gutters = elt("div", null, "CodeMirror-gutters");
+    d.lineGutter = null;
+    // Actual scrollable element.
+    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
+    d.scroller.setAttribute("tabIndex", "-1");
+    // The element in which the editor lives.
+    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
+
+    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
+    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
+    if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
+
+    if (place) {
+      if (place.appendChild) { place.appendChild(d.wrapper); }
+      else { place(d.wrapper); }
+    }
+
+    // Current rendered range (may be bigger than the view window).
+    d.viewFrom = d.viewTo = doc.first;
+    d.reportedViewFrom = d.reportedViewTo = doc.first;
+    // Information about the rendered lines.
+    d.view = [];
+    d.renderedView = null;
+    // Holds info about a single rendered line when it was rendered
+    // for measurement, while not in view.
+    d.externalMeasured = null;
+    // Empty space (in pixels) above the view
+    d.viewOffset = 0;
+    d.lastWrapHeight = d.lastWrapWidth = 0;
+    d.updateLineNumbers = null;
+
+    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
+    d.scrollbarsClipped = false;
+
+    // Used to only resize the line number gutter when necessary (when
+    // the amount of lines crosses a boundary that makes its width change)
+    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
+    // Set to true when a non-horizontal-scrolling line widget is
+    // added. As an optimization, line widget aligning is skipped when
+    // this is false.
+    d.alignWidgets = false;
+
+    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
+
+    // Tracks the maximum line length so that the horizontal scrollbar
+    // can be kept static when scrolling.
+    d.maxLine = null;
+    d.maxLineLength = 0;
+    d.maxLineChanged = false;
+
+    // Used for measuring wheel scrolling granularity
+    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
+
+    // True when shift is held down.
+    d.shift = false;
+
+    // Used to track whether anything happened since the context menu
+    // was opened.
+    d.selForContextMenu = null;
+
+    d.activeTouch = null;
+
+    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
+    renderGutters(d);
+
+    input.init(d);
+  }
+
+  // Since the delta values reported on mouse wheel events are
+  // unstandardized between browsers and even browser versions, and
+  // generally horribly unpredictable, this code starts by measuring
+  // the scroll effect that the first few mouse wheel events have,
+  // and, from that, detects the way it can convert deltas to pixel
+  // offsets afterwards.
+  //
+  // The reason we want to know the amount a wheel event will scroll
+  // is that it gives us a chance to update the display before the
+  // actual scrolling happens, reducing flickering.
+
+  var wheelSamples = 0, wheelPixelsPerUnit = null;
+  // Fill in a browser-detected starting value on browsers where we
+  // know one. These don't have to be accurate -- the result of them
+  // being wrong would just be a slight flicker on the first wheel
+  // scroll (if it is large enough).
+  if (ie) { wheelPixelsPerUnit = -.53; }
+  else if (gecko) { wheelPixelsPerUnit = 15; }
+  else if (chrome) { wheelPixelsPerUnit = -.7; }
+  else if (safari) { wheelPixelsPerUnit = -1/3; }
+
+  function wheelEventDelta(e) {
+    var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
+    if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
+    if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
+    else if (dy == null) { dy = e.wheelDelta; }
+    return {x: dx, y: dy}
+  }
+  function wheelEventPixels(e) {
+    var delta = wheelEventDelta(e);
+    delta.x *= wheelPixelsPerUnit;
+    delta.y *= wheelPixelsPerUnit;
+    return delta
+  }
+
+  function onScrollWheel(cm, e) {
+    var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
+
+    var display = cm.display, scroll = display.scroller;
+    // Quit if there's nothing to scroll here
+    var canScrollX = scroll.scrollWidth > scroll.clientWidth;
+    var canScrollY = scroll.scrollHeight > scroll.clientHeight;
+    if (!(dx && canScrollX || dy && canScrollY)) { return }
+
+    // Webkit browsers on OS X abort momentum scrolls when the target
+    // of the scroll event is removed from the scrollable element.
+    // This hack (see related code in patchDisplay) makes sure the
+    // element is kept around.
+    if (dy && mac && webkit) {
+      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
+        for (var i = 0; i < view.length; i++) {
+          if (view[i].node == cur) {
+            cm.display.currentWheelTarget = cur;
+            break outer
+          }
+        }
+      }
+    }
+
+    // On some browsers, horizontal scrolling will cause redraws to
+    // happen before the gutter has been realigned, causing it to
+    // wriggle around in a most unseemly way. When we have an
+    // estimated pixels/delta value, we just handle horizontal
+    // scrolling entirely here. It'll be slightly off from native, but
+    // better than glitching out.
+    if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
+      if (dy && canScrollY)
+        { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
+      setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
+      // Only prevent default scrolling if vertical scrolling is
+      // actually possible. Otherwise, it causes vertical scroll
+      // jitter on OSX trackpads when deltaX is small and deltaY
+      // is large (issue #3579)
+      if (!dy || (dy && canScrollY))
+        { e_preventDefault(e); }
+      display.wheelStartX = null; // Abort measurement, if in progress
+      return
+    }
+
+    // 'Project' the visible viewport to cover the area that is being
+    // scrolled into view (if we know enough to estimate it).
+    if (dy && wheelPixelsPerUnit != null) {
+      var pixels = dy * wheelPixelsPerUnit;
+      var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
+      if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
+      else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
+      updateDisplaySimple(cm, {top: top, bottom: bot});
+    }
+
+    if (wheelSamples < 20) {
+      if (display.wheelStartX == null) {
+        display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
+        display.wheelDX = dx; display.wheelDY = dy;
+        setTimeout(function () {
+          if (display.wheelStartX == null) { return }
+          var movedX = scroll.scrollLeft - display.wheelStartX;
+          var movedY = scroll.scrollTop - display.wheelStartY;
+          var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
+            (movedX && display.wheelDX && movedX / display.wheelDX);
+          display.wheelStartX = display.wheelStartY = null;
+          if (!sample) { return }
+          wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
+          ++wheelSamples;
+        }, 200);
+      } else {
+        display.wheelDX += dx; display.wheelDY += dy;
+      }
+    }
+  }
+
+  // Selection objects are immutable. A new one is created every time
+  // the selection changes. A selection is one or more non-overlapping
+  // (and non-touching) ranges, sorted, and an integer that indicates
+  // which one is the primary selection (the one that's scrolled into
+  // view, that getCursor returns, etc).
+  var Selection = function(ranges, primIndex) {
+    this.ranges = ranges;
+    this.primIndex = primIndex;
+  };
+
+  Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
+
+  Selection.prototype.equals = function (other) {
+      var this$1 = this;
+
+    if (other == this) { return true }
+    if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
+    for (var i = 0; i < this.ranges.length; i++) {
+      var here = this$1.ranges[i], there = other.ranges[i];
+      if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
+    }
+    return true
+  };
+
+  Selection.prototype.deepCopy = function () {
+      var this$1 = this;
+
+    var out = [];
+    for (var i = 0; i < this.ranges.length; i++)
+      { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
+    return new Selection(out, this.primIndex)
+  };
+
+  Selection.prototype.somethingSelected = function () {
+      var this$1 = this;
+
+    for (var i = 0; i < this.ranges.length; i++)
+      { if (!this$1.ranges[i].empty()) { return true } }
+    return false
+  };
+
+  Selection.prototype.contains = function (pos, end) {
+      var this$1 = this;
+
+    if (!end) { end = pos; }
+    for (var i = 0; i < this.ranges.length; i++) {
+      var range = this$1.ranges[i];
+      if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
+        { return i }
+    }
+    return -1
+  };
+
+  var Range = function(anchor, head) {
+    this.anchor = anchor; this.head = head;
+  };
+
+  Range.prototype.from = function () { return minPos(this.anchor, this.head) };
+  Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
+  Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
+
+  // Take an unsorted, potentially overlapping set of ranges, and
+  // build a selection out of it. 'Consumes' ranges array (modifying
+  // it).
+  function normalizeSelection(cm, ranges, primIndex) {
+    var mayTouch = cm && cm.options.selectionsMayTouch;
+    var prim = ranges[primIndex];
+    ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
+    primIndex = indexOf(ranges, prim);
+    for (var i = 1; i < ranges.length; i++) {
+      var cur = ranges[i], prev = ranges[i - 1];
+      var diff = cmp(prev.to(), cur.from());
+      if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
+        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
+        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
+        if (i <= primIndex) { --primIndex; }
+        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
+      }
+    }
+    return new Selection(ranges, primIndex)
+  }
+
+  function simpleSelection(anchor, head) {
+    return new Selection([new Range(anchor, head || anchor)], 0)
+  }
+
+  // Compute the position of the end of a change (its 'to' property
+  // refers to the pre-change end).
+  function changeEnd(change) {
+    if (!change.text) { return change.to }
+    return Pos(change.from.line + change.text.length - 1,
+               lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
+  }
+
+  // Adjust a position to refer to the post-change position of the
+  // same text, or the end of the change if the change covers it.
+  function adjustForChange(pos, change) {
+    if (cmp(pos, change.from) < 0) { return pos }
+    if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
+
+    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
+    if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
+    return Pos(line, ch)
+  }
+
+  function computeSelAfterChange(doc, change) {
+    var out = [];
+    for (var i = 0; i < doc.sel.ranges.length; i++) {
+      var range = doc.sel.ranges[i];
+      out.push(new Range(adjustForChange(range.anchor, change),
+                         adjustForChange(range.head, change)));
+    }
+    return normalizeSelection(doc.cm, out, doc.sel.primIndex)
+  }
+
+  function offsetPos(pos, old, nw) {
+    if (pos.line == old.line)
+      { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
+    else
+      { return Pos(nw.line + (pos.line - old.line), pos.ch) }
+  }
+
+  // Used by replaceSelections to allow moving the selection to the
+  // start or around the replaced test. Hint may be "start" or "around".
+  function computeReplacedSel(doc, changes, hint) {
+    var out = [];
+    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
+    for (var i = 0; i < changes.length; i++) {
+      var change = changes[i];
+      var from = offsetPos(change.from, oldPrev, newPrev);
+      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
+      oldPrev = change.to;
+      newPrev = to;
+      if (hint == "around") {
+        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
+        out[i] = new Range(inv ? to : from, inv ? from : to);
+      } else {
+        out[i] = new Range(from, from);
+      }
+    }
+    return new Selection(out, doc.sel.primIndex)
+  }
+
+  // Used to get the editor into a consistent state again when options change.
+
+  function loadMode(cm) {
+    cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
+    resetModeState(cm);
+  }
+
+  function resetModeState(cm) {
+    cm.doc.iter(function (line) {
+      if (line.stateAfter) { line.stateAfter = null; }
+      if (line.styles) { line.styles = null; }
+    });
+    cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
+    startWorker(cm, 100);
+    cm.state.modeGen++;
+    if (cm.curOp) { regChange(cm); }
+  }
+
+  // DOCUMENT DATA STRUCTURE
+
+  // By default, updates that start and end at the beginning of a line
+  // are treated specially, in order to make the association of line
+  // widgets and marker elements with the text behave more intuitive.
+  function isWholeLineUpdate(doc, change) {
+    return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
+      (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
+  }
+
+  // Perform a change on the document data structure.
+  function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
+    function spansFor(n) {return markedSpans ? markedSpans[n] : null}
+    function update(line, text, spans) {
+      updateLine(line, text, spans, estimateHeight$$1);
+      signalLater(line, "change", line, change);
+    }
+    function linesFor(start, end) {
+      var result = [];
+      for (var i = start; i < end; ++i)
+        { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
+      return result
+    }
+
+    var from = change.from, to = change.to, text = change.text;
+    var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
+    var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
+
+    // Adjust the line structure
+    if (change.full) {
+      doc.insert(0, linesFor(0, text.length));
+      doc.remove(text.length, doc.size - text.length);
+    } else if (isWholeLineUpdate(doc, change)) {
+      // This is a whole-line replace. Treated specially to make
+      // sure line objects move the way they are supposed to.
+      var added = linesFor(0, text.length - 1);
+      update(lastLine, lastLine.text, lastSpans);
+      if (nlines) { doc.remove(from.line, nlines); }
+      if (added.length) { doc.insert(from.line, added); }
+    } else if (firstLine == lastLine) {
+      if (text.length == 1) {
+        update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
+      } else {
+        var added$1 = linesFor(1, text.length - 1);
+        added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
+        update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+        doc.insert(from.line + 1, added$1);
+      }
+    } else if (text.length == 1) {
+      update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
+      doc.remove(from.line + 1, nlines);
+    } else {
+      update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+      update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
+      var added$2 = linesFor(1, text.length - 1);
+      if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
+      doc.insert(from.line + 1, added$2);
+    }
+
+    signalLater(doc, "change", doc, change);
+  }
+
+  // Call f for all linked documents.
+  function linkedDocs(doc, f, sharedHistOnly) {
+    function propagate(doc, skip, sharedHist) {
+      if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
+        var rel = doc.linked[i];
+        if (rel.doc == skip) { continue }
+        var shared = sharedHist && rel.sharedHist;
+        if (sharedHistOnly && !shared) { continue }
+        f(rel.doc, shared);
+        propagate(rel.doc, doc, shared);
+      } }
+    }
+    propagate(doc, null, true);
+  }
+
+  // Attach a document to an editor.
+  function attachDoc(cm, doc) {
+    if (doc.cm) { throw new Error("This document is already in use.") }
+    cm.doc = doc;
+    doc.cm = cm;
+    estimateLineHeights(cm);
+    loadMode(cm);
+    setDirectionClass(cm);
+    if (!cm.options.lineWrapping) { findMaxLine(cm); }
+    cm.options.mode = doc.modeOption;
+    regChange(cm);
+  }
+
+  function setDirectionClass(cm) {
+  (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
+  }
+
+  function directionChanged(cm) {
+    runInOp(cm, function () {
+      setDirectionClass(cm);
+      regChange(cm);
+    });
+  }
+
+  function History(startGen) {
+    // Arrays of change events and selections. Doing something adds an
+    // event to done and clears undo. Undoing moves events from done
+    // to undone, redoing moves them in the other direction.
+    this.done = []; this.undone = [];
+    this.undoDepth = Infinity;
+    // Used to track when changes can be merged into a single undo
+    // event
+    this.lastModTime = this.lastSelTime = 0;
+    this.lastOp = this.lastSelOp = null;
+    this.lastOrigin = this.lastSelOrigin = null;
+    // Used by the isClean() method
+    this.generation = this.maxGeneration = startGen || 1;
+  }
+
+  // Create a history change event from an updateDoc-style change
+  // object.
+  function historyChangeFromChange(doc, change) {
+    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
+    attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
+    linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
+    return histChange
+  }
+
+  // Pop all selection events off the end of a history array. Stop at
+  // a change event.
+  function clearSelectionEvents(array) {
+    while (array.length) {
+      var last = lst(array);
+      if (last.ranges) { array.pop(); }
+      else { break }
+    }
+  }
+
+  // Find the top change event in the history. Pop off selection
+  // events that are in the way.
+  function lastChangeEvent(hist, force) {
+    if (force) {
+      clearSelectionEvents(hist.done);
+      return lst(hist.done)
+    } else if (hist.done.length && !lst(hist.done).ranges) {
+      return lst(hist.done)
+    } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
+      hist.done.pop();
+      return lst(hist.done)
+    }
+  }
+
+  // Register a change in the history. Merges changes that are within
+  // a single operation, or are close together with an origin that
+  // allows merging (starting with "+") into a single event.
+  function addChangeToHistory(doc, change, selAfter, opId) {
+    var hist = doc.history;
+    hist.undone.length = 0;
+    var time = +new Date, cur;
+    var last;
+
+    if ((hist.lastOp == opId ||
+         hist.lastOrigin == change.origin && change.origin &&
+         ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
+          change.origin.charAt(0) == "*")) &&
+        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
+      // Merge this change into the last event
+      last = lst(cur.changes);
+      if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
+        // Optimized case for simple insertion -- don't want to add
+        // new changesets for every character typed
+        last.to = changeEnd(change);
+      } else {
+        // Add new sub-event
+        cur.changes.push(historyChangeFromChange(doc, change));
+      }
+    } else {
+      // Can not be merged, start a new event.
+      var before = lst(hist.done);
+      if (!before || !before.ranges)
+        { pushSelectionToHistory(doc.sel, hist.done); }
+      cur = {changes: [historyChangeFromChange(doc, change)],
+             generation: hist.generation};
+      hist.done.push(cur);
+      while (hist.done.length > hist.undoDepth) {
+        hist.done.shift();
+        if (!hist.done[0].ranges) { hist.done.shift(); }
+      }
+    }
+    hist.done.push(selAfter);
+    hist.generation = ++hist.maxGeneration;
+    hist.lastModTime = hist.lastSelTime = time;
+    hist.lastOp = hist.lastSelOp = opId;
+    hist.lastOrigin = hist.lastSelOrigin = change.origin;
+
+    if (!last) { signal(doc, "historyAdded"); }
+  }
+
+  function selectionEventCanBeMerged(doc, origin, prev, sel) {
+    var ch = origin.charAt(0);
+    return ch == "*" ||
+      ch == "+" &&
+      prev.ranges.length == sel.ranges.length &&
+      prev.somethingSelected() == sel.somethingSelected() &&
+      new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
+  }
+
+  // Called whenever the selection changes, sets the new selection as
+  // the pending selection in the history, and pushes the old pending
+  // selection into the 'done' array when it was significantly
+  // different (in number of selected ranges, emptiness, or time).
+  function addSelectionToHistory(doc, sel, opId, options) {
+    var hist = doc.history, origin = options && options.origin;
+
+    // A new event is started when the previous origin does not match
+    // the current, or the origins don't allow matching. Origins
+    // starting with * are always merged, those starting with + are
+    // merged when similar and close together in time.
+    if (opId == hist.lastSelOp ||
+        (origin && hist.lastSelOrigin == origin &&
+         (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
+          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
+      { hist.done[hist.done.length - 1] = sel; }
+    else
+      { pushSelectionToHistory(sel, hist.done); }
+
+    hist.lastSelTime = +new Date;
+    hist.lastSelOrigin = origin;
+    hist.lastSelOp = opId;
+    if (options && options.clearRedo !== false)
+      { clearSelectionEvents(hist.undone); }
+  }
+
+  function pushSelectionToHistory(sel, dest) {
+    var top = lst(dest);
+    if (!(top && top.ranges && top.equals(sel)))
+      { dest.push(sel); }
+  }
+
+  // Used to store marked span information in the history.
+  function attachLocalSpans(doc, change, from, to) {
+    var existing = change["spans_" + doc.id], n = 0;
+    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
+      if (line.markedSpans)
+        { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
+      ++n;
+    });
+  }
+
+  // When un/re-doing restores text containing marked spans, those
+  // that have been explicitly cleared should not be restored.
+  function removeClearedSpans(spans) {
+    if (!spans) { return null }
+    var out;
+    for (var i = 0; i < spans.length; ++i) {
+      if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
+      else if (out) { out.push(spans[i]); }
+    }
+    return !out ? spans : out.length ? out : null
+  }
+
+  // Retrieve and filter the old marked spans stored in a change event.
+  function getOldSpans(doc, change) {
+    var found = change["spans_" + doc.id];
+    if (!found) { return null }
+    var nw = [];
+    for (var i = 0; i < change.text.length; ++i)
+      { nw.push(removeClearedSpans(found[i])); }
+    return nw
+  }
+
+  // Used for un/re-doing changes from the history. Combines the
+  // result of computing the existing spans with the set of spans that
+  // existed in the history (so that deleting around a span and then
+  // undoing brings back the span).
+  function mergeOldSpans(doc, change) {
+    var old = getOldSpans(doc, change);
+    var stretched = stretchSpansOverChange(doc, change);
+    if (!old) { return stretched }
+    if (!stretched) { return old }
+
+    for (var i = 0; i < old.length; ++i) {
+      var oldCur = old[i], stretchCur = stretched[i];
+      if (oldCur && stretchCur) {
+        spans: for (var j = 0; j < stretchCur.length; ++j) {
+          var span = stretchCur[j];
+          for (var k = 0; k < oldCur.length; ++k)
+            { if (oldCur[k].marker == span.marker) { continue spans } }
+          oldCur.push(span);
+        }
+      } else if (stretchCur) {
+        old[i] = stretchCur;
+      }
+    }
+    return old
+  }
+
+  // Used both to provide a JSON-safe object in .getHistory, and, when
+  // detaching a document, to split the history in two
+  function copyHistoryArray(events, newGroup, instantiateSel) {
+    var copy = [];
+    for (var i = 0; i < events.length; ++i) {
+      var event = events[i];
+      if (event.ranges) {
+        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
+        continue
+      }
+      var changes = event.changes, newChanges = [];
+      copy.push({changes: newChanges});
+      for (var j = 0; j < changes.length; ++j) {
+        var change = changes[j], m = (void 0);
+        newChanges.push({from: change.from, to: change.to, text: change.text});
+        if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
+          if (indexOf(newGroup, Number(m[1])) > -1) {
+            lst(newChanges)[prop] = change[prop];
+            delete change[prop];
+          }
+        } } }
+      }
+    }
+    return copy
+  }
+
+  // The 'scroll' parameter given to many of these indicated whether
+  // the new cursor position should be scrolled into view after
+  // modifying the selection.
+
+  // If shift is held or the extend flag is set, extends a range to
+  // include a given position (and optionally a second position).
+  // Otherwise, simply returns the range between the given positions.
+  // Used for cursor motion and such.
+  function extendRange(range, head, other, extend) {
+    if (extend) {
+      var anchor = range.anchor;
+      if (other) {
+        var posBefore = cmp(head, anchor) < 0;
+        if (posBefore != (cmp(other, anchor) < 0)) {
+          anchor = head;
+          head = other;
+        } else if (posBefore != (cmp(head, other) < 0)) {
+          head = other;
+        }
+      }
+      return new Range(anchor, head)
+    } else {
+      return new Range(other || head, head)
+    }
+  }
+
+  // Extend the primary selection range, discard the rest.
+  function extendSelection(doc, head, other, options, extend) {
+    if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
+    setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
+  }
+
+  // Extend all selections (pos is an array of selections with length
+  // equal the number of selections)
+  function extendSelections(doc, heads, options) {
+    var out = [];
+    var extend = doc.cm && (doc.cm.display.shift || doc.extend);
+    for (var i = 0; i < doc.sel.ranges.length; i++)
+      { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
+    var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
+    setSelection(doc, newSel, options);
+  }
+
+  // Updates a single range in the selection.
+  function replaceOneSelection(doc, i, range, options) {
+    var ranges = doc.sel.ranges.slice(0);
+    ranges[i] = range;
+    setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
+  }
+
+  // Reset the selection to a single range.
+  function setSimpleSelection(doc, anchor, head, options) {
+    setSelection(doc, simpleSelection(anchor, head), options);
+  }
+
+  // Give beforeSelectionChange handlers a change to influence a
+  // selection update.
+  function filterSelectionChange(doc, sel, options) {
+    var obj = {
+      ranges: sel.ranges,
+      update: function(ranges) {
+        var this$1 = this;
+
+        this.ranges = [];
+        for (var i = 0; i < ranges.length; i++)
+          { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
+                                     clipPos(doc, ranges[i].head)); }
+      },
+      origin: options && options.origin
+    };
+    signal(doc, "beforeSelectionChange", doc, obj);
+    if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
+    if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
+    else { return sel }
+  }
+
+  function setSelectionReplaceHistory(doc, sel, options) {
+    var done = doc.history.done, last = lst(done);
+    if (last && last.ranges) {
+      done[done.length - 1] = sel;
+      setSelectionNoUndo(doc, sel, options);
+    } else {
+      setSelection(doc, sel, options);
+    }
+  }
+
+  // Set a new selection.
+  function setSelection(doc, sel, options) {
+    setSelectionNoUndo(doc, sel, options);
+    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
+  }
+
+  function setSelectionNoUndo(doc, sel, options) {
+    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
+      { sel = filterSelectionChange(doc, sel, options); }
+
+    var bias = options && options.bias ||
+      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
+    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
+
+    if (!(options && options.scroll === false) && doc.cm)
+      { ensureCursorVisible(doc.cm); }
+  }
+
+  function setSelectionInner(doc, sel) {
+    if (sel.equals(doc.sel)) { return }
+
+    doc.sel = sel;
+
+    if (doc.cm) {
+      doc.cm.curOp.updateInput = 1;
+      doc.cm.curOp.selectionChanged = true;
+      signalCursorActivity(doc.cm);
+    }
+    signalLater(doc, "cursorActivity", doc);
+  }
+
+  // Verify that the selection does not partially select any atomic
+  // marked ranges.
+  function reCheckSelection(doc) {
+    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
+  }
+
+  // Return a selection that does not partially select any atomic
+  // ranges.
+  function skipAtomicInSelection(doc, sel, bias, mayClear) {
+    var out;
+    for (var i = 0; i < sel.ranges.length; i++) {
+      var range = sel.ranges[i];
+      var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
+      var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
+      var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
+      if (out || newAnchor != range.anchor || newHead != range.head) {
+        if (!out) { out = sel.ranges.slice(0, i); }
+        out[i] = new Range(newAnchor, newHead);
+      }
+    }
+    return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
+  }
+
+  function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
+    var line = getLine(doc, pos.line);
+    if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
+      var sp = line.markedSpans[i], m = sp.marker;
+
+      // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
+      // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
+      // is with selectLeft/Right
+      var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
+      var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
+
+      if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
+          (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
+        if (mayClear) {
+          signal(m, "beforeCursorEnter");
+          if (m.explicitlyCleared) {
+            if (!line.markedSpans) { break }
+            else {--i; continue}
+          }
+        }
+        if (!m.atomic) { continue }
+
+        if (oldPos) {
+          var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
+          if (dir < 0 ? preventCursorRight : preventCursorLeft)
+            { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
+          if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
+            { return skipAtomicInner(doc, near, pos, dir, mayClear) }
+        }
+
+        var far = m.find(dir < 0 ? -1 : 1);
+        if (dir < 0 ? preventCursorLeft : preventCursorRight)
+          { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
+        return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
+      }
+    } }
+    return pos
+  }
+
+  // Ensure a given position is not inside an atomic range.
+  function skipAtomic(doc, pos, oldPos, bias, mayClear) {
+    var dir = bias || 1;
+    var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
+        (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
+        skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
+        (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
+    if (!found) {
+      doc.cantEdit = true;
+      return Pos(doc.first, 0)
+    }
+    return found
+  }
+
+  function movePos(doc, pos, dir, line) {
+    if (dir < 0 && pos.ch == 0) {
+      if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
+      else { return null }
+    } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
+      if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
+      else { return null }
+    } else {
+      return new Pos(pos.line, pos.ch + dir)
+    }
+  }
+
+  function selectAll(cm) {
+    cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
+  }
+
+  // UPDATING
+
+  // Allow "beforeChange" event handlers to influence a change
+  function filterChange(doc, change, update) {
+    var obj = {
+      canceled: false,
+      from: change.from,
+      to: change.to,
+      text: change.text,
+      origin: change.origin,
+      cancel: function () { return obj.canceled = true; }
+    };
+    if (update) { obj.update = function (from, to, text, origin) {
+      if (from) { obj.from = clipPos(doc, from); }
+      if (to) { obj.to = clipPos(doc, to); }
+      if (text) { obj.text = text; }
+      if (origin !== undefined) { obj.origin = origin; }
+    }; }
+    signal(doc, "beforeChange", doc, obj);
+    if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
+
+    if (obj.canceled) {
+      if (doc.cm) { doc.cm.curOp.updateInput = 2; }
+      return null
+    }
+    return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
+  }
+
+  // Apply a change to a document, and add it to the document's
+  // history, and propagating it to all linked documents.
+  function makeChange(doc, change, ignoreReadOnly) {
+    if (doc.cm) {
+      if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
+      if (doc.cm.state.suppressEdits) { return }
+    }
+
+    if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
+      change = filterChange(doc, change, true);
+      if (!change) { return }
+    }
+
+    // Possibly split or suppress the update based on the presence
+    // of read-only spans in its range.
+    var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
+    if (split) {
+      for (var i = split.length - 1; i >= 0; --i)
+        { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
+    } else {
+      makeChangeInner(doc, change);
+    }
+  }
+
+  function makeChangeInner(doc, change) {
+    if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
+    var selAfter = computeSelAfterChange(doc, change);
+    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
+
+    makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
+    var rebased = [];
+
+    linkedDocs(doc, function (doc, sharedHist) {
+      if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+        rebaseHist(doc.history, change);
+        rebased.push(doc.history);
+      }
+      makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
+    });
+  }
+
+  // Revert a change stored in a document's history.
+  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
+    var suppress = doc.cm && doc.cm.state.suppressEdits;
+    if (suppress && !allowSelectionOnly) { return }
+
+    var hist = doc.history, event, selAfter = doc.sel;
+    var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
+
+    // Verify that there is a useable event (so that ctrl-z won't
+    // needlessly clear selection events)
+    var i = 0;
+    for (; i < source.length; i++) {
+      event = source[i];
+      if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
+        { break }
+    }
+    if (i == source.length) { return }
+    hist.lastOrigin = hist.lastSelOrigin = null;
+
+    for (;;) {
+      event = source.pop();
+      if (event.ranges) {
+        pushSelectionToHistory(event, dest);
+        if (allowSelectionOnly && !event.equals(doc.sel)) {
+          setSelection(doc, event, {clearRedo: false});
+          return
+        }
+        selAfter = event;
+      } else if (suppress) {
+        source.push(event);
+        return
+      } else { break }
+    }
+
+    // Build up a reverse change object to add to the opposite history
+    // stack (redo when undoing, and vice versa).
+    var antiChanges = [];
+    pushSelectionToHistory(selAfter, dest);
+    dest.push({changes: antiChanges, generation: hist.generation});
+    hist.generation = event.generation || ++hist.maxGeneration;
+
+    var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
+
+    var loop = function ( i ) {
+      var change = event.changes[i];
+      change.origin = type;
+      if (filter && !filterChange(doc, change, false)) {
+        source.length = 0;
+        return {}
+      }
+
+      antiChanges.push(historyChangeFromChange(doc, change));
+
+      var after = i ? computeSelAfterChange(doc, change) : lst(source);
+      makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
+      if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
+      var rebased = [];
+
+      // Propagate to the linked documents
+      linkedDocs(doc, function (doc, sharedHist) {
+        if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+          rebaseHist(doc.history, change);
+          rebased.push(doc.history);
+        }
+        makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
+      });
+    };
+
+    for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
+      var returned = loop( i$1 );
+
+      if ( returned ) return returned.v;
+    }
+  }
+
+  // Sub-views need their line numbers shifted when text is added
+  // above or below them in the parent document.
+  function shiftDoc(doc, distance) {
+    if (distance == 0) { return }
+    doc.first += distance;
+    doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
+      Pos(range.anchor.line + distance, range.anchor.ch),
+      Pos(range.head.line + distance, range.head.ch)
+    ); }), doc.sel.primIndex);
+    if (doc.cm) {
+      regChange(doc.cm, doc.first, doc.first - distance, distance);
+      for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
+        { regLineChange(doc.cm, l, "gutter"); }
+    }
+  }
+
+  // More lower-level change function, handling only a single document
+  // (not linked ones).
+  function makeChangeSingleDoc(doc, change, selAfter, spans) {
+    if (doc.cm && !doc.cm.curOp)
+      { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
+
+    if (change.to.line < doc.first) {
+      shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
+      return
+    }
+    if (change.from.line > doc.lastLine()) { return }
+
+    // Clip the change to the size of this doc
+    if (change.from.line < doc.first) {
+      var shift = change.text.length - 1 - (doc.first - change.from.line);
+      shiftDoc(doc, shift);
+      change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
+                text: [lst(change.text)], origin: change.origin};
+    }
+    var last = doc.lastLine();
+    if (change.to.line > last) {
+      change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
+                text: [change.text[0]], origin: change.origin};
+    }
+
+    change.removed = getBetween(doc, change.from, change.to);
+
+    if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
+    if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
+    else { updateDoc(doc, change, spans); }
+    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
+
+    if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
+      { doc.cantEdit = false; }
+  }
+
+  // Handle the interaction of a change to a document with the editor
+  // that this document is part of.
+  function makeChangeSingleDocInEditor(cm, change, spans) {
+    var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
+
+    var recomputeMaxLength = false, checkWidthStart = from.line;
+    if (!cm.options.lineWrapping) {
+      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
+      doc.iter(checkWidthStart, to.line + 1, function (line) {
+        if (line == display.maxLine) {
+          recomputeMaxLength = true;
+          return true
+        }
+      });
+    }
+
+    if (doc.sel.contains(change.from, change.to) > -1)
+      { signalCursorActivity(cm); }
+
+    updateDoc(doc, change, spans, estimateHeight(cm));
+
+    if (!cm.options.lineWrapping) {
+      doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
+        var len = lineLength(line);
+        if (len > display.maxLineLength) {
+          display.maxLine = line;
+          display.maxLineLength = len;
+          display.maxLineChanged = true;
+          recomputeMaxLength = false;
+        }
+      });
+      if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
+    }
+
+    retreatFrontier(doc, from.line);
+    startWorker(cm, 400);
+
+    var lendiff = change.text.length - (to.line - from.line) - 1;
+    // Remember that these lines changed, for updating the display
+    if (change.full)
+      { regChange(cm); }
+    else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
+      { regLineChange(cm, from.line, "text"); }
+    else
+      { regChange(cm, from.line, to.line + 1, lendiff); }
+
+    var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
+    if (changeHandler || changesHandler) {
+      var obj = {
+        from: from, to: to,
+        text: change.text,
+        removed: change.removed,
+        origin: change.origin
+      };
+      if (changeHandler) { signalLater(cm, "change", cm, obj); }
+      if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
+    }
+    cm.display.selForContextMenu = null;
+  }
+
+  function replaceRange(doc, code, from, to, origin) {
+    var assign;
+
+    if (!to) { to = from; }
+    if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
+    if (typeof code == "string") { code = doc.splitLines(code); }
+    makeChange(doc, {from: from, to: to, text: code, origin: origin});
+  }
+
+  // Rebasing/resetting history to deal with externally-sourced changes
+
+  function rebaseHistSelSingle(pos, from, to, diff) {
+    if (to < pos.line) {
+      pos.line += diff;
+    } else if (from < pos.line) {
+      pos.line = from;
+      pos.ch = 0;
+    }
+  }
+
+  // Tries to rebase an array of history events given a change in the
+  // document. If the change touches the same lines as the event, the
+  // event, and everything 'behind' it, is discarded. If the change is
+  // before the event, the event's positions are updated. Uses a
+  // copy-on-write scheme for the positions, to avoid having to
+  // reallocate them all on every rebase, but also avoid problems with
+  // shared position objects being unsafely updated.
+  function rebaseHistArray(array, from, to, diff) {
+    for (var i = 0; i < array.length; ++i) {
+      var sub = array[i], ok = true;
+      if (sub.ranges) {
+        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
+        for (var j = 0; j < sub.ranges.length; j++) {
+          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
+          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
+        }
+        continue
+      }
+      for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
+        var cur = sub.changes[j$1];
+        if (to < cur.from.line) {
+          cur.from = Pos(cur.from.line + diff, cur.from.ch);
+          cur.to = Pos(cur.to.line + diff, cur.to.ch);
+        } else if (from <= cur.to.line) {
+          ok = false;
+          break
+        }
+      }
+      if (!ok) {
+        array.splice(0, i + 1);
+        i = 0;
+      }
+    }
+  }
+
+  function rebaseHist(hist, change) {
+    var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
+    rebaseHistArray(hist.done, from, to, diff);
+    rebaseHistArray(hist.undone, from, to, diff);
+  }
+
+  // Utility for applying a change to a line by handle or number,
+  // returning the number and optionally registering the line as
+  // changed.
+  function changeLine(doc, handle, changeType, op) {
+    var no = handle, line = handle;
+    if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
+    else { no = lineNo(handle); }
+    if (no == null) { return null }
+    if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
+    return line
+  }
+
+  // The document is represented as a BTree consisting of leaves, with
+  // chunk of lines in them, and branches, with up to ten leaves or
+  // other branch nodes below them. The top node is always a branch
+  // node, and is the document object itself (meaning it has
+  // additional methods and properties).
+  //
+  // All nodes have parent links. The tree is used both to go from
+  // line numbers to line objects, and to go from objects to numbers.
+  // It also indexes by height, and is used to convert between height
+  // and line object, and to find the total height of the document.
+  //
+  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
+
+  function LeafChunk(lines) {
+    var this$1 = this;
+
+    this.lines = lines;
+    this.parent = null;
+    var height = 0;
+    for (var i = 0; i < lines.length; ++i) {
+      lines[i].parent = this$1;
+      height += lines[i].height;
+    }
+    this.height = height;
+  }
+
+  LeafChunk.prototype = {
+    chunkSize: function() { return this.lines.length },
+
+    // Remove the n lines at offset 'at'.
+    removeInner: function(at, n) {
+      var this$1 = this;
+
+      for (var i = at, e = at + n; i < e; ++i) {
+        var line = this$1.lines[i];
+        this$1.height -= line.height;
+        cleanUpLine(line);
+        signalLater(line, "delete");
+      }
+      this.lines.splice(at, n);
+    },
+
+    // Helper used to collapse a small branch into a single leaf.
+    collapse: function(lines) {
+      lines.push.apply(lines, this.lines);
+    },
+
+    // Insert the given array of lines at offset 'at', count them as
+    // having the given height.
+    insertInner: function(at, lines, height) {
+      var this$1 = this;
+
+      this.height += height;
+      this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
+      for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
+    },
+
+    // Used to iterate over a part of the tree.
+    iterN: function(at, n, op) {
+      var this$1 = this;
+
+      for (var e = at + n; at < e; ++at)
+        { if (op(this$1.lines[at])) { return true } }
+    }
+  };
+
+  function BranchChunk(children) {
+    var this$1 = this;
+
+    this.children = children;
+    var size = 0, height = 0;
+    for (var i = 0; i < children.length; ++i) {
+      var ch = children[i];
+      size += ch.chunkSize(); height += ch.height;
+      ch.parent = this$1;
+    }
+    this.size = size;
+    this.height = height;
+    this.parent = null;
+  }
+
+  BranchChunk.prototype = {
+    chunkSize: function() { return this.size },
+
+    removeInner: function(at, n) {
+      var this$1 = this;
+
+      this.size -= n;
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this$1.children[i], sz = child.chunkSize();
+        if (at < sz) {
+          var rm = Math.min(n, sz - at), oldHeight = child.height;
+          child.removeInner(at, rm);
+          this$1.height -= oldHeight - child.height;
+          if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
+          if ((n -= rm) == 0) { break }
+          at = 0;
+        } else { at -= sz; }
+      }
+      // If the result is smaller than 25 lines, ensure that it is a
+      // single leaf node.
+      if (this.size - n < 25 &&
+          (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
+        var lines = [];
+        this.collapse(lines);
+        this.children = [new LeafChunk(lines)];
+        this.children[0].parent = this;
+      }
+    },
+
+    collapse: function(lines) {
+      var this$1 = this;
+
+      for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
+    },
+
+    insertInner: function(at, lines, height) {
+      var this$1 = this;
+
+      this.size += lines.length;
+      this.height += height;
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this$1.children[i], sz = child.chunkSize();
+        if (at <= sz) {
+          child.insertInner(at, lines, height);
+          if (child.lines && child.lines.length > 50) {
+            // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
+            // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
+            var remaining = child.lines.length % 25 + 25;
+            for (var pos = remaining; pos < child.lines.length;) {
+              var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
+              child.height -= leaf.height;
+              this$1.children.splice(++i, 0, leaf);
+              leaf.parent = this$1;
+            }
+            child.lines = child.lines.slice(0, remaining);
+            this$1.maybeSpill();
+          }
+          break
+        }
+        at -= sz;
+      }
+    },
+
+    // When a node has grown, check whether it should be split.
+    maybeSpill: function() {
+      if (this.children.length <= 10) { return }
+      var me = this;
+      do {
+        var spilled = me.children.splice(me.children.length - 5, 5);
+        var sibling = new BranchChunk(spilled);
+        if (!me.parent) { // Become the parent node
+          var copy = new BranchChunk(me.children);
+          copy.parent = me;
+          me.children = [copy, sibling];
+          me = copy;
+       } else {
+          me.size -= sibling.size;
+          me.height -= sibling.height;
+          var myIndex = indexOf(me.parent.children, me);
+          me.parent.children.splice(myIndex + 1, 0, sibling);
+        }
+        sibling.parent = me.parent;
+      } while (me.children.length > 10)
+      me.parent.maybeSpill();
+    },
+
+    iterN: function(at, n, op) {
+      var this$1 = this;
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this$1.children[i], sz = child.chunkSize();
+        if (at < sz) {
+          var used = Math.min(n, sz - at);
+          if (child.iterN(at, used, op)) { return true }
+          if ((n -= used) == 0) { break }
+          at = 0;
+        } else { at -= sz; }
+      }
+    }
+  };
+
+  // Line widgets are block elements displayed above or below a line.
+
+  var LineWidget = function(doc, node, options) {
+    var this$1 = this;
+
+    if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
+      { this$1[opt] = options[opt]; } } }
+    this.doc = doc;
+    this.node = node;
+  };
+
+  LineWidget.prototype.clear = function () {
+      var this$1 = this;
+
+    var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
+    if (no == null || !ws) { return }
+    for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
+    if (!ws.length) { line.widgets = null; }
+    var height = widgetHeight(this);
+    updateLineHeight(line, Math.max(0, line.height - height));
+    if (cm) {
+      runInOp(cm, function () {
+        adjustScrollWhenAboveVisible(cm, line, -height);
+        regLineChange(cm, no, "widget");
+      });
+      signalLater(cm, "lineWidgetCleared", cm, this, no);
+    }
+  };
+
+  LineWidget.prototype.changed = function () {
+      var this$1 = this;
+
+    var oldH = this.height, cm = this.doc.cm, line = this.line;
+    this.height = null;
+    var diff = widgetHeight(this) - oldH;
+    if (!diff) { return }
+    if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
+    if (cm) {
+      runInOp(cm, function () {
+        cm.curOp.forceUpdate = true;
+        adjustScrollWhenAboveVisible(cm, line, diff);
+        signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
+      });
+    }
+  };
+  eventMixin(LineWidget);
+
+  function adjustScrollWhenAboveVisible(cm, line, diff) {
+    if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
+      { addToScrollTop(cm, diff); }
+  }
+
+  function addLineWidget(doc, handle, node, options) {
+    var widget = new LineWidget(doc, node, options);
+    var cm = doc.cm;
+    if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
+    changeLine(doc, handle, "widget", function (line) {
+      var widgets = line.widgets || (line.widgets = []);
+      if (widget.insertAt == null) { widgets.push(widget); }
+      else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
+      widget.line = line;
+      if (cm && !lineIsHidden(doc, line)) {
+        var aboveVisible = heightAtLine(line) < doc.scrollTop;
+        updateLineHeight(line, line.height + widgetHeight(widget));
+        if (aboveVisible) { addToScrollTop(cm, widget.height); }
+        cm.curOp.forceUpdate = true;
+      }
+      return true
+    });
+    if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
+    return widget
+  }
+
+  // TEXTMARKERS
+
+  // Created with markText and setBookmark methods. A TextMarker is a
+  // handle that can be used to clear or find a marked position in the
+  // document. Line objects hold arrays (markedSpans) containing
+  // {from, to, marker} object pointing to such marker objects, and
+  // indicating that such a marker is present on that line. Multiple
+  // lines may point to the same marker when it spans across lines.
+  // The spans will have null for their from/to properties when the
+  // marker continues beyond the start/end of the line. Markers have
+  // links back to the lines they currently touch.
+
+  // Collapsed markers have unique ids, in order to be able to order
+  // them, which is needed for uniquely determining an outer marker
+  // when they overlap (they may nest, but not partially overlap).
+  var nextMarkerId = 0;
+
+  var TextMarker = function(doc, type) {
+    this.lines = [];
+    this.type = type;
+    this.doc = doc;
+    this.id = ++nextMarkerId;
+  };
+
+  // Clear the marker.
+  TextMarker.prototype.clear = function () {
+      var this$1 = this;
+
+    if (this.explicitlyCleared) { return }
+    var cm = this.doc.cm, withOp = cm && !cm.curOp;
+    if (withOp) { startOperation(cm); }
+    if (hasHandler(this, "clear")) {
+      var found = this.find();
+      if (found) { signalLater(this, "clear", found.from, found.to); }
+    }
+    var min = null, max = null;
+    for (var i = 0; i < this.lines.length; ++i) {
+      var line = this$1.lines[i];
+      var span = getMarkedSpanFor(line.markedSpans, this$1);
+      if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
+      else if (cm) {
+        if (span.to != null) { max = lineNo(line); }
+        if (span.from != null) { min = lineNo(line); }
+      }
+      line.markedSpans = removeMarkedSpan(line.markedSpans, span);
+      if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
+        { updateLineHeight(line, textHeight(cm.display)); }
+    }
+    if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
+      var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
+      if (len > cm.display.maxLineLength) {
+        cm.display.maxLine = visual;
+        cm.display.maxLineLength = len;
+        cm.display.maxLineChanged = true;
+      }
+    } }
+
+    if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
+    this.lines.length = 0;
+    this.explicitlyCleared = true;
+    if (this.atomic && this.doc.cantEdit) {
+      this.doc.cantEdit = false;
+      if (cm) { reCheckSelection(cm.doc); }
+    }
+    if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
+    if (withOp) { endOperation(cm); }
+    if (this.parent) { this.parent.clear(); }
+  };
+
+  // Find the position of the marker in the document. Returns a {from,
+  // to} object by default. Side can be passed to get a specific side
+  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
+  // Pos objects returned contain a line object, rather than a line
+  // number (used to prevent looking up the same line twice).
+  TextMarker.prototype.find = function (side, lineObj) {
+      var this$1 = this;
+
+    if (side == null && this.type == "bookmark") { side = 1; }
+    var from, to;
+    for (var i = 0; i < this.lines.length; ++i) {
+      var line = this$1.lines[i];
+      var span = getMarkedSpanFor(line.markedSpans, this$1);
+      if (span.from != null) {
+        from = Pos(lineObj ? line : lineNo(line), span.from);
+        if (side == -1) { return from }
+      }
+      if (span.to != null) {
+        to = Pos(lineObj ? line : lineNo(line), span.to);
+        if (side == 1) { return to }
+      }
+    }
+    return from && {from: from, to: to}
+  };
+
+  // Signals that the marker's widget changed, and surrounding layout
+  // should be recomputed.
+  TextMarker.prototype.changed = function () {
+      var this$1 = this;
+
+    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
+    if (!pos || !cm) { return }
+    runInOp(cm, function () {
+      var line = pos.line, lineN = lineNo(pos.line);
+      var view = findViewForLine(cm, lineN);
+      if (view) {
+        clearLineMeasurementCacheFor(view);
+        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
+      }
+      cm.curOp.updateMaxLine = true;
+      if (!lineIsHidden(widget.doc, line) && widget.height != null) {
+        var oldHeight = widget.height;
+        widget.height = null;
+        var dHeight = widgetHeight(widget) - oldHeight;
+        if (dHeight)
+          { updateLineHeight(line, line.height + dHeight); }
+      }
+      signalLater(cm, "markerChanged", cm, this$1);
+    });
+  };
+
+  TextMarker.prototype.attachLine = function (line) {
+    if (!this.lines.length && this.doc.cm) {
+      var op = this.doc.cm.curOp;
+      if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
+        { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
+    }
+    this.lines.push(line);
+  };
+
+  TextMarker.prototype.detachLine = function (line) {
+    this.lines.splice(indexOf(this.lines, line), 1);
+    if (!this.lines.length && this.doc.cm) {
+      var op = this.doc.cm.curOp
+      ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
+    }
+  };
+  eventMixin(TextMarker);
+
+  // Create a marker, wire it up to the right lines, and
+  function markText(doc, from, to, options, type) {
+    // Shared markers (across linked documents) are handled separately
+    // (markTextShared will call out to this again, once per
+    // document).
+    if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
+    // Ensure we are in an operation.
+    if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
+
+    var marker = new TextMarker(doc, type), diff = cmp(from, to);
+    if (options) { copyObj(options, marker, false); }
+    // Don't connect empty markers unless clearWhenEmpty is false
+    if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
+      { return marker }
+    if (marker.replacedWith) {
+      // Showing up as a widget implies collapsed (widget replaces text)
+      marker.collapsed = true;
+      marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
+      if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
+      if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
+    }
+    if (marker.collapsed) {
+      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
+          from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
+        { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
+      seeCollapsedSpans();
+    }
+
+    if (marker.addToHistory)
+      { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
+
+    var curLine = from.line, cm = doc.cm, updateMaxLine;
+    doc.iter(curLine, to.line + 1, function (line) {
+      if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
+        { updateMaxLine = true; }
+      if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
+      addMarkedSpan(line, new MarkedSpan(marker,
+                                         curLine == from.line ? from.ch : null,
+                                         curLine == to.line ? to.ch : null));
+      ++curLine;
+    });
+    // lineIsHidden depends on the presence of the spans, so needs a second pass
+    if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
+      if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
+    }); }
+
+    if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
+
+    if (marker.readOnly) {
+      seeReadOnlySpans();
+      if (doc.history.done.length || doc.history.undone.length)
+        { doc.clearHistory(); }
+    }
+    if (marker.collapsed) {
+      marker.id = ++nextMarkerId;
+      marker.atomic = true;
+    }
+    if (cm) {
+      // Sync editor state
+      if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
+      if (marker.collapsed)
+        { regChange(cm, from.line, to.line + 1); }
+      else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
+               marker.attributes || marker.title)
+        { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
+      if (marker.atomic) { reCheckSelection(cm.doc); }
+      signalLater(cm, "markerAdded", cm, marker);
+    }
+    return marker
+  }
+
+  // SHARED TEXTMARKERS
+
+  // A shared marker spans multiple linked documents. It is
+  // implemented as a meta-marker-object controlling multiple normal
+  // markers.
+  var SharedTextMarker = function(markers, primary) {
+    var this$1 = this;
+
+    this.markers = markers;
+    this.primary = primary;
+    for (var i = 0; i < markers.length; ++i)
+      { markers[i].parent = this$1; }
+  };
+
+  SharedTextMarker.prototype.clear = function () {
+      var this$1 = this;
+
+    if (this.explicitlyCleared) { return }
+    this.explicitlyCleared = true;
+    for (var i = 0; i < this.markers.length; ++i)
+      { this$1.markers[i].clear(); }
+    signalLater(this, "clear");
+  };
+
+  SharedTextMarker.prototype.find = function (side, lineObj) {
+    return this.primary.find(side, lineObj)
+  };
+  eventMixin(SharedTextMarker);
+
+  function markTextShared(doc, from, to, options, type) {
+    options = copyObj(options);
+    options.shared = false;
+    var markers = [markText(doc, from, to, options, type)], primary = markers[0];
+    var widget = options.widgetNode;
+    linkedDocs(doc, function (doc) {
+      if (widget) { options.widgetNode = widget.cloneNode(true); }
+      markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
+      for (var i = 0; i < doc.linked.length; ++i)
+        { if (doc.linked[i].isParent) { return } }
+      primary = lst(markers);
+    });
+    return new SharedTextMarker(markers, primary)
+  }
+
+  function findSharedMarkers(doc) {
+    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
+  }
+
+  function copySharedMarkers(doc, markers) {
+    for (var i = 0; i < markers.length; i++) {
+      var marker = markers[i], pos = marker.find();
+      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
+      if (cmp(mFrom, mTo)) {
+        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
+        marker.markers.push(subMark);
+        subMark.parent = marker;
+      }
+    }
+  }
+
+  function detachSharedMarkers(markers) {
+    var loop = function ( i ) {
+      var marker = markers[i], linked = [marker.primary.doc];
+      linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
+      for (var j = 0; j < marker.markers.length; j++) {
+        var subMarker = marker.markers[j];
+        if (indexOf(linked, subMarker.doc) == -1) {
+          subMarker.parent = null;
+          marker.markers.splice(j--, 1);
+        }
+      }
+    };
+
+    for (var i = 0; i < markers.length; i++) loop( i );
+  }
+
+  var nextDocId = 0;
+  var Doc = function(text, mode, firstLine, lineSep, direction) {
+    if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
+    if (firstLine == null) { firstLine = 0; }
+
+    BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
+    this.first = firstLine;
+    this.scrollTop = this.scrollLeft = 0;
+    this.cantEdit = false;
+    this.cleanGeneration = 1;
+    this.modeFrontier = this.highlightFrontier = firstLine;
+    var start = Pos(firstLine, 0);
+    this.sel = simpleSelection(start);
+    this.history = new History(null);
+    this.id = ++nextDocId;
+    this.modeOption = mode;
+    this.lineSep = lineSep;
+    this.direction = (direction == "rtl") ? "rtl" : "ltr";
+    this.extend = false;
+
+    if (typeof text == "string") { text = this.splitLines(text); }
+    updateDoc(this, {from: start, to: start, text: text});
+    setSelection(this, simpleSelection(start), sel_dontScroll);
+  };
+
+  Doc.prototype = createObj(BranchChunk.prototype, {
+    constructor: Doc,
+    // Iterate over the document. Supports two forms -- with only one
+    // argument, it calls that for each line in the document. With
+    // three, it iterates over the range given by the first two (with
+    // the second being non-inclusive).
+    iter: function(from, to, op) {
+      if (op) { this.iterN(from - this.first, to - from, op); }
+      else { this.iterN(this.first, this.first + this.size, from); }
+    },
+
+    // Non-public interface for adding and removing lines.
+    insert: function(at, lines) {
+      var height = 0;
+      for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
+      this.insertInner(at - this.first, lines, height);
+    },
+    remove: function(at, n) { this.removeInner(at - this.first, n); },
+
+    // From here, the methods are part of the public interface. Most
+    // are also available from CodeMirror (editor) instances.
+
+    getValue: function(lineSep) {
+      var lines = getLines(this, this.first, this.first + this.size);
+      if (lineSep === false) { return lines }
+      return lines.join(lineSep || this.lineSeparator())
+    },
+    setValue: docMethodOp(function(code) {
+      var top = Pos(this.first, 0), last = this.first + this.size - 1;
+      makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
+                        text: this.splitLines(code), origin: "setValue", full: true}, true);
+      if (this.cm) { scrollToCoords(this.cm, 0, 0); }
+      setSelection(this, simpleSelection(top), sel_dontScroll);
+    }),
+    replaceRange: function(code, from, to, origin) {
+      from = clipPos(this, from);
+      to = to ? clipPos(this, to) : from;
+      replaceRange(this, code, from, to, origin);
+    },
+    getRange: function(from, to, lineSep) {
+      var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
+      if (lineSep === false) { return lines }
+      return lines.join(lineSep || this.lineSeparator())
+    },
+
+    getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
+
+    getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
+    getLineNumber: function(line) {return lineNo(line)},
+
+    getLineHandleVisualStart: function(line) {
+      if (typeof line == "number") { line = getLine(this, line); }
+      return visualLine(line)
+    },
+
+    lineCount: function() {return this.size},
+    firstLine: function() {return this.first},
+    lastLine: function() {return this.first + this.size - 1},
+
+    clipPos: function(pos) {return clipPos(this, pos)},
+
+    getCursor: function(start) {
+      var range$$1 = this.sel.primary(), pos;
+      if (start == null || start == "head") { pos = range$$1.head; }
+      else if (start == "anchor") { pos = range$$1.anchor; }
+      else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
+      else { pos = range$$1.from(); }
+      return pos
+    },
+    listSelections: function() { return this.sel.ranges },
+    somethingSelected: function() {return this.sel.somethingSelected()},
+
+    setCursor: docMethodOp(function(line, ch, options) {
+      setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
+    }),
+    setSelection: docMethodOp(function(anchor, head, options) {
+      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
+    }),
+    extendSelection: docMethodOp(function(head, other, options) {
+      extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
+    }),
+    extendSelections: docMethodOp(function(heads, options) {
+      extendSelections(this, clipPosArray(this, heads), options);
+    }),
+    extendSelectionsBy: docMethodOp(function(f, options) {
+      var heads = map(this.sel.ranges, f);
+      extendSelections(this, clipPosArray(this, heads), options);
+    }),
+    setSelections: docMethodOp(function(ranges, primary, options) {
+      var this$1 = this;
+
+      if (!ranges.length) { return }
+      var out = [];
+      for (var i = 0; i < ranges.length; i++)
+        { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
+                           clipPos(this$1, ranges[i].head)); }
+      if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
+      setSelection(this, normalizeSelection(this.cm, out, primary), options);
+    }),
+    addSelection: docMethodOp(function(anchor, head, options) {
+      var ranges = this.sel.ranges.slice(0);
+      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
+      setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
+    }),
+
+    getSelection: function(lineSep) {
+      var this$1 = this;
+
+      var ranges = this.sel.ranges, lines;
+      for (var i = 0; i < ranges.length; i++) {
+        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
+        lines = lines ? lines.concat(sel) : sel;
+      }
+      if (lineSep === false) { return lines }
+      else { return lines.join(lineSep || this.lineSeparator()) }
+    },
+    getSelections: function(lineSep) {
+      var this$1 = this;
+
+      var parts = [], ranges = this.sel.ranges;
+      for (var i = 0; i < ranges.length; i++) {
+        var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
+        if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
+        parts[i] = sel;
+      }
+      return parts
+    },
+    replaceSelection: function(code, collapse, origin) {
+      var dup = [];
+      for (var i = 0; i < this.sel.ranges.length; i++)
+        { dup[i] = code; }
+      this.replaceSelections(dup, collapse, origin || "+input");
+    },
+    replaceSelections: docMethodOp(function(code, collapse, origin) {
+      var this$1 = this;
+
+      var changes = [], sel = this.sel;
+      for (var i = 0; i < sel.ranges.length; i++) {
+        var range$$1 = sel.ranges[i];
+        changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
+      }
+      var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
+      for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
+        { makeChange(this$1, changes[i$1]); }
+      if (newSel) { setSelectionReplaceHistory(this, newSel); }
+      else if (this.cm) { ensureCursorVisible(this.cm); }
+    }),
+    undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
+    redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
+    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
+    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
+
+    setExtending: function(val) {this.extend = val;},
+    getExtending: function() {return this.extend},
+
+    historySize: function() {
+      var hist = this.history, done = 0, undone = 0;
+      for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
+      for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
+      return {undo: done, redo: undone}
+    },
+    clearHistory: function() {this.history = new History(this.history.maxGeneration);},
+
+    markClean: function() {
+      this.cleanGeneration = this.changeGeneration(true);
+    },
+    changeGeneration: function(forceSplit) {
+      if (forceSplit)
+        { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
+      return this.history.generation
+    },
+    isClean: function (gen) {
+      return this.history.generation == (gen || this.cleanGeneration)
+    },
+
+    getHistory: function() {
+      return {done: copyHistoryArray(this.history.done),
+              undone: copyHistoryArray(this.history.undone)}
+    },
+    setHistory: function(histData) {
+      var hist = this.history = new History(this.history.maxGeneration);
+      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
+      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
+    },
+
+    setGutterMarker: docMethodOp(function(line, gutterID, value) {
+      return changeLine(this, line, "gutter", function (line) {
+        var markers = line.gutterMarkers || (line.gutterMarkers = {});
+        markers[gutterID] = value;
+        if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
+        return true
+      })
+    }),
+
+    clearGutter: docMethodOp(function(gutterID) {
+      var this$1 = this;
+
+      this.iter(function (line) {
+        if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
+          changeLine(this$1, line, "gutter", function () {
+            line.gutterMarkers[gutterID] = null;
+            if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
+            return true
+          });
+        }
+      });
+    }),
+
+    lineInfo: function(line) {
+      var n;
+      if (typeof line == "number") {
+        if (!isLine(this, line)) { return null }
+        n = line;
+        line = getLine(this, line);
+        if (!line) { return null }
+      } else {
+        n = lineNo(line);
+        if (n == null) { return null }
+      }
+      return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
+              textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
+              widgets: line.widgets}
+    },
+
+    addLineClass: docMethodOp(function(handle, where, cls) {
+      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
+        var prop = where == "text" ? "textClass"
+                 : where == "background" ? "bgClass"
+                 : where == "gutter" ? "gutterClass" : "wrapClass";
+        if (!line[prop]) { line[prop] = cls; }
+        else if (classTest(cls).test(line[prop])) { return false }
+        else { line[prop] += " " + cls; }
+        return true
+      })
+    }),
+    removeLineClass: docMethodOp(function(handle, where, cls) {
+      return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
+        var prop = where == "text" ? "textClass"
+                 : where == "background" ? "bgClass"
+                 : where == "gutter" ? "gutterClass" : "wrapClass";
+        var cur = line[prop];
+        if (!cur) { return false }
+        else if (cls == null) { line[prop] = null; }
+        else {
+          var found = cur.match(classTest(cls));
+          if (!found) { return false }
+          var end = found.index + found[0].length;
+          line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
+        }
+        return true
+      })
+    }),
+
+    addLineWidget: docMethodOp(function(handle, node, options) {
+      return addLineWidget(this, handle, node, options)
+    }),
+    removeLineWidget: function(widget) { widget.clear(); },
+
+    markText: function(from, to, options) {
+      return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
+    },
+    setBookmark: function(pos, options) {
+      var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
+                      insertLeft: options && options.insertLeft,
+                      clearWhenEmpty: false, shared: options && options.shared,
+                      handleMouseEvents: options && options.handleMouseEvents};
+      pos = clipPos(this, pos);
+      return markText(this, pos, pos, realOpts, "bookmark")
+    },
+    findMarksAt: function(pos) {
+      pos = clipPos(this, pos);
+      var markers = [], spans = getLine(this, pos.line).markedSpans;
+      if (spans) { for (var i = 0; i < spans.length; ++i) {
+        var span = spans[i];
+        if ((span.from == null || span.from <= pos.ch) &&
+            (span.to == null || span.to >= pos.ch))
+          { markers.push(span.marker.parent || span.marker); }
+      } }
+      return markers
+    },
+    findMarks: function(from, to, filter) {
+      from = clipPos(this, from); to = clipPos(this, to);
+      var found = [], lineNo$$1 = from.line;
+      this.iter(from.line, to.line + 1, function (line) {
+        var spans = line.markedSpans;
+        if (spans) { for (var i = 0; i < spans.length; i++) {
+          var span = spans[i];
+          if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
+                span.from == null && lineNo$$1 != from.line ||
+                span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
+              (!filter || filter(span.marker)))
+            { found.push(span.marker.parent || span.marker); }
+        } }
+        ++lineNo$$1;
+      });
+      return found
+    },
+    getAllMarks: function() {
+      var markers = [];
+      this.iter(function (line) {
+        var sps = line.markedSpans;
+        if (sps) { for (var i = 0; i < sps.length; ++i)
+          { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
+      });
+      return markers
+    },
+
+    posFromIndex: function(off) {
+      var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
+      this.iter(function (line) {
+        var sz = line.text.length + sepSize;
+        if (sz > off) { ch = off; return true }
+        off -= sz;
+        ++lineNo$$1;
+      });
+      return clipPos(this, Pos(lineNo$$1, ch))
+    },
+    indexFromPos: function (coords) {
+      coords = clipPos(this, coords);
+      var index = coords.ch;
+      if (coords.line < this.first || coords.ch < 0) { return 0 }
+      var sepSize = this.lineSeparator().length;
+      this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
+        index += line.text.length + sepSize;
+      });
+      return index
+    },
+
+    copy: function(copyHistory) {
+      var doc = new Doc(getLines(this, this.first, this.first + this.size),
+                        this.modeOption, this.first, this.lineSep, this.direction);
+      doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
+      doc.sel = this.sel;
+      doc.extend = false;
+      if (copyHistory) {
+        doc.history.undoDepth = this.history.undoDepth;
+        doc.setHistory(this.getHistory());
+      }
+      return doc
+    },
+
+    linkedDoc: function(options) {
+      if (!options) { options = {}; }
+      var from = this.first, to = this.first + this.size;
+      if (options.from != null && options.from > from) { from = options.from; }
+      if (options.to != null && options.to < to) { to = options.to; }
+      var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
+      if (options.sharedHist) { copy.history = this.history
+      ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
+      copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
+      copySharedMarkers(copy, findSharedMarkers(this));
+      return copy
+    },
+    unlinkDoc: function(other) {
+      var this$1 = this;
+
+      if (other instanceof CodeMirror) { other = other.doc; }
+      if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
+        var link = this$1.linked[i];
+        if (link.doc != other) { continue }
+        this$1.linked.splice(i, 1);
+        other.unlinkDoc(this$1);
+        detachSharedMarkers(findSharedMarkers(this$1));
+        break
+      } }
+      // If the histories were shared, split them again
+      if (other.history == this.history) {
+        var splitIds = [other.id];
+        linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
+        other.history = new History(null);
+        other.history.done = copyHistoryArray(this.history.done, splitIds);
+        other.history.undone = copyHistoryArray(this.history.undone, splitIds);
+      }
+    },
+    iterLinkedDocs: function(f) {linkedDocs(this, f);},
+
+    getMode: function() {return this.mode},
+    getEditor: function() {return this.cm},
+
+    splitLines: function(str) {
+      if (this.lineSep) { return str.split(this.lineSep) }
+      return splitLinesAuto(str)
+    },
+    lineSeparator: function() { return this.lineSep || "\n" },
+
+    setDirection: docMethodOp(function (dir) {
+      if (dir != "rtl") { dir = "ltr"; }
+      if (dir == this.direction) { return }
+      this.direction = dir;
+      this.iter(function (line) { return line.order = null; });
+      if (this.cm) { directionChanged(this.cm); }
+    })
+  });
+
+  // Public alias.
+  Doc.prototype.eachLine = Doc.prototype.iter;
+
+  // Kludge to work around strange IE behavior where it'll sometimes
+  // re-fire a series of drag-related events right after the drop (#1551)
+  var lastDrop = 0;
+
+  function onDrop(e) {
+    var cm = this;
+    clearDragCursor(cm);
+    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
+      { return }
+    e_preventDefault(e);
+    if (ie) { lastDrop = +new Date; }
+    var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
+    if (!pos || cm.isReadOnly()) { return }
+    // Might be a file drop, in which case we simply extract the text
+    // and insert it.
+    if (files && files.length && window.FileReader && window.File) {
+      var n = files.length, text = Array(n), read = 0;
+      var loadFile = function (file, i) {
+        if (cm.options.allowDropFileTypes &&
+            indexOf(cm.options.allowDropFileTypes, file.type) == -1)
+          { return }
+
+        var reader = new FileReader;
+        reader.onload = operation(cm, function () {
+          var content = reader.result;
+          if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
+          text[i] = content;
+          if (++read == n) {
+            pos = clipPos(cm.doc, pos);
+            var change = {from: pos, to: pos,
+                          text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
+                          origin: "paste"};
+            makeChange(cm.doc, change);
+            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
+          }
+        });
+        reader.readAsText(file);
+      };
+      for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
+    } else { // Normal drop
+      // Don't do a replace if the drop happened inside of the selected text.
+      if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
+        cm.state.draggingText(e);
+        // Ensure the editor is re-focused
+        setTimeout(function () { return cm.display.input.focus(); }, 20);
+        return
+      }
+      try {
+        var text$1 = e.dataTransfer.getData("Text");
+        if (text$1) {
+          var selected;
+          if (cm.state.draggingText && !cm.state.draggingText.copy)
+            { selected = cm.listSelections(); }
+          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
+          if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
+            { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
+          cm.replaceSelection(text$1, "around", "paste");
+          cm.display.input.focus();
+        }
+      }
+      catch(e){}
+    }
+  }
+
+  function onDragStart(cm, e) {
+    if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
+    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
+
+    e.dataTransfer.setData("Text", cm.getSelection());
+    e.dataTransfer.effectAllowed = "copyMove";
+
+    // Use dummy image instead of default browsers image.
+    // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
+    if (e.dataTransfer.setDragImage && !safari) {
+      var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
+      img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+      if (presto) {
+        img.width = img.height = 1;
+        cm.display.wrapper.appendChild(img);
+        // Force a relayout, or Opera won't use our image for some obscure reason
+        img._top = img.offsetTop;
+      }
+      e.dataTransfer.setDragImage(img, 0, 0);
+      if (presto) { img.parentNode.removeChild(img); }
+    }
+  }
+
+  function onDragOver(cm, e) {
+    var pos = posFromMouse(cm, e);
+    if (!pos) { return }
+    var frag = document.createDocumentFragment();
+    drawSelectionCursor(cm, pos, frag);
+    if (!cm.display.dragCursor) {
+      cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
+      cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
+    }
+    removeChildrenAndAdd(cm.display.dragCursor, frag);
+  }
+
+  function clearDragCursor(cm) {
+    if (cm.display.dragCursor) {
+      cm.display.lineSpace.removeChild(cm.display.dragCursor);
+      cm.display.dragCursor = null;
+    }
+  }
+
+  // These must be handled carefully, because naively registering a
+  // handler for each editor will cause the editors to never be
+  // garbage collected.
+
+  function forEachCodeMirror(f) {
+    if (!document.getElementsByClassName) { return }
+    var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
+    for (var i = 0; i < byClass.length; i++) {
+      var cm = byClass[i].CodeMirror;
+      if (cm) { editors.push(cm); }
+    }
+    if (editors.length) { editors[0].operation(function () {
+      for (var i = 0; i < editors.length; i++) { f(editors[i]); }
+    }); }
+  }
+
+  var globalsRegistered = false;
+  function ensureGlobalHandlers() {
+    if (globalsRegistered) { return }
+    registerGlobalHandlers();
+    globalsRegistered = true;
+  }
+  function registerGlobalHandlers() {
+    // When the window resizes, we need to refresh active editors.
+    var resizeTimer;
+    on(window, "resize", function () {
+      if (resizeTimer == null) { resizeTimer = setTimeout(function () {
+        resizeTimer = null;
+        forEachCodeMirror(onResize);
+      }, 100); }
+    });
+    // When the window loses focus, we want to show the editor as blurred
+    on(window, "blur", function () { return forEachCodeMirror(onBlur); });
+  }
+  // Called when the window resizes
+  function onResize(cm) {
+    var d = cm.display;
+    // Might be a text scaling operation, clear size caches.
+    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
+    d.scrollbarsClipped = false;
+    cm.setSize();
+  }
+
+  var keyNames = {
+    3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+    19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+    36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+    46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
+    106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
+    173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
+    221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
+    63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
+  };
+
+  // Number keys
+  for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
+  // Alphabetic keys
+  for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
+  // Function keys
+  for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
+
+  var keyMap = {};
+
+  keyMap.basic = {
+    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
+    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
+    "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
+    "Tab": "defaultTab", "Shift-Tab": "indentAuto",
+    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
+    "Esc": "singleSelection"
+  };
+  // Note that the save and find-related commands aren't defined by
+  // default. User code or addons can define them. Unknown commands
+  // are simply ignored.
+  keyMap.pcDefault = {
+    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
+    "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
+    "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+    "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
+    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
+    "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
+    "fallthrough": "basic"
+  };
+  // Very basic readline/emacs-style bindings, which are standard on Mac.
+  keyMap.emacsy = {
+    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
+    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+    "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
+    "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
+    "Ctrl-O": "openLine"
+  };
+  keyMap.macDefault = {
+    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
+    "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
+    "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
+    "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
+    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+    "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
+    "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
+    "fallthrough": ["basic", "emacsy"]
+  };
+  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
+
+  // KEYMAP DISPATCH
+
+  function normalizeKeyName(name) {
+    var parts = name.split(/-(?!$)/);
+    name = parts[parts.length - 1];
+    var alt, ctrl, shift, cmd;
+    for (var i = 0; i < parts.length - 1; i++) {
+      var mod = parts[i];
+      if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
+      else if (/^a(lt)?$/i.test(mod)) { alt = true; }
+      else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
+      else if (/^s(hift)?$/i.test(mod)) { shift = true; }
+      else { throw new Error("Unrecognized modifier name: " + mod) }
+    }
+    if (alt) { name = "Alt-" + name; }
+    if (ctrl) { name = "Ctrl-" + name; }
+    if (cmd) { name = "Cmd-" + name; }
+    if (shift) { name = "Shift-" + name; }
+    return name
+  }
+
+  // This is a kludge to keep keymaps mostly working as raw objects
+  // (backwards compatibility) while at the same time support features
+  // like normalization and multi-stroke key bindings. It compiles a
+  // new normalized keymap, and then updates the old object to reflect
+  // this.
+  function normalizeKeyMap(keymap) {
+    var copy = {};
+    for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
+      var value = keymap[keyname];
+      if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
+      if (value == "...") { delete keymap[keyname]; continue }
+
+      var keys = map(keyname.split(" "), normalizeKeyName);
+      for (var i = 0; i < keys.length; i++) {
+        var val = (void 0), name = (void 0);
+        if (i == keys.length - 1) {
+          name = keys.join(" ");
+          val = value;
+        } else {
+          name = keys.slice(0, i + 1).join(" ");
+          val = "...";
+        }
+        var prev = copy[name];
+        if (!prev) { copy[name] = val; }
+        else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
+      }
+      delete keymap[keyname];
+    } }
+    for (var prop in copy) { keymap[prop] = copy[prop]; }
+    return keymap
+  }
+
+  function lookupKey(key, map$$1, handle, context) {
+    map$$1 = getKeyMap(map$$1);
+    var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
+    if (found === false) { return "nothing" }
+    if (found === "...") { return "multi" }
+    if (found != null && handle(found)) { return "handled" }
+
+    if (map$$1.fallthrough) {
+      if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
+        { return lookupKey(key, map$$1.fallthrough, handle, context) }
+      for (var i = 0; i < map$$1.fallthrough.length; i++) {
+        var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
+        if (result) { return result }
+      }
+    }
+  }
+
+  // Modifier key presses don't count as 'real' key presses for the
+  // purpose of keymap fallthrough.
+  function isModifierKey(value) {
+    var name = typeof value == "string" ? value : keyNames[value.keyCode];
+    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
+  }
+
+  function addModifierNames(name, event, noShift) {
+    var base = name;
+    if (event.altKey && base != "Alt") { name = "Alt-" + name; }
+    if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
+    if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
+    if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
+    return name
+  }
+
+  // Look up the name of a key as indicated by an event object.
+  function keyName(event, noShift) {
+    if (presto && event.keyCode == 34 && event["char"]) { return false }
+    var name = keyNames[event.keyCode];
+    if (name == null || event.altGraphKey) { return false }
+    // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
+    // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
+    if (event.keyCode == 3 && event.code) { name = event.code; }
+    return addModifierNames(name, event, noShift)
+  }
+
+  function getKeyMap(val) {
+    return typeof val == "string" ? keyMap[val] : val
+  }
+
+  // Helper for deleting text near the selection(s), used to implement
+  // backspace, delete, and similar functionality.
+  function deleteNearSelection(cm, compute) {
+    var ranges = cm.doc.sel.ranges, kill = [];
+    // Build up a set of ranges to kill first, merging overlapping
+    // ranges.
+    for (var i = 0; i < ranges.length; i++) {
+      var toKill = compute(ranges[i]);
+      while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
+        var replaced = kill.pop();
+        if (cmp(replaced.from, toKill.from) < 0) {
+          toKill.from = replaced.from;
+          break
+        }
+      }
+      kill.push(toKill);
+    }
+    // Next, remove those actual ranges.
+    runInOp(cm, function () {
+      for (var i = kill.length - 1; i >= 0; i--)
+        { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
+      ensureCursorVisible(cm);
+    });
+  }
+
+  function moveCharLogically(line, ch, dir) {
+    var target = skipExtendingChars(line.text, ch + dir, dir);
+    return target < 0 || target > line.text.length ? null : target
+  }
+
+  function moveLogically(line, start, dir) {
+    var ch = moveCharLogically(line, start.ch, dir);
+    return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
+  }
+
+  function endOfLine(visually, cm, lineObj, lineNo, dir) {
+    if (visually) {
+      var order = getOrder(lineObj, cm.doc.direction);
+      if (order) {
+        var part = dir < 0 ? lst(order) : order[0];
+        var moveInStorageOrder = (dir < 0) == (part.level == 1);
+        var sticky = moveInStorageOrder ? "after" : "before";
+        var ch;
+        // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
+        // it could be that the last bidi part is not on the last visual line,
+        // since visual lines contain content order-consecutive chunks.
+        // Thus, in rtl, we are looking for the first (content-order) character
+        // in the rtl chunk that is on the last line (that is, the same line
+        // as the last (content-order) character).
+        if (part.level > 0 || cm.doc.direction == "rtl") {
+          var prep = prepareMeasureForLine(cm, lineObj);
+          ch = dir < 0 ? lineObj.text.length - 1 : 0;
+          var targetTop = measureCharPrepared(cm, prep, ch).top;
+          ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
+          if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
+        } else { ch = dir < 0 ? part.to : part.from; }
+        return new Pos(lineNo, ch, sticky)
+      }
+    }
+    return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
+  }
+
+  function moveVisually(cm, line, start, dir) {
+    var bidi = getOrder(line, cm.doc.direction);
+    if (!bidi) { return moveLogically(line, start, dir) }
+    if (start.ch >= line.text.length) {
+      start.ch = line.text.length;
+      start.sticky = "before";
+    } else if (start.ch <= 0) {
+      start.ch = 0;
+      start.sticky = "after";
+    }
+    var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
+    if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
+      // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
+      // nothing interesting happens.
+      return moveLogically(line, start, dir)
+    }
+
+    var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
+    var prep;
+    var getWrappedLineExtent = function (ch) {
+      if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
+      prep = prep || prepareMeasureForLine(cm, line);
+      return wrappedLineExtentChar(cm, line, prep, ch)
+    };
+    var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
+
+    if (cm.doc.direction == "rtl" || part.level == 1) {
+      var moveInStorageOrder = (part.level == 1) == (dir < 0);
+      var ch = mv(start, moveInStorageOrder ? 1 : -1);
+      if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
+        // Case 2: We move within an rtl part or in an rtl editor on the same visual line
+        var sticky = moveInStorageOrder ? "before" : "after";
+        return new Pos(start.line, ch, sticky)
+      }
+    }
+
+    // Case 3: Could not move within this bidi part in this visual line, so leave
+    // the current bidi part
+
+    var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
+      var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
+        ? new Pos(start.line, mv(ch, 1), "before")
+        : new Pos(start.line, ch, "after"); };
+
+      for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
+        var part = bidi[partPos];
+        var moveInStorageOrder = (dir > 0) == (part.level != 1);
+        var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
+        if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
+        ch = moveInStorageOrder ? part.from : mv(part.to, -1);
+        if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
+      }
+    };
+
+    // Case 3a: Look for other bidi parts on the same visual line
+    var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
+    if (res) { return res }
+
+    // Case 3b: Look for other bidi parts on the next visual line
+    var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
+    if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
+      res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
+      if (res) { return res }
+    }
+
+    // Case 4: Nowhere to move
+    return null
+  }
+
+  // Commands are parameter-less actions that can be performed on an
+  // editor, mostly used for keybindings.
+  var commands = {
+    selectAll: selectAll,
+    singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
+    killLine: function (cm) { return deleteNearSelection(cm, function (range) {
+      if (range.empty()) {
+        var len = getLine(cm.doc, range.head.line).text.length;
+        if (range.head.ch == len && range.head.line < cm.lastLine())
+          { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
+        else
+          { return {from: range.head, to: Pos(range.head.line, len)} }
+      } else {
+        return {from: range.from(), to: range.to()}
+      }
+    }); },
+    deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
+      from: Pos(range.from().line, 0),
+      to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
+    }); }); },
+    delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
+      from: Pos(range.from().line, 0), to: range.from()
+    }); }); },
+    delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
+      var top = cm.charCoords(range.head, "div").top + 5;
+      var leftPos = cm.coordsChar({left: 0, top: top}, "div");
+      return {from: leftPos, to: range.from()}
+    }); },
+    delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
+      var top = cm.charCoords(range.head, "div").top + 5;
+      var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
+      return {from: range.from(), to: rightPos }
+    }); },
+    undo: function (cm) { return cm.undo(); },
+    redo: function (cm) { return cm.redo(); },
+    undoSelection: function (cm) { return cm.undoSelection(); },
+    redoSelection: function (cm) { return cm.redoSelection(); },
+    goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
+    goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
+    goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
+      {origin: "+move", bias: 1}
+    ); },
+    goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
+      {origin: "+move", bias: 1}
+    ); },
+    goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
+      {origin: "+move", bias: -1}
+    ); },
+    goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
+      var top = cm.cursorCoords(range.head, "div").top + 5;
+      return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
+    }, sel_move); },
+    goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
+      var top = cm.cursorCoords(range.head, "div").top + 5;
+      return cm.coordsChar({left: 0, top: top}, "div")
+    }, sel_move); },
+    goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
+      var top = cm.cursorCoords(range.head, "div").top + 5;
+      var pos = cm.coordsChar({left: 0, top: top}, "div");
+      if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
+      return pos
+    }, sel_move); },
+    goLineUp: function (cm) { return cm.moveV(-1, "line"); },
+    goLineDown: function (cm) { return cm.moveV(1, "line"); },
+    goPageUp: function (cm) { return cm.moveV(-1, "page"); },
+    goPageDown: function (cm) { return cm.moveV(1, "page"); },
+    goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
+    goCharRight: function (cm) { return cm.moveH(1, "char"); },
+    goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
+    goColumnRight: function (cm) { return cm.moveH(1, "column"); },
+    goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
+    goGroupRight: function (cm) { return cm.moveH(1, "group"); },
+    goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
+    goWordRight: function (cm) { return cm.moveH(1, "word"); },
+    delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
+    delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
+    delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
+    delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
+    delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
+    delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
+    indentAuto: function (cm) { return cm.indentSelection("smart"); },
+    indentMore: function (cm) { return cm.indentSelection("add"); },
+    indentLess: function (cm) { return cm.indentSelection("subtract"); },
+    insertTab: function (cm) { return cm.replaceSelection("\t"); },
+    insertSoftTab: function (cm) {
+      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
+      for (var i = 0; i < ranges.length; i++) {
+        var pos = ranges[i].from();
+        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
+        spaces.push(spaceStr(tabSize - col % tabSize));
+      }
+      cm.replaceSelections(spaces);
+    },
+    defaultTab: function (cm) {
+      if (cm.somethingSelected()) { cm.indentSelection("add"); }
+      else { cm.execCommand("insertTab"); }
+    },
+    // Swap the two chars left and right of each selection's head.
+    // Move cursor behind the two swapped characters afterwards.
+    //
+    // Doesn't consider line feeds a character.
+    // Doesn't scan more than one line above to find a character.
+    // Doesn't do anything on an empty line.
+    // Doesn't do anything with non-empty selections.
+    transposeChars: function (cm) { return runInOp(cm, function () {
+      var ranges = cm.listSelections(), newSel = [];
+      for (var i = 0; i < ranges.length; i++) {
+        if (!ranges[i].empty()) { continue }
+        var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
+        if (line) {
+          if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
+          if (cur.ch > 0) {
+            cur = new Pos(cur.line, cur.ch + 1);
+            cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
+                            Pos(cur.line, cur.ch - 2), cur, "+transpose");
+          } else if (cur.line > cm.doc.first) {
+            var prev = getLine(cm.doc, cur.line - 1).text;
+            if (prev) {
+              cur = new Pos(cur.line, 1);
+              cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
+                              prev.charAt(prev.length - 1),
+                              Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
+            }
+          }
+        }
+        newSel.push(new Range(cur, cur));
+      }
+      cm.setSelections(newSel);
+    }); },
+    newlineAndIndent: function (cm) { return runInOp(cm, function () {
+      var sels = cm.listSelections();
+      for (var i = sels.length - 1; i >= 0; i--)
+        { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
+      sels = cm.listSelections();
+      for (var i$1 = 0; i$1 < sels.length; i$1++)
+        { cm.indentLine(sels[i$1].from().line, null, true); }
+      ensureCursorVisible(cm);
+    }); },
+    openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
+    toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
+  };
+
+
+  function lineStart(cm, lineN) {
+    var line = getLine(cm.doc, lineN);
+    var visual = visualLine(line);
+    if (visual != line) { lineN = lineNo(visual); }
+    return endOfLine(true, cm, visual, lineN, 1)
+  }
+  function lineEnd(cm, lineN) {
+    var line = getLine(cm.doc, lineN);
+    var visual = visualLineEnd(line);
+    if (visual != line) { lineN = lineNo(visual); }
+    return endOfLine(true, cm, line, lineN, -1)
+  }
+  function lineStartSmart(cm, pos) {
+    var start = lineStart(cm, pos.line);
+    var line = getLine(cm.doc, start.line);
+    var order = getOrder(line, cm.doc.direction);
+    if (!order || order[0].level == 0) {
+      var firstNonWS = Math.max(0, line.text.search(/\S/));
+      var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
+      return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
+    }
+    return start
+  }
+
+  // Run a handler that was bound to a key.
+  function doHandleBinding(cm, bound, dropShift) {
+    if (typeof bound == "string") {
+      bound = commands[bound];
+      if (!bound) { return false }
+    }
+    // Ensure previous input has been read, so that the handler sees a
+    // consistent view of the document
+    cm.display.input.ensurePolled();
+    var prevShift = cm.display.shift, done = false;
+    try {
+      if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
+      if (dropShift) { cm.display.shift = false; }
+      done = bound(cm) != Pass;
+    } finally {
+      cm.display.shift = prevShift;
+      cm.state.suppressEdits = false;
+    }
+    return done
+  }
+
+  function lookupKeyForEditor(cm, name, handle) {
+    for (var i = 0; i < cm.state.keyMaps.length; i++) {
+      var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
+      if (result) { return result }
+    }
+    return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
+      || lookupKey(name, cm.options.keyMap, handle, cm)
+  }
+
+  // Note that, despite the name, this function is also used to check
+  // for bound mouse clicks.
+
+  var stopSeq = new Delayed;
+
+  function dispatchKey(cm, name, e, handle) {
+    var seq = cm.state.keySeq;
+    if (seq) {
+      if (isModifierKey(name)) { return "handled" }
+      if (/\'$/.test(name))
+        { cm.state.keySeq = null; }
+      else
+        { stopSeq.set(50, function () {
+          if (cm.state.keySeq == seq) {
+            cm.state.keySeq = null;
+            cm.display.input.reset();
+          }
+        }); }
+      if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
+    }
+    return dispatchKeyInner(cm, name, e, handle)
+  }
+
+  function dispatchKeyInner(cm, name, e, handle) {
+    var result = lookupKeyForEditor(cm, name, handle);
+
+    if (result == "multi")
+      { cm.state.keySeq = name; }
+    if (result == "handled")
+      { signalLater(cm, "keyHandled", cm, name, e); }
+
+    if (result == "handled" || result == "multi") {
+      e_preventDefault(e);
+      restartBlink(cm);
+    }
+
+    return !!result
+  }
+
+  // Handle a key from the keydown event.
+  function handleKeyBinding(cm, e) {
+    var name = keyName(e, true);
+    if (!name) { return false }
+
+    if (e.shiftKey && !cm.state.keySeq) {
+      // First try to resolve full name (including 'Shift-'). Failing
+      // that, see if there is a cursor-motion command (starting with
+      // 'go') bound to the keyname without 'Shift-'.
+      return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
+          || dispatchKey(cm, name, e, function (b) {
+               if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
+                 { return doHandleBinding(cm, b) }
+             })
+    } else {
+      return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
+    }
+  }
+
+  // Handle a key from the keypress event
+  function handleCharBinding(cm, e, ch) {
+    return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
+  }
+
+  var lastStoppedKey = null;
+  function onKeyDown(e) {
+    var cm = this;
+    cm.curOp.focus = activeElt();
+    if (signalDOMEvent(cm, e)) { return }
+    // IE does strange things with escape.
+    if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
+    var code = e.keyCode;
+    cm.display.shift = code == 16 || e.shiftKey;
+    var handled = handleKeyBinding(cm, e);
+    if (presto) {
+      lastStoppedKey = handled ? code : null;
+      // Opera has no cut event... we try to at least catch the key combo
+      if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
+        { cm.replaceSelection("", null, "cut"); }
+    }
+
+    // Turn mouse into crosshair when Alt is held on Mac.
+    if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
+      { showCrossHair(cm); }
+  }
+
+  function showCrossHair(cm) {
+    var lineDiv = cm.display.lineDiv;
+    addClass(lineDiv, "CodeMirror-crosshair");
+
+    function up(e) {
+      if (e.keyCode == 18 || !e.altKey) {
+        rmClass(lineDiv, "CodeMirror-crosshair");
+        off(document, "keyup", up);
+        off(document, "mouseover", up);
+      }
+    }
+    on(document, "keyup", up);
+    on(document, "mouseover", up);
+  }
+
+  function onKeyUp(e) {
+    if (e.keyCode == 16) { this.doc.sel.shift = false; }
+    signalDOMEvent(this, e);
+  }
+
+  function onKeyPress(e) {
+    var cm = this;
+    if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
+    var keyCode = e.keyCode, charCode = e.charCode;
+    if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
+    if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
+    var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
+    // Some browsers fire keypress events for backspace
+    if (ch == "\x08") { return }
+    if (handleCharBinding(cm, e, ch)) { return }
+    cm.display.input.onKeyPress(e);
+  }
+
+  var DOUBLECLICK_DELAY = 400;
+
+  var PastClick = function(time, pos, button) {
+    this.time = time;
+    this.pos = pos;
+    this.button = button;
+  };
+
+  PastClick.prototype.compare = function (time, pos, button) {
+    return this.time + DOUBLECLICK_DELAY > time &&
+      cmp(pos, this.pos) == 0 && button == this.button
+  };
+
+  var lastClick, lastDoubleClick;
+  function clickRepeat(pos, button) {
+    var now = +new Date;
+    if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
+      lastClick = lastDoubleClick = null;
+      return "triple"
+    } else if (lastClick && lastClick.compare(now, pos, button)) {
+      lastDoubleClick = new PastClick(now, pos, button);
+      lastClick = null;
+      return "double"
+    } else {
+      lastClick = new PastClick(now, pos, button);
+      lastDoubleClick = null;
+      return "single"
+    }
+  }
+
+  // A mouse down can be a single click, double click, triple click,
+  // start of selection drag, start of text drag, new cursor
+  // (ctrl-click), rectangle drag (alt-drag), or xwin
+  // middle-click-paste. Or it might be a click on something we should
+  // not interfere with, such as a scrollbar or widget.
+  function onMouseDown(e) {
+    var cm = this, display = cm.display;
+    if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
+    display.input.ensurePolled();
+    display.shift = e.shiftKey;
+
+    if (eventInWidget(display, e)) {
+      if (!webkit) {
+        // Briefly turn off draggability, to allow widgets to do
+        // normal dragging things.
+        display.scroller.draggable = false;
+        setTimeout(function () { return display.scroller.draggable = true; }, 100);
+      }
+      return
+    }
+    if (clickInGutter(cm, e)) { return }
+    var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
+    window.focus();
+
+    // #3261: make sure, that we're not starting a second selection
+    if (button == 1 && cm.state.selectingText)
+      { cm.state.selectingText(e); }
+
+    if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
+
+    if (button == 1) {
+      if (pos) { leftButtonDown(cm, pos, repeat, e); }
+      else if (e_target(e) == display.scroller) { e_preventDefault(e); }
+    } else if (button == 2) {
+      if (pos) { extendSelection(cm.doc, pos); }
+      setTimeout(function () { return display.input.focus(); }, 20);
+    } else if (button == 3) {
+      if (captureRightClick) { cm.display.input.onContextMenu(e); }
+      else { delayBlurEvent(cm); }
+    }
+  }
+
+  function handleMappedButton(cm, button, pos, repeat, event) {
+    var name = "Click";
+    if (repeat == "double") { name = "Double" + name; }
+    else if (repeat == "triple") { name = "Triple" + name; }
+    name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
+
+    return dispatchKey(cm,  addModifierNames(name, event), event, function (bound) {
+      if (typeof bound == "string") { bound = commands[bound]; }
+      if (!bound) { return false }
+      var done = false;
+      try {
+        if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
+        done = bound(cm, pos) != Pass;
+      } finally {
+        cm.state.suppressEdits = false;
+      }
+      return done
+    })
+  }
+
+  function configureMouse(cm, repeat, event) {
+    var option = cm.getOption("configureMouse");
+    var value = option ? option(cm, repeat, event) : {};
+    if (value.unit == null) {
+      var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
+      value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
+    }
+    if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
+    if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
+    if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
+    return value
+  }
+
+  function leftButtonDown(cm, pos, repeat, event) {
+    if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
+    else { cm.curOp.focus = activeElt(); }
+
+    var behavior = configureMouse(cm, repeat, event);
+
+    var sel = cm.doc.sel, contained;
+    if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
+        repeat == "single" && (contained = sel.contains(pos)) > -1 &&
+        (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
+        (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
+      { leftButtonStartDrag(cm, event, pos, behavior); }
+    else
+      { leftButtonSelect(cm, event, pos, behavior); }
+  }
+
+  // Start a text drag. When it ends, see if any dragging actually
+  // happen, and treat as a click if it didn't.
+  function leftButtonStartDrag(cm, event, pos, behavior) {
+    var display = cm.display, moved = false;
+    var dragEnd = operation(cm, function (e) {
+      if (webkit) { display.scroller.draggable = false; }
+      cm.state.draggingText = false;
+      off(display.wrapper.ownerDocument, "mouseup", dragEnd);
+      off(display.wrapper.ownerDocument, "mousemove", mouseMove);
+      off(display.scroller, "dragstart", dragStart);
+      off(display.scroller, "drop", dragEnd);
+      if (!moved) {
+        e_preventDefault(e);
+        if (!behavior.addNew)
+          { extendSelection(cm.doc, pos, null, null, behavior.extend); }
+        // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
+        if (webkit || ie && ie_version == 9)
+          { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
+        else
+          { display.input.focus(); }
+      }
+    });
+    var mouseMove = function(e2) {
+      moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
+    };
+    var dragStart = function () { return moved = true; };
+    // Let the drag handler handle this.
+    if (webkit) { display.scroller.draggable = true; }
+    cm.state.draggingText = dragEnd;
+    dragEnd.copy = !behavior.moveOnDrag;
+    // IE's approach to draggable
+    if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
+    on(display.wrapper.ownerDocument, "mouseup", dragEnd);
+    on(display.wrapper.ownerDocument, "mousemove", mouseMove);
+    on(display.scroller, "dragstart", dragStart);
+    on(display.scroller, "drop", dragEnd);
+
+    delayBlurEvent(cm);
+    setTimeout(function () { return display.input.focus(); }, 20);
+  }
+
+  function rangeForUnit(cm, pos, unit) {
+    if (unit == "char") { return new Range(pos, pos) }
+    if (unit == "word") { return cm.findWordAt(pos) }
+    if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
+    var result = unit(cm, pos);
+    return new Range(result.from, result.to)
+  }
+
+  // Normal selection, as opposed to text dragging.
+  function leftButtonSelect(cm, event, start, behavior) {
+    var display = cm.display, doc = cm.doc;
+    e_preventDefault(event);
+
+    var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
+    if (behavior.addNew && !behavior.extend) {
+      ourIndex = doc.sel.contains(start);
+      if (ourIndex > -1)
+        { ourRange = ranges[ourIndex]; }
+      else
+        { ourRange = new Range(start, start); }
+    } else {
+      ourRange = doc.sel.primary();
+      ourIndex = doc.sel.primIndex;
+    }
+
+    if (behavior.unit == "rectangle") {
+      if (!behavior.addNew) { ourRange = new Range(start, start); }
+      start = posFromMouse(cm, event, true, true);
+      ourIndex = -1;
+    } else {
+      var range$$1 = rangeForUnit(cm, start, behavior.unit);
+      if (behavior.extend)
+        { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
+      else
+        { ourRange = range$$1; }
+    }
+
+    if (!behavior.addNew) {
+      ourIndex = 0;
+      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
+      startSel = doc.sel;
+    } else if (ourIndex == -1) {
+      ourIndex = ranges.length;
+      setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
+                   {scroll: false, origin: "*mouse"});
+    } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
+      setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
+                   {scroll: false, origin: "*mouse"});
+      startSel = doc.sel;
+    } else {
+      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
+    }
+
+    var lastPos = start;
+    function extendTo(pos) {
+      if (cmp(lastPos, pos) == 0) { return }
+      lastPos = pos;
+
+      if (behavior.unit == "rectangle") {
+        var ranges = [], tabSize = cm.options.tabSize;
+        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
+        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
+        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
+        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
+             line <= end; line++) {
+          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
+          if (left == right)
+            { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
+          else if (text.length > leftPos)
+            { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
+        }
+        if (!ranges.length) { ranges.push(new Range(start, start)); }
+        setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
+                     {origin: "*mouse", scroll: false});
+        cm.scrollIntoView(pos);
+      } else {
+        var oldRange = ourRange;
+        var range$$1 = rangeForUnit(cm, pos, behavior.unit);
+        var anchor = oldRange.anchor, head;
+        if (cmp(range$$1.anchor, anchor) > 0) {
+          head = range$$1.head;
+          anchor = minPos(oldRange.from(), range$$1.anchor);
+        } else {
+          head = range$$1.anchor;
+          anchor = maxPos(oldRange.to(), range$$1.head);
+        }
+        var ranges$1 = startSel.ranges.slice(0);
+        ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
+        setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
+      }
+    }
+
+    var editorSize = display.wrapper.getBoundingClientRect();
+    // Used to ensure timeout re-tries don't fire when another extend
+    // happened in the meantime (clearTimeout isn't reliable -- at
+    // least on Chrome, the timeouts still happen even when cleared,
+    // if the clear happens after their scheduled firing time).
+    var counter = 0;
+
+    function extend(e) {
+      var curCount = ++counter;
+      var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
+      if (!cur) { return }
+      if (cmp(cur, lastPos) != 0) {
+        cm.curOp.focus = activeElt();
+        extendTo(cur);
+        var visible = visibleLines(display, doc);
+        if (cur.line >= visible.to || cur.line < visible.from)
+          { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
+      } else {
+        var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
+        if (outside) { setTimeout(operation(cm, function () {
+          if (counter != curCount) { return }
+          display.scroller.scrollTop += outside;
+          extend(e);
+        }), 50); }
+      }
+    }
+
+    function done(e) {
+      cm.state.selectingText = false;
+      counter = Infinity;
+      // If e is null or undefined we interpret this as someone trying
+      // to explicitly cancel the selection rather than the user
+      // letting go of the mouse button.
+      if (e) {
+        e_preventDefault(e);
+        display.input.focus();
+      }
+      off(display.wrapper.ownerDocument, "mousemove", move);
+      off(display.wrapper.ownerDocument, "mouseup", up);
+      doc.history.lastSelOrigin = null;
+    }
+
+    var move = operation(cm, function (e) {
+      if (e.buttons === 0 || !e_button(e)) { done(e); }
+      else { extend(e); }
+    });
+    var up = operation(cm, done);
+    cm.state.selectingText = up;
+    on(display.wrapper.ownerDocument, "mousemove", move);
+    on(display.wrapper.ownerDocument, "mouseup", up);
+  }
+
+  // Used when mouse-selecting to adjust the anchor to the proper side
+  // of a bidi jump depending on the visual position of the head.
+  function bidiSimplify(cm, range$$1) {
+    var anchor = range$$1.anchor;
+    var head = range$$1.head;
+    var anchorLine = getLine(cm.doc, anchor.line);
+    if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
+    var order = getOrder(anchorLine);
+    if (!order) { return range$$1 }
+    var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
+    if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
+    var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
+    if (boundary == 0 || boundary == order.length) { return range$$1 }
+
+    // Compute the relative visual position of the head compared to the
+    // anchor (<0 is to the left, >0 to the right)
+    var leftSide;
+    if (head.line != anchor.line) {
+      leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
+    } else {
+      var headIndex = getBidiPartAt(order, head.ch, head.sticky);
+      var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
+      if (headIndex == boundary - 1 || headIndex == boundary)
+        { leftSide = dir < 0; }
+      else
+        { leftSide = dir > 0; }
+    }
+
+    var usePart = order[boundary + (leftSide ? -1 : 0)];
+    var from = leftSide == (usePart.level == 1);
+    var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
+    return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
+  }
+
+
+  // Determines whether an event happened in the gutter, and fires the
+  // handlers for the corresponding event.
+  function gutterEvent(cm, e, type, prevent) {
+    var mX, mY;
+    if (e.touches) {
+      mX = e.touches[0].clientX;
+      mY = e.touches[0].clientY;
+    } else {
+      try { mX = e.clientX; mY = e.clientY; }
+      catch(e) { return false }
+    }
+    if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
+    if (prevent) { e_preventDefault(e); }
+
+    var display = cm.display;
+    var lineBox = display.lineDiv.getBoundingClientRect();
+
+    if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
+    mY -= lineBox.top - display.viewOffset;
+
+    for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
+      var g = display.gutters.childNodes[i];
+      if (g && g.getBoundingClientRect().right >= mX) {
+        var line = lineAtHeight(cm.doc, mY);
+        var gutter = cm.display.gutterSpecs[i];
+        signal(cm, type, cm, line, gutter.className, e);
+        return e_defaultPrevented(e)
+      }
+    }
+  }
+
+  function clickInGutter(cm, e) {
+    return gutterEvent(cm, e, "gutterClick", true)
+  }
+
+  // CONTEXT MENU HANDLING
+
+  // To make the context menu work, we need to briefly unhide the
+  // textarea (making it as unobtrusive as possible) to let the
+  // right-click take effect on it.
+  function onContextMenu(cm, e) {
+    if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
+    if (signalDOMEvent(cm, e, "contextmenu")) { return }
+    if (!captureRightClick) { cm.display.input.onContextMenu(e); }
+  }
+
+  function contextMenuInGutter(cm, e) {
+    if (!hasHandler(cm, "gutterContextMenu")) { return false }
+    return gutterEvent(cm, e, "gutterContextMenu", false)
+  }
+
+  function themeChanged(cm) {
+    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
+      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
+    clearCaches(cm);
+  }
+
+  var Init = {toString: function(){return "CodeMirror.Init"}};
+
+  var defaults = {};
+  var optionHandlers = {};
+
+  function defineOptions(CodeMirror) {
+    var optionHandlers = CodeMirror.optionHandlers;
+
+    function option(name, deflt, handle, notOnInit) {
+      CodeMirror.defaults[name] = deflt;
+      if (handle) { optionHandlers[name] =
+        notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
+    }
+
+    CodeMirror.defineOption = option;
+
+    // Passed to option handlers when there is no old value.
+    CodeMirror.Init = Init;
+
+    // These two are, on init, called from the constructor because they
+    // have to be initialized before the editor can start at all.
+    option("value", "", function (cm, val) { return cm.setValue(val); }, true);
+    option("mode", null, function (cm, val) {
+      cm.doc.modeOption = val;
+      loadMode(cm);
+    }, true);
+
+    option("indentUnit", 2, loadMode, true);
+    option("indentWithTabs", false);
+    option("smartIndent", true);
+    option("tabSize", 4, function (cm) {
+      resetModeState(cm);
+      clearCaches(cm);
+      regChange(cm);
+    }, true);
+
+    option("lineSeparator", null, function (cm, val) {
+      cm.doc.lineSep = val;
+      if (!val) { return }
+      var newBreaks = [], lineNo = cm.doc.first;
+      cm.doc.iter(function (line) {
+        for (var pos = 0;;) {
+          var found = line.text.indexOf(val, pos);
+          if (found == -1) { break }
+          pos = found + val.length;
+          newBreaks.push(Pos(lineNo, found));
+        }
+        lineNo++;
+      });
+      for (var i = newBreaks.length - 1; i >= 0; i--)
+        { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
+    });
+    option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
+      cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
+      if (old != Init) { cm.refresh(); }
+    });
+    option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
+    option("electricChars", true);
+    option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
+      throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
+    }, true);
+    option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
+    option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
+    option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
+    option("rtlMoveVisually", !windows);
+    option("wholeLineUpdateBefore", true);
+
+    option("theme", "default", function (cm) {
+      themeChanged(cm);
+      updateGutters(cm);
+    }, true);
+    option("keyMap", "default", function (cm, val, old) {
+      var next = getKeyMap(val);
+      var prev = old != Init && getKeyMap(old);
+      if (prev && prev.detach) { prev.detach(cm, next); }
+      if (next.attach) { next.attach(cm, prev || null); }
+    });
+    option("extraKeys", null);
+    option("configureMouse", null);
+
+    option("lineWrapping", false, wrappingChanged, true);
+    option("gutters", [], function (cm, val) {
+      cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
+      updateGutters(cm);
+    }, true);
+    option("fixedGutter", true, function (cm, val) {
+      cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
+      cm.refresh();
+    }, true);
+    option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
+    option("scrollbarStyle", "native", function (cm) {
+      initScrollbars(cm);
+      updateScrollbars(cm);
+      cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
+      cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
+    }, true);
+    option("lineNumbers", false, function (cm, val) {
+      cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
+      updateGutters(cm);
+    }, true);
+    option("firstLineNumber", 1, updateGutters, true);
+    option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
+    option("showCursorWhenSelecting", false, updateSelection, true);
+
+    option("resetSelectionOnContextMenu", true);
+    option("lineWiseCopyCut", true);
+    option("pasteLinesPerSelection", true);
+    option("selectionsMayTouch", false);
+
+    option("readOnly", false, function (cm, val) {
+      if (val == "nocursor") {
+        onBlur(cm);
+        cm.display.input.blur();
+      }
+      cm.display.input.readOnlyChanged(val);
+    });
+    option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
+    option("dragDrop", true, dragDropChanged);
+    option("allowDropFileTypes", null);
+
+    option("cursorBlinkRate", 530);
+    option("cursorScrollMargin", 0);
+    option("cursorHeight", 1, updateSelection, true);
+    option("singleCursorHeightPerLine", true, updateSelection, true);
+    option("workTime", 100);
+    option("workDelay", 100);
+    option("flattenSpans", true, resetModeState, true);
+    option("addModeClass", false, resetModeState, true);
+    option("pollInterval", 100);
+    option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
+    option("historyEventDelay", 1250);
+    option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
+    option("maxHighlightLength", 10000, resetModeState, true);
+    option("moveInputWithCursor", true, function (cm, val) {
+      if (!val) { cm.display.input.resetPosition(); }
+    });
+
+    option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
+    option("autofocus", null);
+    option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
+    option("phrases", null);
+  }
+
+  function dragDropChanged(cm, value, old) {
+    var wasOn = old && old != Init;
+    if (!value != !wasOn) {
+      var funcs = cm.display.dragFunctions;
+      var toggle = value ? on : off;
+      toggle(cm.display.scroller, "dragstart", funcs.start);
+      toggle(cm.display.scroller, "dragenter", funcs.enter);
+      toggle(cm.display.scroller, "dragover", funcs.over);
+      toggle(cm.display.scroller, "dragleave", funcs.leave);
+      toggle(cm.display.scroller, "drop", funcs.drop);
+    }
+  }
+
+  function wrappingChanged(cm) {
+    if (cm.options.lineWrapping) {
+      addClass(cm.display.wrapper, "CodeMirror-wrap");
+      cm.display.sizer.style.minWidth = "";
+      cm.display.sizerWidth = null;
+    } else {
+      rmClass(cm.display.wrapper, "CodeMirror-wrap");
+      findMaxLine(cm);
+    }
+    estimateLineHeights(cm);
+    regChange(cm);
+    clearCaches(cm);
+    setTimeout(function () { return updateScrollbars(cm); }, 100);
+  }
+
+  // A CodeMirror instance represents an editor. This is the object
+  // that user code is usually dealing with.
+
+  function CodeMirror(place, options) {
+    var this$1 = this;
+
+    if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
+
+    this.options = options = options ? copyObj(options) : {};
+    // Determine effective options based on given values and defaults.
+    copyObj(defaults, options, false);
+
+    var doc = options.value;
+    if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
+    else if (options.mode) { doc.modeOption = options.mode; }
+    this.doc = doc;
+
+    var input = new CodeMirror.inputStyles[options.inputStyle](this);
+    var display = this.display = new Display(place, doc, input, options);
+    display.wrapper.CodeMirror = this;
+    themeChanged(this);
+    if (options.lineWrapping)
+      { this.display.wrapper.className += " CodeMirror-wrap"; }
+    initScrollbars(this);
+
+    this.state = {
+      keyMaps: [],  // stores maps added by addKeyMap
+      overlays: [], // highlighting overlays, as added by addOverlay
+      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
+      overwrite: false,
+      delayingBlurEvent: false,
+      focused: false,
+      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
+      pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
+      selectingText: false,
+      draggingText: false,
+      highlight: new Delayed(), // stores highlight worker timeout
+      keySeq: null,  // Unfinished key sequence
+      specialChars: null
+    };
+
+    if (options.autofocus && !mobile) { display.input.focus(); }
+
+    // Override magic textarea content restore that IE sometimes does
+    // on our hidden textarea on reload
+    if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
+
+    registerEventHandlers(this);
+    ensureGlobalHandlers();
+
+    startOperation(this);
+    this.curOp.forceUpdate = true;
+    attachDoc(this, doc);
+
+    if ((options.autofocus && !mobile) || this.hasFocus())
+      { setTimeout(bind(onFocus, this), 20); }
+    else
+      { onBlur(this); }
+
+    for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
+      { optionHandlers[opt](this$1, options[opt], Init); } }
+    maybeUpdateLineNumberWidth(this);
+    if (options.finishInit) { options.finishInit(this); }
+    for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
+    endOperation(this);
+    // Suppress optimizelegibility in Webkit, since it breaks text
+    // measuring on line wrapping boundaries.
+    if (webkit && options.lineWrapping &&
+        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
+      { display.lineDiv.style.textRendering = "auto"; }
+  }
+
+  // The default configuration options.
+  CodeMirror.defaults = defaults;
+  // Functions to run when options are changed.
+  CodeMirror.optionHandlers = optionHandlers;
+
+  // Attach the necessary event handlers when initializing the editor
+  function registerEventHandlers(cm) {
+    var d = cm.display;
+    on(d.scroller, "mousedown", operation(cm, onMouseDown));
+    // Older IE's will not fire a second mousedown for a double click
+    if (ie && ie_version < 11)
+      { on(d.scroller, "dblclick", operation(cm, function (e) {
+        if (signalDOMEvent(cm, e)) { return }
+        var pos = posFromMouse(cm, e);
+        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
+        e_preventDefault(e);
+        var word = cm.findWordAt(pos);
+        extendSelection(cm.doc, word.anchor, word.head);
+      })); }
+    else
+      { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
+    // Some browsers fire contextmenu *after* opening the menu, at
+    // which point we can't mess with it anymore. Context menu is
+    // handled in onMouseDown for these browsers.
+    on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
+
+    // Used to suppress mouse event handling when a touch happens
+    var touchFinished, prevTouch = {end: 0};
+    function finishTouch() {
+      if (d.activeTouch) {
+        touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
+        prevTouch = d.activeTouch;
+        prevTouch.end = +new Date;
+      }
+    }
+    function isMouseLikeTouchEvent(e) {
+      if (e.touches.length != 1) { return false }
+      var touch = e.touches[0];
+      return touch.radiusX <= 1 && touch.radiusY <= 1
+    }
+    function farAway(touch, other) {
+      if (other.left == null) { return true }
+      var dx = other.left - touch.left, dy = other.top - touch.top;
+      return dx * dx + dy * dy > 20 * 20
+    }
+    on(d.scroller, "touchstart", function (e) {
+      if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
+        d.input.ensurePolled();
+        clearTimeout(touchFinished);
+        var now = +new Date;
+        d.activeTouch = {start: now, moved: false,
+                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
+        if (e.touches.length == 1) {
+          d.activeTouch.left = e.touches[0].pageX;
+          d.activeTouch.top = e.touches[0].pageY;
+        }
+      }
+    });
+    on(d.scroller, "touchmove", function () {
+      if (d.activeTouch) { d.activeTouch.moved = true; }
+    });
+    on(d.scroller, "touchend", function (e) {
+      var touch = d.activeTouch;
+      if (touch && !eventInWidget(d, e) && touch.left != null &&
+          !touch.moved && new Date - touch.start < 300) {
+        var pos = cm.coordsChar(d.activeTouch, "page"), range;
+        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
+          { range = new Range(pos, pos); }
+        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
+          { range = cm.findWordAt(pos); }
+        else // Triple tap
+          { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
+        cm.setSelection(range.anchor, range.head);
+        cm.focus();
+        e_preventDefault(e);
+      }
+      finishTouch();
+    });
+    on(d.scroller, "touchcancel", finishTouch);
+
+    // Sync scrolling between fake scrollbars and real scrollable
+    // area, ensure viewport is updated when scrolling.
+    on(d.scroller, "scroll", function () {
+      if (d.scroller.clientHeight) {
+        updateScrollTop(cm, d.scroller.scrollTop);
+        setScrollLeft(cm, d.scroller.scrollLeft, true);
+        signal(cm, "scroll", cm);
+      }
+    });
+
+    // Listen to wheel events in order to try and update the viewport on time.
+    on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
+    on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
+
+    // Prevent wrapper from ever scrolling
+    on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
+
+    d.dragFunctions = {
+      enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
+      over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
+      start: function (e) { return onDragStart(cm, e); },
+      drop: operation(cm, onDrop),
+      leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
+    };
+
+    var inp = d.input.getField();
+    on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
+    on(inp, "keydown", operation(cm, onKeyDown));
+    on(inp, "keypress", operation(cm, onKeyPress));
+    on(inp, "focus", function (e) { return onFocus(cm, e); });
+    on(inp, "blur", function (e) { return onBlur(cm, e); });
+  }
+
+  var initHooks = [];
+  CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
+
+  // Indent the given line. The how parameter can be "smart",
+  // "add"/null, "subtract", or "prev". When aggressive is false
+  // (typically set to true for forced single-line indents), empty
+  // lines are not indented, and places where the mode returns Pass
+  // are left alone.
+  function indentLine(cm, n, how, aggressive) {
+    var doc = cm.doc, state;
+    if (how == null) { how = "add"; }
+    if (how == "smart") {
+      // Fall back to "prev" when the mode doesn't have an indentation
+      // method.
+      if (!doc.mode.indent) { how = "prev"; }
+      else { state = getContextBefore(cm, n).state; }
+    }
+
+    var tabSize = cm.options.tabSize;
+    var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
+    if (line.stateAfter) { line.stateAfter = null; }
+    var curSpaceString = line.text.match(/^\s*/)[0], indentation;
+    if (!aggressive && !/\S/.test(line.text)) {
+      indentation = 0;
+      how = "not";
+    } else if (how == "smart") {
+      indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+      if (indentation == Pass || indentation > 150) {
+        if (!aggressive) { return }
+        how = "prev";
+      }
+    }
+    if (how == "prev") {
+      if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
+      else { indentation = 0; }
+    } else if (how == "add") {
+      indentation = curSpace + cm.options.indentUnit;
+    } else if (how == "subtract") {
+      indentation = curSpace - cm.options.indentUnit;
+    } else if (typeof how == "number") {
+      indentation = curSpace + how;
+    }
+    indentation = Math.max(0, indentation);
+
+    var indentString = "", pos = 0;
+    if (cm.options.indentWithTabs)
+      { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
+    if (pos < indentation) { indentString += spaceStr(indentation - pos); }
+
+    if (indentString != curSpaceString) {
+      replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
+      line.stateAfter = null;
+      return true
+    } else {
+      // Ensure that, if the cursor was in the whitespace at the start
+      // of the line, it is moved to the end of that space.
+      for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
+        var range = doc.sel.ranges[i$1];
+        if (range.head.line == n && range.head.ch < curSpaceString.length) {
+          var pos$1 = Pos(n, curSpaceString.length);
+          replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
+          break
+        }
+      }
+    }
+  }
+
+  // This will be set to a {lineWise: bool, text: [string]} object, so
+  // that, when pasting, we know what kind of selections the copied
+  // text was made out of.
+  var lastCopied = null;
+
+  function setLastCopied(newLastCopied) {
+    lastCopied = newLastCopied;
+  }
+
+  function applyTextInput(cm, inserted, deleted, sel, origin) {
+    var doc = cm.doc;
+    cm.display.shift = false;
+    if (!sel) { sel = doc.sel; }
+
+    var recent = +new Date - 200;
+    var paste = origin == "paste" || cm.state.pasteIncoming > recent;
+    var textLines = splitLinesAuto(inserted), multiPaste = null;
+    // When pasting N lines into N selections, insert one line per selection
+    if (paste && sel.ranges.length > 1) {
+      if (lastCopied && lastCopied.text.join("\n") == inserted) {
+        if (sel.ranges.length % lastCopied.text.length == 0) {
+          multiPaste = [];
+          for (var i = 0; i < lastCopied.text.length; i++)
+            { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
+        }
+      } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
+        multiPaste = map(textLines, function (l) { return [l]; });
+      }
+    }
+
+    var updateInput = cm.curOp.updateInput;
+    // Normal behavior is to insert the new text into every selection
+    for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
+      var range$$1 = sel.ranges[i$1];
+      var from = range$$1.from(), to = range$$1.to();
+      if (range$$1.empty()) {
+        if (deleted && deleted > 0) // Handle deletion
+          { from = Pos(from.line, from.ch - deleted); }
+        else if (cm.state.overwrite && !paste) // Handle overwrite
+          { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
+        else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
+          { from = to = Pos(from.line, 0); }
+      }
+      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
+                         origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
+      makeChange(cm.doc, changeEvent);
+      signalLater(cm, "inputRead", cm, changeEvent);
+    }
+    if (inserted && !paste)
+      { triggerElectric(cm, inserted); }
+
+    ensureCursorVisible(cm);
+    if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
+    cm.curOp.typing = true;
+    cm.state.pasteIncoming = cm.state.cutIncoming = -1;
+  }
+
+  function handlePaste(e, cm) {
+    var pasted = e.clipboardData && e.clipboardData.getData("Text");
+    if (pasted) {
+      e.preventDefault();
+      if (!cm.isReadOnly() && !cm.options.disableInput)
+        { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
+      return true
+    }
+  }
+
+  function triggerElectric(cm, inserted) {
+    // When an 'electric' character is inserted, immediately trigger a reindent
+    if (!cm.options.electricChars || !cm.options.smartIndent) { return }
+    var sel = cm.doc.sel;
+
+    for (var i = sel.ranges.length - 1; i >= 0; i--) {
+      var range$$1 = sel.ranges[i];
+      if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
+      var mode = cm.getModeAt(range$$1.head);
+      var indented = false;
+      if (mode.electricChars) {
+        for (var j = 0; j < mode.electricChars.length; j++)
+          { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
+            indented = indentLine(cm, range$$1.head.line, "smart");
+            break
+          } }
+      } else if (mode.electricInput) {
+        if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
+          { indented = indentLine(cm, range$$1.head.line, "smart"); }
+      }
+      if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
+    }
+  }
+
+  function copyableRanges(cm) {
+    var text = [], ranges = [];
+    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
+      var line = cm.doc.sel.ranges[i].head.line;
+      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
+      ranges.push(lineRange);
+      text.push(cm.getRange(lineRange.anchor, lineRange.head));
+    }
+    return {text: text, ranges: ranges}
+  }
+
+  function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
+    field.setAttribute("autocorrect", autocorrect ? "" : "off");
+    field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
+    field.setAttribute("spellcheck", !!spellcheck);
+  }
+
+  function hiddenTextarea() {
+    var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
+    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
+    // The textarea is kept positioned near the cursor to prevent the
+    // fact that it'll be scrolled into view on input from scrolling
+    // our fake cursor out of view. On webkit, when wrap=off, paste is
+    // very slow. So make the area wide instead.
+    if (webkit) { te.style.width = "1000px"; }
+    else { te.setAttribute("wrap", "off"); }
+    // If border: 0; -- iOS fails to open keyboard (issue #1287)
+    if (ios) { te.style.border = "1px solid black"; }
+    disableBrowserMagic(te);
+    return div
+  }
+
+  // The publicly visible API. Note that methodOp(f) means
+  // 'wrap f in an operation, performed on its `this` parameter'.
+
+  // This is not the complete set of editor methods. Most of the
+  // methods defined on the Doc type are also injected into
+  // CodeMirror.prototype, for backwards compatibility and
+  // convenience.
+
+  function addEditorMethods(CodeMirror) {
+    var optionHandlers = CodeMirror.optionHandlers;
+
+    var helpers = CodeMirror.helpers = {};
+
+    CodeMirror.prototype = {
+      constructor: CodeMirror,
+      focus: function(){window.focus(); this.display.input.focus();},
+
+      setOption: function(option, value) {
+        var options = this.options, old = options[option];
+        if (options[option] == value && option != "mode") { return }
+        options[option] = value;
+        if (optionHandlers.hasOwnProperty(option))
+          { operation(this, optionHandlers[option])(this, value, old); }
+        signal(this, "optionChange", this, option);
+      },
+
+      getOption: function(option) {return this.options[option]},
+      getDoc: function() {return this.doc},
+
+      addKeyMap: function(map$$1, bottom) {
+        this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
+      },
+      removeKeyMap: function(map$$1) {
+        var maps = this.state.keyMaps;
+        for (var i = 0; i < maps.length; ++i)
+          { if (maps[i] == map$$1 || maps[i].name == map$$1) {
+            maps.splice(i, 1);
+            return true
+          } }
+      },
+
+      addOverlay: methodOp(function(spec, options) {
+        var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
+        if (mode.startState) { throw new Error("Overlays may not be stateful.") }
+        insertSorted(this.state.overlays,
+                     {mode: mode, modeSpec: spec, opaque: options && options.opaque,
+                      priority: (options && options.priority) || 0},
+                     function (overlay) { return overlay.priority; });
+        this.state.modeGen++;
+        regChange(this);
+      }),
+      removeOverlay: methodOp(function(spec) {
+        var this$1 = this;
+
+        var overlays = this.state.overlays;
+        for (var i = 0; i < overlays.length; ++i) {
+          var cur = overlays[i].modeSpec;
+          if (cur == spec || typeof spec == "string" && cur.name == spec) {
+            overlays.splice(i, 1);
+            this$1.state.modeGen++;
+            regChange(this$1);
+            return
+          }
+        }
+      }),
+
+      indentLine: methodOp(function(n, dir, aggressive) {
+        if (typeof dir != "string" && typeof dir != "number") {
+          if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
+          else { dir = dir ? "add" : "subtract"; }
+        }
+        if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
+      }),
+      indentSelection: methodOp(function(how) {
+        var this$1 = this;
+
+        var ranges = this.doc.sel.ranges, end = -1;
+        for (var i = 0; i < ranges.length; i++) {
+          var range$$1 = ranges[i];
+          if (!range$$1.empty()) {
+            var from = range$$1.from(), to = range$$1.to();
+            var start = Math.max(end, from.line);
+            end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
+            for (var j = start; j < end; ++j)
+              { indentLine(this$1, j, how); }
+            var newRanges = this$1.doc.sel.ranges;
+            if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
+              { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
+          } else if (range$$1.head.line > end) {
+            indentLine(this$1, range$$1.head.line, how, true);
+            end = range$$1.head.line;
+            if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
+          }
+        }
+      }),
+
+      // Fetch the parser token for a given character. Useful for hacks
+      // that want to inspect the mode state (say, for completion).
+      getTokenAt: function(pos, precise) {
+        return takeToken(this, pos, precise)
+      },
+
+      getLineTokens: function(line, precise) {
+        return takeToken(this, Pos(line), precise, true)
+      },
+
+      getTokenTypeAt: function(pos) {
+        pos = clipPos(this.doc, pos);
+        var styles = getLineStyles(this, getLine(this.doc, pos.line));
+        var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
+        var type;
+        if (ch == 0) { type = styles[2]; }
+        else { for (;;) {
+          var mid = (before + after) >> 1;
+          if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
+          else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
+          else { type = styles[mid * 2 + 2]; break }
+        } }
+        var cut = type ? type.indexOf("overlay ") : -1;
+        return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
+      },
+
+      getModeAt: function(pos) {
+        var mode = this.doc.mode;
+        if (!mode.innerMode) { return mode }
+        return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
+      },
+
+      getHelper: function(pos, type) {
+        return this.getHelpers(pos, type)[0]
+      },
+
+      getHelpers: function(pos, type) {
+        var this$1 = this;
+
+        var found = [];
+        if (!helpers.hasOwnProperty(type)) { return found }
+        var help = helpers[type], mode = this.getModeAt(pos);
+        if (typeof mode[type] == "string") {
+          if (help[mode[type]]) { found.push(help[mode[type]]); }
+        } else if (mode[type]) {
+          for (var i = 0; i < mode[type].length; i++) {
+            var val = help[mode[type][i]];
+            if (val) { found.push(val); }
+          }
+        } else if (mode.helperType && help[mode.helperType]) {
+          found.push(help[mode.helperType]);
+        } else if (help[mode.name]) {
+          found.push(help[mode.name]);
+        }
+        for (var i$1 = 0; i$1 < help._global.length; i$1++) {
+          var cur = help._global[i$1];
+          if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
+            { found.push(cur.val); }
+        }
+        return found
+      },
+
+      getStateAfter: function(line, precise) {
+        var doc = this.doc;
+        line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
+        return getContextBefore(this, line + 1, precise).state
+      },
+
+      cursorCoords: function(start, mode) {
+        var pos, range$$1 = this.doc.sel.primary();
+        if (start == null) { pos = range$$1.head; }
+        else if (typeof start == "object") { pos = clipPos(this.doc, start); }
+        else { pos = start ? range$$1.from() : range$$1.to(); }
+        return cursorCoords(this, pos, mode || "page")
+      },
+
+      charCoords: function(pos, mode) {
+        return charCoords(this, clipPos(this.doc, pos), mode || "page")
+      },
+
+      coordsChar: function(coords, mode) {
+        coords = fromCoordSystem(this, coords, mode || "page");
+        return coordsChar(this, coords.left, coords.top)
+      },
+
+      lineAtHeight: function(height, mode) {
+        height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
+        return lineAtHeight(this.doc, height + this.display.viewOffset)
+      },
+      heightAtLine: function(line, mode, includeWidgets) {
+        var end = false, lineObj;
+        if (typeof line == "number") {
+          var last = this.doc.first + this.doc.size - 1;
+          if (line < this.doc.first) { line = this.doc.first; }
+          else if (line > last) { line = last; end = true; }
+          lineObj = getLine(this.doc, line);
+        } else {
+          lineObj = line;
+        }
+        return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
+          (end ? this.doc.height - heightAtLine(lineObj) : 0)
+      },
+
+      defaultTextHeight: function() { return textHeight(this.display) },
+      defaultCharWidth: function() { return charWidth(this.display) },
+
+      getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
+
+      addWidget: function(pos, node, scroll, vert, horiz) {
+        var display = this.display;
+        pos = cursorCoords(this, clipPos(this.doc, pos));
+        var top = pos.bottom, left = pos.left;
+        node.style.position = "absolute";
+        node.setAttribute("cm-ignore-events", "true");
+        this.display.input.setUneditable(node);
+        display.sizer.appendChild(node);
+        if (vert == "over") {
+          top = pos.top;
+        } else if (vert == "above" || vert == "near") {
+          var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
+          hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
+          // Default to positioning above (if specified and possible); otherwise default to positioning below
+          if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
+            { top = pos.top - node.offsetHeight; }
+          else if (pos.bottom + node.offsetHeight <= vspace)
+            { top = pos.bottom; }
+          if (left + node.offsetWidth > hspace)
+            { left = hspace - node.offsetWidth; }
+        }
+        node.style.top = top + "px";
+        node.style.left = node.style.right = "";
+        if (horiz == "right") {
+          left = display.sizer.clientWidth - node.offsetWidth;
+          node.style.right = "0px";
+        } else {
+          if (horiz == "left") { left = 0; }
+          else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
+          node.style.left = left + "px";
+        }
+        if (scroll)
+          { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
+      },
+
+      triggerOnKeyDown: methodOp(onKeyDown),
+      triggerOnKeyPress: methodOp(onKeyPress),
+      triggerOnKeyUp: onKeyUp,
+      triggerOnMouseDown: methodOp(onMouseDown),
+
+      execCommand: function(cmd) {
+        if (commands.hasOwnProperty(cmd))
+          { return commands[cmd].call(null, this) }
+      },
+
+      triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
+
+      findPosH: function(from, amount, unit, visually) {
+        var this$1 = this;
+
+        var dir = 1;
+        if (amount < 0) { dir = -1; amount = -amount; }
+        var cur = clipPos(this.doc, from);
+        for (var i = 0; i < amount; ++i) {
+          cur = findPosH(this$1.doc, cur, dir, unit, visually);
+          if (cur.hitSide) { break }
+        }
+        return cur
+      },
+
+      moveH: methodOp(function(dir, unit) {
+        var this$1 = this;
+
+        this.extendSelectionsBy(function (range$$1) {
+          if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
+            { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
+          else
+            { return dir < 0 ? range$$1.from() : range$$1.to() }
+        }, sel_move);
+      }),
+
+      deleteH: methodOp(function(dir, unit) {
+        var sel = this.doc.sel, doc = this.doc;
+        if (sel.somethingSelected())
+          { doc.replaceSelection("", null, "+delete"); }
+        else
+          { deleteNearSelection(this, function (range$$1) {
+            var other = findPosH(doc, range$$1.head, dir, unit, false);
+            return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
+          }); }
+      }),
+
+      findPosV: function(from, amount, unit, goalColumn) {
+        var this$1 = this;
+
+        var dir = 1, x = goalColumn;
+        if (amount < 0) { dir = -1; amount = -amount; }
+        var cur = clipPos(this.doc, from);
+        for (var i = 0; i < amount; ++i) {
+          var coords = cursorCoords(this$1, cur, "div");
+          if (x == null) { x = coords.left; }
+          else { coords.left = x; }
+          cur = findPosV(this$1, coords, dir, unit);
+          if (cur.hitSide) { break }
+        }
+        return cur
+      },
+
+      moveV: methodOp(function(dir, unit) {
+        var this$1 = this;
+
+        var doc = this.doc, goals = [];
+        var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
+        doc.extendSelectionsBy(function (range$$1) {
+          if (collapse)
+            { return dir < 0 ? range$$1.from() : range$$1.to() }
+          var headPos = cursorCoords(this$1, range$$1.head, "div");
+          if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
+          goals.push(headPos.left);
+          var pos = findPosV(this$1, headPos, dir, unit);
+          if (unit == "page" && range$$1 == doc.sel.primary())
+            { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
+          return pos
+        }, sel_move);
+        if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
+          { doc.sel.ranges[i].goalColumn = goals[i]; } }
+      }),
+
+      // Find the word at the given position (as returned by coordsChar).
+      findWordAt: function(pos) {
+        var doc = this.doc, line = getLine(doc, pos.line).text;
+        var start = pos.ch, end = pos.ch;
+        if (line) {
+          var helper = this.getHelper(pos, "wordChars");
+          if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
+          var startChar = line.charAt(start);
+          var check = isWordChar(startChar, helper)
+            ? function (ch) { return isWordChar(ch, helper); }
+            : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
+            : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
+          while (start > 0 && check(line.charAt(start - 1))) { --start; }
+          while (end < line.length && check(line.charAt(end))) { ++end; }
+        }
+        return new Range(Pos(pos.line, start), Pos(pos.line, end))
+      },
+
+      toggleOverwrite: function(value) {
+        if (value != null && value == this.state.overwrite) { return }
+        if (this.state.overwrite = !this.state.overwrite)
+          { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
+        else
+          { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
+
+        signal(this, "overwriteToggle", this, this.state.overwrite);
+      },
+      hasFocus: function() { return this.display.input.getField() == activeElt() },
+      isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
+
+      scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
+      getScrollInfo: function() {
+        var scroller = this.display.scroller;
+        return {left: scroller.scrollLeft, top: scroller.scrollTop,
+                height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
+                width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
+                clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
+      },
+
+      scrollIntoView: methodOp(function(range$$1, margin) {
+        if (range$$1 == null) {
+          range$$1 = {from: this.doc.sel.primary().head, to: null};
+          if (margin == null) { margin = this.options.cursorScrollMargin; }
+        } else if (typeof range$$1 == "number") {
+          range$$1 = {from: Pos(range$$1, 0), to: null};
+        } else if (range$$1.from == null) {
+          range$$1 = {from: range$$1, to: null};
+        }
+        if (!range$$1.to) { range$$1.to = range$$1.from; }
+        range$$1.margin = margin || 0;
+
+        if (range$$1.from.line != null) {
+          scrollToRange(this, range$$1);
+        } else {
+          scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
+        }
+      }),
+
+      setSize: methodOp(function(width, height) {
+        var this$1 = this;
+
+        var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
+        if (width != null) { this.display.wrapper.style.width = interpret(width); }
+        if (height != null) { this.display.wrapper.style.height = interpret(height); }
+        if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
+        var lineNo$$1 = this.display.viewFrom;
+        this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
+          if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
+            { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
+          ++lineNo$$1;
+        });
+        this.curOp.forceUpdate = true;
+        signal(this, "refresh", this);
+      }),
+
+      operation: function(f){return runInOp(this, f)},
+      startOperation: function(){return startOperation(this)},
+      endOperation: function(){return endOperation(this)},
+
+      refresh: methodOp(function() {
+        var oldHeight = this.display.cachedTextHeight;
+        regChange(this);
+        this.curOp.forceUpdate = true;
+        clearCaches(this);
+        scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
+        updateGutterSpace(this.display);
+        if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
+          { estimateLineHeights(this); }
+        signal(this, "refresh", this);
+      }),
+
+      swapDoc: methodOp(function(doc) {
+        var old = this.doc;
+        old.cm = null;
+        // Cancel the current text selection if any (#5821)
+        if (this.state.selectingText) { this.state.selectingText(); }
+        attachDoc(this, doc);
+        clearCaches(this);
+        this.display.input.reset();
+        scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
+        this.curOp.forceScroll = true;
+        signalLater(this, "swapDoc", this, old);
+        return old
+      }),
+
+      phrase: function(phraseText) {
+        var phrases = this.options.phrases;
+        return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
+      },
+
+      getInputField: function(){return this.display.input.getField()},
+      getWrapperElement: function(){return this.display.wrapper},
+      getScrollerElement: function(){return this.display.scroller},
+      getGutterElement: function(){return this.display.gutters}
+    };
+    eventMixin(CodeMirror);
+
+    CodeMirror.registerHelper = function(type, name, value) {
+      if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
+      helpers[type][name] = value;
+    };
+    CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
+      CodeMirror.registerHelper(type, name, value);
+      helpers[type]._global.push({pred: predicate, val: value});
+    };
+  }
+
+  // Used for horizontal relative motion. Dir is -1 or 1 (left or
+  // right), unit can be "char", "column" (like char, but doesn't
+  // cross line boundaries), "word" (across next word), or "group" (to
+  // the start of next group of word or non-word-non-whitespace
+  // chars). The visually param controls whether, in right-to-left
+  // text, direction 1 means to move towards the next index in the
+  // string, or towards the character to the right of the current
+  // position. The resulting position will have a hitSide=true
+  // property if it reached the end of the document.
+  function findPosH(doc, pos, dir, unit, visually) {
+    var oldPos = pos;
+    var origDir = dir;
+    var lineObj = getLine(doc, pos.line);
+    function findNextLine() {
+      var l = pos.line + dir;
+      if (l < doc.first || l >= doc.first + doc.size) { return false }
+      pos = new Pos(l, pos.ch, pos.sticky);
+      return lineObj = getLine(doc, l)
+    }
+    function moveOnce(boundToLine) {
+      var next;
+      if (visually) {
+        next = moveVisually(doc.cm, lineObj, pos, dir);
+      } else {
+        next = moveLogically(lineObj, pos, dir);
+      }
+      if (next == null) {
+        if (!boundToLine && findNextLine())
+          { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
+        else
+          { return false }
+      } else {
+        pos = next;
+      }
+      return true
+    }
+
+    if (unit == "char") {
+      moveOnce();
+    } else if (unit == "column") {
+      moveOnce(true);
+    } else if (unit == "word" || unit == "group") {
+      var sawType = null, group = unit == "group";
+      var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
+      for (var first = true;; first = false) {
+        if (dir < 0 && !moveOnce(!first)) { break }
+        var cur = lineObj.text.charAt(pos.ch) || "\n";
+        var type = isWordChar(cur, helper) ? "w"
+          : group && cur == "\n" ? "n"
+          : !group || /\s/.test(cur) ? null
+          : "p";
+        if (group && !first && !type) { type = "s"; }
+        if (sawType && sawType != type) {
+          if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
+          break
+        }
+
+        if (type) { sawType = type; }
+        if (dir > 0 && !moveOnce(!first)) { break }
+      }
+    }
+    var result = skipAtomic(doc, pos, oldPos, origDir, true);
+    if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
+    return result
+  }
+
+  // For relative vertical movement. Dir may be -1 or 1. Unit can be
+  // "page" or "line". The resulting position will have a hitSide=true
+  // property if it reached the end of the document.
+  function findPosV(cm, pos, dir, unit) {
+    var doc = cm.doc, x = pos.left, y;
+    if (unit == "page") {
+      var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
+      var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
+      y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
+
+    } else if (unit == "line") {
+      y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+    }
+    var target;
+    for (;;) {
+      target = coordsChar(cm, x, y);
+      if (!target.outside) { break }
+      if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
+      y += dir * 5;
+    }
+    return target
+  }
+
+  // CONTENTEDITABLE INPUT STYLE
+
+  var ContentEditableInput = function(cm) {
+    this.cm = cm;
+    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
+    this.polling = new Delayed();
+    this.composing = null;
+    this.gracePeriod = false;
+    this.readDOMTimeout = null;
+  };
+
+  ContentEditableInput.prototype.init = function (display) {
+      var this$1 = this;
+
+    var input = this, cm = input.cm;
+    var div = input.div = display.lineDiv;
+    disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
+
+    on(div, "paste", function (e) {
+      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
+      // IE doesn't fire input events, so we schedule a read for the pasted content in this way
+      if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
+    });
+
+    on(div, "compositionstart", function (e) {
+      this$1.composing = {data: e.data, done: false};
+    });
+    on(div, "compositionupdate", function (e) {
+      if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
+    });
+    on(div, "compositionend", function (e) {
+      if (this$1.composing) {
+        if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
+        this$1.composing.done = true;
+      }
+    });
+
+    on(div, "touchstart", function () { return input.forceCompositionEnd(); });
+
+    on(div, "input", function () {
+      if (!this$1.composing) { this$1.readFromDOMSoon(); }
+    });
+
+    function onCopyCut(e) {
+      if (signalDOMEvent(cm, e)) { return }
+      if (cm.somethingSelected()) {
+        setLastCopied({lineWise: false, text: cm.getSelections()});
+        if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
+      } else if (!cm.options.lineWiseCopyCut) {
+        return
+      } else {
+        var ranges = copyableRanges(cm);
+        setLastCopied({lineWise: true, text: ranges.text});
+        if (e.type == "cut") {
+          cm.operation(function () {
+            cm.setSelections(ranges.ranges, 0, sel_dontScroll);
+            cm.replaceSelection("", null, "cut");
+          });
+        }
+      }
+      if (e.clipboardData) {
+        e.clipboardData.clearData();
+        var content = lastCopied.text.join("\n");
+        // iOS exposes the clipboard API, but seems to discard content inserted into it
+        e.clipboardData.setData("Text", content);
+        if (e.clipboardData.getData("Text") == content) {
+          e.preventDefault();
+          return
+        }
+      }
+      // Old-fashioned briefly-focus-a-textarea hack
+      var kludge = hiddenTextarea(), te = kludge.firstChild;
+      cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
+      te.value = lastCopied.text.join("\n");
+      var hadFocus = document.activeElement;
+      selectInput(te);
+      setTimeout(function () {
+        cm.display.lineSpace.removeChild(kludge);
+        hadFocus.focus();
+        if (hadFocus == div) { input.showPrimarySelection(); }
+      }, 50);
+    }
+    on(div, "copy", onCopyCut);
+    on(div, "cut", onCopyCut);
+  };
+
+  ContentEditableInput.prototype.prepareSelection = function () {
+    var result = prepareSelection(this.cm, false);
+    result.focus = this.cm.state.focused;
+    return result
+  };
+
+  ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
+    if (!info || !this.cm.display.view.length) { return }
+    if (info.focus || takeFocus) { this.showPrimarySelection(); }
+    this.showMultipleSelections(info);
+  };
+
+  ContentEditableInput.prototype.getSelection = function () {
+    return this.cm.display.wrapper.ownerDocument.getSelection()
+  };
+
+  ContentEditableInput.prototype.showPrimarySelection = function () {
+    var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
+    var from = prim.from(), to = prim.to();
+
+    if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
+      sel.removeAllRanges();
+      return
+    }
+
+    var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
+    var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
+    if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
+        cmp(minPos(curAnchor, curFocus), from) == 0 &&
+        cmp(maxPos(curAnchor, curFocus), to) == 0)
+      { return }
+
+    var view = cm.display.view;
+    var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
+        {node: view[0].measure.map[2], offset: 0};
+    var end = to.line < cm.display.viewTo && posToDOM(cm, to);
+    if (!end) {
+      var measure = view[view.length - 1].measure;
+      var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
+      end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
+    }
+
+    if (!start || !end) {
+      sel.removeAllRanges();
+      return
+    }
+
+    var old = sel.rangeCount && sel.getRangeAt(0), rng;
+    try { rng = range(start.node, start.offset, end.offset, end.node); }
+    catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
+    if (rng) {
+      if (!gecko && cm.state.focused) {
+        sel.collapse(start.node, start.offset);
+        if (!rng.collapsed) {
+          sel.removeAllRanges();
+          sel.addRange(rng);
+        }
+      } else {
+        sel.removeAllRanges();
+        sel.addRange(rng);
+      }
+      if (old && sel.anchorNode == null) { sel.addRange(old); }
+      else if (gecko) { this.startGracePeriod(); }
+    }
+    this.rememberSelection();
+  };
+
+  ContentEditableInput.prototype.startGracePeriod = function () {
+      var this$1 = this;
+
+    clearTimeout(this.gracePeriod);
+    this.gracePeriod = setTimeout(function () {
+      this$1.gracePeriod = false;
+      if (this$1.selectionChanged())
+        { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
+    }, 20);
+  };
+
+  ContentEditableInput.prototype.showMultipleSelections = function (info) {
+    removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
+    removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
+  };
+
+  ContentEditableInput.prototype.rememberSelection = function () {
+    var sel = this.getSelection();
+    this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
+    this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
+  };
+
+  ContentEditableInput.prototype.selectionInEditor = function () {
+    var sel = this.getSelection();
+    if (!sel.rangeCount) { return false }
+    var node = sel.getRangeAt(0).commonAncestorContainer;
+    return contains(this.div, node)
+  };
+
+  ContentEditableInput.prototype.focus = function () {
+    if (this.cm.options.readOnly != "nocursor") {
+      if (!this.selectionInEditor())
+        { this.showSelection(this.prepareSelection(), true); }
+      this.div.focus();
+    }
+  };
+  ContentEditableInput.prototype.blur = function () { this.div.blur(); };
+  ContentEditableInput.prototype.getField = function () { return this.div };
+
+  ContentEditableInput.prototype.supportsTouch = function () { return true };
+
+  ContentEditableInput.prototype.receivedFocus = function () {
+    var input = this;
+    if (this.selectionInEditor())
+      { this.pollSelection(); }
+    else
+      { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
+
+    function poll() {
+      if (input.cm.state.focused) {
+        input.pollSelection();
+        input.polling.set(input.cm.options.pollInterval, poll);
+      }
+    }
+    this.polling.set(this.cm.options.pollInterval, poll);
+  };
+
+  ContentEditableInput.prototype.selectionChanged = function () {
+    var sel = this.getSelection();
+    return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
+      sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
+  };
+
+  ContentEditableInput.prototype.pollSelection = function () {
+    if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
+    var sel = this.getSelection(), cm = this.cm;
+    // On Android Chrome (version 56, at least), backspacing into an
+    // uneditable block element will put the cursor in that element,
+    // and then, because it's not editable, hide the virtual keyboard.
+    // Because Android doesn't allow us to actually detect backspace
+    // presses in a sane way, this code checks for when that happens
+    // and simulates a backspace press in this case.
+    if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
+      this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
+      this.blur();
+      this.focus();
+      return
+    }
+    if (this.composing) { return }
+    this.rememberSelection();
+    var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
+    var head = domToPos(cm, sel.focusNode, sel.focusOffset);
+    if (anchor && head) { runInOp(cm, function () {
+      setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
+      if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
+    }); }
+  };
+
+  ContentEditableInput.prototype.pollContent = function () {
+    if (this.readDOMTimeout != null) {
+      clearTimeout(this.readDOMTimeout);
+      this.readDOMTimeout = null;
+    }
+
+    var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
+    var from = sel.from(), to = sel.to();
+    if (from.ch == 0 && from.line > cm.firstLine())
+      { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
+    if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
+      { to = Pos(to.line + 1, 0); }
+    if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
+
+    var fromIndex, fromLine, fromNode;
+    if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
+      fromLine = lineNo(display.view[0].line);
+      fromNode = display.view[0].node;
+    } else {
+      fromLine = lineNo(display.view[fromIndex].line);
+      fromNode = display.view[fromIndex - 1].node.nextSibling;
+    }
+    var toIndex = findViewIndex(cm, to.line);
+    var toLine, toNode;
+    if (toIndex == display.view.length - 1) {
+      toLine = display.viewTo - 1;
+      toNode = display.lineDiv.lastChild;
+    } else {
+      toLine = lineNo(display.view[toIndex + 1].line) - 1;
+      toNode = display.view[toIndex + 1].node.previousSibling;
+    }
+
+    if (!fromNode) { return false }
+    var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
+    var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
+    while (newText.length > 1 && oldText.length > 1) {
+      if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
+      else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
+      else { break }
+    }
+
+    var cutFront = 0, cutEnd = 0;
+    var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
+    while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
+      { ++cutFront; }
+    var newBot = lst(newText), oldBot = lst(oldText);
+    var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
+                             oldBot.length - (oldText.length == 1 ? cutFront : 0));
+    while (cutEnd < maxCutEnd &&
+           newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
+      { ++cutEnd; }
+    // Try to move start of change to start of selection if ambiguous
+    if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
+      while (cutFront && cutFront > from.ch &&
+             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
+        cutFront--;
+        cutEnd++;
+      }
+    }
+
+    newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
+    newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
+
+    var chFrom = Pos(fromLine, cutFront);
+    var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
+    if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
+      replaceRange(cm.doc, newText, chFrom, chTo, "+input");
+      return true
+    }
+  };
+
+  ContentEditableInput.prototype.ensurePolled = function () {
+    this.forceCompositionEnd();
+  };
+  ContentEditableInput.prototype.reset = function () {
+    this.forceCompositionEnd();
+  };
+  ContentEditableInput.prototype.forceCompositionEnd = function () {
+    if (!this.composing) { return }
+    clearTimeout(this.readDOMTimeout);
+    this.composing = null;
+    this.updateFromDOM();
+    this.div.blur();
+    this.div.focus();
+  };
+  ContentEditableInput.prototype.readFromDOMSoon = function () {
+      var this$1 = this;
+
+    if (this.readDOMTimeout != null) { return }
+    this.readDOMTimeout = setTimeout(function () {
+      this$1.readDOMTimeout = null;
+      if (this$1.composing) {
+        if (this$1.composing.done) { this$1.composing = null; }
+        else { return }
+      }
+      this$1.updateFromDOM();
+    }, 80);
+  };
+
+  ContentEditableInput.prototype.updateFromDOM = function () {
+      var this$1 = this;
+
+    if (this.cm.isReadOnly() || !this.pollContent())
+      { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
+  };
+
+  ContentEditableInput.prototype.setUneditable = function (node) {
+    node.contentEditable = "false";
+  };
+
+  ContentEditableInput.prototype.onKeyPress = function (e) {
+    if (e.charCode == 0 || this.composing) { return }
+    e.preventDefault();
+    if (!this.cm.isReadOnly())
+      { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
+  };
+
+  ContentEditableInput.prototype.readOnlyChanged = function (val) {
+    this.div.contentEditable = String(val != "nocursor");
+  };
+
+  ContentEditableInput.prototype.onContextMenu = function () {};
+  ContentEditableInput.prototype.resetPosition = function () {};
+
+  ContentEditableInput.prototype.needsContentAttribute = true;
+
+  function posToDOM(cm, pos) {
+    var view = findViewForLine(cm, pos.line);
+    if (!view || view.hidden) { return null }
+    var line = getLine(cm.doc, pos.line);
+    var info = mapFromLineView(view, line, pos.line);
+
+    var order = getOrder(line, cm.doc.direction), side = "left";
+    if (order) {
+      var partPos = getBidiPartAt(order, pos.ch);
+      side = partPos % 2 ? "right" : "left";
+    }
+    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
+    result.offset = result.collapse == "right" ? result.end : result.start;
+    return result
+  }
+
+  function isInGutter(node) {
+    for (var scan = node; scan; scan = scan.parentNode)
+      { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
+    return false
+  }
+
+  function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
+
+  function domTextBetween(cm, from, to, fromLine, toLine) {
+    var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
+    function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
+    function close() {
+      if (closing) {
+        text += lineSep;
+        if (extraLinebreak) { text += lineSep; }
+        closing = extraLinebreak = false;
+      }
+    }
+    function addText(str) {
+      if (str) {
+        close();
+        text += str;
+      }
+    }
+    function walk(node) {
+      if (node.nodeType == 1) {
+        var cmText = node.getAttribute("cm-text");
+        if (cmText) {
+          addText(cmText);
+          return
+        }
+        var markerID = node.getAttribute("cm-marker"), range$$1;
+        if (markerID) {
+          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
+          if (found.length && (range$$1 = found[0].find(0)))
+            { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
+          return
+        }
+        if (node.getAttribute("contenteditable") == "false") { return }
+        var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
+        if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
+
+        if (isBlock) { close(); }
+        for (var i = 0; i < node.childNodes.length; i++)
+          { walk(node.childNodes[i]); }
+
+        if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
+        if (isBlock) { closing = true; }
+      } else if (node.nodeType == 3) {
+        addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
+      }
+    }
+    for (;;) {
+      walk(from);
+      if (from == to) { break }
+      from = from.nextSibling;
+      extraLinebreak = false;
+    }
+    return text
+  }
+
+  function domToPos(cm, node, offset) {
+    var lineNode;
+    if (node == cm.display.lineDiv) {
+      lineNode = cm.display.lineDiv.childNodes[offset];
+      if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
+      node = null; offset = 0;
+    } else {
+      for (lineNode = node;; lineNode = lineNode.parentNode) {
+        if (!lineNode || lineNode == cm.display.lineDiv) { return null }
+        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
+      }
+    }
+    for (var i = 0; i < cm.display.view.length; i++) {
+      var lineView = cm.display.view[i];
+      if (lineView.node == lineNode)
+        { return locateNodeInLineView(lineView, node, offset) }
+    }
+  }
+
+  function locateNodeInLineView(lineView, node, offset) {
+    var wrapper = lineView.text.firstChild, bad = false;
+    if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
+    if (node == wrapper) {
+      bad = true;
+      node = wrapper.childNodes[offset];
+      offset = 0;
+      if (!node) {
+        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
+        return badPos(Pos(lineNo(line), line.text.length), bad)
+      }
+    }
+
+    var textNode = node.nodeType == 3 ? node : null, topNode = node;
+    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
+      textNode = node.firstChild;
+      if (offset) { offset = textNode.nodeValue.length; }
+    }
+    while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
+    var measure = lineView.measure, maps = measure.maps;
+
+    function find(textNode, topNode, offset) {
+      for (var i = -1; i < (maps ? maps.length : 0); i++) {
+        var map$$1 = i < 0 ? measure.map : maps[i];
+        for (var j = 0; j < map$$1.length; j += 3) {
+          var curNode = map$$1[j + 2];
+          if (curNode == textNode || curNode == topNode) {
+            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
+            var ch = map$$1[j] + offset;
+            if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
+            return Pos(line, ch)
+          }
+        }
+      }
+    }
+    var found = find(textNode, topNode, offset);
+    if (found) { return badPos(found, bad) }
+
+    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
+    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
+      found = find(after, after.firstChild, 0);
+      if (found)
+        { return badPos(Pos(found.line, found.ch - dist), bad) }
+      else
+        { dist += after.textContent.length; }
+    }
+    for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
+      found = find(before, before.firstChild, -1);
+      if (found)
+        { return badPos(Pos(found.line, found.ch + dist$1), bad) }
+      else
+        { dist$1 += before.textContent.length; }
+    }
+  }
+
+  // TEXTAREA INPUT STYLE
+
+  var TextareaInput = function(cm) {
+    this.cm = cm;
+    // See input.poll and input.reset
+    this.prevInput = "";
+
+    // Flag that indicates whether we expect input to appear real soon
+    // now (after some event like 'keypress' or 'input') and are
+    // polling intensively.
+    this.pollingFast = false;
+    // Self-resetting timeout for the poller
+    this.polling = new Delayed();
+    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
+    this.hasSelection = false;
+    this.composing = null;
+  };
+
+  TextareaInput.prototype.init = function (display) {
+      var this$1 = this;
+
+    var input = this, cm = this.cm;
+    this.createField(display);
+    var te = this.textarea;
+
+    display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
+
+    // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
+    if (ios) { te.style.width = "0px"; }
+
+    on(te, "input", function () {
+      if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
+      input.poll();
+    });
+
+    on(te, "paste", function (e) {
+      if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
+
+      cm.state.pasteIncoming = +new Date;
+      input.fastPoll();
+    });
+
+    function prepareCopyCut(e) {
+      if (signalDOMEvent(cm, e)) { return }
+      if (cm.somethingSelected()) {
+        setLastCopied({lineWise: false, text: cm.getSelections()});
+      } else if (!cm.options.lineWiseCopyCut) {
+        return
+      } else {
+        var ranges = copyableRanges(cm);
+        setLastCopied({lineWise: true, text: ranges.text});
+        if (e.type == "cut") {
+          cm.setSelections(ranges.ranges, null, sel_dontScroll);
+        } else {
+          input.prevInput = "";
+          te.value = ranges.text.join("\n");
+          selectInput(te);
+        }
+      }
+      if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
+    }
+    on(te, "cut", prepareCopyCut);
+    on(te, "copy", prepareCopyCut);
+
+    on(display.scroller, "paste", function (e) {
+      if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
+      if (!te.dispatchEvent) {
+        cm.state.pasteIncoming = +new Date;
+        input.focus();
+        return
+      }
+
+      // Pass the `paste` event to the textarea so it's handled by its event listener.
+      var event = new Event("paste");
+      event.clipboardData = e.clipboardData;
+      te.dispatchEvent(event);
+    });
+
+    // Prevent normal selection in the editor (we handle our own)
+    on(display.lineSpace, "selectstart", function (e) {
+      if (!eventInWidget(display, e)) { e_preventDefault(e); }
+    });
+
+    on(te, "compositionstart", function () {
+      var start = cm.getCursor("from");
+      if (input.composing) { input.composing.range.clear(); }
+      input.composing = {
+        start: start,
+        range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
+      };
+    });
+    on(te, "compositionend", function () {
+      if (input.composing) {
+        input.poll();
+        input.composing.range.clear();
+        input.composing = null;
+      }
+    });
+  };
+
+  TextareaInput.prototype.createField = function (_display) {
+    // Wraps and hides input textarea
+    this.wrapper = hiddenTextarea();
+    // The semihidden textarea that is focused when the editor is
+    // focused, and receives input.
+    this.textarea = this.wrapper.firstChild;
+  };
+
+  TextareaInput.prototype.prepareSelection = function () {
+    // Redraw the selection and/or cursor
+    var cm = this.cm, display = cm.display, doc = cm.doc;
+    var result = prepareSelection(cm);
+
+    // Move the hidden textarea near the cursor to prevent scrolling artifacts
+    if (cm.options.moveInputWithCursor) {
+      var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
+      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
+      result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+                                          headPos.top + lineOff.top - wrapOff.top));
+      result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+                                           headPos.left + lineOff.left - wrapOff.left));
+    }
+
+    return result
+  };
+
+  TextareaInput.prototype.showSelection = function (drawn) {
+    var cm = this.cm, display = cm.display;
+    removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
+    removeChildrenAndAdd(display.selectionDiv, drawn.selection);
+    if (drawn.teTop != null) {
+      this.wrapper.style.top = drawn.teTop + "px";
+      this.wrapper.style.left = drawn.teLeft + "px";
+    }
+  };
+
+  // Reset the input to correspond to the selection (or to be empty,
+  // when not typing and nothing is selected)
+  TextareaInput.prototype.reset = function (typing) {
+    if (this.contextMenuPending || this.composing) { return }
+    var cm = this.cm;
+    if (cm.somethingSelected()) {
+      this.prevInput = "";
+      var content = cm.getSelection();
+      this.textarea.value = content;
+      if (cm.state.focused) { selectInput(this.textarea); }
+      if (ie && ie_version >= 9) { this.hasSelection = content; }
+    } else if (!typing) {
+      this.prevInput = this.textarea.value = "";
+      if (ie && ie_version >= 9) { this.hasSelection = null; }
+    }
+  };
+
+  TextareaInput.prototype.getField = function () { return this.textarea };
+
+  TextareaInput.prototype.supportsTouch = function () { return false };
+
+  TextareaInput.prototype.focus = function () {
+    if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
+      try { this.textarea.focus(); }
+      catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
+    }
+  };
+
+  TextareaInput.prototype.blur = function () { this.textarea.blur(); };
+
+  TextareaInput.prototype.resetPosition = function () {
+    this.wrapper.style.top = this.wrapper.style.left = 0;
+  };
+
+  TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
+
+  // Poll for input changes, using the normal rate of polling. This
+  // runs as long as the editor is focused.
+  TextareaInput.prototype.slowPoll = function () {
+      var this$1 = this;
+
+    if (this.pollingFast) { return }
+    this.polling.set(this.cm.options.pollInterval, function () {
+      this$1.poll();
+      if (this$1.cm.state.focused) { this$1.slowPoll(); }
+    });
+  };
+
+  // When an event has just come in that is likely to add or change
+  // something in the input textarea, we poll faster, to ensure that
+  // the change appears on the screen quickly.
+  TextareaInput.prototype.fastPoll = function () {
+    var missed = false, input = this;
+    input.pollingFast = true;
+    function p() {
+      var changed = input.poll();
+      if (!changed && !missed) {missed = true; input.polling.set(60, p);}
+      else {input.pollingFast = false; input.slowPoll();}
+    }
+    input.polling.set(20, p);
+  };
+
+  // Read input from the textarea, and update the document to match.
+  // When something is selected, it is present in the textarea, and
+  // selected (unless it is huge, in which case a placeholder is
+  // used). When nothing is selected, the cursor sits after previously
+  // seen text (can be empty), which is stored in prevInput (we must
+  // not reset the textarea when typing, because that breaks IME).
+  TextareaInput.prototype.poll = function () {
+      var this$1 = this;
+
+    var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
+    // Since this is called a *lot*, try to bail out as cheaply as
+    // possible when it is clear that nothing happened. hasSelection
+    // will be the case when there is a lot of text in the textarea,
+    // in which case reading its value would be expensive.
+    if (this.contextMenuPending || !cm.state.focused ||
+        (hasSelection(input) && !prevInput && !this.composing) ||
+        cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
+      { return false }
+
+    var text = input.value;
+    // If nothing changed, bail.
+    if (text == prevInput && !cm.somethingSelected()) { return false }
+    // Work around nonsensical selection resetting in IE9/10, and
+    // inexplicable appearance of private area unicode characters on
+    // some key combos in Mac (#2689).
+    if (ie && ie_version >= 9 && this.hasSelection === text ||
+        mac && /[\uf700-\uf7ff]/.test(text)) {
+      cm.display.input.reset();
+      return false
+    }
+
+    if (cm.doc.sel == cm.display.selForContextMenu) {
+      var first = text.charCodeAt(0);
+      if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
+      if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
+    }
+    // Find the part of the input that is actually new
+    var same = 0, l = Math.min(prevInput.length, text.length);
+    while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
+
+    runInOp(cm, function () {
+      applyTextInput(cm, text.slice(same), prevInput.length - same,
+                     null, this$1.composing ? "*compose" : null);
+
+      // Don't leave long text in the textarea, since it makes further polling slow
+      if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
+      else { this$1.prevInput = text; }
+
+      if (this$1.composing) {
+        this$1.composing.range.clear();
+        this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
+                                           {className: "CodeMirror-composing"});
+      }
+    });
+    return true
+  };
+
+  TextareaInput.prototype.ensurePolled = function () {
+    if (this.pollingFast && this.poll()) { this.pollingFast = false; }
+  };
+
+  TextareaInput.prototype.onKeyPress = function () {
+    if (ie && ie_version >= 9) { this.hasSelection = null; }
+    this.fastPoll();
+  };
+
+  TextareaInput.prototype.onContextMenu = function (e) {
+    var input = this, cm = input.cm, display = cm.display, te = input.textarea;
+    if (input.contextMenuPending) { input.contextMenuPending(); }
+    var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
+    if (!pos || presto) { return } // Opera is difficult.
+
+    // Reset the current text selection only if the click is done outside of the selection
+    // and 'resetSelectionOnContextMenu' option is true.
+    var reset = cm.options.resetSelectionOnContextMenu;
+    if (reset && cm.doc.sel.contains(pos) == -1)
+      { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
+
+    var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
+    var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
+    input.wrapper.style.cssText = "position: static";
+    te.style.cssText = "position: absolute; width: 30px; height: 30px;\n      top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n      z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+    var oldScrollY;
+    if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
+    display.input.focus();
+    if (webkit) { window.scrollTo(null, oldScrollY); }
+    display.input.reset();
+    // Adds "Select all" to context menu in FF
+    if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
+    input.contextMenuPending = rehide;
+    display.selForContextMenu = cm.doc.sel;
+    clearTimeout(display.detectingSelectAll);
+
+    // Select-all will be greyed out if there's nothing to select, so
+    // this adds a zero-width space so that we can later check whether
+    // it got selected.
+    function prepareSelectAllHack() {
+      if (te.selectionStart != null) {
+        var selected = cm.somethingSelected();
+        var extval = "\u200b" + (selected ? te.value : "");
+        te.value = "\u21da"; // Used to catch context-menu undo
+        te.value = extval;
+        input.prevInput = selected ? "" : "\u200b";
+        te.selectionStart = 1; te.selectionEnd = extval.length;
+        // Re-set this, in case some other handler touched the
+        // selection in the meantime.
+        display.selForContextMenu = cm.doc.sel;
+      }
+    }
+    function rehide() {
+      if (input.contextMenuPending != rehide) { return }
+      input.contextMenuPending = false;
+      input.wrapper.style.cssText = oldWrapperCSS;
+      te.style.cssText = oldCSS;
+      if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
+
+      // Try to detect the user choosing select-all
+      if (te.selectionStart != null) {
+        if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
+        var i = 0, poll = function () {
+          if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
+              te.selectionEnd > 0 && input.prevInput == "\u200b") {
+            operation(cm, selectAll)(cm);
+          } else if (i++ < 10) {
+            display.detectingSelectAll = setTimeout(poll, 500);
+          } else {
+            display.selForContextMenu = null;
+            display.input.reset();
+          }
+        };
+        display.detectingSelectAll = setTimeout(poll, 200);
+      }
+    }
+
+    if (ie && ie_version >= 9) { prepareSelectAllHack(); }
+    if (captureRightClick) {
+      e_stop(e);
+      var mouseup = function () {
+        off(window, "mouseup", mouseup);
+        setTimeout(rehide, 20);
+      };
+      on(window, "mouseup", mouseup);
+    } else {
+      setTimeout(rehide, 50);
+    }
+  };
+
+  TextareaInput.prototype.readOnlyChanged = function (val) {
+    if (!val) { this.reset(); }
+    this.textarea.disabled = val == "nocursor";
+  };
+
+  TextareaInput.prototype.setUneditable = function () {};
+
+  TextareaInput.prototype.needsContentAttribute = false;
+
+  function fromTextArea(textarea, options) {
+    options = options ? copyObj(options) : {};
+    options.value = textarea.value;
+    if (!options.tabindex && textarea.tabIndex)
+      { options.tabindex = textarea.tabIndex; }
+    if (!options.placeholder && textarea.placeholder)
+      { options.placeholder = textarea.placeholder; }
+    // Set autofocus to true if this textarea is focused, or if it has
+    // autofocus and no other element is focused.
+    if (options.autofocus == null) {
+      var hasFocus = activeElt();
+      options.autofocus = hasFocus == textarea ||
+        textarea.getAttribute("autofocus") != null && hasFocus == document.body;
+    }
+
+    function save() {textarea.value = cm.getValue();}
+
+    var realSubmit;
+    if (textarea.form) {
+      on(textarea.form, "submit", save);
+      // Deplorable hack to make the submit method do the right thing.
+      if (!options.leaveSubmitMethodAlone) {
+        var form = textarea.form;
+        realSubmit = form.submit;
+        try {
+          var wrappedSubmit = form.submit = function () {
+            save();
+            form.submit = realSubmit;
+            form.submit();
+            form.submit = wrappedSubmit;
+          };
+        } catch(e) {}
+      }
+    }
+
+    options.finishInit = function (cm) {
+      cm.save = save;
+      cm.getTextArea = function () { return textarea; };
+      cm.toTextArea = function () {
+        cm.toTextArea = isNaN; // Prevent this from being ran twice
+        save();
+        textarea.parentNode.removeChild(cm.getWrapperElement());
+        textarea.style.display = "";
+        if (textarea.form) {
+          off(textarea.form, "submit", save);
+          if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
+            { textarea.form.submit = realSubmit; }
+        }
+      };
+    };
+
+    textarea.style.display = "none";
+    var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
+      options);
+    return cm
+  }
+
+  function addLegacyProps(CodeMirror) {
+    CodeMirror.off = off;
+    CodeMirror.on = on;
+    CodeMirror.wheelEventPixels = wheelEventPixels;
+    CodeMirror.Doc = Doc;
+    CodeMirror.splitLines = splitLinesAuto;
+    CodeMirror.countColumn = countColumn;
+    CodeMirror.findColumn = findColumn;
+    CodeMirror.isWordChar = isWordCharBasic;
+    CodeMirror.Pass = Pass;
+    CodeMirror.signal = signal;
+    CodeMirror.Line = Line;
+    CodeMirror.changeEnd = changeEnd;
+    CodeMirror.scrollbarModel = scrollbarModel;
+    CodeMirror.Pos = Pos;
+    CodeMirror.cmpPos = cmp;
+    CodeMirror.modes = modes;
+    CodeMirror.mimeModes = mimeModes;
+    CodeMirror.resolveMode = resolveMode;
+    CodeMirror.getMode = getMode;
+    CodeMirror.modeExtensions = modeExtensions;
+    CodeMirror.extendMode = extendMode;
+    CodeMirror.copyState = copyState;
+    CodeMirror.startState = startState;
+    CodeMirror.innerMode = innerMode;
+    CodeMirror.commands = commands;
+    CodeMirror.keyMap = keyMap;
+    CodeMirror.keyName = keyName;
+    CodeMirror.isModifierKey = isModifierKey;
+    CodeMirror.lookupKey = lookupKey;
+    CodeMirror.normalizeKeyMap = normalizeKeyMap;
+    CodeMirror.StringStream = StringStream;
+    CodeMirror.SharedTextMarker = SharedTextMarker;
+    CodeMirror.TextMarker = TextMarker;
+    CodeMirror.LineWidget = LineWidget;
+    CodeMirror.e_preventDefault = e_preventDefault;
+    CodeMirror.e_stopPropagation = e_stopPropagation;
+    CodeMirror.e_stop = e_stop;
+    CodeMirror.addClass = addClass;
+    CodeMirror.contains = contains;
+    CodeMirror.rmClass = rmClass;
+    CodeMirror.keyNames = keyNames;
+  }
+
+  // EDITOR CONSTRUCTOR
+
+  defineOptions(CodeMirror);
+
+  addEditorMethods(CodeMirror);
+
+  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
+  var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
+  for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
+    { CodeMirror.prototype[prop] = (function(method) {
+      return function() {return method.apply(this.doc, arguments)}
+    })(Doc.prototype[prop]); } }
+
+  eventMixin(Doc);
+  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
+
+  // Extra arguments are stored as the mode's dependencies, which is
+  // used by (legacy) mechanisms like loadmode.js to automatically
+  // load a mode. (Preferred mechanism is the require/define calls.)
+  CodeMirror.defineMode = function(name/*, mode, …*/) {
+    if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
+    defineMode.apply(this, arguments);
+  };
+
+  CodeMirror.defineMIME = defineMIME;
+
+  // Minimal default mode.
+  CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
+  CodeMirror.defineMIME("text/plain", "null");
+
+  // EXTENSIONS
+
+  CodeMirror.defineExtension = function (name, func) {
+    CodeMirror.prototype[name] = func;
+  };
+  CodeMirror.defineDocExtension = function (name, func) {
+    Doc.prototype[name] = func;
+  };
+
+  CodeMirror.fromTextArea = fromTextArea;
+
+  addLegacyProps(CodeMirror);
+
+  CodeMirror.version = "5.49.0";
+
+  return CodeMirror;
+
+})));
diff --git a/dist/firephp.build/lib/codemirror/addon/selection/active-line.js b/dist/firephp.build/lib/codemirror/addon/selection/active-line.js
new file mode 100644
index 0000000..c7b14ce
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror/addon/selection/active-line.js
@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+  var WRAP_CLASS = "CodeMirror-activeline";
+  var BACK_CLASS = "CodeMirror-activeline-background";
+  var GUTT_CLASS = "CodeMirror-activeline-gutter";
+
+  CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
+    var prev = old == CodeMirror.Init ? false : old;
+    if (val == prev) return
+    if (prev) {
+      cm.off("beforeSelectionChange", selectionChange);
+      clearActiveLines(cm);
+      delete cm.state.activeLines;
+    }
+    if (val) {
+      cm.state.activeLines = [];
+      updateActiveLines(cm, cm.listSelections());
+      cm.on("beforeSelectionChange", selectionChange);
+    }
+  });
+
+  function clearActiveLines(cm) {
+    for (var i = 0; i < cm.state.activeLines.length; i++) {
+      cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
+      cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
+      cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
+    }
+  }
+
+  function sameArray(a, b) {
+    if (a.length != b.length) return false;
+    for (var i = 0; i < a.length; i++)
+      if (a[i] != b[i]) return false;
+    return true;
+  }
+
+  function updateActiveLines(cm, ranges) {
+    var active = [];
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      var option = cm.getOption("styleActiveLine");
+      if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
+        continue
+      var line = cm.getLineHandleVisualStart(range.head.line);
+      if (active[active.length - 1] != line) active.push(line);
+    }
+    if (sameArray(cm.state.activeLines, active)) return;
+    cm.operation(function() {
+      clearActiveLines(cm);
+      for (var i = 0; i < active.length; i++) {
+        cm.addLineClass(active[i], "wrap", WRAP_CLASS);
+        cm.addLineClass(active[i], "background", BACK_CLASS);
+        cm.addLineClass(active[i], "gutter", GUTT_CLASS);
+      }
+      cm.state.activeLines = active;
+    });
+  }
+
+  function selectionChange(cm, sel) {
+    updateActiveLines(cm, sel.ranges);
+  }
+});
diff --git a/dist/firephp.build/lib/codemirror/mode/clike/clike.js b/dist/firephp.build/lib/codemirror/mode/clike/clike.js
new file mode 100644
index 0000000..b3cf54e
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror/mode/clike/clike.js
@@ -0,0 +1,889 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+function Context(indented, column, type, info, align, prev) {
+  this.indented = indented;
+  this.column = column;
+  this.type = type;
+  this.info = info;
+  this.align = align;
+  this.prev = prev;
+}
+function pushContext(state, col, type, info) {
+  var indent = state.indented;
+  if (state.context && state.context.type == "statement" && type != "statement")
+    indent = state.context.indented;
+  return state.context = new Context(indent, col, type, info, null, state.context);
+}
+function popContext(state) {
+  var t = state.context.type;
+  if (t == ")" || t == "]" || t == "}")
+    state.indented = state.context.indented;
+  return state.context = state.context.prev;
+}
+
+function typeBefore(stream, state, pos) {
+  if (state.prevToken == "variable" || state.prevToken == "type") return true;
+  if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true;
+  if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;
+}
+
+function isTopScope(context) {
+  for (;;) {
+    if (!context || context.type == "top") return true;
+    if (context.type == "}" && context.prev.info != "namespace") return false;
+    context = context.prev;
+  }
+}
+
+CodeMirror.defineMode("clike", function(config, parserConfig) {
+  var indentUnit = config.indentUnit,
+      statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
+      dontAlignCalls = parserConfig.dontAlignCalls,
+      keywords = parserConfig.keywords || {},
+      types = parserConfig.types || {},
+      builtin = parserConfig.builtin || {},
+      blockKeywords = parserConfig.blockKeywords || {},
+      defKeywords = parserConfig.defKeywords || {},
+      atoms = parserConfig.atoms || {},
+      hooks = parserConfig.hooks || {},
+      multiLineStrings = parserConfig.multiLineStrings,
+      indentStatements = parserConfig.indentStatements !== false,
+      indentSwitch = parserConfig.indentSwitch !== false,
+      namespaceSeparator = parserConfig.namespaceSeparator,
+      isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
+      numberStart = parserConfig.numberStart || /[\d\.]/,
+      number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
+      isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
+      isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/,
+      // An optional function that takes a {string} token and returns true if it
+      // should be treated as a builtin.
+      isReservedIdentifier = parserConfig.isReservedIdentifier || false;
+
+  var curPunc, isDefKeyword;
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (hooks[ch]) {
+      var result = hooks[ch](stream, state);
+      if (result !== false) return result;
+    }
+    if (ch == '"' || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    }
+    if (isPunctuationChar.test(ch)) {
+      curPunc = ch;
+      return null;
+    }
+    if (numberStart.test(ch)) {
+      stream.backUp(1)
+      if (stream.match(number)) return "number"
+      stream.next()
+    }
+    if (ch == "/") {
+      if (stream.eat("*")) {
+        state.tokenize = tokenComment;
+        return tokenComment(stream, state);
+      }
+      if (stream.eat("/")) {
+        stream.skipToEnd();
+        return "comment";
+      }
+    }
+    if (isOperatorChar.test(ch)) {
+      while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
+      return "operator";
+    }
+    stream.eatWhile(isIdentifierChar);
+    if (namespaceSeparator) while (stream.match(namespaceSeparator))
+      stream.eatWhile(isIdentifierChar);
+
+    var cur = stream.current();
+    if (contains(keywords, cur)) {
+      if (contains(blockKeywords, cur)) curPunc = "newstatement";
+      if (contains(defKeywords, cur)) isDefKeyword = true;
+      return "keyword";
+    }
+    if (contains(types, cur)) return "type";
+    if (contains(builtin, cur)
+        || (isReservedIdentifier && isReservedIdentifier(cur))) {
+      if (contains(blockKeywords, cur)) curPunc = "newstatement";
+      return "builtin";
+    }
+    if (contains(atoms, cur)) return "atom";
+    return "variable";
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, next, end = false;
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) {end = true; break;}
+        escaped = !escaped && next == "\\";
+      }
+      if (end || !(escaped || multiLineStrings))
+        state.tokenize = null;
+      return "string";
+    };
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = null;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return "comment";
+  }
+
+  function maybeEOL(stream, state) {
+    if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))
+      state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      return {
+        tokenize: null,
+        context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false),
+        indented: 0,
+        startOfLine: true,
+        prevToken: null
+      };
+    },
+
+    token: function(stream, state) {
+      var ctx = state.context;
+      if (stream.sol()) {
+        if (ctx.align == null) ctx.align = false;
+        state.indented = stream.indentation();
+        state.startOfLine = true;
+      }
+      if (stream.eatSpace()) { maybeEOL(stream, state); return null; }
+      curPunc = isDefKeyword = null;
+      var style = (state.tokenize || tokenBase)(stream, state);
+      if (style == "comment" || style == "meta") return style;
+      if (ctx.align == null) ctx.align = true;
+
+      if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false)))
+        while (state.context.type == "statement") popContext(state);
+      else if (curPunc == "{") pushContext(state, stream.column(), "}");
+      else if (curPunc == "[") pushContext(state, stream.column(), "]");
+      else if (curPunc == "(") pushContext(state, stream.column(), ")");
+      else if (curPunc == "}") {
+        while (ctx.type == "statement") ctx = popContext(state);
+        if (ctx.type == "}") ctx = popContext(state);
+        while (ctx.type == "statement") ctx = popContext(state);
+      }
+      else if (curPunc == ctx.type) popContext(state);
+      else if (indentStatements &&
+               (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
+                (ctx.type == "statement" && curPunc == "newstatement"))) {
+        pushContext(state, stream.column(), "statement", stream.current());
+      }
+
+      if (style == "variable" &&
+          ((state.prevToken == "def" ||
+            (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&
+             isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
+        style = "def";
+
+      if (hooks.token) {
+        var result = hooks.token(stream, state, style);
+        if (result !== undefined) style = result;
+      }
+
+      if (style == "def" && parserConfig.styleDefs === false) style = "variable";
+
+      state.startOfLine = false;
+      state.prevToken = isDefKeyword ? "def" : style || curPunc;
+      maybeEOL(stream, state);
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
+      var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
+      var closing = firstChar == ctx.type;
+      if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
+      if (parserConfig.dontIndentStatements)
+        while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
+          ctx = ctx.prev
+      if (hooks.indent) {
+        var hook = hooks.indent(state, ctx, textAfter, indentUnit);
+        if (typeof hook == "number") return hook
+      }
+      var switchBlock = ctx.prev && ctx.prev.info == "switch";
+      if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
+        while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
+        return ctx.indented
+      }
+      if (ctx.type == "statement")
+        return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
+      if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
+        return ctx.column + (closing ? 0 : 1);
+      if (ctx.type == ")" && !closing)
+        return ctx.indented + statementIndentUnit;
+
+      return ctx.indented + (closing ? 0 : indentUnit) +
+        (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
+    },
+
+    electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    blockCommentContinue: " * ",
+    lineComment: "//",
+    fold: "brace"
+  };
+});
+
+  function words(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+  function contains(words, word) {
+    if (typeof words === "function") {
+      return words(word);
+    } else {
+      return words.propertyIsEnumerable(word);
+    }
+  }
+  var cKeywords = "auto if break case register continue return default do sizeof " +
+    "static else struct switch extern typedef union for goto while enum const " +
+    "volatile inline restrict asm fortran";
+
+  // Do not use this. Use the cTypes function below. This is global just to avoid
+  // excessive calls when cTypes is being called multiple times during a parse.
+  var basicCTypes = words("int long char short double float unsigned signed " +
+    "void bool");
+
+  // Do not use this. Use the objCTypes function below. This is global just to avoid
+  // excessive calls when objCTypes is being called multiple times during a parse.
+  var basicObjCTypes = words("SEL instancetype id Class Protocol BOOL");
+
+  // Returns true if identifier is a "C" type.
+  // C type is defined as those that are reserved by the compiler (basicTypes),
+  // and those that end in _t (Reserved by POSIX for types)
+  // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html
+  function cTypes(identifier) {
+    return contains(basicCTypes, identifier) || /.+_t$/.test(identifier);
+  }
+
+  // Returns true if identifier is a "Objective C" type.
+  function objCTypes(identifier) {
+    return cTypes(identifier) || contains(basicObjCTypes, identifier);
+  }
+
+  var cBlockKeywords = "case do else for if switch while struct enum union";
+  var cDefKeywords = "struct enum union";
+
+  function cppHook(stream, state) {
+    if (!state.startOfLine) return false
+    for (var ch, next = null; ch = stream.peek();) {
+      if (ch == "\\" && stream.match(/^.$/)) {
+        next = cppHook
+        break
+      } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
+        break
+      }
+      stream.next()
+    }
+    state.tokenize = next
+    return "meta"
+  }
+
+  function pointerHook(_stream, state) {
+    if (state.prevToken == "type") return "type";
+    return false;
+  }
+
+  // For C and C++ (and ObjC): identifiers starting with __
+  // or _ followed by a capital letter are reserved for the compiler.
+  function cIsReservedIdentifier(token) {
+    if (!token || token.length < 2) return false;
+    if (token[0] != '_') return false;
+    return (token[1] == '_') || (token[1] !== token[1].toLowerCase());
+  }
+
+  function cpp14Literal(stream) {
+    stream.eatWhile(/[\w\.']/);
+    return "number";
+  }
+
+  function cpp11StringHook(stream, state) {
+    stream.backUp(1);
+    // Raw strings.
+    if (stream.match(/(R|u8R|uR|UR|LR)/)) {
+      var match = stream.match(/"([^\s\\()]{0,16})\(/);
+      if (!match) {
+        return false;
+      }
+      state.cpp11RawStringDelim = match[1];
+      state.tokenize = tokenRawString;
+      return tokenRawString(stream, state);
+    }
+    // Unicode strings/chars.
+    if (stream.match(/(u8|u|U|L)/)) {
+      if (stream.match(/["']/, /* eat */ false)) {
+        return "string";
+      }
+      return false;
+    }
+    // Ignore this hook.
+    stream.next();
+    return false;
+  }
+
+  function cppLooksLikeConstructor(word) {
+    var lastTwo = /(\w+)::~?(\w+)$/.exec(word);
+    return lastTwo && lastTwo[1] == lastTwo[2];
+  }
+
+  // C#-style strings where "" escapes a quote.
+  function tokenAtString(stream, state) {
+    var next;
+    while ((next = stream.next()) != null) {
+      if (next == '"' && !stream.eat('"')) {
+        state.tokenize = null;
+        break;
+      }
+    }
+    return "string";
+  }
+
+  // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
+  // <delim> can be a string up to 16 characters long.
+  function tokenRawString(stream, state) {
+    // Escape characters that have special regex meanings.
+    var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
+    var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
+    if (match)
+      state.tokenize = null;
+    else
+      stream.skipToEnd();
+    return "string";
+  }
+
+  function def(mimes, mode) {
+    if (typeof mimes == "string") mimes = [mimes];
+    var words = [];
+    function add(obj) {
+      if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
+        words.push(prop);
+    }
+    add(mode.keywords);
+    add(mode.types);
+    add(mode.builtin);
+    add(mode.atoms);
+    if (words.length) {
+      mode.helperType = mimes[0];
+      CodeMirror.registerHelper("hintWords", mimes[0], words);
+    }
+
+    for (var i = 0; i < mimes.length; ++i)
+      CodeMirror.defineMIME(mimes[i], mode);
+  }
+
+  def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
+    name: "clike",
+    keywords: words(cKeywords),
+    types: cTypes,
+    blockKeywords: words(cBlockKeywords),
+    defKeywords: words(cDefKeywords),
+    typeFirstDefinitions: true,
+    atoms: words("NULL true false"),
+    isReservedIdentifier: cIsReservedIdentifier,
+    hooks: {
+      "#": cppHook,
+      "*": pointerHook,
+    },
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def(["text/x-c++src", "text/x-c++hdr"], {
+    name: "clike",
+    // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20.
+    keywords: words(cKeywords + "alignas alignof and and_eq audit axiom bitand bitor catch " +
+                    "class compl concept constexpr const_cast decltype delete dynamic_cast " +
+                    "explicit export final friend import module mutable namespace new noexcept " +
+                    "not not_eq operator or or_eq override private protected public " +
+                    "reinterpret_cast requires static_assert static_cast template this " +
+                    "thread_local throw try typeid typename using virtual xor xor_eq"),
+    types: cTypes,
+    blockKeywords: words(cBlockKeywords + " class try catch"),
+    defKeywords: words(cDefKeywords + " class namespace"),
+    typeFirstDefinitions: true,
+    atoms: words("true false NULL nullptr"),
+    dontIndentStatements: /^template$/,
+    isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
+    isReservedIdentifier: cIsReservedIdentifier,
+    hooks: {
+      "#": cppHook,
+      "*": pointerHook,
+      "u": cpp11StringHook,
+      "U": cpp11StringHook,
+      "L": cpp11StringHook,
+      "R": cpp11StringHook,
+      "0": cpp14Literal,
+      "1": cpp14Literal,
+      "2": cpp14Literal,
+      "3": cpp14Literal,
+      "4": cpp14Literal,
+      "5": cpp14Literal,
+      "6": cpp14Literal,
+      "7": cpp14Literal,
+      "8": cpp14Literal,
+      "9": cpp14Literal,
+      token: function(stream, state, style) {
+        if (style == "variable" && stream.peek() == "(" &&
+            (state.prevToken == ";" || state.prevToken == null ||
+             state.prevToken == "}") &&
+            cppLooksLikeConstructor(stream.current()))
+          return "def";
+      }
+    },
+    namespaceSeparator: "::",
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def("text/x-java", {
+    name: "clike",
+    keywords: words("abstract assert break case catch class const continue default " +
+                    "do else enum extends final finally for goto if implements import " +
+                    "instanceof interface native new package private protected public " +
+                    "return static strictfp super switch synchronized this throw throws transient " +
+                    "try volatile while @interface"),
+    types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
+                 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
+    blockKeywords: words("catch class do else finally for if switch try while"),
+    defKeywords: words("class interface enum @interface"),
+    typeFirstDefinitions: true,
+    atoms: words("true false null"),
+    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+    hooks: {
+      "@": function(stream) {
+        // Don't match the @interface keyword.
+        if (stream.match('interface', false)) return false;
+
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      }
+    },
+    modeProps: {fold: ["brace", "import"]}
+  });
+
+  def("text/x-csharp", {
+    name: "clike",
+    keywords: words("abstract as async await base break case catch checked class const continue" +
+                    " default delegate do else enum event explicit extern finally fixed for" +
+                    " foreach goto if implicit in interface internal is lock namespace new" +
+                    " operator out override params private protected public readonly ref return sealed" +
+                    " sizeof stackalloc static struct switch this throw try typeof unchecked" +
+                    " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
+                    " global group into join let orderby partial remove select set value var yield"),
+    types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
+                 " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
+                 " UInt64 bool byte char decimal double short int long object"  +
+                 " sbyte float string ushort uint ulong"),
+    blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
+    defKeywords: words("class interface namespace struct var"),
+    typeFirstDefinitions: true,
+    atoms: words("true false null"),
+    hooks: {
+      "@": function(stream, state) {
+        if (stream.eat('"')) {
+          state.tokenize = tokenAtString;
+          return tokenAtString(stream, state);
+        }
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      }
+    }
+  });
+
+  function tokenTripleString(stream, state) {
+    var escaped = false;
+    while (!stream.eol()) {
+      if (!escaped && stream.match('"""')) {
+        state.tokenize = null;
+        break;
+      }
+      escaped = stream.next() == "\\" && !escaped;
+    }
+    return "string";
+  }
+
+  function tokenNestedComment(depth) {
+    return function (stream, state) {
+      var ch
+      while (ch = stream.next()) {
+        if (ch == "*" && stream.eat("/")) {
+          if (depth == 1) {
+            state.tokenize = null
+            break
+          } else {
+            state.tokenize = tokenNestedComment(depth - 1)
+            return state.tokenize(stream, state)
+          }
+        } else if (ch == "/" && stream.eat("*")) {
+          state.tokenize = tokenNestedComment(depth + 1)
+          return state.tokenize(stream, state)
+        }
+      }
+      return "comment"
+    }
+  }
+
+  def("text/x-scala", {
+    name: "clike",
+    keywords: words(
+      /* scala */
+      "abstract case catch class def do else extends final finally for forSome if " +
+      "implicit import lazy match new null object override package private protected return " +
+      "sealed super this throw trait try type val var while with yield _ " +
+
+      /* package scala */
+      "assert assume require print println printf readLine readBoolean readByte readShort " +
+      "readChar readInt readLong readFloat readDouble"
+    ),
+    types: words(
+      "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
+      "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " +
+      "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
+      "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
+      "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +
+
+      /* package java.lang */
+      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+    ),
+    multiLineStrings: true,
+    blockKeywords: words("catch class enum do else finally for forSome if match switch try while"),
+    defKeywords: words("class enum def object package trait type val var"),
+    atoms: words("true false null"),
+    indentStatements: false,
+    indentSwitch: false,
+    isOperatorChar: /[+\-*&%=<>!?|\/#:@]/,
+    hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      },
+      '"': function(stream, state) {
+        if (!stream.match('""')) return false;
+        state.tokenize = tokenTripleString;
+        return state.tokenize(stream, state);
+      },
+      "'": function(stream) {
+        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+        return "atom";
+      },
+      "=": function(stream, state) {
+        var cx = state.context
+        if (cx.type == "}" && cx.align && stream.eat(">")) {
+          state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
+          return "operator"
+        } else {
+          return false
+        }
+      },
+
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false
+        state.tokenize = tokenNestedComment(1)
+        return state.tokenize(stream, state)
+      }
+    },
+    modeProps: {closeBrackets: {pairs: '()[]{}""', triples: '"'}}
+  });
+
+  function tokenKotlinString(tripleString){
+    return function (stream, state) {
+      var escaped = false, next, end = false;
+      while (!stream.eol()) {
+        if (!tripleString && !escaped && stream.match('"') ) {end = true; break;}
+        if (tripleString && stream.match('"""')) {end = true; break;}
+        next = stream.next();
+        if(!escaped && next == "$" && stream.match('{'))
+          stream.skipTo("}");
+        escaped = !escaped && next == "\\" && !tripleString;
+      }
+      if (end || !tripleString)
+        state.tokenize = null;
+      return "string";
+    }
+  }
+
+  def("text/x-kotlin", {
+    name: "clike",
+    keywords: words(
+      /*keywords*/
+      "package as typealias class interface this super val operator " +
+      "var fun for is in This throw return annotation " +
+      "break continue object if else while do try when !in !is as? " +
+
+      /*soft keywords*/
+      "file import where by get set abstract enum open inner override private public internal " +
+      "protected catch finally out final vararg reified dynamic companion constructor init " +
+      "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
+      "external annotation crossinline const operator infix suspend actual expect setparam"
+    ),
+    types: words(
+      /* package java.lang */
+      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
+      "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
+      "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
+    ),
+    intendSwitch: false,
+    indentStatements: false,
+    multiLineStrings: true,
+    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+    blockKeywords: words("catch class do else finally for if where try while enum"),
+    defKeywords: words("class val var object interface fun"),
+    atoms: words("true false null this"),
+    hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      },
+      '*': function(_stream, state) {
+        return state.prevToken == '.' ? 'variable' : 'operator';
+      },
+      '"': function(stream, state) {
+        state.tokenize = tokenKotlinString(stream.match('""'));
+        return state.tokenize(stream, state);
+      },
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false;
+        state.tokenize = tokenNestedComment(1);
+        return state.tokenize(stream, state)
+      },
+      indent: function(state, ctx, textAfter, indentUnit) {
+        var firstChar = textAfter && textAfter.charAt(0);
+        if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "")
+          return state.indented;
+        if ((state.prevToken == "operator" && textAfter != "}" && state.context.type != "}") ||
+          state.prevToken == "variable" && firstChar == "." ||
+          (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".")
+          return indentUnit * 2 + ctx.indented;
+        if (ctx.align && ctx.type == "}")
+          return ctx.indented + (state.context.type == (textAfter || "").charAt(0) ? 0 : indentUnit);
+      }
+    },
+    modeProps: {closeBrackets: {triples: '"'}}
+  });
+
+  def(["x-shader/x-vertex", "x-shader/x-fragment"], {
+    name: "clike",
+    keywords: words("sampler1D sampler2D sampler3D samplerCube " +
+                    "sampler1DShadow sampler2DShadow " +
+                    "const attribute uniform varying " +
+                    "break continue discard return " +
+                    "for while do if else struct " +
+                    "in out inout"),
+    types: words("float int bool void " +
+                 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
+                 "mat2 mat3 mat4"),
+    blockKeywords: words("for while do if else struct"),
+    builtin: words("radians degrees sin cos tan asin acos atan " +
+                    "pow exp log exp2 sqrt inversesqrt " +
+                    "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
+                    "length distance dot cross normalize ftransform faceforward " +
+                    "reflect refract matrixCompMult " +
+                    "lessThan lessThanEqual greaterThan greaterThanEqual " +
+                    "equal notEqual any all not " +
+                    "texture1D texture1DProj texture1DLod texture1DProjLod " +
+                    "texture2D texture2DProj texture2DLod texture2DProjLod " +
+                    "texture3D texture3DProj texture3DLod texture3DProjLod " +
+                    "textureCube textureCubeLod " +
+                    "shadow1D shadow2D shadow1DProj shadow2DProj " +
+                    "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
+                    "dFdx dFdy fwidth " +
+                    "noise1 noise2 noise3 noise4"),
+    atoms: words("true false " +
+                "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
+                "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
+                "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
+                "gl_FogCoord gl_PointCoord " +
+                "gl_Position gl_PointSize gl_ClipVertex " +
+                "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
+                "gl_TexCoord gl_FogFragCoord " +
+                "gl_FragCoord gl_FrontFacing " +
+                "gl_FragData gl_FragDepth " +
+                "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
+                "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
+                "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
+                "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
+                "gl_ProjectionMatrixInverseTranspose " +
+                "gl_ModelViewProjectionMatrixInverseTranspose " +
+                "gl_TextureMatrixInverseTranspose " +
+                "gl_NormalScale gl_DepthRange gl_ClipPlane " +
+                "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
+                "gl_FrontLightModelProduct gl_BackLightModelProduct " +
+                "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
+                "gl_FogParameters " +
+                "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
+                "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
+                "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
+                "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
+                "gl_MaxDrawBuffers"),
+    indentSwitch: false,
+    hooks: {"#": cppHook},
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def("text/x-nesc", {
+    name: "clike",
+    keywords: words(cKeywords + " as atomic async call command component components configuration event generic " +
+                    "implementation includes interface module new norace nx_struct nx_union post provides " +
+                    "signal task uses abstract extends"),
+    types: cTypes,
+    blockKeywords: words(cBlockKeywords),
+    atoms: words("null true false"),
+    hooks: {"#": cppHook},
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def("text/x-objectivec", {
+    name: "clike",
+    keywords: words(cKeywords + " bycopy byref in inout oneway out self super atomic nonatomic retain copy " +
+                    "readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd " +
+                    "@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class " +
+                    "@public @package @private @protected @required @optional @try @catch @finally @import " +
+                    "@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available"),
+    types: objCTypes,
+    builtin: words("FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION NS_RETURNS_RETAINED " +
+                   "NS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER NS_DESIGNATED_INITIALIZER " +
+                   "NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION NS_ASSUME_NONNULL_BEGIN " +
+                   "NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT"),
+    blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized"),
+    defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class"),
+    dontIndentStatements: /^@.*$/,
+    typeFirstDefinitions: true,
+    atoms: words("YES NO NULL Nil nil true false nullptr"),
+    isReservedIdentifier: cIsReservedIdentifier,
+    hooks: {
+      "#": cppHook,
+      "*": pointerHook,
+    },
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  def("text/x-squirrel", {
+    name: "clike",
+    keywords: words("base break clone continue const default delete enum extends function in class" +
+                    " foreach local resume return this throw typeof yield constructor instanceof static"),
+    types: cTypes,
+    blockKeywords: words("case catch class else for foreach if switch try while"),
+    defKeywords: words("function local class"),
+    typeFirstDefinitions: true,
+    atoms: words("true false null"),
+    hooks: {"#": cppHook},
+    modeProps: {fold: ["brace", "include"]}
+  });
+
+  // Ceylon Strings need to deal with interpolation
+  var stringTokenizer = null;
+  function tokenCeylonString(type) {
+    return function(stream, state) {
+      var escaped = false, next, end = false;
+      while (!stream.eol()) {
+        if (!escaped && stream.match('"') &&
+              (type == "single" || stream.match('""'))) {
+          end = true;
+          break;
+        }
+        if (!escaped && stream.match('``')) {
+          stringTokenizer = tokenCeylonString(type);
+          end = true;
+          break;
+        }
+        next = stream.next();
+        escaped = type == "single" && !escaped && next == "\\";
+      }
+      if (end)
+          state.tokenize = null;
+      return "string";
+    }
+  }
+
+  def("text/x-ceylon", {
+    name: "clike",
+    keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" +
+                    " exists extends finally for function given if import in interface is let module new" +
+                    " nonempty object of out outer package return satisfies super switch then this throw" +
+                    " try value void while"),
+    types: function(word) {
+        // In Ceylon all identifiers that start with an uppercase are types
+        var first = word.charAt(0);
+        return (first === first.toUpperCase() && first !== first.toLowerCase());
+    },
+    blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"),
+    defKeywords: words("class dynamic function interface module object package value"),
+    builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" +
+                   " native optional sealed see serializable shared suppressWarnings tagged throws variable"),
+    isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
+    isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
+    numberStart: /[\d#$]/,
+    number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
+    multiLineStrings: true,
+    typeFirstDefinitions: true,
+    atoms: words("true false null larger smaller equal empty finished"),
+    indentSwitch: false,
+    styleDefs: false,
+    hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      },
+      '"': function(stream, state) {
+          state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single");
+          return state.tokenize(stream, state);
+        },
+      '`': function(stream, state) {
+          if (!stringTokenizer || !stream.match('`')) return false;
+          state.tokenize = stringTokenizer;
+          stringTokenizer = null;
+          return state.tokenize(stream, state);
+        },
+      "'": function(stream) {
+        stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+        return "atom";
+      },
+      token: function(_stream, state, style) {
+          if ((style == "variable" || style == "type") &&
+              state.prevToken == ".") {
+            return "variable-2";
+          }
+        }
+    },
+    modeProps: {
+        fold: ["brace", "import"],
+        closeBrackets: {triples: '"'}
+    }
+  });
+
+});
diff --git a/dist/firephp.build/lib/codemirror/mode/css/css.js b/dist/firephp.build/lib/codemirror/mode/css/css.js
new file mode 100644
index 0000000..05742c5
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror/mode/css/css.js
@@ -0,0 +1,831 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("css", function(config, parserConfig) {
+  var inline = parserConfig.inline
+  if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
+
+  var indentUnit = config.indentUnit,
+      tokenHooks = parserConfig.tokenHooks,
+      documentTypes = parserConfig.documentTypes || {},
+      mediaTypes = parserConfig.mediaTypes || {},
+      mediaFeatures = parserConfig.mediaFeatures || {},
+      mediaValueKeywords = parserConfig.mediaValueKeywords || {},
+      propertyKeywords = parserConfig.propertyKeywords || {},
+      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
+      fontProperties = parserConfig.fontProperties || {},
+      counterDescriptors = parserConfig.counterDescriptors || {},
+      colorKeywords = parserConfig.colorKeywords || {},
+      valueKeywords = parserConfig.valueKeywords || {},
+      allowNested = parserConfig.allowNested,
+      lineComment = parserConfig.lineComment,
+      supportsAtComponent = parserConfig.supportsAtComponent === true;
+
+  var type, override;
+  function ret(style, tp) { type = tp; return style; }
+
+  // Tokenizers
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (tokenHooks[ch]) {
+      var result = tokenHooks[ch](stream, state);
+      if (result !== false) return result;
+    }
+    if (ch == "@") {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("def", stream.current());
+    } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
+      return ret(null, "compare");
+    } else if (ch == "\"" || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    } else if (ch == "#") {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("atom", "hash");
+    } else if (ch == "!") {
+      stream.match(/^\s*\w*/);
+      return ret("keyword", "important");
+    } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
+      stream.eatWhile(/[\w.%]/);
+      return ret("number", "unit");
+    } else if (ch === "-") {
+      if (/[\d.]/.test(stream.peek())) {
+        stream.eatWhile(/[\w.%]/);
+        return ret("number", "unit");
+      } else if (stream.match(/^-[\w\\\-]*/)) {
+        stream.eatWhile(/[\w\\\-]/);
+        if (stream.match(/^\s*:/, false))
+          return ret("variable-2", "variable-definition");
+        return ret("variable-2", "variable");
+      } else if (stream.match(/^\w+-/)) {
+        return ret("meta", "meta");
+      }
+    } else if (/[,+>*\/]/.test(ch)) {
+      return ret(null, "select-op");
+    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+      return ret("qualifier", "qualifier");
+    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
+      return ret(null, ch);
+    } else if (stream.match(/[\w-.]+(?=\()/)) {
+      if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
+        state.tokenize = tokenParenthesized;
+      }
+      return ret("variable callee", "variable");
+    } else if (/[\w\\\-]/.test(ch)) {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("property", "word");
+    } else {
+      return ret(null, null);
+    }
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped) {
+          if (quote == ")") stream.backUp(1);
+          break;
+        }
+        escaped = !escaped && ch == "\\";
+      }
+      if (ch == quote || !escaped && quote != ")") state.tokenize = null;
+      return ret("string", "string");
+    };
+  }
+
+  function tokenParenthesized(stream, state) {
+    stream.next(); // Must be '('
+    if (!stream.match(/\s*[\"\')]/, false))
+      state.tokenize = tokenString(")");
+    else
+      state.tokenize = null;
+    return ret(null, "(");
+  }
+
+  // Context management
+
+  function Context(type, indent, prev) {
+    this.type = type;
+    this.indent = indent;
+    this.prev = prev;
+  }
+
+  function pushContext(state, stream, type, indent) {
+    state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
+    return type;
+  }
+
+  function popContext(state) {
+    if (state.context.prev)
+      state.context = state.context.prev;
+    return state.context.type;
+  }
+
+  function pass(type, stream, state) {
+    return states[state.context.type](type, stream, state);
+  }
+  function popAndPass(type, stream, state, n) {
+    for (var i = n || 1; i > 0; i--)
+      state.context = state.context.prev;
+    return pass(type, stream, state);
+  }
+
+  // Parser
+
+  function wordAsValue(stream) {
+    var word = stream.current().toLowerCase();
+    if (valueKeywords.hasOwnProperty(word))
+      override = "atom";
+    else if (colorKeywords.hasOwnProperty(word))
+      override = "keyword";
+    else
+      override = "variable";
+  }
+
+  var states = {};
+
+  states.top = function(type, stream, state) {
+    if (type == "{") {
+      return pushContext(state, stream, "block");
+    } else if (type == "}" && state.context.prev) {
+      return popContext(state);
+    } else if (supportsAtComponent && /@component/i.test(type)) {
+      return pushContext(state, stream, "atComponentBlock");
+    } else if (/^@(-moz-)?document$/i.test(type)) {
+      return pushContext(state, stream, "documentTypes");
+    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
+      return pushContext(state, stream, "atBlock");
+    } else if (/^@(font-face|counter-style)/i.test(type)) {
+      state.stateArg = type;
+      return "restricted_atBlock_before";
+    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
+      return "keyframes";
+    } else if (type && type.charAt(0) == "@") {
+      return pushContext(state, stream, "at");
+    } else if (type == "hash") {
+      override = "builtin";
+    } else if (type == "word") {
+      override = "tag";
+    } else if (type == "variable-definition") {
+      return "maybeprop";
+    } else if (type == "interpolation") {
+      return pushContext(state, stream, "interpolation");
+    } else if (type == ":") {
+      return "pseudo";
+    } else if (allowNested && type == "(") {
+      return pushContext(state, stream, "parens");
+    }
+    return state.context.type;
+  };
+
+  states.block = function(type, stream, state) {
+    if (type == "word") {
+      var word = stream.current().toLowerCase();
+      if (propertyKeywords.hasOwnProperty(word)) {
+        override = "property";
+        return "maybeprop";
+      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
+        override = "string-2";
+        return "maybeprop";
+      } else if (allowNested) {
+        override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
+        return "block";
+      } else {
+        override += " error";
+        return "maybeprop";
+      }
+    } else if (type == "meta") {
+      return "block";
+    } else if (!allowNested && (type == "hash" || type == "qualifier")) {
+      override = "error";
+      return "block";
+    } else {
+      return states.top(type, stream, state);
+    }
+  };
+
+  states.maybeprop = function(type, stream, state) {
+    if (type == ":") return pushContext(state, stream, "prop");
+    return pass(type, stream, state);
+  };
+
+  states.prop = function(type, stream, state) {
+    if (type == ";") return popContext(state);
+    if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
+    if (type == "}" || type == "{") return popAndPass(type, stream, state);
+    if (type == "(") return pushContext(state, stream, "parens");
+
+    if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
+      override += " error";
+    } else if (type == "word") {
+      wordAsValue(stream);
+    } else if (type == "interpolation") {
+      return pushContext(state, stream, "interpolation");
+    }
+    return "prop";
+  };
+
+  states.propBlock = function(type, _stream, state) {
+    if (type == "}") return popContext(state);
+    if (type == "word") { override = "property"; return "maybeprop"; }
+    return state.context.type;
+  };
+
+  states.parens = function(type, stream, state) {
+    if (type == "{" || type == "}") return popAndPass(type, stream, state);
+    if (type == ")") return popContext(state);
+    if (type == "(") return pushContext(state, stream, "parens");
+    if (type == "interpolation") return pushContext(state, stream, "interpolation");
+    if (type == "word") wordAsValue(stream);
+    return "parens";
+  };
+
+  states.pseudo = function(type, stream, state) {
+    if (type == "meta") return "pseudo";
+
+    if (type == "word") {
+      override = "variable-3";
+      return state.context.type;
+    }
+    return pass(type, stream, state);
+  };
+
+  states.documentTypes = function(type, stream, state) {
+    if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
+      override = "tag";
+      return state.context.type;
+    } else {
+      return states.atBlock(type, stream, state);
+    }
+  };
+
+  states.atBlock = function(type, stream, state) {
+    if (type == "(") return pushContext(state, stream, "atBlock_parens");
+    if (type == "}" || type == ";") return popAndPass(type, stream, state);
+    if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
+
+    if (type == "interpolation") return pushContext(state, stream, "interpolation");
+
+    if (type == "word") {
+      var word = stream.current().toLowerCase();
+      if (word == "only" || word == "not" || word == "and" || word == "or")
+        override = "keyword";
+      else if (mediaTypes.hasOwnProperty(word))
+        override = "attribute";
+      else if (mediaFeatures.hasOwnProperty(word))
+        override = "property";
+      else if (mediaValueKeywords.hasOwnProperty(word))
+        override = "keyword";
+      else if (propertyKeywords.hasOwnProperty(word))
+        override = "property";
+      else if (nonStandardPropertyKeywords.hasOwnProperty(word))
+        override = "string-2";
+      else if (valueKeywords.hasOwnProperty(word))
+        override = "atom";
+      else if (colorKeywords.hasOwnProperty(word))
+        override = "keyword";
+      else
+        override = "error";
+    }
+    return state.context.type;
+  };
+
+  states.atComponentBlock = function(type, stream, state) {
+    if (type == "}")
+      return popAndPass(type, stream, state);
+    if (type == "{")
+      return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
+    if (type == "word")
+      override = "error";
+    return state.context.type;
+  };
+
+  states.atBlock_parens = function(type, stream, state) {
+    if (type == ")") return popContext(state);
+    if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
+    return states.atBlock(type, stream, state);
+  };
+
+  states.restricted_atBlock_before = function(type, stream, state) {
+    if (type == "{")
+      return pushContext(state, stream, "restricted_atBlock");
+    if (type == "word" && state.stateArg == "@counter-style") {
+      override = "variable";
+      return "restricted_atBlock_before";
+    }
+    return pass(type, stream, state);
+  };
+
+  states.restricted_atBlock = function(type, stream, state) {
+    if (type == "}") {
+      state.stateArg = null;
+      return popContext(state);
+    }
+    if (type == "word") {
+      if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
+          (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
+        override = "error";
+      else
+        override = "property";
+      return "maybeprop";
+    }
+    return "restricted_atBlock";
+  };
+
+  states.keyframes = function(type, stream, state) {
+    if (type == "word") { override = "variable"; return "keyframes"; }
+    if (type == "{") return pushContext(state, stream, "top");
+    return pass(type, stream, state);
+  };
+
+  states.at = function(type, stream, state) {
+    if (type == ";") return popContext(state);
+    if (type == "{" || type == "}") return popAndPass(type, stream, state);
+    if (type == "word") override = "tag";
+    else if (type == "hash") override = "builtin";
+    return "at";
+  };
+
+  states.interpolation = function(type, stream, state) {
+    if (type == "}") return popContext(state);
+    if (type == "{" || type == ";") return popAndPass(type, stream, state);
+    if (type == "word") override = "variable";
+    else if (type != "variable" && type != "(" && type != ")") override = "error";
+    return "interpolation";
+  };
+
+  return {
+    startState: function(base) {
+      return {tokenize: null,
+              state: inline ? "block" : "top",
+              stateArg: null,
+              context: new Context(inline ? "block" : "top", base || 0, null)};
+    },
+
+    token: function(stream, state) {
+      if (!state.tokenize && stream.eatSpace()) return null;
+      var style = (state.tokenize || tokenBase)(stream, state);
+      if (style && typeof style == "object") {
+        type = style[1];
+        style = style[0];
+      }
+      override = style;
+      if (type != "comment")
+        state.state = states[state.state](type, stream, state);
+      return override;
+    },
+
+    indent: function(state, textAfter) {
+      var cx = state.context, ch = textAfter && textAfter.charAt(0);
+      var indent = cx.indent;
+      if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
+      if (cx.prev) {
+        if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
+                          cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
+          // Resume indentation from parent context.
+          cx = cx.prev;
+          indent = cx.indent;
+        } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
+            ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
+          // Dedent relative to current context.
+          indent = Math.max(0, cx.indent - indentUnit);
+        }
+      }
+      return indent;
+    },
+
+    electricChars: "}",
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    blockCommentContinue: " * ",
+    lineComment: lineComment,
+    fold: "brace"
+  };
+});
+
+  function keySet(array) {
+    var keys = {};
+    for (var i = 0; i < array.length; ++i) {
+      keys[array[i].toLowerCase()] = true;
+    }
+    return keys;
+  }
+
+  var documentTypes_ = [
+    "domain", "regexp", "url", "url-prefix"
+  ], documentTypes = keySet(documentTypes_);
+
+  var mediaTypes_ = [
+    "all", "aural", "braille", "handheld", "print", "projection", "screen",
+    "tty", "tv", "embossed"
+  ], mediaTypes = keySet(mediaTypes_);
+
+  var mediaFeatures_ = [
+    "width", "min-width", "max-width", "height", "min-height", "max-height",
+    "device-width", "min-device-width", "max-device-width", "device-height",
+    "min-device-height", "max-device-height", "aspect-ratio",
+    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
+    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
+    "max-color", "color-index", "min-color-index", "max-color-index",
+    "monochrome", "min-monochrome", "max-monochrome", "resolution",
+    "min-resolution", "max-resolution", "scan", "grid", "orientation",
+    "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
+    "pointer", "any-pointer", "hover", "any-hover"
+  ], mediaFeatures = keySet(mediaFeatures_);
+
+  var mediaValueKeywords_ = [
+    "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
+    "interlace", "progressive"
+  ], mediaValueKeywords = keySet(mediaValueKeywords_);
+
+  var propertyKeywords_ = [
+    "align-content", "align-items", "align-self", "alignment-adjust",
+    "alignment-baseline", "anchor-point", "animation", "animation-delay",
+    "animation-direction", "animation-duration", "animation-fill-mode",
+    "animation-iteration-count", "animation-name", "animation-play-state",
+    "animation-timing-function", "appearance", "azimuth", "backface-visibility",
+    "background", "background-attachment", "background-blend-mode", "background-clip",
+    "background-color", "background-image", "background-origin", "background-position",
+    "background-repeat", "background-size", "baseline-shift", "binding",
+    "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
+    "bookmark-target", "border", "border-bottom", "border-bottom-color",
+    "border-bottom-left-radius", "border-bottom-right-radius",
+    "border-bottom-style", "border-bottom-width", "border-collapse",
+    "border-color", "border-image", "border-image-outset",
+    "border-image-repeat", "border-image-slice", "border-image-source",
+    "border-image-width", "border-left", "border-left-color",
+    "border-left-style", "border-left-width", "border-radius", "border-right",
+    "border-right-color", "border-right-style", "border-right-width",
+    "border-spacing", "border-style", "border-top", "border-top-color",
+    "border-top-left-radius", "border-top-right-radius", "border-top-style",
+    "border-top-width", "border-width", "bottom", "box-decoration-break",
+    "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
+    "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
+    "column-fill", "column-gap", "column-rule", "column-rule-color",
+    "column-rule-style", "column-rule-width", "column-span", "column-width",
+    "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
+    "cue-after", "cue-before", "cursor", "direction", "display",
+    "dominant-baseline", "drop-initial-after-adjust",
+    "drop-initial-after-align", "drop-initial-before-adjust",
+    "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
+    "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
+    "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
+    "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
+    "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
+    "font-stretch", "font-style", "font-synthesis", "font-variant",
+    "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
+    "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+    "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
+    "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
+    "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
+    "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
+    "grid-template-rows", "hanging-punctuation", "height", "hyphens",
+    "icon", "image-orientation", "image-rendering", "image-resolution",
+    "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
+    "line-break", "line-height", "line-stacking", "line-stacking-ruby",
+    "line-stacking-shift", "line-stacking-strategy", "list-style",
+    "list-style-image", "list-style-position", "list-style-type", "margin",
+    "margin-bottom", "margin-left", "margin-right", "margin-top",
+    "marks", "marquee-direction", "marquee-loop",
+    "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+    "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index",
+    "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
+    "opacity", "order", "orphans", "outline",
+    "outline-color", "outline-offset", "outline-style", "outline-width",
+    "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
+    "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
+    "page", "page-break-after", "page-break-before", "page-break-inside",
+    "page-policy", "pause", "pause-after", "pause-before", "perspective",
+    "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
+    "presentation-level", "punctuation-trim", "quotes", "region-break-after",
+    "region-break-before", "region-break-inside", "region-fragment",
+    "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
+    "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
+    "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
+    "shape-outside", "size", "speak", "speak-as", "speak-header",
+    "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
+    "tab-size", "table-layout", "target", "target-name", "target-new",
+    "target-position", "text-align", "text-align-last", "text-decoration",
+    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
+    "text-decoration-style", "text-emphasis", "text-emphasis-color",
+    "text-emphasis-position", "text-emphasis-style", "text-height",
+    "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
+    "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
+    "text-wrap", "top", "transform", "transform-origin", "transform-style",
+    "transition", "transition-delay", "transition-duration",
+    "transition-property", "transition-timing-function", "unicode-bidi",
+    "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
+    "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
+    "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
+    "word-spacing", "word-wrap", "z-index",
+    // SVG-specific
+    "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
+    "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
+    "color-interpolation", "color-interpolation-filters",
+    "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
+    "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
+    "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
+    "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
+    "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
+    "glyph-orientation-vertical", "text-anchor", "writing-mode"
+  ], propertyKeywords = keySet(propertyKeywords_);
+
+  var nonStandardPropertyKeywords_ = [
+    "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
+    "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
+    "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
+    "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
+    "searchfield-results-decoration", "zoom"
+  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
+
+  var fontProperties_ = [
+    "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
+    "font-stretch", "font-weight", "font-style"
+  ], fontProperties = keySet(fontProperties_);
+
+  var counterDescriptors_ = [
+    "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
+    "speak-as", "suffix", "symbols", "system"
+  ], counterDescriptors = keySet(counterDescriptors_);
+
+  var colorKeywords_ = [
+    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
+    "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
+    "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
+    "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
+    "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
+    "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+    "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
+    "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
+    "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+    "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
+    "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
+    "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
+    "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
+    "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
+    "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
+    "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
+    "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+    "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
+    "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
+    "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
+    "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+    "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
+    "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
+    "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
+    "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
+    "whitesmoke", "yellow", "yellowgreen"
+  ], colorKeywords = keySet(colorKeywords_);
+
+  var valueKeywords_ = [
+    "above", "absolute", "activeborder", "additive", "activecaption", "afar",
+    "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
+    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
+    "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
+    "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
+    "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
+    "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
+    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
+    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
+    "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
+    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
+    "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
+    "compact", "condensed", "contain", "content", "contents",
+    "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
+    "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
+    "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
+    "destination-in", "destination-out", "destination-over", "devanagari", "difference",
+    "disc", "discard", "disclosure-closed", "disclosure-open", "document",
+    "dot-dash", "dot-dot-dash",
+    "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
+    "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
+    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
+    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
+    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
+    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
+    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
+    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
+    "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
+    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
+    "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
+    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
+    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
+    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
+    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
+    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
+    "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
+    "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
+    "katakana", "katakana-iroha", "keep-all", "khmer",
+    "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
+    "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
+    "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
+    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
+    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
+    "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
+    "media-controls-background", "media-current-time-display",
+    "media-fullscreen-button", "media-mute-button", "media-play-button",
+    "media-return-to-realtime-button", "media-rewind-button",
+    "media-seek-back-button", "media-seek-forward-button", "media-slider",
+    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
+    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
+    "menu", "menulist", "menulist-button", "menulist-text",
+    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
+    "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
+    "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
+    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
+    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
+    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
+    "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
+    "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
+    "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
+    "progress", "push-button", "radial-gradient", "radio", "read-only",
+    "read-write", "read-write-plaintext-only", "rectangle", "region",
+    "relative", "repeat", "repeating-linear-gradient",
+    "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
+    "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
+    "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
+    "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
+    "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
+    "searchfield-cancel-button", "searchfield-decoration",
+    "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
+    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
+    "simp-chinese-formal", "simp-chinese-informal", "single",
+    "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
+    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+    "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
+    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
+    "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
+    "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
+    "table-caption", "table-cell", "table-column", "table-column-group",
+    "table-footer-group", "table-header-group", "table-row", "table-row-group",
+    "tamil",
+    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
+    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
+    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
+    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
+    "trad-chinese-formal", "trad-chinese-informal", "transform",
+    "translate", "translate3d", "translateX", "translateY", "translateZ",
+    "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
+    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
+    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
+    "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
+    "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
+    "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
+    "xx-large", "xx-small"
+  ], valueKeywords = keySet(valueKeywords_);
+
+  var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
+    .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
+    .concat(valueKeywords_);
+  CodeMirror.registerHelper("hintWords", "css", allWords);
+
+  function tokenCComment(stream, state) {
+    var maybeEnd = false, ch;
+    while ((ch = stream.next()) != null) {
+      if (maybeEnd && ch == "/") {
+        state.tokenize = null;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ["comment", "comment"];
+  }
+
+  CodeMirror.defineMIME("text/css", {
+    documentTypes: documentTypes,
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    mediaValueKeywords: mediaValueKeywords,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    fontProperties: fontProperties,
+    counterDescriptors: counterDescriptors,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false;
+        state.tokenize = tokenCComment;
+        return tokenCComment(stream, state);
+      }
+    },
+    name: "css"
+  });
+
+  CodeMirror.defineMIME("text/x-scss", {
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    mediaValueKeywords: mediaValueKeywords,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    fontProperties: fontProperties,
+    allowNested: true,
+    lineComment: "//",
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (stream.eat("/")) {
+          stream.skipToEnd();
+          return ["comment", "comment"];
+        } else if (stream.eat("*")) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        } else {
+          return ["operator", "operator"];
+        }
+      },
+      ":": function(stream) {
+        if (stream.match(/\s*\{/, false))
+          return [null, null]
+        return false;
+      },
+      "$": function(stream) {
+        stream.match(/^[\w-]+/);
+        if (stream.match(/^\s*:/, false))
+          return ["variable-2", "variable-definition"];
+        return ["variable-2", "variable"];
+      },
+      "#": function(stream) {
+        if (!stream.eat("{")) return false;
+        return [null, "interpolation"];
+      }
+    },
+    name: "css",
+    helperType: "scss"
+  });
+
+  CodeMirror.defineMIME("text/x-less", {
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    mediaValueKeywords: mediaValueKeywords,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    fontProperties: fontProperties,
+    allowNested: true,
+    lineComment: "//",
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (stream.eat("/")) {
+          stream.skipToEnd();
+          return ["comment", "comment"];
+        } else if (stream.eat("*")) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        } else {
+          return ["operator", "operator"];
+        }
+      },
+      "@": function(stream) {
+        if (stream.eat("{")) return [null, "interpolation"];
+        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
+        stream.eatWhile(/[\w\\\-]/);
+        if (stream.match(/^\s*:/, false))
+          return ["variable-2", "variable-definition"];
+        return ["variable-2", "variable"];
+      },
+      "&": function() {
+        return ["atom", "atom"];
+      }
+    },
+    name: "css",
+    helperType: "less"
+  });
+
+  CodeMirror.defineMIME("text/x-gss", {
+    documentTypes: documentTypes,
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    fontProperties: fontProperties,
+    counterDescriptors: counterDescriptors,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    supportsAtComponent: true,
+    tokenHooks: {
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false;
+        state.tokenize = tokenCComment;
+        return tokenCComment(stream, state);
+      }
+    },
+    name: "css",
+    helperType: "gss"
+  });
+
+});
diff --git a/dist/firephp.build/lib/codemirror/mode/htmlmixed/htmlmixed.js b/dist/firephp.build/lib/codemirror/mode/htmlmixed/htmlmixed.js
new file mode 100644
index 0000000..8341ac8
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror/mode/htmlmixed/htmlmixed.js
@@ -0,0 +1,152 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var defaultTags = {
+    script: [
+      ["lang", /(javascript|babel)/i, "javascript"],
+      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
+      ["type", /./, "text/plain"],
+      [null, null, "javascript"]
+    ],
+    style:  [
+      ["lang", /^css$/i, "css"],
+      ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
+      ["type", /./, "text/plain"],
+      [null, null, "css"]
+    ]
+  };
+
+  function maybeBackup(stream, pat, style) {
+    var cur = stream.current(), close = cur.search(pat);
+    if (close > -1) {
+      stream.backUp(cur.length - close);
+    } else if (cur.match(/<\/?$/)) {
+      stream.backUp(cur.length);
+      if (!stream.match(pat, false)) stream.match(cur);
+    }
+    return style;
+  }
+
+  var attrRegexpCache = {};
+  function getAttrRegexp(attr) {
+    var regexp = attrRegexpCache[attr];
+    if (regexp) return regexp;
+    return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
+  }
+
+  function getAttrValue(text, attr) {
+    var match = text.match(getAttrRegexp(attr))
+    return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
+  }
+
+  function getTagRegexp(tagName, anchored) {
+    return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
+  }
+
+  function addTags(from, to) {
+    for (var tag in from) {
+      var dest = to[tag] || (to[tag] = []);
+      var source = from[tag];
+      for (var i = source.length - 1; i >= 0; i--)
+        dest.unshift(source[i])
+    }
+  }
+
+  function findMatchingMode(tagInfo, tagText) {
+    for (var i = 0; i < tagInfo.length; i++) {
+      var spec = tagInfo[i];
+      if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
+    }
+  }
+
+  CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
+    var htmlMode = CodeMirror.getMode(config, {
+      name: "xml",
+      htmlMode: true,
+      multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
+      multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
+    });
+
+    var tags = {};
+    var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
+    addTags(defaultTags, tags);
+    if (configTags) addTags(configTags, tags);
+    if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
+      tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
+
+    function html(stream, state) {
+      var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
+      if (tag && !/[<>\s\/]/.test(stream.current()) &&
+          (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
+          tags.hasOwnProperty(tagName)) {
+        state.inTag = tagName + " "
+      } else if (state.inTag && tag && />$/.test(stream.current())) {
+        var inTag = /^([\S]+) (.*)/.exec(state.inTag)
+        state.inTag = null
+        var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
+        var mode = CodeMirror.getMode(config, modeSpec)
+        var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
+        state.token = function (stream, state) {
+          if (stream.match(endTagA, false)) {
+            state.token = html;
+            state.localState = state.localMode = null;
+            return null;
+          }
+          return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
+        };
+        state.localMode = mode;
+        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
+      } else if (state.inTag) {
+        state.inTag += stream.current()
+        if (stream.eol()) state.inTag += " "
+      }
+      return style;
+    };
+
+    return {
+      startState: function () {
+        var state = CodeMirror.startState(htmlMode);
+        return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
+      },
+
+      copyState: function (state) {
+        var local;
+        if (state.localState) {
+          local = CodeMirror.copyState(state.localMode, state.localState);
+        }
+        return {token: state.token, inTag: state.inTag,
+                localMode: state.localMode, localState: local,
+                htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
+      },
+
+      token: function (stream, state) {
+        return state.token(stream, state);
+      },
+
+      indent: function (state, textAfter, line) {
+        if (!state.localMode || /^\s*<\//.test(textAfter))
+          return htmlMode.indent(state.htmlState, textAfter, line);
+        else if (state.localMode.indent)
+          return state.localMode.indent(state.localState, textAfter, line);
+        else
+          return CodeMirror.Pass;
+      },
+
+      innerMode: function (state) {
+        return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
+      }
+    };
+  }, "xml", "javascript", "css");
+
+  CodeMirror.defineMIME("text/html", "htmlmixed");
+});
diff --git a/dist/firephp.build/lib/codemirror/mode/javascript/javascript.js b/dist/firephp.build/lib/codemirror/mode/javascript/javascript.js
new file mode 100644
index 0000000..16943a9
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror/mode/javascript/javascript.js
@@ -0,0 +1,930 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var statementIndent = parserConfig.statementIndent;
+  var jsonldMode = parserConfig.jsonld;
+  var jsonMode = parserConfig.json || jsonldMode;
+  var isTS = parserConfig.typescript;
+  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
+
+  // Tokenizer
+
+  var keywords = function(){
+    function kw(type) {return {type: type, style: "keyword"};}
+    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
+    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+
+    return {
+      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
+      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
+      "function": kw("function"), "catch": kw("catch"),
+      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+      "in": operator, "typeof": operator, "instanceof": operator,
+      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
+      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
+      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
+      "await": C
+    };
+  }();
+
+  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
+  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
+
+  function readRegexp(stream) {
+    var escaped = false, next, inSet = false;
+    while ((next = stream.next()) != null) {
+      if (!escaped) {
+        if (next == "/" && !inSet) return;
+        if (next == "[") inSet = true;
+        else if (inSet && next == "]") inSet = false;
+      }
+      escaped = !escaped && next == "\\";
+    }
+  }
+
+  // Used as scratch variables to communicate multiple values without
+  // consing up tons of objects.
+  var type, content;
+  function ret(tp, style, cont) {
+    type = tp; content = cont;
+    return style;
+  }
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == '"' || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
+      return ret("number", "number");
+    } else if (ch == "." && stream.match("..")) {
+      return ret("spread", "meta");
+    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+      return ret(ch);
+    } else if (ch == "=" && stream.eat(">")) {
+      return ret("=>", "operator");
+    } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
+      return ret("number", "number");
+    } else if (/\d/.test(ch)) {
+      stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
+      return ret("number", "number");
+    } else if (ch == "/") {
+      if (stream.eat("*")) {
+        state.tokenize = tokenComment;
+        return tokenComment(stream, state);
+      } else if (stream.eat("/")) {
+        stream.skipToEnd();
+        return ret("comment", "comment");
+      } else if (expressionAllowed(stream, state, 1)) {
+        readRegexp(stream);
+        stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
+        return ret("regexp", "string-2");
+      } else {
+        stream.eat("=");
+        return ret("operator", "operator", stream.current());
+      }
+    } else if (ch == "`") {
+      state.tokenize = tokenQuasi;
+      return tokenQuasi(stream, state);
+    } else if (ch == "#") {
+      stream.skipToEnd();
+      return ret("error", "error");
+    } else if (ch == "<" && stream.match("!--") || ch == "-" && stream.match("->")) {
+      stream.skipToEnd()
+      return ret("comment", "comment")
+    } else if (isOperatorChar.test(ch)) {
+      if (ch != ">" || !state.lexical || state.lexical.type != ">") {
+        if (stream.eat("=")) {
+          if (ch == "!" || ch == "=") stream.eat("=")
+        } else if (/[<>*+\-]/.test(ch)) {
+          stream.eat(ch)
+          if (ch == ">") stream.eat(ch)
+        }
+      }
+      return ret("operator", "operator", stream.current());
+    } else if (wordRE.test(ch)) {
+      stream.eatWhile(wordRE);
+      var word = stream.current()
+      if (state.lastType != ".") {
+        if (keywords.propertyIsEnumerable(word)) {
+          var kw = keywords[word]
+          return ret(kw.type, kw.style, word)
+        }
+        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
+          return ret("async", "keyword", word)
+      }
+      return ret("variable", "variable", word)
+    }
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, next;
+      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
+        state.tokenize = tokenBase;
+        return ret("jsonld-keyword", "meta");
+      }
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) break;
+        escaped = !escaped && next == "\\";
+      }
+      if (!escaped) state.tokenize = tokenBase;
+      return ret("string", "string");
+    };
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+
+  function tokenQuasi(stream, state) {
+    var escaped = false, next;
+    while ((next = stream.next()) != null) {
+      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      escaped = !escaped && next == "\\";
+    }
+    return ret("quasi", "string-2", stream.current());
+  }
+
+  var brackets = "([{}])";
+  // This is a crude lookahead trick to try and notice that we're
+  // parsing the argument patterns for a fat-arrow function before we
+  // actually hit the arrow token. It only works if the arrow is on
+  // the same line as the arguments and there's no strange noise
+  // (comments) in between. Fallback is to only notice when we hit the
+  // arrow, and not declare the arguments as locals for the arrow
+  // body.
+  function findFatArrow(stream, state) {
+    if (state.fatArrowAt) state.fatArrowAt = null;
+    var arrow = stream.string.indexOf("=>", stream.start);
+    if (arrow < 0) return;
+
+    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
+      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
+      if (m) arrow = m.index
+    }
+
+    var depth = 0, sawSomething = false;
+    for (var pos = arrow - 1; pos >= 0; --pos) {
+      var ch = stream.string.charAt(pos);
+      var bracket = brackets.indexOf(ch);
+      if (bracket >= 0 && bracket < 3) {
+        if (!depth) { ++pos; break; }
+        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
+      } else if (bracket >= 3 && bracket < 6) {
+        ++depth;
+      } else if (wordRE.test(ch)) {
+        sawSomething = true;
+      } else if (/["'\/`]/.test(ch)) {
+        for (;; --pos) {
+          if (pos == 0) return
+          var next = stream.string.charAt(pos - 1)
+          if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
+        }
+      } else if (sawSomething && !depth) {
+        ++pos;
+        break;
+      }
+    }
+    if (sawSomething && !depth) state.fatArrowAt = pos;
+  }
+
+  // Parser
+
+  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
+
+  function JSLexical(indented, column, type, align, prev, info) {
+    this.indented = indented;
+    this.column = column;
+    this.type = type;
+    this.prev = prev;
+    this.info = info;
+    if (align != null) this.align = align;
+  }
+
+  function inScope(state, varname) {
+    for (var v = state.localVars; v; v = v.next)
+      if (v.name == varname) return true;
+    for (var cx = state.context; cx; cx = cx.prev) {
+      for (var v = cx.vars; v; v = v.next)
+        if (v.name == varname) return true;
+    }
+  }
+
+  function parseJS(state, style, type, content, stream) {
+    var cc = state.cc;
+    // Communicate our context to the combinators.
+    // (Less wasteful than consing up a hundred closures on every call.)
+    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
+
+    if (!state.lexical.hasOwnProperty("align"))
+      state.lexical.align = true;
+
+    while(true) {
+      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+      if (combinator(type, content)) {
+        while(cc.length && cc[cc.length - 1].lex)
+          cc.pop()();
+        if (cx.marked) return cx.marked;
+        if (type == "variable" && inScope(state, content)) return "variable-2";
+        return style;
+      }
+    }
+  }
+
+  // Combinator utils
+
+  var cx = {state: null, column: null, marked: null, cc: null};
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+  function inList(name, list) {
+    for (var v = list; v; v = v.next) if (v.name == name) return true
+    return false;
+  }
+  function register(varname) {
+    var state = cx.state;
+    cx.marked = "def";
+    if (state.context) {
+      if (state.lexical.info == "var" && state.context && state.context.block) {
+        // FIXME function decls are also not block scoped
+        var newContext = registerVarScoped(varname, state.context)
+        if (newContext != null) {
+          state.context = newContext
+          return
+        }
+      } else if (!inList(varname, state.localVars)) {
+        state.localVars = new Var(varname, state.localVars)
+        return
+      }
+    }
+    // Fall through means this is global
+    if (parserConfig.globalVars && !inList(varname, state.globalVars))
+      state.globalVars = new Var(varname, state.globalVars)
+  }
+  function registerVarScoped(varname, context) {
+    if (!context) {
+      return null
+    } else if (context.block) {
+      var inner = registerVarScoped(varname, context.prev)
+      if (!inner) return null
+      if (inner == context.prev) return context
+      return new Context(inner, context.vars, true)
+    } else if (inList(varname, context.vars)) {
+      return context
+    } else {
+      return new Context(context.prev, new Var(varname, context.vars), false)
+    }
+  }
+
+  function isModifier(name) {
+    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
+  }
+
+  // Combinators
+
+  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
+  function Var(name, next) { this.name = name; this.next = next }
+
+  var defaultVars = new Var("this", new Var("arguments", null))
+  function pushcontext() {
+    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
+    cx.state.localVars = defaultVars
+  }
+  function pushblockcontext() {
+    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
+    cx.state.localVars = null
+  }
+  function popcontext() {
+    cx.state.localVars = cx.state.context.vars
+    cx.state.context = cx.state.context.prev
+  }
+  popcontext.lex = true
+  function pushlex(type, info) {
+    var result = function() {
+      var state = cx.state, indent = state.indented;
+      if (state.lexical.type == "stat") indent = state.lexical.indented;
+      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
+        indent = outer.indented;
+      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
+    };
+    result.lex = true;
+    return result;
+  }
+  function poplex() {
+    var state = cx.state;
+    if (state.lexical.prev) {
+      if (state.lexical.type == ")")
+        state.indented = state.lexical.indented;
+      state.lexical = state.lexical.prev;
+    }
+  }
+  poplex.lex = true;
+
+  function expect(wanted) {
+    function exp(type) {
+      if (type == wanted) return cont();
+      else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
+      else return cont(exp);
+    };
+    return exp;
+  }
+
+  function statement(type, value) {
+    if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
+    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
+    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+    if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
+    if (type == "debugger") return cont(expect(";"));
+    if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
+    if (type == ";") return cont();
+    if (type == "if") {
+      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+        cx.state.cc.pop()();
+      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+    }
+    if (type == "function") return cont(functiondef);
+    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+    if (type == "class" || (isTS && value == "interface")) {
+      cx.marked = "keyword"
+      return cont(pushlex("form", type == "class" ? type : value), className, poplex)
+    }
+    if (type == "variable") {
+      if (isTS && value == "declare") {
+        cx.marked = "keyword"
+        return cont(statement)
+      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
+        cx.marked = "keyword"
+        if (value == "enum") return cont(enumdef);
+        else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
+        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+      } else if (isTS && value == "namespace") {
+        cx.marked = "keyword"
+        return cont(pushlex("form"), expression, statement, poplex)
+      } else if (isTS && value == "abstract") {
+        cx.marked = "keyword"
+        return cont(statement)
+      } else {
+        return cont(pushlex("stat"), maybelabel);
+      }
+    }
+    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
+                                      block, poplex, poplex, popcontext);
+    if (type == "case") return cont(expression, expect(":"));
+    if (type == "default") return cont(expect(":"));
+    if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
+    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
+    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+    if (type == "async") return cont(statement)
+    if (value == "@") return cont(expression, statement)
+    return pass(pushlex("stat"), expression, expect(";"), poplex);
+  }
+  function maybeCatchBinding(type) {
+    if (type == "(") return cont(funarg, expect(")"))
+  }
+  function expression(type, value) {
+    return expressionInner(type, value, false);
+  }
+  function expressionNoComma(type, value) {
+    return expressionInner(type, value, true);
+  }
+  function parenExpr(type) {
+    if (type != "(") return pass()
+    return cont(pushlex(")"), expression, expect(")"), poplex)
+  }
+  function expressionInner(type, value, noComma) {
+    if (cx.state.fatArrowAt == cx.stream.start) {
+      var body = noComma ? arrowBodyNoComma : arrowBody;
+      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
+      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
+    }
+
+    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
+    if (type == "function") return cont(functiondef, maybeop);
+    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
+    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
+    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+    if (type == "quasi") return pass(quasi, maybeop);
+    if (type == "new") return cont(maybeTarget(noComma));
+    if (type == "import") return cont(expression);
+    return cont();
+  }
+  function maybeexpression(type) {
+    if (type.match(/[;\}\)\],]/)) return pass();
+    return pass(expression);
+  }
+
+  function maybeoperatorComma(type, value) {
+    if (type == ",") return cont(expression);
+    return maybeoperatorNoComma(type, value, false);
+  }
+  function maybeoperatorNoComma(type, value, noComma) {
+    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
+    var expr = noComma == false ? expression : expressionNoComma;
+    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
+    if (type == "operator") {
+      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
+      if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
+        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
+      if (value == "?") return cont(expression, expect(":"), expr);
+      return cont(expr);
+    }
+    if (type == "quasi") { return pass(quasi, me); }
+    if (type == ";") return;
+    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
+    if (type == ".") return cont(property, me);
+    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+    if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
+    if (type == "regexp") {
+      cx.state.lastType = cx.marked = "operator"
+      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
+      return cont(expr)
+    }
+  }
+  function quasi(type, value) {
+    if (type != "quasi") return pass();
+    if (value.slice(value.length - 2) != "${") return cont(quasi);
+    return cont(expression, continueQuasi);
+  }
+  function continueQuasi(type) {
+    if (type == "}") {
+      cx.marked = "string-2";
+      cx.state.tokenize = tokenQuasi;
+      return cont(quasi);
+    }
+  }
+  function arrowBody(type) {
+    findFatArrow(cx.stream, cx.state);
+    return pass(type == "{" ? statement : expression);
+  }
+  function arrowBodyNoComma(type) {
+    findFatArrow(cx.stream, cx.state);
+    return pass(type == "{" ? statement : expressionNoComma);
+  }
+  function maybeTarget(noComma) {
+    return function(type) {
+      if (type == ".") return cont(noComma ? targetNoComma : target);
+      else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
+      else return pass(noComma ? expressionNoComma : expression);
+    };
+  }
+  function target(_, value) {
+    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
+  }
+  function targetNoComma(_, value) {
+    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
+  }
+  function maybelabel(type) {
+    if (type == ":") return cont(poplex, statement);
+    return pass(maybeoperatorComma, expect(";"), poplex);
+  }
+  function property(type) {
+    if (type == "variable") {cx.marked = "property"; return cont();}
+  }
+  function objprop(type, value) {
+    if (type == "async") {
+      cx.marked = "property";
+      return cont(objprop);
+    } else if (type == "variable" || cx.style == "keyword") {
+      cx.marked = "property";
+      if (value == "get" || value == "set") return cont(getterSetter);
+      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
+      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
+        cx.state.fatArrowAt = cx.stream.pos + m[0].length
+      return cont(afterprop);
+    } else if (type == "number" || type == "string") {
+      cx.marked = jsonldMode ? "property" : (cx.style + " property");
+      return cont(afterprop);
+    } else if (type == "jsonld-keyword") {
+      return cont(afterprop);
+    } else if (isTS && isModifier(value)) {
+      cx.marked = "keyword"
+      return cont(objprop)
+    } else if (type == "[") {
+      return cont(expression, maybetype, expect("]"), afterprop);
+    } else if (type == "spread") {
+      return cont(expressionNoComma, afterprop);
+    } else if (value == "*") {
+      cx.marked = "keyword";
+      return cont(objprop);
+    } else if (type == ":") {
+      return pass(afterprop)
+    }
+  }
+  function getterSetter(type) {
+    if (type != "variable") return pass(afterprop);
+    cx.marked = "property";
+    return cont(functiondef);
+  }
+  function afterprop(type) {
+    if (type == ":") return cont(expressionNoComma);
+    if (type == "(") return pass(functiondef);
+  }
+  function commasep(what, end, sep) {
+    function proceed(type, value) {
+      if (sep ? sep.indexOf(type) > -1 : type == ",") {
+        var lex = cx.state.lexical;
+        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
+        return cont(function(type, value) {
+          if (type == end || value == end) return pass()
+          return pass(what)
+        }, proceed);
+      }
+      if (type == end || value == end) return cont();
+      if (sep && sep.indexOf(";") > -1) return pass(what)
+      return cont(expect(end));
+    }
+    return function(type, value) {
+      if (type == end || value == end) return cont();
+      return pass(what, proceed);
+    };
+  }
+  function contCommasep(what, end, info) {
+    for (var i = 3; i < arguments.length; i++)
+      cx.cc.push(arguments[i]);
+    return cont(pushlex(end, info), commasep(what, end), poplex);
+  }
+  function block(type) {
+    if (type == "}") return cont();
+    return pass(statement, block);
+  }
+  function maybetype(type, value) {
+    if (isTS) {
+      if (type == ":") return cont(typeexpr);
+      if (value == "?") return cont(maybetype);
+    }
+  }
+  function maybetypeOrIn(type, value) {
+    if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
+  }
+  function mayberettype(type) {
+    if (isTS && type == ":") {
+      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
+      else return cont(typeexpr)
+    }
+  }
+  function isKW(_, value) {
+    if (value == "is") {
+      cx.marked = "keyword"
+      return cont()
+    }
+  }
+  function typeexpr(type, value) {
+    if (value == "keyof" || value == "typeof" || value == "infer") {
+      cx.marked = "keyword"
+      return cont(value == "typeof" ? expressionNoComma : typeexpr)
+    }
+    if (type == "variable" || value == "void") {
+      cx.marked = "type"
+      return cont(afterType)
+    }
+    if (value == "|" || value == "&") return cont(typeexpr)
+    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
+    if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
+    if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
+    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
+    if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
+  }
+  function maybeReturnType(type) {
+    if (type == "=>") return cont(typeexpr)
+  }
+  function typeprop(type, value) {
+    if (type == "variable" || cx.style == "keyword") {
+      cx.marked = "property"
+      return cont(typeprop)
+    } else if (value == "?" || type == "number" || type == "string") {
+      return cont(typeprop)
+    } else if (type == ":") {
+      return cont(typeexpr)
+    } else if (type == "[") {
+      return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
+    } else if (type == "(") {
+      return pass(functiondecl, typeprop)
+    }
+  }
+  function typearg(type, value) {
+    if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
+    if (type == ":") return cont(typeexpr)
+    if (type == "spread") return cont(typearg)
+    return pass(typeexpr)
+  }
+  function afterType(type, value) {
+    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
+    if (type == "[") return cont(typeexpr, expect("]"), afterType)
+    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
+    if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
+  }
+  function maybeTypeArgs(_, value) {
+    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+  }
+  function typeparam() {
+    return pass(typeexpr, maybeTypeDefault)
+  }
+  function maybeTypeDefault(_, value) {
+    if (value == "=") return cont(typeexpr)
+  }
+  function vardef(_, value) {
+    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
+    return pass(pattern, maybetype, maybeAssign, vardefCont);
+  }
+  function pattern(type, value) {
+    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
+    if (type == "variable") { register(value); return cont(); }
+    if (type == "spread") return cont(pattern);
+    if (type == "[") return contCommasep(eltpattern, "]");
+    if (type == "{") return contCommasep(proppattern, "}");
+  }
+  function proppattern(type, value) {
+    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
+      register(value);
+      return cont(maybeAssign);
+    }
+    if (type == "variable") cx.marked = "property";
+    if (type == "spread") return cont(pattern);
+    if (type == "}") return pass();
+    if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
+    return cont(expect(":"), pattern, maybeAssign);
+  }
+  function eltpattern() {
+    return pass(pattern, maybeAssign)
+  }
+  function maybeAssign(_type, value) {
+    if (value == "=") return cont(expressionNoComma);
+  }
+  function vardefCont(type) {
+    if (type == ",") return cont(vardef);
+  }
+  function maybeelse(type, value) {
+    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
+  }
+  function forspec(type, value) {
+    if (value == "await") return cont(forspec);
+    if (type == "(") return cont(pushlex(")"), forspec1, poplex);
+  }
+  function forspec1(type) {
+    if (type == "var") return cont(vardef, forspec2);
+    if (type == "variable") return cont(forspec2);
+    return pass(forspec2)
+  }
+  function forspec2(type, value) {
+    if (type == ")") return cont()
+    if (type == ";") return cont(forspec2)
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
+    return pass(expression, forspec2)
+  }
+  function functiondef(type, value) {
+    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+    if (type == "variable") {register(value); return cont(functiondef);}
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
+    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
+  }
+  function functiondecl(type, value) {
+    if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
+    if (type == "variable") {register(value); return cont(functiondecl);}
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
+    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
+  }
+  function typename(type, value) {
+    if (type == "keyword" || type == "variable") {
+      cx.marked = "type"
+      return cont(typename)
+    } else if (value == "<") {
+      return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
+    }
+  }
+  function funarg(type, value) {
+    if (value == "@") cont(expression, funarg)
+    if (type == "spread") return cont(funarg);
+    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
+    if (isTS && type == "this") return cont(maybetype, maybeAssign)
+    return pass(pattern, maybetype, maybeAssign);
+  }
+  function classExpression(type, value) {
+    // Class expressions may have an optional name.
+    if (type == "variable") return className(type, value);
+    return classNameAfter(type, value);
+  }
+  function className(type, value) {
+    if (type == "variable") {register(value); return cont(classNameAfter);}
+  }
+  function classNameAfter(type, value) {
+    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
+      if (value == "implements") cx.marked = "keyword";
+      return cont(isTS ? typeexpr : expression, classNameAfter);
+    }
+    if (type == "{") return cont(pushlex("}"), classBody, poplex);
+  }
+  function classBody(type, value) {
+    if (type == "async" ||
+        (type == "variable" &&
+         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
+         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
+      cx.marked = "keyword";
+      return cont(classBody);
+    }
+    if (type == "variable" || cx.style == "keyword") {
+      cx.marked = "property";
+      return cont(isTS ? classfield : functiondef, classBody);
+    }
+    if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody);
+    if (type == "[")
+      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
+    if (value == "*") {
+      cx.marked = "keyword";
+      return cont(classBody);
+    }
+    if (isTS && type == "(") return pass(functiondecl, classBody)
+    if (type == ";" || type == ",") return cont(classBody);
+    if (type == "}") return cont();
+    if (value == "@") return cont(expression, classBody)
+  }
+  function classfield(type, value) {
+    if (value == "?") return cont(classfield)
+    if (type == ":") return cont(typeexpr, maybeAssign)
+    if (value == "=") return cont(expressionNoComma)
+    var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
+    return pass(isInterface ? functiondecl : functiondef)
+  }
+  function afterExport(type, value) {
+    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
+    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
+    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
+    return pass(statement);
+  }
+  function exportField(type, value) {
+    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
+    if (type == "variable") return pass(expressionNoComma, exportField);
+  }
+  function afterImport(type) {
+    if (type == "string") return cont();
+    if (type == "(") return pass(expression);
+    return pass(importSpec, maybeMoreImports, maybeFrom);
+  }
+  function importSpec(type, value) {
+    if (type == "{") return contCommasep(importSpec, "}");
+    if (type == "variable") register(value);
+    if (value == "*") cx.marked = "keyword";
+    return cont(maybeAs);
+  }
+  function maybeMoreImports(type) {
+    if (type == ",") return cont(importSpec, maybeMoreImports)
+  }
+  function maybeAs(_type, value) {
+    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
+  }
+  function maybeFrom(_type, value) {
+    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
+  }
+  function arrayLiteral(type) {
+    if (type == "]") return cont();
+    return pass(commasep(expressionNoComma, "]"));
+  }
+  function enumdef() {
+    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
+  }
+  function enummember() {
+    return pass(pattern, maybeAssign);
+  }
+
+  function isContinuedStatement(state, textAfter) {
+    return state.lastType == "operator" || state.lastType == "," ||
+      isOperatorChar.test(textAfter.charAt(0)) ||
+      /[,.]/.test(textAfter.charAt(0));
+  }
+
+  function expressionAllowed(stream, state, backUp) {
+    return state.tokenize == tokenBase &&
+      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
+      (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      var state = {
+        tokenize: tokenBase,
+        lastType: "sof",
+        cc: [],
+        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+        localVars: parserConfig.localVars,
+        context: parserConfig.localVars && new Context(null, null, false),
+        indented: basecolumn || 0
+      };
+      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
+        state.globalVars = parserConfig.globalVars;
+      return state;
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (!state.lexical.hasOwnProperty("align"))
+          state.lexical.align = false;
+        state.indented = stream.indentation();
+        findFatArrow(stream, state);
+      }
+      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      if (type == "comment") return style;
+      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
+      return parseJS(state, style, type, content, stream);
+    },
+
+    indent: function(state, textAfter) {
+      if (state.tokenize == tokenComment) return CodeMirror.Pass;
+      if (state.tokenize != tokenBase) return 0;
+      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
+      // Kludge to prevent 'maybelse' from blocking lexical scope pops
+      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
+        var c = state.cc[i];
+        if (c == poplex) lexical = lexical.prev;
+        else if (c != maybeelse) break;
+      }
+      while ((lexical.type == "stat" || lexical.type == "form") &&
+             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
+                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
+                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
+        lexical = lexical.prev;
+      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
+        lexical = lexical.prev;
+      var type = lexical.type, closing = firstChar == type;
+
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
+      else if (type == "form" && firstChar == "{") return lexical.indented;
+      else if (type == "form") return lexical.indented + indentUnit;
+      else if (type == "stat")
+        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
+      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
+        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+      else return lexical.indented + (closing ? 0 : indentUnit);
+    },
+
+    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
+    blockCommentStart: jsonMode ? null : "/*",
+    blockCommentEnd: jsonMode ? null : "*/",
+    blockCommentContinue: jsonMode ? null : " * ",
+    lineComment: jsonMode ? null : "//",
+    fold: "brace",
+    closeBrackets: "()[]{}''\"\"``",
+
+    helperType: jsonMode ? "json" : "javascript",
+    jsonldMode: jsonldMode,
+    jsonMode: jsonMode,
+
+    expressionAllowed: expressionAllowed,
+
+    skipExpression: function(state) {
+      var top = state.cc[state.cc.length - 1]
+      if (top == expression || top == expressionNoComma) state.cc.pop()
+    }
+  };
+});
+
+CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("text/ecmascript", "javascript");
+CodeMirror.defineMIME("application/javascript", "javascript");
+CodeMirror.defineMIME("application/x-javascript", "javascript");
+CodeMirror.defineMIME("application/ecmascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
+CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
+CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
+
+});
diff --git a/dist/firephp.build/lib/codemirror/mode/php/php.js b/dist/firephp.build/lib/codemirror/mode/php/php.js
new file mode 100644
index 0000000..5f3a143
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror/mode/php/php.js
@@ -0,0 +1,234 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  function keywords(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+
+  // Helper for phpString
+  function matchSequence(list, end, escapes) {
+    if (list.length == 0) return phpString(end);
+    return function (stream, state) {
+      var patterns = list[0];
+      for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {
+        state.tokenize = matchSequence(list.slice(1), end);
+        return patterns[i][1];
+      }
+      state.tokenize = phpString(end, escapes);
+      return "string";
+    };
+  }
+  function phpString(closing, escapes) {
+    return function(stream, state) { return phpString_(stream, state, closing, escapes); };
+  }
+  function phpString_(stream, state, closing, escapes) {
+    // "Complex" syntax
+    if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) {
+      state.tokenize = null;
+      return "string";
+    }
+
+    // Simple syntax
+    if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) {
+      // After the variable name there may appear array or object operator.
+      if (stream.match("[", false)) {
+        // Match array operator
+        state.tokenize = matchSequence([
+          [["[", null]],
+          [[/\d[\w\.]*/, "number"],
+           [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"],
+           [/[\w\$]+/, "variable"]],
+          [["]", null]]
+        ], closing, escapes);
+      }
+      if (stream.match(/\-\>\w/, false)) {
+        // Match object operator
+        state.tokenize = matchSequence([
+          [["->", null]],
+          [[/[\w]+/, "variable"]]
+        ], closing, escapes);
+      }
+      return "variable-2";
+    }
+
+    var escaped = false;
+    // Normal string
+    while (!stream.eol() &&
+           (escaped || escapes === false ||
+            (!stream.match("{$", false) &&
+             !stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) {
+      if (!escaped && stream.match(closing)) {
+        state.tokenize = null;
+        state.tokStack.pop(); state.tokStack.pop();
+        break;
+      }
+      escaped = stream.next() == "\\" && !escaped;
+    }
+    return "string";
+  }
+
+  var phpKeywords = "abstract and array as break case catch class clone const continue declare default " +
+    "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
+    "for foreach function global goto if implements interface instanceof namespace " +
+    "new or private protected public static switch throw trait try use var while xor " +
+    "die echo empty exit eval include include_once isset list require require_once return " +
+    "print unset __halt_compiler self static parent yield insteadof finally";
+  var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__";
+  var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count";
+  CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" "));
+  CodeMirror.registerHelper("wordChars", "php", /[\w$]/);
+
+  var phpConfig = {
+    name: "clike",
+    helperType: "php",
+    keywords: keywords(phpKeywords),
+    blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"),
+    defKeywords: keywords("class function interface namespace trait"),
+    atoms: keywords(phpAtoms),
+    builtin: keywords(phpBuiltin),
+    multiLineStrings: true,
+    hooks: {
+      "$": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "variable-2";
+      },
+      "<": function(stream, state) {
+        var before;
+        if (before = stream.match(/<<\s*/)) {
+          var quoted = stream.eat(/['"]/);
+          stream.eatWhile(/[\w\.]/);
+          var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1));
+          if (quoted) stream.eat(quoted);
+          if (delim) {
+            (state.tokStack || (state.tokStack = [])).push(delim, 0);
+            state.tokenize = phpString(delim, quoted != "'");
+            return "string";
+          }
+        }
+        return false;
+      },
+      "#": function(stream) {
+        while (!stream.eol() && !stream.match("?>", false)) stream.next();
+        return "comment";
+      },
+      "/": function(stream) {
+        if (stream.eat("/")) {
+          while (!stream.eol() && !stream.match("?>", false)) stream.next();
+          return "comment";
+        }
+        return false;
+      },
+      '"': function(_stream, state) {
+        (state.tokStack || (state.tokStack = [])).push('"', 0);
+        state.tokenize = phpString('"');
+        return "string";
+      },
+      "{": function(_stream, state) {
+        if (state.tokStack && state.tokStack.length)
+          state.tokStack[state.tokStack.length - 1]++;
+        return false;
+      },
+      "}": function(_stream, state) {
+        if (state.tokStack && state.tokStack.length > 0 &&
+            !--state.tokStack[state.tokStack.length - 1]) {
+          state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);
+        }
+        return false;
+      }
+    }
+  };
+
+  CodeMirror.defineMode("php", function(config, parserConfig) {
+    var htmlMode = CodeMirror.getMode(config, (parserConfig && parserConfig.htmlMode) || "text/html");
+    var phpMode = CodeMirror.getMode(config, phpConfig);
+
+    function dispatch(stream, state) {
+      var isPHP = state.curMode == phpMode;
+      if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null;
+      if (!isPHP) {
+        if (stream.match(/^<\?\w*/)) {
+          state.curMode = phpMode;
+          if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, "", ""))
+          state.curState = state.php;
+          return "meta";
+        }
+        if (state.pending == '"' || state.pending == "'") {
+          while (!stream.eol() && stream.next() != state.pending) {}
+          var style = "string";
+        } else if (state.pending && stream.pos < state.pending.end) {
+          stream.pos = state.pending.end;
+          var style = state.pending.style;
+        } else {
+          var style = htmlMode.token(stream, state.curState);
+        }
+        if (state.pending) state.pending = null;
+        var cur = stream.current(), openPHP = cur.search(/<\?/), m;
+        if (openPHP != -1) {
+          if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0];
+          else state.pending = {end: stream.pos, style: style};
+          stream.backUp(cur.length - openPHP);
+        }
+        return style;
+      } else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
+        state.curMode = htmlMode;
+        state.curState = state.html;
+        if (!state.php.context.prev) state.php = null;
+        return "meta";
+      } else {
+        return phpMode.token(stream, state.curState);
+      }
+    }
+
+    return {
+      startState: function() {
+        var html = CodeMirror.startState(htmlMode)
+        var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null
+        return {html: html,
+                php: php,
+                curMode: parserConfig.startOpen ? phpMode : htmlMode,
+                curState: parserConfig.startOpen ? php : html,
+                pending: null};
+      },
+
+      copyState: function(state) {
+        var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
+            php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur;
+        if (state.curMode == htmlMode) cur = htmlNew;
+        else cur = phpNew;
+        return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
+                pending: state.pending};
+      },
+
+      token: dispatch,
+
+      indent: function(state, textAfter, line) {
+        if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
+            (state.curMode == phpMode && /^\?>/.test(textAfter)))
+          return htmlMode.indent(state.html, textAfter, line);
+        return state.curMode.indent(state.curState, textAfter, line);
+      },
+
+      blockCommentStart: "/*",
+      blockCommentEnd: "*/",
+      lineComment: "//",
+
+      innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
+    };
+  }, "htmlmixed", "clike");
+
+  CodeMirror.defineMIME("application/x-httpd-php", "php");
+  CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
+  CodeMirror.defineMIME("text/x-php", phpConfig);
+});
diff --git a/dist/firephp.build/lib/codemirror/mode/xml/xml.js b/dist/firephp.build/lib/codemirror/mode/xml/xml.js
new file mode 100644
index 0000000..73c6e0e
--- /dev/null
+++ b/dist/firephp.build/lib/codemirror/mode/xml/xml.js
@@ -0,0 +1,413 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+var htmlConfig = {
+  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
+                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
+                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
+                    'track': true, 'wbr': true, 'menuitem': true},
+  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
+                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
+                     'th': true, 'tr': true},
+  contextGrabbers: {
+    'dd': {'dd': true, 'dt': true},
+    'dt': {'dd': true, 'dt': true},
+    'li': {'li': true},
+    'option': {'option': true, 'optgroup': true},
+    'optgroup': {'optgroup': true},
+    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
+          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
+          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
+          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
+          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
+    'rp': {'rp': true, 'rt': true},
+    'rt': {'rp': true, 'rt': true},
+    'tbody': {'tbody': true, 'tfoot': true},
+    'td': {'td': true, 'th': true},
+    'tfoot': {'tbody': true},
+    'th': {'td': true, 'th': true},
+    'thead': {'tbody': true, 'tfoot': true},
+    'tr': {'tr': true}
+  },
+  doNotIndent: {"pre": true},
+  allowUnquoted: true,
+  allowMissing: true,
+  caseFold: true
+}
+
+var xmlConfig = {
+  autoSelfClosers: {},
+  implicitlyClosed: {},
+  contextGrabbers: {},
+  doNotIndent: {},
+  allowUnquoted: false,
+  allowMissing: false,
+  allowMissingTagName: false,
+  caseFold: false
+}
+
+CodeMirror.defineMode("xml", function(editorConf, config_) {
+  var indentUnit = editorConf.indentUnit
+  var config = {}
+  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
+  for (var prop in defaults) config[prop] = defaults[prop]
+  for (var prop in config_) config[prop] = config_[prop]
+
+  // Return variables for tokenizers
+  var type, setStyle;
+
+  function inText(stream, state) {
+    function chain(parser) {
+      state.tokenize = parser;
+      return parser(stream, state);
+    }
+
+    var ch = stream.next();
+    if (ch == "<") {
+      if (stream.eat("!")) {
+        if (stream.eat("[")) {
+          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+          else return null;
+        } else if (stream.match("--")) {
+          return chain(inBlock("comment", "-->"));
+        } else if (stream.match("DOCTYPE", true, true)) {
+          stream.eatWhile(/[\w\._\-]/);
+          return chain(doctype(1));
+        } else {
+          return null;
+        }
+      } else if (stream.eat("?")) {
+        stream.eatWhile(/[\w\._\-]/);
+        state.tokenize = inBlock("meta", "?>");
+        return "meta";
+      } else {
+        type = stream.eat("/") ? "closeTag" : "openTag";
+        state.tokenize = inTag;
+        return "tag bracket";
+      }
+    } else if (ch == "&") {
+      var ok;
+      if (stream.eat("#")) {
+        if (stream.eat("x")) {
+          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
+        } else {
+          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
+        }
+      } else {
+        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
+      }
+      return ok ? "atom" : "error";
+    } else {
+      stream.eatWhile(/[^&<]/);
+      return null;
+    }
+  }
+  inText.isInText = true;
+
+  function inTag(stream, state) {
+    var ch = stream.next();
+    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+      state.tokenize = inText;
+      type = ch == ">" ? "endTag" : "selfcloseTag";
+      return "tag bracket";
+    } else if (ch == "=") {
+      type = "equals";
+      return null;
+    } else if (ch == "<") {
+      state.tokenize = inText;
+      state.state = baseState;
+      state.tagName = state.tagStart = null;
+      var next = state.tokenize(stream, state);
+      return next ? next + " tag error" : "tag error";
+    } else if (/[\'\"]/.test(ch)) {
+      state.tokenize = inAttribute(ch);
+      state.stringStartCol = stream.column();
+      return state.tokenize(stream, state);
+    } else {
+      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
+      return "word";
+    }
+  }
+
+  function inAttribute(quote) {
+    var closure = function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.next() == quote) {
+          state.tokenize = inTag;
+          break;
+        }
+      }
+      return "string";
+    };
+    closure.isInAttribute = true;
+    return closure;
+  }
+
+  function inBlock(style, terminator) {
+    return function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.match(terminator)) {
+          state.tokenize = inText;
+          break;
+        }
+        stream.next();
+      }
+      return style;
+    }
+  }
+
+  function doctype(depth) {
+    return function(stream, state) {
+      var ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == "<") {
+          state.tokenize = doctype(depth + 1);
+          return state.tokenize(stream, state);
+        } else if (ch == ">") {
+          if (depth == 1) {
+            state.tokenize = inText;
+            break;
+          } else {
+            state.tokenize = doctype(depth - 1);
+            return state.tokenize(stream, state);
+          }
+        }
+      }
+      return "meta";
+    };
+  }
+
+  function Context(state, tagName, startOfLine) {
+    this.prev = state.context;
+    this.tagName = tagName;
+    this.indent = state.indented;
+    this.startOfLine = startOfLine;
+    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
+      this.noIndent = true;
+  }
+  function popContext(state) {
+    if (state.context) state.context = state.context.prev;
+  }
+  function maybePopContext(state, nextTagName) {
+    var parentTagName;
+    while (true) {
+      if (!state.context) {
+        return;
+      }
+      parentTagName = state.context.tagName;
+      if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
+          !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+        return;
+      }
+      popContext(state);
+    }
+  }
+
+  function baseState(type, stream, state) {
+    if (type == "openTag") {
+      state.tagStart = stream.column();
+      return tagNameState;
+    } else if (type == "closeTag") {
+      return closeTagNameState;
+    } else {
+      return baseState;
+    }
+  }
+  function tagNameState(type, stream, state) {
+    if (type == "word") {
+      state.tagName = stream.current();
+      setStyle = "tag";
+      return attrState;
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return attrState(type, stream, state);
+    } else {
+      setStyle = "error";
+      return tagNameState;
+    }
+  }
+  function closeTagNameState(type, stream, state) {
+    if (type == "word") {
+      var tagName = stream.current();
+      if (state.context && state.context.tagName != tagName &&
+          config.implicitlyClosed.hasOwnProperty(state.context.tagName))
+        popContext(state);
+      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
+        setStyle = "tag";
+        return closeState;
+      } else {
+        setStyle = "tag error";
+        return closeStateErr;
+      }
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return closeState(type, stream, state);
+    } else {
+      setStyle = "error";
+      return closeStateErr;
+    }
+  }
+
+  function closeState(type, _stream, state) {
+    if (type != "endTag") {
+      setStyle = "error";
+      return closeState;
+    }
+    popContext(state);
+    return baseState;
+  }
+  function closeStateErr(type, stream, state) {
+    setStyle = "error";
+    return closeState(type, stream, state);
+  }
+
+  function attrState(type, _stream, state) {
+    if (type == "word") {
+      setStyle = "attribute";
+      return attrEqState;
+    } else if (type == "endTag" || type == "selfcloseTag") {
+      var tagName = state.tagName, tagStart = state.tagStart;
+      state.tagName = state.tagStart = null;
+      if (type == "selfcloseTag" ||
+          config.autoSelfClosers.hasOwnProperty(tagName)) {
+        maybePopContext(state, tagName);
+      } else {
+        maybePopContext(state, tagName);
+        state.context = new Context(state, tagName, tagStart == state.indented);
+      }
+      return baseState;
+    }
+    setStyle = "error";
+    return attrState;
+  }
+  function attrEqState(type, stream, state) {
+    if (type == "equals") return attrValueState;
+    if (!config.allowMissing) setStyle = "error";
+    return attrState(type, stream, state);
+  }
+  function attrValueState(type, stream, state) {
+    if (type == "string") return attrContinuedState;
+    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
+    setStyle = "error";
+    return attrState(type, stream, state);
+  }
+  function attrContinuedState(type, stream, state) {
+    if (type == "string") return attrContinuedState;
+    return attrState(type, stream, state);
+  }
+
+  return {
+    startState: function(baseIndent) {
+      var state = {tokenize: inText,
+                   state: baseState,
+                   indented: baseIndent || 0,
+                   tagName: null, tagStart: null,
+                   context: null}
+      if (baseIndent != null) state.baseIndent = baseIndent
+      return state
+    },
+
+    token: function(stream, state) {
+      if (!state.tagName && stream.sol())
+        state.indented = stream.indentation();
+
+      if (stream.eatSpace()) return null;
+      type = null;
+      var style = state.tokenize(stream, state);
+      if ((style || type) && style != "comment") {
+        setStyle = null;
+        state.state = state.state(type || style, stream, state);
+        if (setStyle)
+          style = setStyle == "error" ? style + " error" : setStyle;
+      }
+      return style;
+    },
+
+    indent: function(state, textAfter, fullLine) {
+      var context = state.context;
+      // Indent multi-line strings (e.g. css).
+      if (state.tokenize.isInAttribute) {
+        if (state.tagStart == state.indented)
+          return state.stringStartCol + 1;
+        else
+          return state.indented + indentUnit;
+      }
+      if (context && context.noIndent) return CodeMirror.Pass;
+      if (state.tokenize != inTag && state.tokenize != inText)
+        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+      // Indent the starts of attribute names.
+      if (state.tagName) {
+        if (config.multilineTagIndentPastTag !== false)
+          return state.tagStart + state.tagName.length + 2;
+        else
+          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
+      }
+      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
+      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
+      if (tagAfter && tagAfter[1]) { // Closing tag spotted
+        while (context) {
+          if (context.tagName == tagAfter[2]) {
+            context = context.prev;
+            break;
+          } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
+            context = context.prev;
+          } else {
+            break;
+          }
+        }
+      } else if (tagAfter) { // Opening tag spotted
+        while (context) {
+          var grabbers = config.contextGrabbers[context.tagName];
+          if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
+            context = context.prev;
+          else
+            break;
+        }
+      }
+      while (context && context.prev && !context.startOfLine)
+        context = context.prev;
+      if (context) return context.indent + indentUnit;
+      else return state.baseIndent || 0;
+    },
+
+    electricInput: /<\/[\s\w:]+>$/,
+    blockCommentStart: "<!--",
+    blockCommentEnd: "-->",
+
+    configuration: config.htmlMode ? "html" : "xml",
+    helperType: config.htmlMode ? "html" : "xml",
+
+    skipAttribute: function(state) {
+      if (state.state == attrValueState)
+        state.state = attrState
+    },
+
+    xmlCurrentTag: function(state) {
+      return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
+    },
+
+    xmlCurrentContext: function(state) {
+      var context = []
+      for (var cx = state.context; cx; cx = cx.prev)
+        if (cx.tagName) context.push(cx.tagName)
+      return context.reverse()
+    }
+  };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
+
+});
diff --git a/dist/firephp.build/lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/babel-regenerator-runtime.js b/dist/firephp.build/lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/babel-regenerator-runtime.js
new file mode 100644
index 0000000..4da0b72
--- /dev/null
+++ b/dist/firephp.build/lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/babel-regenerator-runtime.js
@@ -0,0 +1,581 @@
+((function (require, exports, module) {
+var bundle = { require: require, exports: exports, module: module };
+
+(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+"use strict";
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var runtime = function (exports) {
+  "use strict";
+
+  var Op = Object.prototype;
+  var hasOwn = Op.hasOwnProperty;
+  var undefined;
+  var $Symbol = typeof Symbol === "function" ? Symbol : {};
+  var iteratorSymbol = $Symbol.iterator || "@@iterator";
+  var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
+  var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
+
+  function wrap(innerFn, outerFn, self, tryLocsList) {
+    var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
+    var generator = Object.create(protoGenerator.prototype);
+    var context = new Context(tryLocsList || []);
+    generator._invoke = makeInvokeMethod(innerFn, self, context);
+    return generator;
+  }
+
+  exports.wrap = wrap;
+
+  function tryCatch(fn, obj, arg) {
+    try {
+      return {
+        type: "normal",
+        arg: fn.call(obj, arg)
+      };
+    } catch (err) {
+      return {
+        type: "throw",
+        arg: err
+      };
+    }
+  }
+
+  var GenStateSuspendedStart = "suspendedStart";
+  var GenStateSuspendedYield = "suspendedYield";
+  var GenStateExecuting = "executing";
+  var GenStateCompleted = "completed";
+  var ContinueSentinel = {};
+
+  function Generator() {}
+
+  function GeneratorFunction() {}
+
+  function GeneratorFunctionPrototype() {}
+
+  var IteratorPrototype = {};
+
+  IteratorPrototype[iteratorSymbol] = function () {
+    return this;
+  };
+
+  var getProto = Object.getPrototypeOf;
+  var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
+
+  if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
+    IteratorPrototype = NativeIteratorPrototype;
+  }
+
+  var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
+  GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
+  GeneratorFunctionPrototype.constructor = GeneratorFunction;
+  GeneratorFunctionPrototype[toStringTagSymbol] = GeneratorFunction.displayName = "GeneratorFunction";
+
+  function defineIteratorMethods(prototype) {
+    ["next", "throw", "return"].forEach(function (method) {
+      prototype[method] = function (arg) {
+        return this._invoke(method, arg);
+      };
+    });
+  }
+
+  exports.isGeneratorFunction = function (genFun) {
+    var ctor = typeof genFun === "function" && genFun.constructor;
+    return ctor ? ctor === GeneratorFunction || (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
+  };
+
+  exports.mark = function (genFun) {
+    if (Object.setPrototypeOf) {
+      Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
+    } else {
+      genFun.__proto__ = GeneratorFunctionPrototype;
+
+      if (!(toStringTagSymbol in genFun)) {
+        genFun[toStringTagSymbol] = "GeneratorFunction";
+      }
+    }
+
+    genFun.prototype = Object.create(Gp);
+    return genFun;
+  };
+
+  exports.awrap = function (arg) {
+    return {
+      __await: arg
+    };
+  };
+
+  function AsyncIterator(generator) {
+    function invoke(method, arg, resolve, reject) {
+      var record = tryCatch(generator[method], generator, arg);
+
+      if (record.type === "throw") {
+        reject(record.arg);
+      } else {
+        var result = record.arg;
+        var value = result.value;
+
+        if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
+          return Promise.resolve(value.__await).then(function (value) {
+            invoke("next", value, resolve, reject);
+          }, function (err) {
+            invoke("throw", err, resolve, reject);
+          });
+        }
+
+        return Promise.resolve(value).then(function (unwrapped) {
+          result.value = unwrapped;
+          resolve(result);
+        }, function (error) {
+          return invoke("throw", error, resolve, reject);
+        });
+      }
+    }
+
+    var previousPromise;
+
+    function enqueue(method, arg) {
+      function callInvokeWithMethodAndArg() {
+        return new Promise(function (resolve, reject) {
+          invoke(method, arg, resolve, reject);
+        });
+      }
+
+      return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
+    }
+
+    this._invoke = enqueue;
+  }
+
+  defineIteratorMethods(AsyncIterator.prototype);
+
+  AsyncIterator.prototype[asyncIteratorSymbol] = function () {
+    return this;
+  };
+
+  exports.AsyncIterator = AsyncIterator;
+
+  exports.async = function (innerFn, outerFn, self, tryLocsList) {
+    var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList));
+    return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) {
+      return result.done ? result.value : iter.next();
+    });
+  };
+
+  function makeInvokeMethod(innerFn, self, context) {
+    var state = GenStateSuspendedStart;
+    return function invoke(method, arg) {
+      if (state === GenStateExecuting) {
+        throw new Error("Generator is already running");
+      }
+
+      if (state === GenStateCompleted) {
+        if (method === "throw") {
+          throw arg;
+        }
+
+        return doneResult();
+      }
+
+      context.method = method;
+      context.arg = arg;
+
+      while (true) {
+        var delegate = context.delegate;
+
+        if (delegate) {
+          var delegateResult = maybeInvokeDelegate(delegate, context);
+
+          if (delegateResult) {
+            if (delegateResult === ContinueSentinel) continue;
+            return delegateResult;
+          }
+        }
+
+        if (context.method === "next") {
+          context.sent = context._sent = context.arg;
+        } else if (context.method === "throw") {
+          if (state === GenStateSuspendedStart) {
+            state = GenStateCompleted;
+            throw context.arg;
+          }
+
+          context.dispatchException(context.arg);
+        } else if (context.method === "return") {
+          context.abrupt("return", context.arg);
+        }
+
+        state = GenStateExecuting;
+        var record = tryCatch(innerFn, self, context);
+
+        if (record.type === "normal") {
+          state = context.done ? GenStateCompleted : GenStateSuspendedYield;
+
+          if (record.arg === ContinueSentinel) {
+            continue;
+          }
+
+          return {
+            value: record.arg,
+            done: context.done
+          };
+        } else if (record.type === "throw") {
+          state = GenStateCompleted;
+          context.method = "throw";
+          context.arg = record.arg;
+        }
+      }
+    };
+  }
+
+  function maybeInvokeDelegate(delegate, context) {
+    var method = delegate.iterator[context.method];
+
+    if (method === undefined) {
+      context.delegate = null;
+
+      if (context.method === "throw") {
+        if (delegate.iterator["return"]) {
+          context.method = "return";
+          context.arg = undefined;
+          maybeInvokeDelegate(delegate, context);
+
+          if (context.method === "throw") {
+            return ContinueSentinel;
+          }
+        }
+
+        context.method = "throw";
+        context.arg = new TypeError("The iterator does not provide a 'throw' method");
+      }
+
+      return ContinueSentinel;
+    }
+
+    var record = tryCatch(method, delegate.iterator, context.arg);
+
+    if (record.type === "throw") {
+      context.method = "throw";
+      context.arg = record.arg;
+      context.delegate = null;
+      return ContinueSentinel;
+    }
+
+    var info = record.arg;
+
+    if (!info) {
+      context.method = "throw";
+      context.arg = new TypeError("iterator result is not an object");
+      context.delegate = null;
+      return ContinueSentinel;
+    }
+
+    if (info.done) {
+      context[delegate.resultName] = info.value;
+      context.next = delegate.nextLoc;
+
+      if (context.method !== "return") {
+        context.method = "next";
+        context.arg = undefined;
+      }
+    } else {
+      return info;
+    }
+
+    context.delegate = null;
+    return ContinueSentinel;
+  }
+
+  defineIteratorMethods(Gp);
+  Gp[toStringTagSymbol] = "Generator";
+
+  Gp[iteratorSymbol] = function () {
+    return this;
+  };
+
+  Gp.toString = function () {
+    return "[object Generator]";
+  };
+
+  function pushTryEntry(locs) {
+    var entry = {
+      tryLoc: locs[0]
+    };
+
+    if (1 in locs) {
+      entry.catchLoc = locs[1];
+    }
+
+    if (2 in locs) {
+      entry.finallyLoc = locs[2];
+      entry.afterLoc = locs[3];
+    }
+
+    this.tryEntries.push(entry);
+  }
+
+  function resetTryEntry(entry) {
+    var record = entry.completion || {};
+    record.type = "normal";
+    delete record.arg;
+    entry.completion = record;
+  }
+
+  function Context(tryLocsList) {
+    this.tryEntries = [{
+      tryLoc: "root"
+    }];
+    tryLocsList.forEach(pushTryEntry, this);
+    this.reset(true);
+  }
+
+  exports.keys = function (object) {
+    var keys = [];
+
+    for (var key in object) {
+      keys.push(key);
+    }
+
+    keys.reverse();
+    return function next() {
+      while (keys.length) {
+        var key = keys.pop();
+
+        if (key in object) {
+          next.value = key;
+          next.done = false;
+          return next;
+        }
+      }
+
+      next.done = true;
+      return next;
+    };
+  };
+
+  function values(iterable) {
+    if (iterable) {
+      var iteratorMethod = iterable[iteratorSymbol];
+
+      if (iteratorMethod) {
+        return iteratorMethod.call(iterable);
+      }
+
+      if (typeof iterable.next === "function") {
+        return iterable;
+      }
+
+      if (!isNaN(iterable.length)) {
+        var i = -1,
+            next = function next() {
+          while (++i < iterable.length) {
+            if (hasOwn.call(iterable, i)) {
+              next.value = iterable[i];
+              next.done = false;
+              return next;
+            }
+          }
+
+          next.value = undefined;
+          next.done = true;
+          return next;
+        };
+
+        return next.next = next;
+      }
+    }
+
+    return {
+      next: doneResult
+    };
+  }
+
+  exports.values = values;
+
+  function doneResult() {
+    return {
+      value: undefined,
+      done: true
+    };
+  }
+
+  Context.prototype = {
+    constructor: Context,
+    reset: function reset(skipTempReset) {
+      this.prev = 0;
+      this.next = 0;
+      this.sent = this._sent = undefined;
+      this.done = false;
+      this.delegate = null;
+      this.method = "next";
+      this.arg = undefined;
+      this.tryEntries.forEach(resetTryEntry);
+
+      if (!skipTempReset) {
+        for (var name in this) {
+          if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
+            this[name] = undefined;
+          }
+        }
+      }
+    },
+    stop: function stop() {
+      this.done = true;
+      var rootEntry = this.tryEntries[0];
+      var rootRecord = rootEntry.completion;
+
+      if (rootRecord.type === "throw") {
+        throw rootRecord.arg;
+      }
+
+      return this.rval;
+    },
+    dispatchException: function dispatchException(exception) {
+      if (this.done) {
+        throw exception;
+      }
+
+      var context = this;
+
+      function handle(loc, caught) {
+        record.type = "throw";
+        record.arg = exception;
+        context.next = loc;
+
+        if (caught) {
+          context.method = "next";
+          context.arg = undefined;
+        }
+
+        return !!caught;
+      }
+
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+        var record = entry.completion;
+
+        if (entry.tryLoc === "root") {
+          return handle("end");
+        }
+
+        if (entry.tryLoc <= this.prev) {
+          var hasCatch = hasOwn.call(entry, "catchLoc");
+          var hasFinally = hasOwn.call(entry, "finallyLoc");
+
+          if (hasCatch && hasFinally) {
+            if (this.prev < entry.catchLoc) {
+              return handle(entry.catchLoc, true);
+            } else if (this.prev < entry.finallyLoc) {
+              return handle(entry.finallyLoc);
+            }
+          } else if (hasCatch) {
+            if (this.prev < entry.catchLoc) {
+              return handle(entry.catchLoc, true);
+            }
+          } else if (hasFinally) {
+            if (this.prev < entry.finallyLoc) {
+              return handle(entry.finallyLoc);
+            }
+          } else {
+            throw new Error("try statement without catch or finally");
+          }
+        }
+      }
+    },
+    abrupt: function abrupt(type, arg) {
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+
+        if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
+          var finallyEntry = entry;
+          break;
+        }
+      }
+
+      if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
+        finallyEntry = null;
+      }
+
+      var record = finallyEntry ? finallyEntry.completion : {};
+      record.type = type;
+      record.arg = arg;
+
+      if (finallyEntry) {
+        this.method = "next";
+        this.next = finallyEntry.finallyLoc;
+        return ContinueSentinel;
+      }
+
+      return this.complete(record);
+    },
+    complete: function complete(record, afterLoc) {
+      if (record.type === "throw") {
+        throw record.arg;
+      }
+
+      if (record.type === "break" || record.type === "continue") {
+        this.next = record.arg;
+      } else if (record.type === "return") {
+        this.rval = this.arg = record.arg;
+        this.method = "return";
+        this.next = "end";
+      } else if (record.type === "normal" && afterLoc) {
+        this.next = afterLoc;
+      }
+
+      return ContinueSentinel;
+    },
+    finish: function finish(finallyLoc) {
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+
+        if (entry.finallyLoc === finallyLoc) {
+          this.complete(entry.completion, entry.afterLoc);
+          resetTryEntry(entry);
+          return ContinueSentinel;
+        }
+      }
+    },
+    "catch": function _catch(tryLoc) {
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+
+        if (entry.tryLoc === tryLoc) {
+          var record = entry.completion;
+
+          if (record.type === "throw") {
+            var thrown = record.arg;
+            resetTryEntry(entry);
+          }
+
+          return thrown;
+        }
+      }
+
+      throw new Error("illegal catch attempt");
+    },
+    delegateYield: function delegateYield(iterable, resultName, nextLoc) {
+      this.delegate = {
+        iterator: values(iterable),
+        resultName: resultName,
+        nextLoc: nextLoc
+      };
+
+      if (this.method === "next") {
+        this.arg = undefined;
+      }
+
+      return ContinueSentinel;
+    }
+  };
+  return exports;
+}((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" ? module.exports : {});
+
+try {
+  regeneratorRuntime = runtime;
+} catch (accidentalStrictMode) {
+  Function("r", "regeneratorRuntime = r")(runtime);
+}
+},{}]},{},[1]);
+
+})((typeof require !== "undefined" && require) || undefined, (typeof exports !== "undefined" && exports) || undefined, (typeof module !== "undefined" && module) || undefined))
\ No newline at end of file
diff --git a/dist/firephp.build/lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/pinf-loader-core.browser.js b/dist/firephp.build/lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/pinf-loader-core.browser.js
new file mode 100644
index 0000000..017e194
--- /dev/null
+++ b/dist/firephp.build/lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/pinf-loader-core.browser.js
@@ -0,0 +1,790 @@
+((function (_require, _exports, _module) {
+var bundle = { require: _require, exports: _exports, module: _module };
+var exports = undefined;
+var module = undefined;
+var define = function (deps, init) {
+var exports = init();
+[["PINF","PINF"]].forEach(function (expose) {
+if (typeof window !== "undefined") {
+window[expose[0]] = exports[expose[1]];
+} else if (typeof self !== "undefined") {
+self[expose[0]] = exports[expose[1]];
+}
+});
+}; define.amd = true;
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+"use strict";
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+exports.PINF = function (global) {
+  if (!global || _typeof(global) !== "object") {
+    throw new Error("No root object scope provided!");
+  }
+
+  if (typeof global.PINF !== "undefined") {
+    return global.PINF;
+  }
+
+  var LOADER = require('./loader');
+
+  var PINF = LOADER.Loader({
+    document: global.document
+  });
+  global.PINF = PINF;
+
+  if (typeof global.addEventListener === "function") {
+    global.addEventListener("message", function (event) {
+      var m = null;
+
+      if (typeof event.data === "string" && (m = event.data.match(/^notify:\/\/pinf-loader-js\/sandbox\/load\?uri=(.+)$/)) && (m = decodeURIComponent(m[1])) && /^\/[^\/]/.test(m)) {
+        return PINF.sandbox(m, function (sandbox) {
+          sandbox.main();
+
+          if (typeof global.postMessage === "function") {
+            global.postMessage(event.data.replace("/load?", "/loaded?"));
+          }
+        }, function (err) {
+          throw err;
+        });
+      }
+    }, false);
+  }
+
+  return global.PINF;
+}(typeof window !== "undefined" ? window : typeof self !== "undefined" ? self : null);
+},{"./loader":2}],2:[function(require,module,exports){
+"use strict";
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+(function (exports) {
+  var Loader = function Loader(global) {
+    var loadedBundles = [],
+        readyStates = {
+      'loaded': 1,
+      'interactive': 1,
+      'complete': 1
+    },
+        lastModule = null,
+        headTag = null;
+
+    function keys(obj) {
+      var keys = [];
+
+      for (var key in obj) {
+        keys.push(key);
+      }
+
+      return keys;
+    }
+
+    function create(proto) {
+      function F() {}
+
+      F.prototype = proto;
+      return new F();
+    }
+
+    ;
+
+    function normalizeSandboxArguments(implementation) {
+      return function (programIdentifier, options, loadedCallback, errorCallback) {
+        if (typeof options === "function" && _typeof(loadedCallback) === "object") {
+          throw new Error("Callback before options for `require.sandbox(programIdentifier, options, loadedCallback)`");
+        }
+
+        if (typeof options === "function" && !loadedCallback && !errorCallback) {
+          loadedCallback = options;
+          options = {};
+        } else if (typeof options === "function" && typeof loadedCallback === "function" && !errorCallback) {
+          errorCallback = loadedCallback;
+          loadedCallback = options;
+          options = {};
+        } else {
+          options = options || {};
+        }
+
+        implementation(programIdentifier, options, loadedCallback, errorCallback);
+      };
+    }
+
+    function loadInBrowser(uri, loadedCallback) {
+      try {
+        if (typeof importScripts !== "undefined") {
+          importScripts(uri.replace(/^\/?\{host\}/, ""));
+          return loadedCallback(null);
+        }
+
+        var document = global.document;
+
+        if (!document) {
+          throw new Error("Unable to get reference to 'document'!");
+        }
+
+        var location = document.location;
+
+        if (/^\/?\{host\}\//.test(uri)) {
+          uri = location.protocol + "//" + location.host + uri.replace(/^\/?\{host\}/, "");
+        } else if (/^\/\//.test(uri)) {
+          uri = location.protocol + uri;
+        }
+
+        if (!headTag) {
+          headTag = document.getElementsByTagName("head")[0];
+        }
+
+        var element = document.createElement("script");
+        element.type = "text/javascript";
+
+        element.onload = element.onreadystatechange = function (ev) {
+          ev = ev || global.event;
+
+          if (ev.type === "load" || readyStates[this.readyState]) {
+            this.onload = this.onreadystatechange = this.onerror = null;
+            loadedCallback(null, function () {
+              element.parentNode.removeChild(element);
+            });
+          }
+        };
+
+        element.onerror = function (err) {
+          console.error(err);
+          return loadedCallback(new Error("Error loading '" + uri + "'"));
+        };
+
+        element.charset = "utf-8";
+        element.async = true;
+        element.src = uri;
+        element = headTag.insertBefore(element, headTag.firstChild);
+      } catch (err) {
+        loadedCallback(err);
+      }
+    }
+
+    var Sandbox = function Sandbox(sandboxIdentifier, sandboxOptions, loadedCallback) {
+      var moduleInitializers = {},
+          initializedModules = {},
+          bundleIdentifiers = {},
+          packages = {},
+          loadingBundles = {};
+      var sandbox = {
+        id: sandboxIdentifier
+      };
+
+      function logDebug() {
+        if (sandboxOptions.debug !== true) return;
+
+        if (arguments.length === 1) {
+          console.log(arguments[0]);
+        } else if (arguments.length === 2) {
+            console.log(arguments[0], arguments[1]);
+          } else if (arguments.length === 3) {
+              console.log(arguments[0], arguments[1], arguments[2]);
+            } else if (arguments.length === 4) {
+                console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
+              }
+      }
+
+      function rebaseUri(uri) {
+        if (!sandboxOptions.baseUrl) {
+          return uri;
+        }
+
+        return sandboxOptions.baseUrl + "/" + uri;
+      }
+
+      function load(bundleIdentifier, packageIdentifier, bundleSubPath, loadedCallback) {
+        var loadSandboxIdentifier = sandboxIdentifier;
+        var finalBundleIdentifier = null;
+        var moduleIdentifierPrefix = "";
+        var finalPackageIdentifier = "";
+
+        try {
+          if (packageIdentifier !== "") {
+            if (/^@bundle:/.test(packageIdentifier)) {
+              var absPackageIdentifier = packageIdentifier;
+
+              if (/^@bundle:\./.test(absPackageIdentifier)) {
+                absPackageIdentifier = absPackageIdentifier.replace(/^(@bundle:)\./, "$1" + sandboxIdentifier + "/.");
+              }
+
+              moduleIdentifierPrefix = packageIdentifier;
+              finalPackageIdentifier = packageIdentifier;
+              bundleIdentifier = absPackageIdentifier.replace(/^@bundle:/, "") + ".js";
+              loadSandboxIdentifier = "";
+              finalBundleIdentifier = "@bundle:" + packageIdentifier.replace(/^@bundle:/, "") + ".js";
+            } else {
+              bundleIdentifier = ("/" + packageIdentifier + "/" + bundleIdentifier).replace(/\/+/g, "/");
+            }
+          }
+
+          if (initializedModules[bundleIdentifier]) {
+            loadedCallback(null, sandbox);
+          } else {
+            if (loadingBundles[bundleIdentifier]) {
+              loadingBundles[bundleIdentifier].push(loadedCallback);
+            } else {
+              loadingBundles[bundleIdentifier] = [];
+              bundleIdentifier = (loadSandboxIdentifier + bundleSubPath + bundleIdentifier).replace(/\/$/, ".js");
+              bundleIdentifier = bundleIdentifier.replace(/\.php\.js$/, ".php");
+
+              if (!finalBundleIdentifier) {
+                finalBundleIdentifier = bundleIdentifier;
+              }
+
+              (sandboxOptions.rootBundleLoader || sandboxOptions.load || loadInBrowser)(rebaseUri(bundleIdentifier), function (err, cleanupCallback) {
+                if (err) return loadedCallback(err);
+                delete sandboxOptions.rootBundleLoader;
+                finalizeLoad(moduleIdentifierPrefix, finalBundleIdentifier, finalPackageIdentifier, function () {
+                  loadedCallback(null, sandbox);
+
+                  if (cleanupCallback) {
+                    cleanupCallback();
+                  }
+                });
+              });
+            }
+          }
+        } catch (err) {
+          loadedCallback(err);
+        }
+      }
+
+      function finalizeLoad(moduleIdentifierPrefix, bundleIdentifier, packageIdentifier, loadFinalized) {
+        var pending = 0;
+
+        function finalize() {
+          if (pending !== 0) {
+            return;
+          }
+
+          if (loadFinalized) loadFinalized();
+        }
+
+        pending += 1;
+
+        if (!loadedBundles[0]) {
+          throw new Error("No bundle memoized for '" + bundleIdentifier + "'! Check the file to ensure it contains JavaScript and that a bundle is memoized against the correct loader instance.");
+        }
+
+        bundleIdentifiers[bundleIdentifier] = loadedBundles[0][0];
+        var loadedModuleInitializers = loadedBundles[0][1]({
+          id: sandboxIdentifier
+        });
+        var key;
+
+        for (key in loadedModuleInitializers) {
+          var memoizeKey = moduleIdentifierPrefix + key;
+
+          if (/^[^\/]*\/package.json$/.test(key)) {
+            if (sandboxOptions.rewritePackageDescriptor) {
+              loadedModuleInitializers[key][0] = sandboxOptions.rewritePackageDescriptor(loadedModuleInitializers[key][0], memoizeKey);
+            }
+
+            if (loadedModuleInitializers[key][0].mappings) {
+              for (var alias in loadedModuleInitializers[key][0].mappings) {
+                if (/^@script:\/\//.test(loadedModuleInitializers[key][0].mappings[alias])) {
+                  pending += 1;
+                  loadInBrowser(rebaseUri(loadedModuleInitializers[key][0].mappings[alias].replace(/^@script:/, "")), function () {
+                    pending -= 1;
+                    finalize();
+                  });
+                }
+              }
+            }
+
+            if (moduleInitializers[memoizeKey]) {
+              moduleInitializers[memoizeKey][0] = bundleIdentifier;
+
+              if (typeof moduleInitializers[memoizeKey][1].main === "undefined") {
+                moduleInitializers[memoizeKey][1].main = loadedModuleInitializers[key][0].main;
+              }
+
+              if (loadedModuleInitializers[key][0].mappings) {
+                if (!moduleInitializers[memoizeKey][1].mappings) {
+                  moduleInitializers[memoizeKey][1].mappings = {};
+                }
+
+                for (var alias in loadedModuleInitializers[key][0].mappings) {
+                  if (typeof moduleInitializers[memoizeKey][1].mappings[alias] === "undefined") {
+                    moduleInitializers[memoizeKey][1].mappings[alias] = loadedModuleInitializers[key][0].mappings[alias];
+                  }
+                }
+              }
+            } else {
+              moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+            }
+
+            var packageIdentifier = packageIdentifier || key.split("/").shift();
+
+            if (packages[packageIdentifier]) {
+              packages[packageIdentifier].init();
+            }
+          }
+
+          if (typeof moduleInitializers[memoizeKey] === "undefined") {
+            moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+          }
+        }
+
+        loadedBundles.shift();
+        pending -= 1;
+        finalize();
+        return;
+      }
+
+      var Package = function Package(packageIdentifier) {
+        if (packages[packageIdentifier]) {
+          return packages[packageIdentifier];
+        }
+
+        var pkg = {
+          id: packageIdentifier,
+          descriptor: {},
+          main: "/main.js",
+          mappings: {},
+          directories: {},
+          libPath: ""
+        };
+        var parentModule = lastModule;
+
+        pkg.init = function () {
+          var descriptor = moduleInitializers[packageIdentifier + "/package.json"] && moduleInitializers[packageIdentifier + "/package.json"][1] || {};
+
+          if (descriptor) {
+            pkg.descriptor = descriptor;
+
+            if (typeof descriptor.main === "string") {
+              pkg.main = descriptor.main;
+            }
+
+            pkg.mappings = descriptor.mappings || pkg.mappings;
+            pkg.directories = descriptor.directories || pkg.directories;
+            pkg.libPath = typeof pkg.directories.lib !== "undefined" && pkg.directories.lib != "" ? pkg.directories.lib + "/" : pkg.libPath;
+          }
+        };
+
+        pkg.init();
+
+        function normalizeIdentifier(identifier) {
+          if (identifier.split("/").pop().indexOf(".") === -1) {
+            identifier = identifier + ".js";
+          } else if (!/^\//.test(identifier)) {
+            identifier = "/" + identifier;
+          }
+
+          return identifier;
+        }
+
+        var Module = function Module(moduleIdentifier, parentModule) {
+          var moduleIdentifierSegment = null;
+
+          if (/^@bundle:/.test(moduleIdentifier)) {
+            moduleIdentifierSegment = moduleIdentifier.replace(packageIdentifier, "").replace(/\/[^\/]*$/, "").split("/");
+          } else {
+            moduleIdentifierSegment = moduleIdentifier.replace(/\/[^\/]*$/, "").split("/");
+          }
+
+          var module = {
+            id: moduleIdentifier,
+            exports: {},
+            parentModule: parentModule,
+            bundle: null,
+            pkg: packageIdentifier
+          };
+
+          function resolveIdentifier(identifier) {
+            if (/\/$/.test(identifier)) {
+              identifier += "index";
+            }
+
+            lastModule = module;
+            var plugin = null;
+
+            if (/^[^!]*!/.test(identifier)) {
+              var m = identifier.match(/^([^!]*)!(.+)$/);
+              identifier = m[2];
+              plugin = m[1];
+            }
+
+            function pluginify(id) {
+              if (!plugin) return id;
+              id = new String(id);
+              id.plugin = plugin;
+              return id;
+            }
+
+            if (/^\./.test(identifier)) {
+              var segments = identifier.replace(/^\.\//, "").split("../");
+              identifier = "/" + moduleIdentifierSegment.slice(1, moduleIdentifierSegment.length - segments.length + 1).concat(segments[segments.length - 1]).join("/");
+
+              if (identifier === "/.") {
+                return [pkg, pluginify("")];
+              }
+
+              return [pkg, pluginify(normalizeIdentifier(identifier.replace(/\/\.$/, "/")))];
+            }
+
+            var splitIdentifier = identifier.split("/");
+
+            if (typeof pkg.mappings[splitIdentifier[0]] !== "undefined") {
+              return [Package(pkg.mappings[splitIdentifier[0]]), pluginify(splitIdentifier.length > 1 ? normalizeIdentifier(splitIdentifier.slice(1).join("/")) : "")];
+            }
+
+            if (!moduleInitializers["/" + normalizeIdentifier(identifier)]) {
+              throw new Error("Descriptor for package '" + pkg.id + "' in sandbox '" + sandbox.id + "' does not declare 'mappings[\"" + splitIdentifier[0] + "\"]' property nor does sandbox have module memoized at '" + "/" + normalizeIdentifier(identifier) + "' needed to satisfy module path '" + identifier + "' in module '" + moduleIdentifier + "'!");
+            }
+
+            return [Package(""), pluginify("/" + normalizeIdentifier(identifier))];
+          }
+
+          module.require = function (identifier) {
+            identifier = resolveIdentifier(identifier);
+            return identifier[0].require(identifier[1]).exports;
+          };
+
+          module.require.supports = ["ucjs-pinf-0"];
+
+          module.require.id = function (identifier) {
+            identifier = resolveIdentifier(identifier);
+            return identifier[0].require.id(identifier[1]);
+          };
+
+          module.require.async = function (identifier, loadedCallback, errorCallback) {
+            identifier = resolveIdentifier(identifier);
+            var mi = moduleIdentifier;
+
+            if (/^\//.test(identifier[0].id)) {
+              mi = "/main.js";
+            }
+
+            identifier[0].load(identifier[1], module.bundle, function (err, moduleAPI) {
+              if (err) {
+                if (errorCallback) return errorCallback(err);
+                throw err;
+              }
+
+              loadedCallback(moduleAPI);
+            });
+          };
+
+          module.require.sandbox = normalizeSandboxArguments(function (programIdentifier, options, loadedCallback, errorCallback) {
+            options.load = options.load || sandboxOptions.load;
+
+            if (/^\./.test(programIdentifier)) {
+              programIdentifier = sandboxIdentifier + "/" + programIdentifier;
+              programIdentifier = programIdentifier.replace(/\/\.\//g, "/");
+            }
+
+            return PINF.sandbox(programIdentifier, options, loadedCallback, errorCallback);
+          });
+          module.require.sandbox.id = sandboxIdentifier;
+
+          module.load = function () {
+            module.bundle = moduleInitializers[moduleIdentifier][0];
+
+            if (typeof moduleInitializers[moduleIdentifier][1] === "function") {
+              var moduleInterface = {
+                id: module.id,
+                filename: moduleInitializers[moduleIdentifier][2].filename || (module.bundle.replace(/\.js$/, "") + "/" + module.id).replace(/\/+/g, "/"),
+                exports: {}
+              };
+
+              if (packageIdentifier === "" && pkg.main === moduleIdentifier) {
+                module.require.main = moduleInterface;
+              }
+
+              if (sandboxOptions.onInitModule) {
+                sandboxOptions.onInitModule(moduleInterface, module, pkg, sandbox, {
+                  normalizeIdentifier: normalizeIdentifier,
+                  resolveIdentifier: resolveIdentifier,
+                  finalizeLoad: finalizeLoad,
+                  moduleInitializers: moduleInitializers,
+                  initializedModules: initializedModules
+                });
+              }
+
+              var exports = moduleInitializers[moduleIdentifier][1].call(exports, module.require, module.exports, moduleInterface);
+
+              if (typeof moduleInterface.exports !== "undefined" && (_typeof(moduleInterface.exports) !== "object" || keys(moduleInterface.exports).length !== 0)) {
+                module.exports = moduleInterface.exports;
+              } else if (typeof exports !== "undefined") {
+                module.exports = exports;
+              }
+            } else if (typeof moduleInitializers[moduleIdentifier][1] === "string") {
+              module.exports = decodeURIComponent(moduleInitializers[moduleIdentifier][1]);
+            } else {
+              module.exports = moduleInitializers[moduleIdentifier][1];
+            }
+          };
+
+          module.getReport = function () {
+            var exportsCount = 0,
+                key;
+
+            for (key in module.exports) {
+              exportsCount++;
+            }
+
+            return {
+              exports: exportsCount
+            };
+          };
+
+          return module;
+        };
+
+        pkg.load = function (moduleIdentifier, bundleIdentifier, loadedCallback) {
+          if (moduleInitializers[packageIdentifier + (moduleIdentifier || pkg.main)]) {
+            return loadedCallback(null, pkg.require(moduleIdentifier).exports);
+          }
+
+          var bundleSubPath = bundleIdentifier.substring(sandboxIdentifier.length);
+          load((!/^\//.test(moduleIdentifier) ? "/" + pkg.libPath : "") + moduleIdentifier, packageIdentifier, bundleSubPath.replace(/\.js$/g, ""), function (err) {
+            if (err) return loadedCallback(err);
+            loadedCallback(null, pkg.require(moduleIdentifier).exports);
+          });
+        };
+
+        pkg.require = function (moduleIdentifier) {
+          var plugin = moduleIdentifier.plugin;
+
+          if (moduleIdentifier) {
+            if (!/^\//.test(moduleIdentifier)) {
+              moduleIdentifier = ("/" + (moduleIdentifier.substring(0, pkg.libPath.length) === pkg.libPath ? "" : pkg.libPath)).replace(/\/\.\//, "/") + moduleIdentifier;
+            }
+
+            moduleIdentifier = packageIdentifier + moduleIdentifier;
+          } else {
+            moduleIdentifier = packageIdentifier + pkg.main;
+          }
+
+          if (!moduleInitializers[moduleIdentifier] && moduleInitializers[moduleIdentifier.replace(/\.js$/, "/index.js")]) {
+            moduleIdentifier = moduleIdentifier.replace(/\.js$/, "/index.js");
+          }
+
+          if (plugin && moduleInitializers[moduleIdentifier + ":" + plugin]) {
+            moduleIdentifier += ":" + plugin;
+          }
+
+          if (!initializedModules[moduleIdentifier]) {
+            if (!moduleInitializers[moduleIdentifier]) {
+              console.error("[pinf-loader-js]", "moduleInitializers", moduleInitializers);
+              throw new Error("Module '" + moduleIdentifier + "' " + (plugin ? "for format '" + plugin + "' " : "") + "not found in sandbox '" + sandbox.id + "'!");
+            }
+
+            (initializedModules[moduleIdentifier] = Module(moduleIdentifier, lastModule)).load();
+          }
+
+          var loadingBundlesCallbacks;
+
+          if (loadingBundles[moduleIdentifier]) {
+            loadingBundlesCallbacks = loadingBundles[moduleIdentifier];
+            delete loadingBundles[moduleIdentifier];
+
+            for (var i = 0; i < loadingBundlesCallbacks.length; i++) {
+              loadingBundlesCallbacks[i](null, sandbox);
+            }
+          }
+
+          var moduleInfo = create(initializedModules[moduleIdentifier]);
+
+          if (plugin === "i18n") {
+            moduleInfo.exports = moduleInfo.exports.root;
+          }
+
+          return moduleInfo;
+        };
+
+        pkg.require.id = function (moduleIdentifier) {
+          if (!/^\//.test(moduleIdentifier)) {
+            moduleIdentifier = "/" + pkg.libPath + moduleIdentifier;
+          }
+
+          return ((packageIdentifier !== "" ? "/" + packageIdentifier + "/" : "") + moduleIdentifier).replace(/\/+/g, "/");
+        };
+
+        pkg.getReport = function () {
+          return {
+            main: pkg.main,
+            mappings: pkg.mappings,
+            directories: pkg.directories,
+            libPath: pkg.libPath
+          };
+        };
+
+        if (sandboxOptions.onInitPackage) {
+          sandboxOptions.onInitPackage(pkg, sandbox, {
+            normalizeIdentifier: normalizeIdentifier,
+            finalizeLoad: finalizeLoad,
+            moduleInitializers: moduleInitializers,
+            initializedModules: initializedModules
+          });
+        }
+
+        packages[packageIdentifier] = pkg;
+        return pkg;
+      };
+
+      sandbox.require = function (moduleIdentifier) {
+        return Package("").require(moduleIdentifier).exports;
+      };
+
+      sandbox.boot = function () {
+        if (typeof Package("").main !== "string") {
+          throw new Error("No 'main' property declared in '/package.json' in sandbox '" + sandbox.id + "'!");
+        }
+
+        return sandbox.require(Package("").main);
+      };
+
+      sandbox.main = function () {
+        var exports = sandbox.boot();
+        return exports.main ? exports.main.apply(null, arguments) : exports;
+      };
+
+      sandbox.getReport = function () {
+        var report = {
+          bundles: {},
+          packages: {},
+          modules: {}
+        },
+            key;
+
+        for (key in bundleIdentifiers) {
+          report.bundles[key] = bundleIdentifiers[key];
+        }
+
+        for (key in packages) {
+          report.packages[key] = packages[key].getReport();
+        }
+
+        for (key in moduleInitializers) {
+          if (initializedModules[key]) {
+            report.modules[key] = initializedModules[key].getReport();
+          } else {
+            report.modules[key] = {};
+          }
+        }
+
+        return report;
+      };
+
+      sandbox.reset = function () {
+        moduleInitializers = {};
+        initializedModules = {};
+        bundleIdentifiers = {};
+        packages = {};
+        loadingBundles = {};
+      };
+
+      load(sandboxIdentifier.indexOf("?") === -1 ? ".js" : "", "", "", loadedCallback);
+      return sandbox;
+    };
+
+    var bundleIdentifiers = {},
+        sandboxes = {};
+
+    var Require = function Require(bundle) {
+      var self = this;
+
+      var bundleHandler = function bundleHandler(uid, callback) {
+        if (uid && bundleIdentifiers[uid]) {
+          throw new Error("You cannot split require.bundle(UID) calls where UID is constant!");
+        }
+
+        bundleIdentifiers[uid] = true;
+        loadedBundles.push([uid, function (sandbox) {
+          var moduleInitializers = {},
+              req = new Require(uid);
+          delete req.bundle;
+          req.sandbox = sandbox;
+
+          req.memoize = function (moduleIdentifier, moduleInitializer, moduleMeta) {
+            moduleInitializers[moduleIdentifier + (moduleMeta && moduleMeta.variation ? ":" + moduleMeta.variation : "")] = [moduleInitializer, moduleMeta || {}];
+          };
+
+          callback(req, global || null);
+          return moduleInitializers;
+        }]);
+      };
+
+      var activeBundleHandler = bundleHandler;
+
+      this.bundle = function () {
+        return activeBundleHandler.apply(null, arguments);
+      };
+
+      this.setActiveBundleHandler = function (handler) {
+        var oldHandler = activeBundleHandler;
+        activeBundleHandler = handler;
+        return oldHandler;
+      };
+    };
+
+    var PINF = new Require();
+    PINF.supports = ["ucjs-pinf-0"];
+    PINF.sandbox = normalizeSandboxArguments(function (programIdentifier, options, loadedCallback, errorCallback) {
+      if (typeof programIdentifier === "function") {
+        options = options || {};
+        var bundle = programIdentifier;
+        var fallbackLoad = options.load || loadInBrowser;
+
+        options.load = function (uri, loadedCallback) {
+          if (uri === programIdentifier + ".js") {
+            PINF.bundle("", bundle);
+            loadedCallback(null);
+            return;
+          }
+
+          return fallbackLoad(uri, loadedCallback);
+        };
+
+        programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
+      }
+
+      var sandboxIdentifier = programIdentifier.replace(/\.js$/, "");
+      return sandboxes[sandboxIdentifier] = Sandbox(sandboxIdentifier, options, function (err, sandbox) {
+        if (err) {
+          if (errorCallback) return errorCallback(err);
+          throw err;
+        }
+
+        loadedCallback(sandbox);
+      });
+    });
+    PINF.Loader = Loader;
+
+    PINF.getReport = function () {
+      var report = {
+        sandboxes: {}
+      };
+
+      for (var key in sandboxes) {
+        report.sandboxes[key] = sandboxes[key].getReport();
+      }
+
+      return report;
+    };
+
+    PINF.reset = function () {
+      for (var key in sandboxes) {
+        sandboxes[key].reset();
+      }
+
+      sandboxes = {};
+      bundleIdentifiers = {};
+      loadedBundles = [];
+    };
+
+    return PINF;
+  };
+
+  if (exports) exports.Loader = Loader;
+})(typeof exports !== "undefined" ? exports : null);
+},{}]},{},[1])(1)
+});
+
+})((typeof require !== "undefined" && require) || undefined, (typeof exports !== "undefined" && exports) || undefined, (typeof module !== "undefined" && module) || undefined))
\ No newline at end of file
diff --git a/dist/firephp.build/manifest.json b/dist/firephp.build/manifest.json
index e158d08..8f731cc 100644
--- a/dist/firephp.build/manifest.json
+++ b/dist/firephp.build/manifest.json
@@ -1,11 +1,11 @@
 {
     "manifest_version": 2,
-    "description": "Log from PHP to a devtools panel.",
-    "name": "FirePHP",
-    "version": "1.3.0",
+    "description": "(NOTE: This is a PREVIEW build for branch: preview-buildable) Log from PHP to a devtools panel.",
+    "name": "FirePHP (Official) (branch: preview-buildable)",
+    "version": "${version}pre_1589521107",
     "applications": {
         "gecko": {
-            "id": "FirePHPExtension-Build@firephp.org",
+            "id": "FirePHPExtension-Build_branch_preview-buildable@firephp.org",
             "strict_min_version": "56.0"
         }
     },
diff --git a/dist/firephp.build/run.config.json b/dist/firephp.build/run.config.json
new file mode 100644
index 0000000..d8ff29b
--- /dev/null
+++ b/dist/firephp.build/run.config.json
@@ -0,0 +1,107 @@
+{
+    "homepage": "/",
+    "extension": "/opt/christoph/projects/gi0.FirePHP.org/firephp-for-browser-devtools/dist/firephp.build",
+    "browserConsole": true,
+    "firefoxVersion": "firefox",
+    "verbose": true,
+    "routes": {
+        "^/$": {
+            ".@": "github.com~0ink~codeblock/codeblock:Codeblock",
+            "_code": "\\nreturn function (req, res, next) {\\n    \\n    if (\\nreq.headers[\"x-firephp-version\"] ||\\n/\\sFirePHP\\/([\\.|\\d]*)\\s?/.test(req.headers[\"user-agent\"])\\n    ) {\\n    \\nfunction wrap (message) {\\n    return message.length + '|' + message + '|';\\n}\\n    \\nres.writeHead(200, {\\n    'X-Wf-Protocol-1': 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2',\\n    'X-Wf-1-Plugin-1': 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.0.0master1106021548',\\n    'X-Wf-1-Structure-1': 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1',\\n    \\n    'X-Wf-1-1-1-1': wrap('[{\"Type\":\"LOG\",\"File\":\"/path/to/file\",\"Line\":10},\"Hello World\"]'),\\n    'X-Wf-1-1-1-2': wrap('[{\"Type\":\"INFO\",\"File\":\"\\/christoph\\/projects\\/gi0.FireConsole.org\\/rep.js\\/examples\\/03-FirePHPCore\\/index.php\",\"Line\":75},\"\\\\u0427\\\\u0442\\\\u043e-\\\\u0442\\\\u043e\"]'),\\n    'X-Wf-1-1-1-3': wrap('[{\"Type\":\"INFO\",\"File\":\"\\/christoph\\/projects\\/gi0.FireConsole.org\\/rep.js\\/examples\\/03-FirePHPCore\\/index.php\",\"Line\":76},\"Od\\\\u00f3metro\"]'),\\n    \\n    'X-Wf-1-Index': '3'\\n});\\n    \\n    \\nres.end(\"FirePHP Core formatted messages sent in HTTP response headers.\");\\n    } else {\\n    \\nres.end([\\n    \"<p>No FirePHP HTTP request headers found.</p>\",\\n    \"<p><a href=\\\"http://127.0.0.1:8080/FirePHP.php\\\">http://127.0.0.1:8080/FirePHP.php</a></p>\"\\n].join(\"\\\n\"));\\n    }\\n\\n\\n\\n};",
+            "_format": "javascript",
+            "_args": [
+                "API"
+            ],
+            "_compiled": false
+        },
+        "/scripts/devtools/dist/index.js": {
+            "gi0.PINF.it/build/v0 # /scripts/devtools # /": {
+                "@fireconsole.rep.js # builder/v1": {
+                    "externalizeCss": true,
+                    "indexFilename": "index",
+                    "indexFormat": "js",
+                    "include": {
+                        "jquery": false,
+                        "regenerator-runtime": false,
+                        "riot.csp.js": true,
+                        "riot.js": false,
+                        "riot.min.js": false
+                    },
+                    "page": {
+                        "@layout": {
+                            "console": {
+                                "@console": {
+                                    "@fireconsole": {}
+                                }
+                            },
+                            "menu": {
+                                "@menu": {}
+                            },
+                            "settings": {
+                                "@enabler": {}
+                            },
+                            "inspector": {
+                                "@inspector": {}
+                            },
+                            "editor": {
+                                "@editor": {}
+                            },
+                            "manage": {
+                                "@manage": {
+                                    "settings": {
+                                        "@settings": {}
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "reps": {
+                        "layout": "./src/layout.rep.js",
+                        "menu": "./src/menu.rep.js",
+                        "summary": "./src/summary.rep.js",
+                        "settings": "./src/settings.rep.js",
+                        "manage": "./src/manage.rep.js",
+                        "inspector": "./src/inspector.rep.js",
+                        "editor": "./src/editor.rep.js",
+                        "console": "./src/console.rep.js",
+                        "enabler": "./src/enabler.rep.js"
+                    },
+                    "babel": {
+                        "presets": {
+                            "@babel/preset-env": {
+                                "targets": "last 1 Firefox versions"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "/scripts/background.js": {
+            "gi0.PINF.it/build/v0 # /scripts # /background.js": {
+                "@it.pinf.org.browserify # router/v1": {
+                    "src": "./src/background.js",
+                    "format": "pinf",
+                    "babel": {
+                        "presets": {
+                            "@babel/preset-env": {
+                                "targets": "last 1 Firefox versions"
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    },
+    "port": 9000,
+    "postbuild": {
+        ".@": "github.com~0ink~codeblock/codeblock:Codeblock",
+        "_code": "\\nreturn async function () {\\n};",
+        "_format": "javascript",
+        "_args": [
+            "LIB"
+        ],
+        "_compiled": false
+    },
+    "cwd": "/opt/christoph/projects/gi0.FirePHP.org/firephp-for-browser-devtools",
+    "basedir": "/opt/christoph/projects/gi0.FirePHP.org/firephp-for-browser-devtools"
+}
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/background.js b/dist/firephp.build/scripts/background.js
index 481f4d7..debbcbc 100644
--- a/dist/firephp.build/scripts/background.js
+++ b/dist/firephp.build/scripts/background.js
@@ -9,792 +9,6 @@ _module.exports = init();
        var pmodule = bundle.module;
 
 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
-(function (process){
-/*!
- * EventEmitter2
- * https://github.com/hij1nx/EventEmitter2
- *
- * Copyright (c) 2013 hij1nx
- * Licensed under the MIT license.
- */
-;!function(undefined) {
-
-  var isArray = Array.isArray ? Array.isArray : function _isArray(obj) {
-    return Object.prototype.toString.call(obj) === "[object Array]";
-  };
-  var defaultMaxListeners = 10;
-
-  function init() {
-    this._events = {};
-    if (this._conf) {
-      configure.call(this, this._conf);
-    }
-  }
-
-  function configure(conf) {
-    if (conf) {
-      this._conf = conf;
-
-      conf.delimiter && (this.delimiter = conf.delimiter);
-      this._maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners;
-
-      conf.wildcard && (this.wildcard = conf.wildcard);
-      conf.newListener && (this._newListener = conf.newListener);
-      conf.removeListener && (this._removeListener = conf.removeListener);
-      conf.verboseMemoryLeak && (this.verboseMemoryLeak = conf.verboseMemoryLeak);
-
-      if (this.wildcard) {
-        this.listenerTree = {};
-      }
-    } else {
-      this._maxListeners = defaultMaxListeners;
-    }
-  }
-
-  function logPossibleMemoryLeak(count, eventName) {
-    var errorMsg = '(node) warning: possible EventEmitter memory ' +
-        'leak detected. ' + count + ' listeners added. ' +
-        'Use emitter.setMaxListeners() to increase limit.';
-
-    if(this.verboseMemoryLeak){
-      errorMsg += ' Event name: ' + eventName + '.';
-    }
-
-    if(typeof process !== 'undefined' && process.emitWarning){
-      var e = new Error(errorMsg);
-      e.name = 'MaxListenersExceededWarning';
-      e.emitter = this;
-      e.count = count;
-      process.emitWarning(e);
-    } else {
-      console.error(errorMsg);
-
-      if (console.trace){
-        console.trace();
-      }
-    }
-  }
-
-  function EventEmitter(conf) {
-    this._events = {};
-    this._newListener = false;
-    this._removeListener = false;
-    this.verboseMemoryLeak = false;
-    configure.call(this, conf);
-  }
-  EventEmitter.EventEmitter2 = EventEmitter; // backwards compatibility for exporting EventEmitter property
-
-  //
-  // Attention, function return type now is array, always !
-  // It has zero elements if no any matches found and one or more
-  // elements (leafs) if there are matches
-  //
-  function searchListenerTree(handlers, type, tree, i) {
-    if (!tree) {
-      return [];
-    }
-    var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
-        typeLength = type.length, currentType = type[i], nextType = type[i+1];
-    if (i === typeLength && tree._listeners) {
-      //
-      // If at the end of the event(s) list and the tree has listeners
-      // invoke those listeners.
-      //
-      if (typeof tree._listeners === 'function') {
-        handlers && handlers.push(tree._listeners);
-        return [tree];
-      } else {
-        for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
-          handlers && handlers.push(tree._listeners[leaf]);
-        }
-        return [tree];
-      }
-    }
-
-    if ((currentType === '*' || currentType === '**') || tree[currentType]) {
-      //
-      // If the event emitted is '*' at this part
-      // or there is a concrete match at this patch
-      //
-      if (currentType === '*') {
-        for (branch in tree) {
-          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
-            listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1));
-          }
-        }
-        return listeners;
-      } else if(currentType === '**') {
-        endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*'));
-        if(endReached && tree._listeners) {
-          // The next element has a _listeners, add it to the handlers.
-          listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength));
-        }
-
-        for (branch in tree) {
-          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
-            if(branch === '*' || branch === '**') {
-              if(tree[branch]._listeners && !endReached) {
-                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength));
-              }
-              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
-            } else if(branch === nextType) {
-              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2));
-            } else {
-              // No match on this one, shift into the tree but not in the type array.
-              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
-            }
-          }
-        }
-        return listeners;
-      }
-
-      listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1));
-    }
-
-    xTree = tree['*'];
-    if (xTree) {
-      //
-      // If the listener tree will allow any match for this part,
-      // then recursively explore all branches of the tree
-      //
-      searchListenerTree(handlers, type, xTree, i+1);
-    }
-
-    xxTree = tree['**'];
-    if(xxTree) {
-      if(i < typeLength) {
-        if(xxTree._listeners) {
-          // If we have a listener on a '**', it will catch all, so add its handler.
-          searchListenerTree(handlers, type, xxTree, typeLength);
-        }
-
-        // Build arrays of matching next branches and others.
-        for(branch in xxTree) {
-          if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) {
-            if(branch === nextType) {
-              // We know the next element will match, so jump twice.
-              searchListenerTree(handlers, type, xxTree[branch], i+2);
-            } else if(branch === currentType) {
-              // Current node matches, move into the tree.
-              searchListenerTree(handlers, type, xxTree[branch], i+1);
-            } else {
-              isolatedBranch = {};
-              isolatedBranch[branch] = xxTree[branch];
-              searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1);
-            }
-          }
-        }
-      } else if(xxTree._listeners) {
-        // We have reached the end and still on a '**'
-        searchListenerTree(handlers, type, xxTree, typeLength);
-      } else if(xxTree['*'] && xxTree['*']._listeners) {
-        searchListenerTree(handlers, type, xxTree['*'], typeLength);
-      }
-    }
-
-    return listeners;
-  }
-
-  function growListenerTree(type, listener) {
-
-    type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
-
-    //
-    // Looks for two consecutive '**', if so, don't add the event at all.
-    //
-    for(var i = 0, len = type.length; i+1 < len; i++) {
-      if(type[i] === '**' && type[i+1] === '**') {
-        return;
-      }
-    }
-
-    var tree = this.listenerTree;
-    var name = type.shift();
-
-    while (name !== undefined) {
-
-      if (!tree[name]) {
-        tree[name] = {};
-      }
-
-      tree = tree[name];
-
-      if (type.length === 0) {
-
-        if (!tree._listeners) {
-          tree._listeners = listener;
-        }
-        else {
-          if (typeof tree._listeners === 'function') {
-            tree._listeners = [tree._listeners];
-          }
-
-          tree._listeners.push(listener);
-
-          if (
-            !tree._listeners.warned &&
-            this._maxListeners > 0 &&
-            tree._listeners.length > this._maxListeners
-          ) {
-            tree._listeners.warned = true;
-            logPossibleMemoryLeak.call(this, tree._listeners.length, name);
-          }
-        }
-        return true;
-      }
-      name = type.shift();
-    }
-    return true;
-  }
-
-  // By default EventEmitters will print a warning if more than
-  // 10 listeners are added to it. This is a useful default which
-  // helps finding memory leaks.
-  //
-  // Obviously not all Emitters should be limited to 10. This function allows
-  // that to be increased. Set to zero for unlimited.
-
-  EventEmitter.prototype.delimiter = '.';
-
-  EventEmitter.prototype.setMaxListeners = function(n) {
-    if (n !== undefined) {
-      this._maxListeners = n;
-      if (!this._conf) this._conf = {};
-      this._conf.maxListeners = n;
-    }
-  };
-
-  EventEmitter.prototype.event = '';
-
-
-  EventEmitter.prototype.once = function(event, fn) {
-    return this._once(event, fn, false);
-  };
-
-  EventEmitter.prototype.prependOnceListener = function(event, fn) {
-    return this._once(event, fn, true);
-  };
-
-  EventEmitter.prototype._once = function(event, fn, prepend) {
-    this._many(event, 1, fn, prepend);
-    return this;
-  };
-
-  EventEmitter.prototype.many = function(event, ttl, fn) {
-    return this._many(event, ttl, fn, false);
-  }
-
-  EventEmitter.prototype.prependMany = function(event, ttl, fn) {
-    return this._many(event, ttl, fn, true);
-  }
-
-  EventEmitter.prototype._many = function(event, ttl, fn, prepend) {
-    var self = this;
-
-    if (typeof fn !== 'function') {
-      throw new Error('many only accepts instances of Function');
-    }
-
-    function listener() {
-      if (--ttl === 0) {
-        self.off(event, listener);
-      }
-      return fn.apply(this, arguments);
-    }
-
-    listener._origin = fn;
-
-    this._on(event, listener, prepend);
-
-    return self;
-  };
-
-  EventEmitter.prototype.emit = function() {
-
-    this._events || init.call(this);
-
-    var type = arguments[0];
-
-    if (type === 'newListener' && !this._newListener) {
-      if (!this._events.newListener) {
-        return false;
-      }
-    }
-
-    var al = arguments.length;
-    var args,l,i,j;
-    var handler;
-
-    if (this._all && this._all.length) {
-      handler = this._all.slice();
-      if (al > 3) {
-        args = new Array(al);
-        for (j = 0; j < al; j++) args[j] = arguments[j];
-      }
-
-      for (i = 0, l = handler.length; i < l; i++) {
-        this.event = type;
-        switch (al) {
-        case 1:
-          handler[i].call(this, type);
-          break;
-        case 2:
-          handler[i].call(this, type, arguments[1]);
-          break;
-        case 3:
-          handler[i].call(this, type, arguments[1], arguments[2]);
-          break;
-        default:
-          handler[i].apply(this, args);
-        }
-      }
-    }
-
-    if (this.wildcard) {
-      handler = [];
-      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
-      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
-    } else {
-      handler = this._events[type];
-      if (typeof handler === 'function') {
-        this.event = type;
-        switch (al) {
-        case 1:
-          handler.call(this);
-          break;
-        case 2:
-          handler.call(this, arguments[1]);
-          break;
-        case 3:
-          handler.call(this, arguments[1], arguments[2]);
-          break;
-        default:
-          args = new Array(al - 1);
-          for (j = 1; j < al; j++) args[j - 1] = arguments[j];
-          handler.apply(this, args);
-        }
-        return true;
-      } else if (handler) {
-        // need to make copy of handlers because list can change in the middle
-        // of emit call
-        handler = handler.slice();
-      }
-    }
-
-    if (handler && handler.length) {
-      if (al > 3) {
-        args = new Array(al - 1);
-        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
-      }
-      for (i = 0, l = handler.length; i < l; i++) {
-        this.event = type;
-        switch (al) {
-        case 1:
-          handler[i].call(this);
-          break;
-        case 2:
-          handler[i].call(this, arguments[1]);
-          break;
-        case 3:
-          handler[i].call(this, arguments[1], arguments[2]);
-          break;
-        default:
-          handler[i].apply(this, args);
-        }
-      }
-      return true;
-    } else if (!this._all && type === 'error') {
-      if (arguments[1] instanceof Error) {
-        throw arguments[1]; // Unhandled 'error' event
-      } else {
-        throw new Error("Uncaught, unspecified 'error' event.");
-      }
-      return false;
-    }
-
-    return !!this._all;
-  };
-
-  EventEmitter.prototype.emitAsync = function() {
-
-    this._events || init.call(this);
-
-    var type = arguments[0];
-
-    if (type === 'newListener' && !this._newListener) {
-        if (!this._events.newListener) { return Promise.resolve([false]); }
-    }
-
-    var promises= [];
-
-    var al = arguments.length;
-    var args,l,i,j;
-    var handler;
-
-    if (this._all) {
-      if (al > 3) {
-        args = new Array(al);
-        for (j = 1; j < al; j++) args[j] = arguments[j];
-      }
-      for (i = 0, l = this._all.length; i < l; i++) {
-        this.event = type;
-        switch (al) {
-        case 1:
-          promises.push(this._all[i].call(this, type));
-          break;
-        case 2:
-          promises.push(this._all[i].call(this, type, arguments[1]));
-          break;
-        case 3:
-          promises.push(this._all[i].call(this, type, arguments[1], arguments[2]));
-          break;
-        default:
-          promises.push(this._all[i].apply(this, args));
-        }
-      }
-    }
-
-    if (this.wildcard) {
-      handler = [];
-      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
-      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
-    } else {
-      handler = this._events[type];
-    }
-
-    if (typeof handler === 'function') {
-      this.event = type;
-      switch (al) {
-      case 1:
-        promises.push(handler.call(this));
-        break;
-      case 2:
-        promises.push(handler.call(this, arguments[1]));
-        break;
-      case 3:
-        promises.push(handler.call(this, arguments[1], arguments[2]));
-        break;
-      default:
-        args = new Array(al - 1);
-        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
-        promises.push(handler.apply(this, args));
-      }
-    } else if (handler && handler.length) {
-      handler = handler.slice();
-      if (al > 3) {
-        args = new Array(al - 1);
-        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
-      }
-      for (i = 0, l = handler.length; i < l; i++) {
-        this.event = type;
-        switch (al) {
-        case 1:
-          promises.push(handler[i].call(this));
-          break;
-        case 2:
-          promises.push(handler[i].call(this, arguments[1]));
-          break;
-        case 3:
-          promises.push(handler[i].call(this, arguments[1], arguments[2]));
-          break;
-        default:
-          promises.push(handler[i].apply(this, args));
-        }
-      }
-    } else if (!this._all && type === 'error') {
-      if (arguments[1] instanceof Error) {
-        return Promise.reject(arguments[1]); // Unhandled 'error' event
-      } else {
-        return Promise.reject("Uncaught, unspecified 'error' event.");
-      }
-    }
-
-    return Promise.all(promises);
-  };
-
-  EventEmitter.prototype.on = function(type, listener) {
-    return this._on(type, listener, false);
-  };
-
-  EventEmitter.prototype.prependListener = function(type, listener) {
-    return this._on(type, listener, true);
-  };
-
-  EventEmitter.prototype.onAny = function(fn) {
-    return this._onAny(fn, false);
-  };
-
-  EventEmitter.prototype.prependAny = function(fn) {
-    return this._onAny(fn, true);
-  };
-
-  EventEmitter.prototype.addListener = EventEmitter.prototype.on;
-
-  EventEmitter.prototype._onAny = function(fn, prepend){
-    if (typeof fn !== 'function') {
-      throw new Error('onAny only accepts instances of Function');
-    }
-
-    if (!this._all) {
-      this._all = [];
-    }
-
-    // Add the function to the event listener collection.
-    if(prepend){
-      this._all.unshift(fn);
-    }else{
-      this._all.push(fn);
-    }
-
-    return this;
-  }
-
-  EventEmitter.prototype._on = function(type, listener, prepend) {
-    if (typeof type === 'function') {
-      this._onAny(type, listener);
-      return this;
-    }
-
-    if (typeof listener !== 'function') {
-      throw new Error('on only accepts instances of Function');
-    }
-    this._events || init.call(this);
-
-    // To avoid recursion in the case that type == "newListeners"! Before
-    // adding it to the listeners, first emit "newListeners".
-    if (this._newListener)
-       this.emit('newListener', type, listener);
-
-    if (this.wildcard) {
-      growListenerTree.call(this, type, listener);
-      return this;
-    }
-
-    if (!this._events[type]) {
-      // Optimize the case of one listener. Don't need the extra array object.
-      this._events[type] = listener;
-    }
-    else {
-      if (typeof this._events[type] === 'function') {
-        // Change to array.
-        this._events[type] = [this._events[type]];
-      }
-
-      // If we've already got an array, just add
-      if(prepend){
-        this._events[type].unshift(listener);
-      }else{
-        this._events[type].push(listener);
-      }
-
-      // Check for listener leak
-      if (
-        !this._events[type].warned &&
-        this._maxListeners > 0 &&
-        this._events[type].length > this._maxListeners
-      ) {
-        this._events[type].warned = true;
-        logPossibleMemoryLeak.call(this, this._events[type].length, type);
-      }
-    }
-
-    return this;
-  }
-
-  EventEmitter.prototype.off = function(type, listener) {
-    if (typeof listener !== 'function') {
-      throw new Error('removeListener only takes instances of Function');
-    }
-
-    var handlers,leafs=[];
-
-    if(this.wildcard) {
-      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
-      leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
-    }
-    else {
-      // does not use listeners(), so no side effect of creating _events[type]
-      if (!this._events[type]) return this;
-      handlers = this._events[type];
-      leafs.push({_listeners:handlers});
-    }
-
-    for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
-      var leaf = leafs[iLeaf];
-      handlers = leaf._listeners;
-      if (isArray(handlers)) {
-
-        var position = -1;
-
-        for (var i = 0, length = handlers.length; i < length; i++) {
-          if (handlers[i] === listener ||
-            (handlers[i].listener && handlers[i].listener === listener) ||
-            (handlers[i]._origin && handlers[i]._origin === listener)) {
-            position = i;
-            break;
-          }
-        }
-
-        if (position < 0) {
-          continue;
-        }
-
-        if(this.wildcard) {
-          leaf._listeners.splice(position, 1);
-        }
-        else {
-          this._events[type].splice(position, 1);
-        }
-
-        if (handlers.length === 0) {
-          if(this.wildcard) {
-            delete leaf._listeners;
-          }
-          else {
-            delete this._events[type];
-          }
-        }
-        if (this._removeListener)
-          this.emit("removeListener", type, listener);
-
-        return this;
-      }
-      else if (handlers === listener ||
-        (handlers.listener && handlers.listener === listener) ||
-        (handlers._origin && handlers._origin === listener)) {
-        if(this.wildcard) {
-          delete leaf._listeners;
-        }
-        else {
-          delete this._events[type];
-        }
-        if (this._removeListener)
-          this.emit("removeListener", type, listener);
-      }
-    }
-
-    function recursivelyGarbageCollect(root) {
-      if (root === undefined) {
-        return;
-      }
-      var keys = Object.keys(root);
-      for (var i in keys) {
-        var key = keys[i];
-        var obj = root[key];
-        if ((obj instanceof Function) || (typeof obj !== "object") || (obj === null))
-          continue;
-        if (Object.keys(obj).length > 0) {
-          recursivelyGarbageCollect(root[key]);
-        }
-        if (Object.keys(obj).length === 0) {
-          delete root[key];
-        }
-      }
-    }
-    recursivelyGarbageCollect(this.listenerTree);
-
-    return this;
-  };
-
-  EventEmitter.prototype.offAny = function(fn) {
-    var i = 0, l = 0, fns;
-    if (fn && this._all && this._all.length > 0) {
-      fns = this._all;
-      for(i = 0, l = fns.length; i < l; i++) {
-        if(fn === fns[i]) {
-          fns.splice(i, 1);
-          if (this._removeListener)
-            this.emit("removeListenerAny", fn);
-          return this;
-        }
-      }
-    } else {
-      fns = this._all;
-      if (this._removeListener) {
-        for(i = 0, l = fns.length; i < l; i++)
-          this.emit("removeListenerAny", fns[i]);
-      }
-      this._all = [];
-    }
-    return this;
-  };
-
-  EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
-
-  EventEmitter.prototype.removeAllListeners = function(type) {
-    if (type === undefined) {
-      !this._events || init.call(this);
-      return this;
-    }
-
-    if (this.wildcard) {
-      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
-      var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
-
-      for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
-        var leaf = leafs[iLeaf];
-        leaf._listeners = null;
-      }
-    }
-    else if (this._events) {
-      this._events[type] = null;
-    }
-    return this;
-  };
-
-  EventEmitter.prototype.listeners = function(type) {
-    if (this.wildcard) {
-      var handlers = [];
-      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
-      searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
-      return handlers;
-    }
-
-    this._events || init.call(this);
-
-    if (!this._events[type]) this._events[type] = [];
-    if (!isArray(this._events[type])) {
-      this._events[type] = [this._events[type]];
-    }
-    return this._events[type];
-  };
-
-  EventEmitter.prototype.eventNames = function(){
-    return Object.keys(this._events);
-  }
-
-  EventEmitter.prototype.listenerCount = function(type) {
-    return this.listeners(type).length;
-  };
-
-  EventEmitter.prototype.listenersAny = function() {
-
-    if(this._all) {
-      return this._all;
-    }
-    else {
-      return [];
-    }
-
-  };
-
-  if (typeof define === 'function' && define.amd) {
-     // AMD. Register as an anonymous module.
-    define(function() {
-      return EventEmitter;
-    });
-  } else if (typeof exports === 'object') {
-    // CommonJS
-    module.exports = EventEmitter;
-  }
-  else {
-    // Browser global.
-    window.EventEmitter2 = EventEmitter;
-  }
-}();
-
-}).call(this,require('_process'))
-},{"_process":37}],2:[function(require,module,exports){
 
 
 var CHANNEL = require("./channel");
@@ -909,7 +123,7 @@ HttpHeaderChannel.prototype.getMozillaRequestObserverListener = function(globals
     return this.mozillaRequestObserverListener;
 }
 
-},{"./channel":5}],3:[function(require,module,exports){
+},{"./channel":4}],2:[function(require,module,exports){
 
 var CHANNEL = require("./channel"),
     UTIL = require("fp-modules-for-nodejs/lib/util");
@@ -973,7 +187,7 @@ PostMessageChannel.prototype.parseReceivedPostMessage = function(msg)
     });
 }
 
-},{"./channel":5,"fp-modules-for-nodejs/lib/util":22}],4:[function(require,module,exports){
+},{"./channel":4,"fp-modules-for-nodejs/lib/util":21}],3:[function(require,module,exports){
 
 var CHANNEL = require("./channel");
 
@@ -990,7 +204,7 @@ var ShellCommandChannel = exports.ShellCommandChannel = function () {
 
 ShellCommandChannel.prototype = CHANNEL.Channel();
 
-},{"./channel":5}],5:[function(require,module,exports){
+},{"./channel":4}],4:[function(require,module,exports){
 
 var UTIL = require("fp-modules-for-nodejs/lib/util");
 var PROTOCOL = require("./protocol");
@@ -1520,7 +734,7 @@ Channel.prototype.setTransport = function(transport) {
 }
 
 
-},{"./protocol":9,"./transport":12,"fp-modules-for-nodejs/lib/util":22}],6:[function(require,module,exports){
+},{"./protocol":8,"./transport":11,"fp-modules-for-nodejs/lib/util":21}],5:[function(require,module,exports){
 
 var CHANNEL = require("../channel"),
     UTIL = require("fp-modules-for-nodejs/lib/util"),
@@ -1617,7 +831,7 @@ HttpClientChannel.prototype.flush = function(applicator, bypassTransport)
     return self._flush(applicator);
 }
 
-},{"../channel":5,"fp-modules-for-nodejs/lib/http-client":15,"fp-modules-for-nodejs/lib/json":16,"fp-modules-for-nodejs/lib/util":22}],7:[function(require,module,exports){
+},{"../channel":4,"fp-modules-for-nodejs/lib/http-client":14,"fp-modules-for-nodejs/lib/json":15,"fp-modules-for-nodejs/lib/util":21}],6:[function(require,module,exports){
 
 var Dispatcher = exports.Dispatcher = function () {
     if (!(this instanceof exports.Dispatcher))
@@ -1656,7 +870,7 @@ Dispatcher.prototype._dispatch = function(message, bypassReceivers) {
     this.channel.enqueueOutgoing(message, bypassReceivers);
 }
 
-},{}],8:[function(require,module,exports){
+},{}],7:[function(require,module,exports){
 
 var Message = exports.Message = function (dispatcher) {
     if (!(this instanceof exports.Message))
@@ -1714,7 +928,7 @@ Message.prototype.getData = function() {
     return this.data;
 }
 
-},{}],9:[function(require,module,exports){
+},{}],8:[function(require,module,exports){
 
 var MESSAGE = require("./message");
 var JSON = require("fp-modules-for-nodejs/lib/json");
@@ -2554,7 +1768,7 @@ function chunk_split(value, length) {
     return parts;
 }
 
-},{"./message":8,"fp-modules-for-nodejs/lib/json":16,"fp-modules-for-nodejs/lib/util":22}],10:[function(require,module,exports){
+},{"./message":7,"fp-modules-for-nodejs/lib/json":15,"fp-modules-for-nodejs/lib/util":21}],9:[function(require,module,exports){
 
 var Receiver = exports.Receiver = function () {
     if (!(this instanceof exports.Receiver))
@@ -2645,7 +1859,7 @@ Receiver.prototype._dispatch = function(event, args) {
     return returnOptions;
 }
 
-},{}],11:[function(require,module,exports){
+},{}],10:[function(require,module,exports){
 
 var WILDFIRE = require("../wildfire"),
     JSON = require("fp-modules-for-nodejs/lib/json");
@@ -2775,7 +1989,7 @@ CallbackStream.prototype.receive = function(handler)
     this.receiveHandler = handler;
 }
 
-},{"../wildfire":13,"fp-modules-for-nodejs/lib/json":16}],12:[function(require,module,exports){
+},{"../wildfire":12,"fp-modules-for-nodejs/lib/json":15}],11:[function(require,module,exports){
 
 
 const RECEIVER_ID = "http://registry.pinf.org/cadorn.org/wildfire/@meta/receiver/transport/0";
@@ -2891,7 +2105,7 @@ throw new Error("OOPS!!!");
 }
 
 
-},{"./message":8,"./receiver":10,"./wildfire":13,"fp-modules-for-nodejs/lib/json":16,"fp-modules-for-nodejs/lib/md5":17,"fp-modules-for-nodejs/lib/struct":20}],13:[function(require,module,exports){
+},{"./message":7,"./receiver":9,"./wildfire":12,"fp-modules-for-nodejs/lib/json":15,"fp-modules-for-nodejs/lib/md5":16,"fp-modules-for-nodejs/lib/struct":19}],12:[function(require,module,exports){
 
 exports.Receiver = function() {
     return require("./receiver").Receiver();
@@ -2925,7 +2139,7 @@ exports.CallbackStream = function() {
     return require("./stream/callback").CallbackStream();
 }
 
-},{"./channel-httpheader":2,"./channel-postmessage":3,"./channel-shellcommand":4,"./channel/http-client":6,"./dispatcher":7,"./message":8,"./receiver":10,"./stream/callback":11}],14:[function(require,module,exports){
+},{"./channel-httpheader":1,"./channel-postmessage":2,"./channel-shellcommand":3,"./channel/http-client":5,"./dispatcher":6,"./message":7,"./receiver":9,"./stream/callback":10}],13:[function(require,module,exports){
 
 /* Binary */
 // -- tlrobinson Tom Robinson
@@ -3689,7 +2903,7 @@ ByteArray.prototype.toSource = function() {
 };
 
 
-},{"./platform/node/binary":19,"./util":22}],15:[function(require,module,exports){
+},{"./platform/node/binary":18,"./util":21}],14:[function(require,module,exports){
 
 //var ENGINE = require("./platform/{platform}/http-client");
 var ENGINE = require("./platform/browser/http-client");
@@ -3738,12 +2952,12 @@ exports.request = function(options, successCallback, errorCallback)
     return ENGINE.request(options, successCallback, errorCallback);
 }
 
-},{"./platform/browser/http-client":18,"./uri":21}],16:[function(require,module,exports){
+},{"./platform/browser/http-client":17,"./uri":20}],15:[function(require,module,exports){
 
 exports.encode = JSON.stringify;
 exports.decode = JSON.parse;
 
-},{}],17:[function(require,module,exports){
+},{}],16:[function(require,module,exports){
 
 /*!
     A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
@@ -3916,7 +3130,7 @@ var core_hmac_md5 = function (key, data, _characterSize) {
 };
 
 
-},{"./struct":20,"./util":22}],18:[function(require,module,exports){
+},{"./struct":19,"./util":21}],17:[function(require,module,exports){
 
 exports.request = function(options, successCallback, errorCallback)
 {
@@ -3964,7 +3178,7 @@ exports.request = function(options, successCallback, errorCallback)
     }
 }
 
-},{}],19:[function(require,module,exports){
+},{}],18:[function(require,module,exports){
 (function (Buffer){
 
 //var Buffer = require("../../buffer").Buffer;
@@ -4017,7 +3231,7 @@ exports.B_TRANSCODE = function(bytes, offset, length, sourceCharset, targetChars
 };
 
 }).call(this,require("buffer").Buffer)
-},{"buffer":35}],20:[function(require,module,exports){
+},{"buffer":45}],19:[function(require,module,exports){
 
 // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
 
@@ -4283,7 +3497,7 @@ exports.bin2hex = function (bin) {
     return str;
 }
 
-},{"./binary":14,"./util":22}],21:[function(require,module,exports){
+},{"./binary":13,"./util":21}],20:[function(require,module,exports){
 
 // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
 // gmosx, George Moschovitis
@@ -4706,7 +3920,7 @@ exports.pathToUri = function (path) {
 };
 */
 
-},{}],22:[function(require,module,exports){
+},{}],21:[function(require,module,exports){
 
 // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
 // -- isaacs Isaac Schlueter
@@ -5220,676 +4434,1966 @@ exports.cut = exports.operator('cut', 2, function (object, key) {
     return result;
 });
 
-exports.put = exports.operator('put', 2, function (object, key, value) {
-    if (exports.isArrayLike(object))
-        return exports.array.put(object, key, value);
-    return exports.set(object, key, value);
+exports.put = exports.operator('put', 2, function (object, key, value) {
+    if (exports.isArrayLike(object))
+        return exports.array.put(object, key, value);
+    return exports.set(object, key, value);
+});
+
+exports.first = exports.operator('first', 1, function (object) {
+    return object[0];
+});
+
+exports.last = exports.operator('last', 1, function (object) {
+    return object[object.length - 1];
+});
+
+exports.update = exports.operator('update', 2, function () {
+    var args = Array.prototype.slice.call(arguments);
+    return exports.object.update.apply(this, args);
+});
+
+exports.deepUpdate = exports.operator('deepUpdate', 2, function (target, source) {
+    exports.object.deepUpdate(target, source);
+});
+
+exports.complete = exports.operator('complete', 2, function (target, source) {
+    var args = Array.prototype.slice.call(arguments);
+    return exports.object.complete.apply(this, args);
+});
+
+exports.deepComplete = exports.operator('deepComplete', 2, function (target, source) {
+    var args = Array.prototype.slice.call(arguments);
+    return exports.object.deepComplete.apply(this, args);
+});
+
+exports.deepDiff = exports.operator('deepDiff', 2, function (target, source) {
+    var args = Array.prototype.slice.call(arguments);
+    return exports.object.deepDiff.apply(this, args);
+});
+
+exports.deepEqual = function(actual, expected) {
+    
+    // 7.1. All identical values are equivalent, as determined by ===.
+    if (actual === expected) {
+        return true;
+
+    // 7.2. If the expected value is a Date object, the actual value is
+    // equivalent if it is also a Date object that refers to the same time.
+    } else if (actual instanceof Date && expected instanceof Date) {
+        return actual.getTime() === expected.getTime();
+
+    // 7.3. Other pairs that do not both pass typeof value == "object",
+    // equivalence is determined by ==.
+    } else if (typeof actual != 'object' && typeof expected != 'object') {
+        return actual == expected;
+
+    // XXX specification bug: this should be specified
+    } else if (typeof expected == "string" || typeof actual == "string") {
+        return expected == actual;
+
+    // 7.4. For all other Object pairs, including Array objects, equivalence is
+    // determined by having the same number of owned properties (as verified
+    // with Object.prototype.hasOwnProperty.call), the same set of keys
+    // (although not necessarily the same order), equivalent values for every
+    // corresponding key, and an identical "prototype" property. Note: this
+    // accounts for both named and indexed properties on Arrays.
+    } else {
+        return actual.prototype === expected.prototype && exports.object.eq(actual, expected);
+    }
+}
+
+exports.remove = exports.operator('remove', 2, function (list, value) {
+    var index;
+    if ((index = list.indexOf(value))>-1)
+        list.splice(index,1);
+    return list;
+});
+
+// TODO insert
+// TODO discard
+
+exports.range = function () {
+    var start = 0, stop = 0, step = 1;
+    if (arguments.length == 1) {
+        stop = arguments[0];
+    } else {
+        start = arguments[0];
+        stop = arguments[1];
+        step = arguments[2] || 1;
+    }
+    var range = [];
+    for (var i = start; i < stop; i += step)
+        range.push(i);
+    return range;
+};
+
+exports.forEach = function (array, block) {
+    Array.prototype.forEach.call(
+        exports.array.coerce(array),
+        block
+    );
+};
+
+exports.forEachApply = function (array, block) {
+    Array.prototype.forEach.call(
+        exports.array.coerce(array),
+        exports.apply(block)
+    );
+};
+
+exports.map = function (array, block, context) {
+    return Array.prototype.map.call(
+        exports.array.coerce(array),
+        block,
+        context
+    );
+};
+
+exports.mapApply = function (array, block) {
+    return Array.prototype.map.call(
+        exports.array.coerce(array),
+        exports.apply(block)
+    );
+};
+
+exports.every = exports.operator('every', 2, function (array, block, context) {
+    return exports.all(exports.map(array, block, context));
+});
+
+exports.some = exports.operator('some', 2, function (array, block, context) {
+    return exports.any(exports.map(array, block, context));
+});
+
+exports.all = exports.operator('all', 1, function (array) {
+    array = exports.array.coerce(array);
+    for (var i = 0; i < array.length; i++)
+        if (!array[i])
+            return false;
+    return true;
+});
+
+exports.any = exports.operator('all', 1, function (array) {
+    array = exports.array.coerce(array);
+    for (var i = 0; i < array.length; i++)
+        if (array[i])
+            return true;
+    return false;
+});
+
+exports.reduce = exports.operator('reduce', 2, function (array, block, basis) {
+    array = exports.array.coerce(array);
+    return array.reduce.apply(array, arguments);
+});
+
+exports.reduceRight = exports.operator('reduceRight', 2, function (array, block, basis) {
+    array = exports.array.coerce(array);
+    return array.reduceRight.apply(array, arguments);
+});
+
+exports.zip = function () {
+    return exports.transpose(arguments);
+};
+
+exports.transpose = function (array) {
+    array = exports.array.coerce(array);
+    var transpose = [];
+    var length = Math.min.apply(this, exports.map(array, function (row) {
+        return row.length;
+    }));
+    for (var i = 0; i < array.length; i++) {
+        var row = array[i];
+        for (var j = 0; j < length; j++) {
+            var cell = row[j];
+            if (!transpose[j])
+                transpose[j] = [];
+            transpose[j][i] = cell;
+        }
+    }
+    return transpose;
+};
+
+exports.enumerate = function (array, start) {
+    array = exports.array.coerce(array);
+    if (exports.no(start))
+        start = 0;
+    return exports.zip(
+        exports.range(start, start + array.length),
+        array
+    );
+};
+
+// arithmetic, transitive, and logical operators
+
+exports.is = function (a, b) {
+    // <Mark Miller>
+    if (a === b)
+        // 0 === -0, but they are not identical
+        return a !== 0 || 1/a === 1/b;
+    // NaN !== NaN, but they are identical.
+    // NaNs are the only non-reflexive value, i.e., if a !== a,
+    // then a is a NaN.
+    return a !== a && b !== b;
+    // </Mark Miller>
+};
+
+exports.eq = exports.operator('eq', 2, function (a, b, stack) {
+    if (!stack)
+        stack = [];
+    if (a === b)
+        return true;
+    if (typeof a !== typeof b)
+        return false;
+    if (exports.no(a))
+        return exports.no(b);
+    if (a instanceof Date)
+        return a.valueOf() === b.valueOf();
+    if (a instanceof RegExp)
+        return a.source === b.source &&
+            a.global === b.global &&
+            a.ignoreCase === b.ignoreCase &&
+            a.multiline === b.multiline;
+    if (typeof a === "function") { 
+        var caller = stack[stack.length - 1];
+        // XXX what is this for?  can it be axed?
+        // it comes from the "equiv" project code
+        return caller !== Object &&
+            typeof caller !== "undefined";
+    }
+    if (exports.isArrayLike(a))
+        return exports.array.eq(
+            a, b,
+            stack.concat([a.constructor])
+        );
+    if (typeof a === 'object')
+        return exports.object.eq(
+            a, b,
+            stack.concat([a.constructor])
+        );
+    return false;
+});
+
+exports.ne = exports.operator('ne', 2, function (a, b) {
+    return !exports.eq(a, b);
+});
+
+exports.lt = exports.operator('lt', 2, function (a, b) {
+    if (exports.no(a) != exports.no(b))
+        return exports.no(a) > exports.no(b);
+    if (exports.isArrayLike(a) && exports.isArrayLike(b))
+        return exports.array.lt(a, b);
+    return a < b;
+});
+
+exports.gt = exports.operator('gt', 2, function (a, b) {
+    return !(exports.lt(a, b) || exports.eq(a, b));
 });
 
-exports.first = exports.operator('first', 1, function (object) {
-    return object[0];
+exports.le = exports.operator(2, 'le', function (a, b) {
+    return exports.lt(a, b) || exports.eq(a, b);
 });
 
-exports.last = exports.operator('last', 1, function (object) {
-    return object[object.length - 1];
+exports.ge = exports.operator(2, 'ge', function (a, b) {
+    return !exports.lt(a, b);
 });
 
-exports.update = exports.operator('update', 2, function () {
-    var args = Array.prototype.slice.call(arguments);
-    return exports.object.update.apply(this, args);
+exports.mul = exports.operator(2, 'mul', function (a, b) {
+    if (typeof a == "string")
+        return exports.string.mul(a, b);
+    return a * b;
 });
 
-exports.deepUpdate = exports.operator('deepUpdate', 2, function (target, source) {
-    exports.object.deepUpdate(target, source);
-});
+/*** by
+    returns a `comparator` that compares
+    values based on the values resultant from
+    a given `relation`.
+    accepts a `relation` and an optional comparator.
 
-exports.complete = exports.operator('complete', 2, function (target, source) {
-    var args = Array.prototype.slice.call(arguments);
-    return exports.object.complete.apply(this, args);
+    To sort a list of objects based on their
+    "a" key::
+
+        objects.sort(by(get("a")))
+
+    To get those in descending order::
+
+        objects.sort(by(get("a")), desc)
+
+    `by` returns a comparison function that also tracks
+    the arguments you used to construct it.  This permits
+    `sort` and `sorted` to perform a Schwartzian transform
+    which can increase the performance of the sort
+    by a factor of 2.
+*/
+exports.by = function (relation) {
+    var compare = arguments[1];
+    if (exports.no(compare))
+        compare = exports.compare;
+    var comparator = function (a, b) {
+        a = relation(a);
+        b = relation(b);
+        return compare(a, b);
+    };
+    comparator.by = relation;
+    comparator.compare = compare;
+    return comparator;
+};
+
+exports.compare = exports.operator(2, 'compare', function (a, b) {
+    if (exports.no(a) !== exports.no(b))
+        return exports.no(b) - exports.no(a);
+    if (typeof a === "number" && typeof b === "number")
+        return a - b;
+    return exports.eq(a, b) ? 0 : exports.lt(a, b) ? -1 : 1;
 });
 
-exports.deepComplete = exports.operator('deepComplete', 2, function (target, source) {
-    var args = Array.prototype.slice.call(arguments);
-    return exports.object.deepComplete.apply(this, args);
+/*** sort
+    an in-place array sorter that uses a deep comparison
+    function by default (compare), and improves performance if
+    you provide a comparator returned by "by", using a
+    Schwartzian transform.
+*/
+exports.sort = function (array, compare) {
+    if (exports.no(compare))
+        compare = exports.compare;
+    if (compare.by) {
+        /* schwartzian transform */
+        array.splice.apply(
+            array,
+            [0, array.length].concat(
+                array.map(function (value) {
+                    return [compare.by(value), value];
+                }).sort(function (a, b) {
+                    return compare.compare(a[0], b[0]);
+                }).map(function (pair) {
+                    return pair[1];
+                })
+            )
+        );
+    } else {
+        array.sort(compare);
+    }
+    return array;
+};
+
+/*** sorted
+    returns a sorted copy of an array using a deep
+    comparison function by default (compare), and
+    improves performance if you provide a comparator
+    returned by "by", using a Schwartzian transform.
+*/
+exports.sorted = function (array, compare) {
+    return exports.sort(exports.array.copy(array), compare);
+};
+
+exports.reverse = function (array) {
+    return Array.prototype.reverse.call(array);
+};
+
+exports.reversed = function (array) {
+    return exports.reverse(exports.array.copy(array));
+};
+
+exports.hash = exports.operator(1, 'hash', function (object) {
+    return '' + object;
 });
 
-exports.deepDiff = exports.operator('deepDiff', 2, function (target, source) {
-    var args = Array.prototype.slice.call(arguments);
-    return exports.object.deepDiff.apply(this, args);
+exports.unique = exports.operator(1, 'unique', function (array, eq, hash) {
+    var visited = {};
+    if (!eq) eq = exports.eq;
+    if (!hash) hash = exports.hash;
+    return array.filter(function (value) {
+        var bucket = exports.getset(visited, hash(value), []);
+        var finds = bucket.filter(function (other) {
+            return eq(value, other);
+        });
+        if (!finds.length)
+            bucket.push(value);
+        return !finds.length;
+    });
 });
 
-exports.deepEqual = function(actual, expected) {
-    
-    // 7.1. All identical values are equivalent, as determined by ===.
-    if (actual === expected) {
-        return true;
+// string
 
-    // 7.2. If the expected value is a Date object, the actual value is
-    // equivalent if it is also a Date object that refers to the same time.
-    } else if (actual instanceof Date && expected instanceof Date) {
-        return actual.getTime() === expected.getTime();
+exports.string = exports.operator(1, 'toString', function (object) {
+    return '' + object;
+});
 
-    // 7.3. Other pairs that do not both pass typeof value == "object",
-    // equivalence is determined by ==.
-    } else if (typeof actual != 'object' && typeof expected != 'object') {
-        return actual == expected;
+exports.string.mul = function (string, n) {
+    return exports.range(n).map(function () {
+        return string;
+    }).join('');
+};
 
-    // XXX specification bug: this should be specified
-    } else if (typeof expected == "string" || typeof actual == "string") {
-        return expected == actual;
+/*** escape
+    escapes all characters of a string that are
+    special to JavaScript and many other languages.
+    Recognizes all of the relevant
+    control characters and formats all other
+    non-printable characters as Hex byte escape
+    sequences or Unicode escape sequences depending
+    on their size.
 
-    // 7.4. For all other Object pairs, including Array objects, equivalence is
-    // determined by having the same number of owned properties (as verified
-    // with Object.prototype.hasOwnProperty.call), the same set of keys
-    // (although not necessarily the same order), equivalent values for every
-    // corresponding key, and an identical "prototype" property. Note: this
-    // accounts for both named and indexed properties on Arrays.
-    } else {
-        return actual.prototype === expected.prototype && exports.object.eq(actual, expected);
-    }
-}
+    Pass ``true`` as an optional second argument and
+    ``escape`` produces valid contents for escaped
+    JSON strings, wherein non-printable-characters are
+    all escaped with the Unicode ``\u`` notation.
+*/
+/* more Steve Levithan flagrence */
+var escapeExpression = /[^ !#-[\]-~]/g;
+/* from Doug Crockford's JSON library */
+var escapePatterns = {
+    '\b': '\\b',    '\t': '\\t',
+    '\n': '\\n',    '\f': '\\f',    '\r': '\\r',
+    '"' : '\\"',    '\\': '\\\\'
+};
+exports.escape = function (value, strictJson) {
+    if (typeof value != "string")
+        throw new Error(
+            module.path +
+            "#escape: requires a string.  got " +
+            exports.repr(value)
+        );
+    return value.replace(
+        escapeExpression, 
+        function (match) {
+            if (escapePatterns[match])
+                return escapePatterns[match];
+            match = match.charCodeAt();
+            if (!strictJson && match < 256)
+                return "\\x" + exports.padBegin(match.toString(16), 2);
+            return '\\u' + exports.padBegin(match.toString(16), 4);
+        }
+    );
+};
 
-exports.remove = exports.operator('remove', 2, function (list, value) {
-    var index;
-    if ((index = list.indexOf(value))>-1)
-        list.splice(index,1);
-    return list;
-});
+/*** enquote
+    transforms a string into a string literal, escaping
+    all characters of a string that are special to
+    JavaScript and and some other languages.
 
-// TODO insert
-// TODO discard
+    ``enquote`` uses double quotes to be JSON compatible.
 
-exports.range = function () {
-    var start = 0, stop = 0, step = 1;
-    if (arguments.length == 1) {
-        stop = arguments[0];
-    } else {
-        start = arguments[0];
-        stop = arguments[1];
-        step = arguments[2] || 1;
+    Pass ``true`` as an optional second argument to
+    be strictly JSON compliant, wherein all
+    non-printable-characters are represented with
+    Unicode escape sequences.
+*/
+exports.enquote = function (value, strictJson) {
+    return '"' + exports.escape(value, strictJson) + '"';
+};
+
+/*** expand
+    transforms tabs to an equivalent number of spaces.
+*/
+// TODO special case for \r if it ever matters
+exports.expand = function (str, tabLength) {
+    str = String(str);
+    tabLength = tabLength || 4;
+    var output = [],
+        tabLf = /[\t\n]/g,
+        lastLastIndex = 0,
+        lastLfIndex = 0,
+        charsAddedThisLine = 0,
+        tabOffset, match;
+    while (match = tabLf.exec(str)) {
+        if (match[0] == "\t") {
+            tabOffset = (
+                tabLength - 1 -
+                (
+                    (match.index - lastLfIndex) +
+                    charsAddedThisLine
+                ) % tabLength
+            );
+            charsAddedThisLine += tabOffset;
+            output.push(
+                str.slice(lastLastIndex, match.index) +
+                exports.mul(" ", tabOffset + 1)
+            );
+        } else if (match[0] === "\n") {
+            output.push(str.slice(lastLastIndex, tabLf.lastIndex));
+            lastLfIndex = tabLf.lastIndex;
+            charsAddedThisLine = 0;
+        }
+        lastLastIndex = tabLf.lastIndex;
     }
-    var range = [];
-    for (var i = start; i < stop; i += step)
-        range.push(i);
-    return range;
+    return output.join("") + str.slice(lastLastIndex);
 };
 
-exports.forEach = function (array, block) {
-    Array.prototype.forEach.call(
-        exports.array.coerce(array),
-        block
-    );
+var trimBeginExpression = /^\s\s*/g;
+exports.trimBegin = function (value) {
+    return String(value).replace(trimBeginExpression, "");  
 };
 
-exports.forEachApply = function (array, block) {
-    Array.prototype.forEach.call(
-        exports.array.coerce(array),
-        exports.apply(block)
-    );
+var trimEndExpression = /\s\s*$/g;
+exports.trimEnd = function (value) {
+    return String(value).replace(trimEndExpression, "");    
 };
 
-exports.map = function (array, block, context) {
-    return Array.prototype.map.call(
-        exports.array.coerce(array),
-        block,
-        context
-    );
+exports.trim = function (value) {
+    return String(value).replace(trimBeginExpression, "").replace(trimEndExpression, "");
 };
 
-exports.mapApply = function (array, block) {
-    return Array.prototype.map.call(
-        exports.array.coerce(array),
-        exports.apply(block)
-    );
+/* generates padBegin and padEnd */
+var augmentor = function (augment) {
+    return function (value, length, pad) {
+        if (exports.no(pad)) pad = '0';
+        if (exports.no(length)) length = 2;
+        value = String(value);
+        while (value.length < length) {
+            value = augment(value, pad);
+        }
+        return value;
+    };
 };
 
-exports.every = exports.operator('every', 2, function (array, block, context) {
-    return exports.all(exports.map(array, block, context));
-});
+/*** padBegin
 
-exports.some = exports.operator('some', 2, function (array, block, context) {
-    return exports.any(exports.map(array, block, context));
-});
+    accepts:
+     - a `String` or `Number` value
+     - a minimum length of the resultant `String`:
+       by default, 2
+     - a pad string: by default, ``'0'``.
 
-exports.all = exports.operator('all', 1, function (array) {
-    array = exports.array.coerce(array);
-    for (var i = 0; i < array.length; i++)
-        if (!array[i])
-            return false;
-    return true;
-});
+    returns a `String` of the value padded up to at least
+    the minimum length.  adds the padding to the begining
+    side of the `String`.
 
-exports.any = exports.operator('all', 1, function (array) {
-    array = exports.array.coerce(array);
-    for (var i = 0; i < array.length; i++)
-        if (array[i])
-            return true;
-    return false;
+*/
+exports.padBegin = augmentor(function (value, pad) {
+    return pad + value;
 });
 
-exports.reduce = exports.operator('reduce', 2, function (array, block, basis) {
-    array = exports.array.coerce(array);
-    return array.reduce.apply(array, arguments);
-});
+/*** padEnd
 
-exports.reduceRight = exports.operator('reduceRight', 2, function (array, block, basis) {
-    array = exports.array.coerce(array);
-    return array.reduceRight.apply(array, arguments);
+    accepts:
+     - a `String` or `Number` value
+     - a minimum length of the resultant `String`:
+       by default, 2
+     - a pad string: by default, ``'0'``.
+
+    returns a `String` of the value padded up to at least
+    the minimum length.  adds the padding to the end
+    side of the `String`.
+
+*/
+exports.padEnd = augmentor(function (value, pad) {
+    return value + pad;
 });
 
-exports.zip = function () {
-    return exports.transpose(arguments);
+/*** splitName
+    splits a string into an array of words from an original
+    string.
+*/
+// thanks go to Steve Levithan for this regular expression
+// that, in addition to splitting any normal-form identifier
+// in any case convention, splits XMLHttpRequest into
+// "XML", "Http", and "Request"
+var splitNameExpression = /[a-z]+|[A-Z](?:[a-z]+|[A-Z]*(?![a-z]))|[.\d]+/g;
+exports.splitName = function (value) {
+    var result = String(value).match(splitNameExpression);
+    if (result)
+        return result;
+    return [value];
 };
 
-exports.transpose = function (array) {
-    array = exports.array.coerce(array);
-    var transpose = [];
-    var length = Math.min.apply(this, exports.map(array, function (row) {
-        return row.length;
-    }));
-    for (var i = 0; i < array.length; i++) {
-        var row = array[i];
-        for (var j = 0; j < length; j++) {
-            var cell = row[j];
-            if (!transpose[j])
-                transpose[j] = [];
-            transpose[j][i] = cell;
+/*** joinName
+    joins a list of words with a given delimiter
+    between alphanumeric words.
+*/
+exports.joinName = function (delimiter, parts) {
+    if (exports.no(delimiter)) delimiter = '_';
+    parts.unshift([]);
+    return parts.reduce(function (parts, part) {
+        if (
+            part.match(/\d/) &&
+            exports.len(parts) && parts[parts.length-1].match(/\d/)
+        ) {
+            return parts.concat([delimiter + part]);
+        } else {
+            return parts.concat([part]);
         }
-    }
-    return transpose;
+    }).join('');
 };
 
-exports.enumerate = function (array, start) {
-    array = exports.array.coerce(array);
-    if (exports.no(start))
-        start = 0;
-    return exports.zip(
-        exports.range(start, start + array.length),
-        array
+/*** upper
+    converts a name to ``UPPER CASE`` using
+    a given delimiter between numeric words.
+
+    see:
+     - `lower`
+     - `camel`
+     - `title`
+
+*/
+exports.upper = function (value, delimiter) {
+    if (exports.no(delimiter))
+        return value.toUpperCase();
+    return exports.splitName(value).map(function (part) {
+        return part.toUpperCase();
+    }).join(delimiter);
+};
+
+/*** lower
+    converts a name to a ``lower case`` using
+    a given delimiter between numeric words.
+
+    see:
+     - `upper`
+     - `camel`
+     - `title`
+
+*/
+exports.lower = function (value, delimiter) {
+    if (exports.no(delimiter))
+        return String(value).toLowerCase();
+    return exports.splitName(value).map(function (part) {
+        return part.toLowerCase();
+    }).join(delimiter);
+};
+
+/*** camel
+    converts a name to ``camel Case`` using
+    a given delimiter between numeric words.
+
+    see:
+     - `lower`
+     - `upper`
+     - `title`
+
+*/
+exports.camel = function (value, delimiter) {
+    return exports.joinName(
+        delimiter,
+        exports.mapApply(
+            exports.enumerate(exports.splitName(value)),
+            function (n, part) {
+                if (n) {
+                    return (
+                        part.substring(0, 1).toUpperCase() +
+                        part.substring(1).toLowerCase()
+                    );
+                } else {
+                    return part.toLowerCase();
+                }
+            }
+        )
+    );
+};
+
+/*** title
+    converts a name to ``Title Case`` using
+    a given delimiter between numeric words.
+
+    see:
+     - `lower`
+     - `upper`
+     - `camel`
+
+*/
+exports.title = function (value, delimiter) {
+    return exports.joinName(
+        delimiter,
+        exports.splitName(value).map(function (part) {
+            return (
+                part.substring(0, 1).toUpperCase() +
+                part.substring(1).toLowerCase()
+            );
+        })
     );
 };
 
-// arithmetic, transitive, and logical operators
 
-exports.is = function (a, b) {
-    // <Mark Miller>
-    if (a === b)
-        // 0 === -0, but they are not identical
-        return a !== 0 || 1/a === 1/b;
-    // NaN !== NaN, but they are identical.
-    // NaNs are the only non-reflexive value, i.e., if a !== a,
-    // then a is a NaN.
-    return a !== a && b !== b;
-    // </Mark Miller>
-};
+},{}],22:[function(require,module,exports){
+(function (process){
+/*!
+ * EventEmitter2
+ * https://github.com/hij1nx/EventEmitter2
+ *
+ * Copyright (c) 2013 hij1nx
+ * Licensed under the MIT license.
+ */
+;!function(undefined) {
+
+  var isArray = Array.isArray ? Array.isArray : function _isArray(obj) {
+    return Object.prototype.toString.call(obj) === "[object Array]";
+  };
+  var defaultMaxListeners = 10;
+
+  function init() {
+    this._events = {};
+    if (this._conf) {
+      configure.call(this, this._conf);
+    }
+  }
+
+  function configure(conf) {
+    if (conf) {
+      this._conf = conf;
+
+      conf.delimiter && (this.delimiter = conf.delimiter);
+      this._maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners;
+
+      conf.wildcard && (this.wildcard = conf.wildcard);
+      conf.newListener && (this.newListener = conf.newListener);
+      conf.verboseMemoryLeak && (this.verboseMemoryLeak = conf.verboseMemoryLeak);
+
+      if (this.wildcard) {
+        this.listenerTree = {};
+      }
+    } else {
+      this._maxListeners = defaultMaxListeners;
+    }
+  }
+
+  function logPossibleMemoryLeak(count, eventName) {
+    var errorMsg = '(node) warning: possible EventEmitter memory ' +
+        'leak detected. ' + count + ' listeners added. ' +
+        'Use emitter.setMaxListeners() to increase limit.';
+
+    if(this.verboseMemoryLeak){
+      errorMsg += ' Event name: ' + eventName + '.';
+    }
+
+    if(typeof process !== 'undefined' && process.emitWarning){
+      var e = new Error(errorMsg);
+      e.name = 'MaxListenersExceededWarning';
+      e.emitter = this;
+      e.count = count;
+      process.emitWarning(e);
+    } else {
+      console.error(errorMsg);
+
+      if (console.trace){
+        console.trace();
+      }
+    }
+  }
+
+  function EventEmitter(conf) {
+    this._events = {};
+    this.newListener = false;
+    this.verboseMemoryLeak = false;
+    configure.call(this, conf);
+  }
+  EventEmitter.EventEmitter2 = EventEmitter; // backwards compatibility for exporting EventEmitter property
+
+  //
+  // Attention, function return type now is array, always !
+  // It has zero elements if no any matches found and one or more
+  // elements (leafs) if there are matches
+  //
+  function searchListenerTree(handlers, type, tree, i) {
+    if (!tree) {
+      return [];
+    }
+    var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
+        typeLength = type.length, currentType = type[i], nextType = type[i+1];
+    if (i === typeLength && tree._listeners) {
+      //
+      // If at the end of the event(s) list and the tree has listeners
+      // invoke those listeners.
+      //
+      if (typeof tree._listeners === 'function') {
+        handlers && handlers.push(tree._listeners);
+        return [tree];
+      } else {
+        for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
+          handlers && handlers.push(tree._listeners[leaf]);
+        }
+        return [tree];
+      }
+    }
+
+    if ((currentType === '*' || currentType === '**') || tree[currentType]) {
+      //
+      // If the event emitted is '*' at this part
+      // or there is a concrete match at this patch
+      //
+      if (currentType === '*') {
+        for (branch in tree) {
+          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
+            listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1));
+          }
+        }
+        return listeners;
+      } else if(currentType === '**') {
+        endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*'));
+        if(endReached && tree._listeners) {
+          // The next element has a _listeners, add it to the handlers.
+          listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength));
+        }
+
+        for (branch in tree) {
+          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
+            if(branch === '*' || branch === '**') {
+              if(tree[branch]._listeners && !endReached) {
+                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength));
+              }
+              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
+            } else if(branch === nextType) {
+              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2));
+            } else {
+              // No match on this one, shift into the tree but not in the type array.
+              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
+            }
+          }
+        }
+        return listeners;
+      }
+
+      listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1));
+    }
+
+    xTree = tree['*'];
+    if (xTree) {
+      //
+      // If the listener tree will allow any match for this part,
+      // then recursively explore all branches of the tree
+      //
+      searchListenerTree(handlers, type, xTree, i+1);
+    }
+
+    xxTree = tree['**'];
+    if(xxTree) {
+      if(i < typeLength) {
+        if(xxTree._listeners) {
+          // If we have a listener on a '**', it will catch all, so add its handler.
+          searchListenerTree(handlers, type, xxTree, typeLength);
+        }
+
+        // Build arrays of matching next branches and others.
+        for(branch in xxTree) {
+          if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) {
+            if(branch === nextType) {
+              // We know the next element will match, so jump twice.
+              searchListenerTree(handlers, type, xxTree[branch], i+2);
+            } else if(branch === currentType) {
+              // Current node matches, move into the tree.
+              searchListenerTree(handlers, type, xxTree[branch], i+1);
+            } else {
+              isolatedBranch = {};
+              isolatedBranch[branch] = xxTree[branch];
+              searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1);
+            }
+          }
+        }
+      } else if(xxTree._listeners) {
+        // We have reached the end and still on a '**'
+        searchListenerTree(handlers, type, xxTree, typeLength);
+      } else if(xxTree['*'] && xxTree['*']._listeners) {
+        searchListenerTree(handlers, type, xxTree['*'], typeLength);
+      }
+    }
+
+    return listeners;
+  }
+
+  function growListenerTree(type, listener) {
+
+    type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+
+    //
+    // Looks for two consecutive '**', if so, don't add the event at all.
+    //
+    for(var i = 0, len = type.length; i+1 < len; i++) {
+      if(type[i] === '**' && type[i+1] === '**') {
+        return;
+      }
+    }
+
+    var tree = this.listenerTree;
+    var name = type.shift();
+
+    while (name !== undefined) {
+
+      if (!tree[name]) {
+        tree[name] = {};
+      }
+
+      tree = tree[name];
+
+      if (type.length === 0) {
+
+        if (!tree._listeners) {
+          tree._listeners = listener;
+        }
+        else {
+          if (typeof tree._listeners === 'function') {
+            tree._listeners = [tree._listeners];
+          }
+
+          tree._listeners.push(listener);
+
+          if (
+            !tree._listeners.warned &&
+            this._maxListeners > 0 &&
+            tree._listeners.length > this._maxListeners
+          ) {
+            tree._listeners.warned = true;
+            logPossibleMemoryLeak.call(this, tree._listeners.length, name);
+          }
+        }
+        return true;
+      }
+      name = type.shift();
+    }
+    return true;
+  }
+
+  // By default EventEmitters will print a warning if more than
+  // 10 listeners are added to it. This is a useful default which
+  // helps finding memory leaks.
+  //
+  // Obviously not all Emitters should be limited to 10. This function allows
+  // that to be increased. Set to zero for unlimited.
+
+  EventEmitter.prototype.delimiter = '.';
+
+  EventEmitter.prototype.setMaxListeners = function(n) {
+    if (n !== undefined) {
+      this._maxListeners = n;
+      if (!this._conf) this._conf = {};
+      this._conf.maxListeners = n;
+    }
+  };
+
+  EventEmitter.prototype.event = '';
+
+
+  EventEmitter.prototype.once = function(event, fn) {
+    return this._once(event, fn, false);
+  };
+
+  EventEmitter.prototype.prependOnceListener = function(event, fn) {
+    return this._once(event, fn, true);
+  };
+
+  EventEmitter.prototype._once = function(event, fn, prepend) {
+    this._many(event, 1, fn, prepend);
+    return this;
+  };
+
+  EventEmitter.prototype.many = function(event, ttl, fn) {
+    return this._many(event, ttl, fn, false);
+  }
+
+  EventEmitter.prototype.prependMany = function(event, ttl, fn) {
+    return this._many(event, ttl, fn, true);
+  }
+
+  EventEmitter.prototype._many = function(event, ttl, fn, prepend) {
+    var self = this;
+
+    if (typeof fn !== 'function') {
+      throw new Error('many only accepts instances of Function');
+    }
+
+    function listener() {
+      if (--ttl === 0) {
+        self.off(event, listener);
+      }
+      return fn.apply(this, arguments);
+    }
+
+    listener._origin = fn;
+
+    this._on(event, listener, prepend);
+
+    return self;
+  };
+
+  EventEmitter.prototype.emit = function() {
+
+    this._events || init.call(this);
+
+    var type = arguments[0];
+
+    if (type === 'newListener' && !this.newListener) {
+      if (!this._events.newListener) {
+        return false;
+      }
+    }
+
+    var al = arguments.length;
+    var args,l,i,j;
+    var handler;
+
+    if (this._all && this._all.length) {
+      handler = this._all.slice();
+      if (al > 3) {
+        args = new Array(al);
+        for (j = 0; j < al; j++) args[j] = arguments[j];
+      }
+
+      for (i = 0, l = handler.length; i < l; i++) {
+        this.event = type;
+        switch (al) {
+        case 1:
+          handler[i].call(this, type);
+          break;
+        case 2:
+          handler[i].call(this, type, arguments[1]);
+          break;
+        case 3:
+          handler[i].call(this, type, arguments[1], arguments[2]);
+          break;
+        default:
+          handler[i].apply(this, args);
+        }
+      }
+    }
+
+    if (this.wildcard) {
+      handler = [];
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
+    } else {
+      handler = this._events[type];
+      if (typeof handler === 'function') {
+        this.event = type;
+        switch (al) {
+        case 1:
+          handler.call(this);
+          break;
+        case 2:
+          handler.call(this, arguments[1]);
+          break;
+        case 3:
+          handler.call(this, arguments[1], arguments[2]);
+          break;
+        default:
+          args = new Array(al - 1);
+          for (j = 1; j < al; j++) args[j - 1] = arguments[j];
+          handler.apply(this, args);
+        }
+        return true;
+      } else if (handler) {
+        // need to make copy of handlers because list can change in the middle
+        // of emit call
+        handler = handler.slice();
+      }
+    }
+
+    if (handler && handler.length) {
+      if (al > 3) {
+        args = new Array(al - 1);
+        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
+      }
+      for (i = 0, l = handler.length; i < l; i++) {
+        this.event = type;
+        switch (al) {
+        case 1:
+          handler[i].call(this);
+          break;
+        case 2:
+          handler[i].call(this, arguments[1]);
+          break;
+        case 3:
+          handler[i].call(this, arguments[1], arguments[2]);
+          break;
+        default:
+          handler[i].apply(this, args);
+        }
+      }
+      return true;
+    } else if (!this._all && type === 'error') {
+      if (arguments[1] instanceof Error) {
+        throw arguments[1]; // Unhandled 'error' event
+      } else {
+        throw new Error("Uncaught, unspecified 'error' event.");
+      }
+      return false;
+    }
+
+    return !!this._all;
+  };
+
+  EventEmitter.prototype.emitAsync = function() {
+
+    this._events || init.call(this);
+
+    var type = arguments[0];
+
+    if (type === 'newListener' && !this.newListener) {
+        if (!this._events.newListener) { return Promise.resolve([false]); }
+    }
+
+    var promises= [];
+
+    var al = arguments.length;
+    var args,l,i,j;
+    var handler;
+
+    if (this._all) {
+      if (al > 3) {
+        args = new Array(al);
+        for (j = 1; j < al; j++) args[j] = arguments[j];
+      }
+      for (i = 0, l = this._all.length; i < l; i++) {
+        this.event = type;
+        switch (al) {
+        case 1:
+          promises.push(this._all[i].call(this, type));
+          break;
+        case 2:
+          promises.push(this._all[i].call(this, type, arguments[1]));
+          break;
+        case 3:
+          promises.push(this._all[i].call(this, type, arguments[1], arguments[2]));
+          break;
+        default:
+          promises.push(this._all[i].apply(this, args));
+        }
+      }
+    }
+
+    if (this.wildcard) {
+      handler = [];
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
+    } else {
+      handler = this._events[type];
+    }
+
+    if (typeof handler === 'function') {
+      this.event = type;
+      switch (al) {
+      case 1:
+        promises.push(handler.call(this));
+        break;
+      case 2:
+        promises.push(handler.call(this, arguments[1]));
+        break;
+      case 3:
+        promises.push(handler.call(this, arguments[1], arguments[2]));
+        break;
+      default:
+        args = new Array(al - 1);
+        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
+        promises.push(handler.apply(this, args));
+      }
+    } else if (handler && handler.length) {
+      handler = handler.slice();
+      if (al > 3) {
+        args = new Array(al - 1);
+        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
+      }
+      for (i = 0, l = handler.length; i < l; i++) {
+        this.event = type;
+        switch (al) {
+        case 1:
+          promises.push(handler[i].call(this));
+          break;
+        case 2:
+          promises.push(handler[i].call(this, arguments[1]));
+          break;
+        case 3:
+          promises.push(handler[i].call(this, arguments[1], arguments[2]));
+          break;
+        default:
+          promises.push(handler[i].apply(this, args));
+        }
+      }
+    } else if (!this._all && type === 'error') {
+      if (arguments[1] instanceof Error) {
+        return Promise.reject(arguments[1]); // Unhandled 'error' event
+      } else {
+        return Promise.reject("Uncaught, unspecified 'error' event.");
+      }
+    }
+
+    return Promise.all(promises);
+  };
+
+  EventEmitter.prototype.on = function(type, listener) {
+    return this._on(type, listener, false);
+  };
+
+  EventEmitter.prototype.prependListener = function(type, listener) {
+    return this._on(type, listener, true);
+  };
+
+  EventEmitter.prototype.onAny = function(fn) {
+    return this._onAny(fn, false);
+  };
+
+  EventEmitter.prototype.prependAny = function(fn) {
+    return this._onAny(fn, true);
+  };
+
+  EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+  EventEmitter.prototype._onAny = function(fn, prepend){
+    if (typeof fn !== 'function') {
+      throw new Error('onAny only accepts instances of Function');
+    }
+
+    if (!this._all) {
+      this._all = [];
+    }
+
+    // Add the function to the event listener collection.
+    if(prepend){
+      this._all.unshift(fn);
+    }else{
+      this._all.push(fn);
+    }
+
+    return this;
+  }
+
+  EventEmitter.prototype._on = function(type, listener, prepend) {
+    if (typeof type === 'function') {
+      this._onAny(type, listener);
+      return this;
+    }
+
+    if (typeof listener !== 'function') {
+      throw new Error('on only accepts instances of Function');
+    }
+    this._events || init.call(this);
+
+    // To avoid recursion in the case that type == "newListeners"! Before
+    // adding it to the listeners, first emit "newListeners".
+    this.emit('newListener', type, listener);
+
+    if (this.wildcard) {
+      growListenerTree.call(this, type, listener);
+      return this;
+    }
+
+    if (!this._events[type]) {
+      // Optimize the case of one listener. Don't need the extra array object.
+      this._events[type] = listener;
+    }
+    else {
+      if (typeof this._events[type] === 'function') {
+        // Change to array.
+        this._events[type] = [this._events[type]];
+      }
+
+      // If we've already got an array, just add
+      if(prepend){
+        this._events[type].unshift(listener);
+      }else{
+        this._events[type].push(listener);
+      }
+
+      // Check for listener leak
+      if (
+        !this._events[type].warned &&
+        this._maxListeners > 0 &&
+        this._events[type].length > this._maxListeners
+      ) {
+        this._events[type].warned = true;
+        logPossibleMemoryLeak.call(this, this._events[type].length, type);
+      }
+    }
+
+    return this;
+  }
+
+  EventEmitter.prototype.off = function(type, listener) {
+    if (typeof listener !== 'function') {
+      throw new Error('removeListener only takes instances of Function');
+    }
+
+    var handlers,leafs=[];
+
+    if(this.wildcard) {
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
+    }
+    else {
+      // does not use listeners(), so no side effect of creating _events[type]
+      if (!this._events[type]) return this;
+      handlers = this._events[type];
+      leafs.push({_listeners:handlers});
+    }
+
+    for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
+      var leaf = leafs[iLeaf];
+      handlers = leaf._listeners;
+      if (isArray(handlers)) {
+
+        var position = -1;
+
+        for (var i = 0, length = handlers.length; i < length; i++) {
+          if (handlers[i] === listener ||
+            (handlers[i].listener && handlers[i].listener === listener) ||
+            (handlers[i]._origin && handlers[i]._origin === listener)) {
+            position = i;
+            break;
+          }
+        }
+
+        if (position < 0) {
+          continue;
+        }
+
+        if(this.wildcard) {
+          leaf._listeners.splice(position, 1);
+        }
+        else {
+          this._events[type].splice(position, 1);
+        }
+
+        if (handlers.length === 0) {
+          if(this.wildcard) {
+            delete leaf._listeners;
+          }
+          else {
+            delete this._events[type];
+          }
+        }
+
+        this.emit("removeListener", type, listener);
+
+        return this;
+      }
+      else if (handlers === listener ||
+        (handlers.listener && handlers.listener === listener) ||
+        (handlers._origin && handlers._origin === listener)) {
+        if(this.wildcard) {
+          delete leaf._listeners;
+        }
+        else {
+          delete this._events[type];
+        }
+
+        this.emit("removeListener", type, listener);
+      }
+    }
+
+    function recursivelyGarbageCollect(root) {
+      if (root === undefined) {
+        return;
+      }
+      var keys = Object.keys(root);
+      for (var i in keys) {
+        var key = keys[i];
+        var obj = root[key];
+        if ((obj instanceof Function) || (typeof obj !== "object") || (obj === null))
+          continue;
+        if (Object.keys(obj).length > 0) {
+          recursivelyGarbageCollect(root[key]);
+        }
+        if (Object.keys(obj).length === 0) {
+          delete root[key];
+        }
+      }
+    }
+    recursivelyGarbageCollect(this.listenerTree);
+
+    return this;
+  };
+
+  EventEmitter.prototype.offAny = function(fn) {
+    var i = 0, l = 0, fns;
+    if (fn && this._all && this._all.length > 0) {
+      fns = this._all;
+      for(i = 0, l = fns.length; i < l; i++) {
+        if(fn === fns[i]) {
+          fns.splice(i, 1);
+          this.emit("removeListenerAny", fn);
+          return this;
+        }
+      }
+    } else {
+      fns = this._all;
+      for(i = 0, l = fns.length; i < l; i++)
+        this.emit("removeListenerAny", fns[i]);
+      this._all = [];
+    }
+    return this;
+  };
+
+  EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
+
+  EventEmitter.prototype.removeAllListeners = function(type) {
+    if (arguments.length === 0) {
+      !this._events || init.call(this);
+      return this;
+    }
+
+    if (this.wildcard) {
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
+
+      for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
+        var leaf = leafs[iLeaf];
+        leaf._listeners = null;
+      }
+    }
+    else if (this._events) {
+      this._events[type] = null;
+    }
+    return this;
+  };
+
+  EventEmitter.prototype.listeners = function(type) {
+    if (this.wildcard) {
+      var handlers = [];
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
+      return handlers;
+    }
+
+    this._events || init.call(this);
+
+    if (!this._events[type]) this._events[type] = [];
+    if (!isArray(this._events[type])) {
+      this._events[type] = [this._events[type]];
+    }
+    return this._events[type];
+  };
+
+  EventEmitter.prototype.eventNames = function(){
+    return Object.keys(this._events);
+  }
+
+  EventEmitter.prototype.listenerCount = function(type) {
+    return this.listeners(type).length;
+  };
+
+  EventEmitter.prototype.listenersAny = function() {
+
+    if(this._all) {
+      return this._all;
+    }
+    else {
+      return [];
+    }
+
+  };
+
+  if (typeof define === 'function' && define.amd) {
+     // AMD. Register as an anonymous module.
+    define(function() {
+      return EventEmitter;
+    });
+  } else if (typeof exports === 'object') {
+    // CommonJS
+    module.exports = EventEmitter;
+  }
+  else {
+    // Browser global.
+    window.EventEmitter2 = EventEmitter;
+  }
+}();
+
+}).call(this,require('_process'))
+},{"_process":48}],23:[function(require,module,exports){
+var root = require('./_root');
+
+/** Built-in value references. */
+var Symbol = root.Symbol;
 
-exports.eq = exports.operator('eq', 2, function (a, b, stack) {
-    if (!stack)
-        stack = [];
-    if (a === b)
-        return true;
-    if (typeof a !== typeof b)
-        return false;
-    if (exports.no(a))
-        return exports.no(b);
-    if (a instanceof Date)
-        return a.valueOf() === b.valueOf();
-    if (a instanceof RegExp)
-        return a.source === b.source &&
-            a.global === b.global &&
-            a.ignoreCase === b.ignoreCase &&
-            a.multiline === b.multiline;
-    if (typeof a === "function") { 
-        var caller = stack[stack.length - 1];
-        // XXX what is this for?  can it be axed?
-        // it comes from the "equiv" project code
-        return caller !== Object &&
-            typeof caller !== "undefined";
-    }
-    if (exports.isArrayLike(a))
-        return exports.array.eq(
-            a, b,
-            stack.concat([a.constructor])
-        );
-    if (typeof a === 'object')
-        return exports.object.eq(
-            a, b,
-            stack.concat([a.constructor])
-        );
-    return false;
-});
+module.exports = Symbol;
 
-exports.ne = exports.operator('ne', 2, function (a, b) {
-    return !exports.eq(a, b);
-});
+},{"./_root":28}],24:[function(require,module,exports){
+var Symbol = require('./_Symbol'),
+    getRawTag = require('./_getRawTag'),
+    objectToString = require('./_objectToString');
 
-exports.lt = exports.operator('lt', 2, function (a, b) {
-    if (exports.no(a) != exports.no(b))
-        return exports.no(a) > exports.no(b);
-    if (exports.isArrayLike(a) && exports.isArrayLike(b))
-        return exports.array.lt(a, b);
-    return a < b;
-});
+/** `Object#toString` result references. */
+var nullTag = '[object Null]',
+    undefinedTag = '[object Undefined]';
 
-exports.gt = exports.operator('gt', 2, function (a, b) {
-    return !(exports.lt(a, b) || exports.eq(a, b));
-});
+/** Built-in value references. */
+var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
 
-exports.le = exports.operator(2, 'le', function (a, b) {
-    return exports.lt(a, b) || exports.eq(a, b);
-});
+/**
+ * The base implementation of `getTag` without fallbacks for buggy environments.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+function baseGetTag(value) {
+  if (value == null) {
+    return value === undefined ? undefinedTag : nullTag;
+  }
+  return (symToStringTag && symToStringTag in Object(value))
+    ? getRawTag(value)
+    : objectToString(value);
+}
 
-exports.ge = exports.operator(2, 'ge', function (a, b) {
-    return !exports.lt(a, b);
-});
+module.exports = baseGetTag;
 
-exports.mul = exports.operator(2, 'mul', function (a, b) {
-    if (typeof a == "string")
-        return exports.string.mul(a, b);
-    return a * b;
-});
+},{"./_Symbol":23,"./_getRawTag":26,"./_objectToString":27}],25:[function(require,module,exports){
+(function (global){
+/** Detect free variable `global` from Node.js. */
+var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
 
-/*** by
-    returns a `comparator` that compares
-    values based on the values resultant from
-    a given `relation`.
-    accepts a `relation` and an optional comparator.
+module.exports = freeGlobal;
 
-    To sort a list of objects based on their
-    "a" key::
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],26:[function(require,module,exports){
+var Symbol = require('./_Symbol');
 
-        objects.sort(by(get("a")))
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
 
-    To get those in descending order::
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
 
-        objects.sort(by(get("a")), desc)
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var nativeObjectToString = objectProto.toString;
 
-    `by` returns a comparison function that also tracks
-    the arguments you used to construct it.  This permits
-    `sort` and `sorted` to perform a Schwartzian transform
-    which can increase the performance of the sort
-    by a factor of 2.
-*/
-exports.by = function (relation) {
-    var compare = arguments[1];
-    if (exports.no(compare))
-        compare = exports.compare;
-    var comparator = function (a, b) {
-        a = relation(a);
-        b = relation(b);
-        return compare(a, b);
-    };
-    comparator.by = relation;
-    comparator.compare = compare;
-    return comparator;
-};
+/** Built-in value references. */
+var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
 
-exports.compare = exports.operator(2, 'compare', function (a, b) {
-    if (exports.no(a) !== exports.no(b))
-        return exports.no(b) - exports.no(a);
-    if (typeof a === "number" && typeof b === "number")
-        return a - b;
-    return exports.eq(a, b) ? 0 : exports.lt(a, b) ? -1 : 1;
-});
+/**
+ * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the raw `toStringTag`.
+ */
+function getRawTag(value) {
+  var isOwn = hasOwnProperty.call(value, symToStringTag),
+      tag = value[symToStringTag];
 
-/*** sort
-    an in-place array sorter that uses a deep comparison
-    function by default (compare), and improves performance if
-    you provide a comparator returned by "by", using a
-    Schwartzian transform.
-*/
-exports.sort = function (array, compare) {
-    if (exports.no(compare))
-        compare = exports.compare;
-    if (compare.by) {
-        /* schwartzian transform */
-        array.splice.apply(
-            array,
-            [0, array.length].concat(
-                array.map(function (value) {
-                    return [compare.by(value), value];
-                }).sort(function (a, b) {
-                    return compare.compare(a[0], b[0]);
-                }).map(function (pair) {
-                    return pair[1];
-                })
-            )
-        );
+  try {
+    value[symToStringTag] = undefined;
+    var unmasked = true;
+  } catch (e) {}
+
+  var result = nativeObjectToString.call(value);
+  if (unmasked) {
+    if (isOwn) {
+      value[symToStringTag] = tag;
     } else {
-        array.sort(compare);
+      delete value[symToStringTag];
     }
-    return array;
-};
+  }
+  return result;
+}
 
-/*** sorted
-    returns a sorted copy of an array using a deep
-    comparison function by default (compare), and
-    improves performance if you provide a comparator
-    returned by "by", using a Schwartzian transform.
-*/
-exports.sorted = function (array, compare) {
-    return exports.sort(exports.array.copy(array), compare);
-};
+module.exports = getRawTag;
 
-exports.reverse = function (array) {
-    return Array.prototype.reverse.call(array);
-};
+},{"./_Symbol":23}],27:[function(require,module,exports){
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
 
-exports.reversed = function (array) {
-    return exports.reverse(exports.array.copy(array));
-};
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var nativeObjectToString = objectProto.toString;
 
-exports.hash = exports.operator(1, 'hash', function (object) {
-    return '' + object;
-});
+/**
+ * Converts `value` to a string using `Object.prototype.toString`.
+ *
+ * @private
+ * @param {*} value The value to convert.
+ * @returns {string} Returns the converted string.
+ */
+function objectToString(value) {
+  return nativeObjectToString.call(value);
+}
 
-exports.unique = exports.operator(1, 'unique', function (array, eq, hash) {
-    var visited = {};
-    if (!eq) eq = exports.eq;
-    if (!hash) hash = exports.hash;
-    return array.filter(function (value) {
-        var bucket = exports.getset(visited, hash(value), []);
-        var finds = bucket.filter(function (other) {
-            return eq(value, other);
-        });
-        if (!finds.length)
-            bucket.push(value);
-        return !finds.length;
-    });
-});
+module.exports = objectToString;
 
-// string
+},{}],28:[function(require,module,exports){
+var freeGlobal = require('./_freeGlobal');
 
-exports.string = exports.operator(1, 'toString', function (object) {
-    return '' + object;
-});
+/** Detect free variable `self`. */
+var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
 
-exports.string.mul = function (string, n) {
-    return exports.range(n).map(function () {
-        return string;
-    }).join('');
-};
+/** Used as a reference to the global object. */
+var root = freeGlobal || freeSelf || Function('return this')();
 
-/*** escape
-    escapes all characters of a string that are
-    special to JavaScript and many other languages.
-    Recognizes all of the relevant
-    control characters and formats all other
-    non-printable characters as Hex byte escape
-    sequences or Unicode escape sequences depending
-    on their size.
+module.exports = root;
 
-    Pass ``true`` as an optional second argument and
-    ``escape`` produces valid contents for escaped
-    JSON strings, wherein non-printable-characters are
-    all escaped with the Unicode ``\u`` notation.
-*/
-/* more Steve Levithan flagrence */
-var escapeExpression = /[^ !#-[\]-~]/g;
-/* from Doug Crockford's JSON library */
-var escapePatterns = {
-    '\b': '\\b',    '\t': '\\t',
-    '\n': '\\n',    '\f': '\\f',    '\r': '\\r',
-    '"' : '\\"',    '\\': '\\\\'
-};
-exports.escape = function (value, strictJson) {
-    if (typeof value != "string")
-        throw new Error(
-            module.path +
-            "#escape: requires a string.  got " +
-            exports.repr(value)
-        );
-    return value.replace(
-        escapeExpression, 
-        function (match) {
-            if (escapePatterns[match])
-                return escapePatterns[match];
-            match = match.charCodeAt();
-            if (!strictJson && match < 256)
-                return "\\x" + exports.padBegin(match.toString(16), 2);
-            return '\\u' + exports.padBegin(match.toString(16), 4);
-        }
-    );
-};
+},{"./_freeGlobal":25}],29:[function(require,module,exports){
+var isObject = require('./isObject'),
+    now = require('./now'),
+    toNumber = require('./toNumber');
 
-/*** enquote
-    transforms a string into a string literal, escaping
-    all characters of a string that are special to
-    JavaScript and and some other languages.
+/** Error message constants. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/* Built-in method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max,
+    nativeMin = Math.min;
+
+/**
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked. The debounced function comes with a `cancel` method to cancel
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
+ * Provide `options` to indicate whether `func` should be invoked on the
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+ * with the last arguments provided to the debounced function. Subsequent
+ * calls to the debounced function return the result of the last `func`
+ * invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the debounced function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.debounce` and `_.throttle`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} [wait=0] The number of milliseconds to delay.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=false]
+ *  Specify invoking on the leading edge of the timeout.
+ * @param {number} [options.maxWait]
+ *  The maximum time `func` is allowed to be delayed before it's invoked.
+ * @param {boolean} [options.trailing=true]
+ *  Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // Avoid costly calculations while the window size is in flux.
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
+ *   'leading': true,
+ *   'trailing': false
+ * }));
+ *
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', debounced);
+ *
+ * // Cancel the trailing debounced invocation.
+ * jQuery(window).on('popstate', debounced.cancel);
+ */
+function debounce(func, wait, options) {
+  var lastArgs,
+      lastThis,
+      maxWait,
+      result,
+      timerId,
+      lastCallTime,
+      lastInvokeTime = 0,
+      leading = false,
+      maxing = false,
+      trailing = true;
+
+  if (typeof func != 'function') {
+    throw new TypeError(FUNC_ERROR_TEXT);
+  }
+  wait = toNumber(wait) || 0;
+  if (isObject(options)) {
+    leading = !!options.leading;
+    maxing = 'maxWait' in options;
+    maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
+    trailing = 'trailing' in options ? !!options.trailing : trailing;
+  }
+
+  function invokeFunc(time) {
+    var args = lastArgs,
+        thisArg = lastThis;
+
+    lastArgs = lastThis = undefined;
+    lastInvokeTime = time;
+    result = func.apply(thisArg, args);
+    return result;
+  }
+
+  function leadingEdge(time) {
+    // Reset any `maxWait` timer.
+    lastInvokeTime = time;
+    // Start the timer for the trailing edge.
+    timerId = setTimeout(timerExpired, wait);
+    // Invoke the leading edge.
+    return leading ? invokeFunc(time) : result;
+  }
+
+  function remainingWait(time) {
+    var timeSinceLastCall = time - lastCallTime,
+        timeSinceLastInvoke = time - lastInvokeTime,
+        timeWaiting = wait - timeSinceLastCall;
 
-    ``enquote`` uses double quotes to be JSON compatible.
+    return maxing
+      ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
+      : timeWaiting;
+  }
 
-    Pass ``true`` as an optional second argument to
-    be strictly JSON compliant, wherein all
-    non-printable-characters are represented with
-    Unicode escape sequences.
-*/
-exports.enquote = function (value, strictJson) {
-    return '"' + exports.escape(value, strictJson) + '"';
-};
+  function shouldInvoke(time) {
+    var timeSinceLastCall = time - lastCallTime,
+        timeSinceLastInvoke = time - lastInvokeTime;
 
-/*** expand
-    transforms tabs to an equivalent number of spaces.
-*/
-// TODO special case for \r if it ever matters
-exports.expand = function (str, tabLength) {
-    str = String(str);
-    tabLength = tabLength || 4;
-    var output = [],
-        tabLf = /[\t\n]/g,
-        lastLastIndex = 0,
-        lastLfIndex = 0,
-        charsAddedThisLine = 0,
-        tabOffset, match;
-    while (match = tabLf.exec(str)) {
-        if (match[0] == "\t") {
-            tabOffset = (
-                tabLength - 1 -
-                (
-                    (match.index - lastLfIndex) +
-                    charsAddedThisLine
-                ) % tabLength
-            );
-            charsAddedThisLine += tabOffset;
-            output.push(
-                str.slice(lastLastIndex, match.index) +
-                exports.mul(" ", tabOffset + 1)
-            );
-        } else if (match[0] === "\n") {
-            output.push(str.slice(lastLastIndex, tabLf.lastIndex));
-            lastLfIndex = tabLf.lastIndex;
-            charsAddedThisLine = 0;
-        }
-        lastLastIndex = tabLf.lastIndex;
+    // Either this is the first call, activity has stopped and we're at the
+    // trailing edge, the system time has gone backwards and we're treating
+    // it as the trailing edge, or we've hit the `maxWait` limit.
+    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
+      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
+  }
+
+  function timerExpired() {
+    var time = now();
+    if (shouldInvoke(time)) {
+      return trailingEdge(time);
     }
-    return output.join("") + str.slice(lastLastIndex);
-};
+    // Restart the timer.
+    timerId = setTimeout(timerExpired, remainingWait(time));
+  }
 
-var trimBeginExpression = /^\s\s*/g;
-exports.trimBegin = function (value) {
-    return String(value).replace(trimBeginExpression, "");  
-};
+  function trailingEdge(time) {
+    timerId = undefined;
 
-var trimEndExpression = /\s\s*$/g;
-exports.trimEnd = function (value) {
-    return String(value).replace(trimEndExpression, "");    
-};
+    // Only invoke if we have `lastArgs` which means `func` has been
+    // debounced at least once.
+    if (trailing && lastArgs) {
+      return invokeFunc(time);
+    }
+    lastArgs = lastThis = undefined;
+    return result;
+  }
 
-exports.trim = function (value) {
-    return String(value).replace(trimBeginExpression, "").replace(trimEndExpression, "");
-};
+  function cancel() {
+    if (timerId !== undefined) {
+      clearTimeout(timerId);
+    }
+    lastInvokeTime = 0;
+    lastArgs = lastCallTime = lastThis = timerId = undefined;
+  }
 
-/* generates padBegin and padEnd */
-var augmentor = function (augment) {
-    return function (value, length, pad) {
-        if (exports.no(pad)) pad = '0';
-        if (exports.no(length)) length = 2;
-        value = String(value);
-        while (value.length < length) {
-            value = augment(value, pad);
-        }
-        return value;
-    };
-};
+  function flush() {
+    return timerId === undefined ? result : trailingEdge(now());
+  }
 
-/*** padBegin
+  function debounced() {
+    var time = now(),
+        isInvoking = shouldInvoke(time);
 
-    accepts:
-     - a `String` or `Number` value
-     - a minimum length of the resultant `String`:
-       by default, 2
-     - a pad string: by default, ``'0'``.
+    lastArgs = arguments;
+    lastThis = this;
+    lastCallTime = time;
 
-    returns a `String` of the value padded up to at least
-    the minimum length.  adds the padding to the begining
-    side of the `String`.
+    if (isInvoking) {
+      if (timerId === undefined) {
+        return leadingEdge(lastCallTime);
+      }
+      if (maxing) {
+        // Handle invocations in a tight loop.
+        clearTimeout(timerId);
+        timerId = setTimeout(timerExpired, wait);
+        return invokeFunc(lastCallTime);
+      }
+    }
+    if (timerId === undefined) {
+      timerId = setTimeout(timerExpired, wait);
+    }
+    return result;
+  }
+  debounced.cancel = cancel;
+  debounced.flush = flush;
+  return debounced;
+}
 
-*/
-exports.padBegin = augmentor(function (value, pad) {
-    return pad + value;
-});
+module.exports = debounce;
 
-/*** padEnd
+},{"./isObject":30,"./now":33,"./toNumber":34}],30:[function(require,module,exports){
+/**
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+function isObject(value) {
+  var type = typeof value;
+  return value != null && (type == 'object' || type == 'function');
+}
 
-    accepts:
-     - a `String` or `Number` value
-     - a minimum length of the resultant `String`:
-       by default, 2
-     - a pad string: by default, ``'0'``.
+module.exports = isObject;
 
-    returns a `String` of the value padded up to at least
-    the minimum length.  adds the padding to the end
-    side of the `String`.
+},{}],31:[function(require,module,exports){
+/**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+function isObjectLike(value) {
+  return value != null && typeof value == 'object';
+}
 
-*/
-exports.padEnd = augmentor(function (value, pad) {
-    return value + pad;
-});
+module.exports = isObjectLike;
 
-/*** splitName
-    splits a string into an array of words from an original
-    string.
-*/
-// thanks go to Steve Levithan for this regular expression
-// that, in addition to splitting any normal-form identifier
-// in any case convention, splits XMLHttpRequest into
-// "XML", "Http", and "Request"
-var splitNameExpression = /[a-z]+|[A-Z](?:[a-z]+|[A-Z]*(?![a-z]))|[.\d]+/g;
-exports.splitName = function (value) {
-    var result = String(value).match(splitNameExpression);
-    if (result)
-        return result;
-    return [value];
-};
+},{}],32:[function(require,module,exports){
+var baseGetTag = require('./_baseGetTag'),
+    isObjectLike = require('./isObjectLike');
 
-/*** joinName
-    joins a list of words with a given delimiter
-    between alphanumeric words.
-*/
-exports.joinName = function (delimiter, parts) {
-    if (exports.no(delimiter)) delimiter = '_';
-    parts.unshift([]);
-    return parts.reduce(function (parts, part) {
-        if (
-            part.match(/\d/) &&
-            exports.len(parts) && parts[parts.length-1].match(/\d/)
-        ) {
-            return parts.concat([delimiter + part]);
-        } else {
-            return parts.concat([part]);
-        }
-    }).join('');
-};
+/** `Object#toString` result references. */
+var symbolTag = '[object Symbol]';
 
-/*** upper
-    converts a name to ``UPPER CASE`` using
-    a given delimiter between numeric words.
+/**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+function isSymbol(value) {
+  return typeof value == 'symbol' ||
+    (isObjectLike(value) && baseGetTag(value) == symbolTag);
+}
 
-    see:
-     - `lower`
-     - `camel`
-     - `title`
+module.exports = isSymbol;
 
-*/
-exports.upper = function (value, delimiter) {
-    if (exports.no(delimiter))
-        return value.toUpperCase();
-    return exports.splitName(value).map(function (part) {
-        return part.toUpperCase();
-    }).join(delimiter);
+},{"./_baseGetTag":24,"./isObjectLike":31}],33:[function(require,module,exports){
+var root = require('./_root');
+
+/**
+ * Gets the timestamp of the number of milliseconds that have elapsed since
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Date
+ * @returns {number} Returns the timestamp.
+ * @example
+ *
+ * _.defer(function(stamp) {
+ *   console.log(_.now() - stamp);
+ * }, _.now());
+ * // => Logs the number of milliseconds it took for the deferred invocation.
+ */
+var now = function() {
+  return root.Date.now();
 };
 
-/*** lower
-    converts a name to a ``lower case`` using
-    a given delimiter between numeric words.
+module.exports = now;
 
-    see:
-     - `upper`
-     - `camel`
-     - `title`
+},{"./_root":28}],34:[function(require,module,exports){
+var isObject = require('./isObject'),
+    isSymbol = require('./isSymbol');
 
-*/
-exports.lower = function (value, delimiter) {
-    if (exports.no(delimiter))
-        return String(value).toLowerCase();
-    return exports.splitName(value).map(function (part) {
-        return part.toLowerCase();
-    }).join(delimiter);
-};
+/** Used as references for various `Number` constants. */
+var NAN = 0 / 0;
 
-/*** camel
-    converts a name to ``camel Case`` using
-    a given delimiter between numeric words.
+/** Used to match leading and trailing whitespace. */
+var reTrim = /^\s+|\s+$/g;
 
-    see:
-     - `lower`
-     - `upper`
-     - `title`
+/** Used to detect bad signed hexadecimal string values. */
+var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
 
-*/
-exports.camel = function (value, delimiter) {
-    return exports.joinName(
-        delimiter,
-        exports.mapApply(
-            exports.enumerate(exports.splitName(value)),
-            function (n, part) {
-                if (n) {
-                    return (
-                        part.substring(0, 1).toUpperCase() +
-                        part.substring(1).toLowerCase()
-                    );
-                } else {
-                    return part.toLowerCase();
-                }
-            }
-        )
-    );
-};
+/** Used to detect binary string values. */
+var reIsBinary = /^0b[01]+$/i;
 
-/*** title
-    converts a name to ``Title Case`` using
-    a given delimiter between numeric words.
+/** Used to detect octal string values. */
+var reIsOctal = /^0o[0-7]+$/i;
 
-    see:
-     - `lower`
-     - `upper`
-     - `camel`
+/** Built-in method references without a dependency on `root`. */
+var freeParseInt = parseInt;
 
-*/
-exports.title = function (value, delimiter) {
-    return exports.joinName(
-        delimiter,
-        exports.splitName(value).map(function (part) {
-            return (
-                part.substring(0, 1).toUpperCase() +
-                part.substring(1).toLowerCase()
-            );
-        })
-    );
-};
+/**
+ * Converts `value` to a number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
+ * @example
+ *
+ * _.toNumber(3.2);
+ * // => 3.2
+ *
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3.2');
+ * // => 3.2
+ */
+function toNumber(value) {
+  if (typeof value == 'number') {
+    return value;
+  }
+  if (isSymbol(value)) {
+    return NAN;
+  }
+  if (isObject(value)) {
+    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+    value = isObject(other) ? (other + '') : other;
+  }
+  if (typeof value != 'string') {
+    return value === 0 ? value : +value;
+  }
+  value = value.replace(reTrim, '');
+  var isBinary = reIsBinary.test(value);
+  return (isBinary || reIsOctal.test(value))
+    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+    : (reIsBadHex.test(value) ? NAN : +value);
+}
 
+module.exports = toNumber;
 
-},{}],23:[function(require,module,exports){
+},{"./isObject":30,"./isSymbol":32}],35:[function(require,module,exports){
 (function (setImmediate){
 "use strict";
 
@@ -5899,8 +6403,22 @@ var WILDFIRE = exports.WILDFIRE = require("./wildfire");
 
 var LIB = require("./lib");
 
-WILDFIRE.VERBOSE = true;
-WILDFIRE.once("error", function (err) {
+var COMPONENT = require("./component");
+
+var comp = COMPONENT.for({
+  browser: WINDOW.crossbrowser,
+  getOwnTabId: function () {
+    if (!currentContext) {
+      return null;
+    }
+
+    return currentContext.tabId;
+  }
+});
+var wildfire = new WILDFIRE.Client(comp, {
+  verbose: false
+});
+wildfire.once("error", function (err) {
   console.error(err);
 });
 
@@ -5931,18 +6449,40 @@ async function initCurrentContext() {
 }
 
 setImmediate(initCurrentContext);
+var serverUrl = null;
 
 function broadcastForContext(context, message) {
   message.context = context;
-  message.to = "message-listener";
+  message.to = message.forceTo || "message-listener";
+  comp.handleBroadcastMessage(message);
   return LIB.browser.runtime.sendMessage(message).catch(function (err) {
-    if (WILDFIRE.VERBOSE) console.log("WARNING", err);
+    if (wildfire.VERBOSE) console.log("WARNING", err);
   });
 }
 
-WILDFIRE.on("message.firephp", function (message) {
-  if (WILDFIRE.VERBOSE) console.log("[background] WILDFIRE.on -| message.firephp (message):", message);
+wildfire.on("message.firephp", function (message) {
+  if (wildfire.VERBOSE) console.log("[background] WILDFIRE.on -| message.firephp (message):", message);
+
+  if (message.context.serverUrl) {
+    serverUrl = message.context.serverUrl;
+  }
+
+  broadcastForContext(message.context, {
+    message: {
+      sender: message.sender,
+      receiver: message.receiver,
+      meta: message.meta,
+      data: message.data
+    }
+  });
+});
+wildfire.on("message.insight.selective", function (message) {
+  if (wildfire.VERBOSE) console.log("[background] WILDFIRE.on -| message.insight.selective (message):", message);
+});
+wildfire.on("message.insight.request", function (message) {
+  if (wildfire.VERBOSE) console.log("[background] WILDFIRE.on -| message.insight.request (message):", message);
   broadcastForContext(message.context, {
+    forceTo: "protocol",
     message: {
       sender: message.sender,
       receiver: message.receiver,
@@ -5957,7 +6497,9 @@ var lastDetailsForTabId = {};
 function setCurrentContextFromDetails(details, clearIfNew) {
   if (!details) {
     if (currentContext) {
+      console.log("CLEAR CONTEXT", "reset serverUrl");
       currentContext = null;
+      serverUrl = null;
       broadcastForContext(currentContext, {
         event: "currentContext"
       });
@@ -5971,6 +6513,8 @@ function setCurrentContextFromDetails(details, clearIfNew) {
     newCtx.hostname = details.url.replace(/^[^:]+:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1");
 
     if (newCtx !== currentContext && (!newCtx || !currentContext || newCtx.pageUid !== currentContext.pageUid)) {
+      console.log("NEW CONTEXT", "reset serverUrl", currentContext, newCtx);
+      serverUrl = null;
       currentContext = newCtx;
       lastDetailsForTabId[currentContext.tabId] = details;
       broadcastForContext(currentContext, {
@@ -5991,7 +6535,7 @@ function setCurrentContextFromDetails(details, clearIfNew) {
 }
 
 async function runtime_onMessage(message) {
-  if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.runtime -| onMessage (message):", message);
+  if (wildfire.VERBOSE) console.log("[background] BROWSER.runtime -| onMessage (message):", message);
 
   if (message.to === "broadcast") {
     if (message.event === "currentContext") {
@@ -6012,51 +6556,88 @@ async function runtime_onMessage(message) {
       LIB.browser.tabs.reload(message.context.tabId, {
         bypassCache: true
       });
+    } else if (message.event === "load-file") {
+      console.log("LOAD FILE FROM:::", serverUrl);
+      var file = message.file;
+      var line = message.line;
+
+      if (!serverUrl) {
+        console.log("SLIP LOAD FILE FROM::: DUE TO NO serverUrl");
+        return;
+      }
+
+      try {
+        var response = await wildfire.callServer(serverUrl, {
+          target: 'Insight_Plugin_FileViewer',
+          action: 'GetFile',
+          args: {
+            path: file
+          }
+        });
+        console.log("SERVER response:", response);
+
+        if (!response) {
+          return;
+        }
+
+        broadcastForContext(currentContext || null, {
+          action: "show-file",
+          args: {
+            file: file,
+            line: line,
+            content: response
+          }
+        });
+      } catch (err) {
+        console.error("Error calling server:", err);
+      }
     }
   }
 }
 
 BROWSER.runtime.onMessage.addListener(runtime_onMessage);
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
   BROWSER.runtime.onMessage.removeListener(runtime_onMessage);
 });
 
 function webNavigation_onBeforeNavigate(details) {
-  if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.webNavigation -| onBeforeNavigate (details):", details);
+  if (wildfire.VERBOSE) console.log("[background] BROWSER.webNavigation -| onBeforeNavigate (details):", details);
 
   if (details.parentFrameId !== -1) {
     return;
   }
 
+  console.log("ON BEFORE NAVIGATE", details);
   setCurrentContextFromDetails(details);
 }
 
 BROWSER.webNavigation.onBeforeNavigate.addListener(webNavigation_onBeforeNavigate, {
   url: [{}]
 });
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
   BROWSER.webNavigation.onBeforeNavigate.removeListener(webNavigation_onBeforeNavigate);
 });
 
 function webRequest_onBeforeRequest(details) {
-  if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.webRequest -| onBeforeRequest (details):", details);
+  if (wildfire.VERBOSE) console.log("[background] BROWSER.webRequest -| onBeforeRequest (details):", details);
 
   if (typeof details.documentUrl !== "undefined" || typeof details.initiator !== "undefined" || details.parentFrameId !== -1) {
     return;
   }
 
+  console.log("ON BEFORE REQUEST", details);
   setCurrentContextFromDetails(details, true);
 }
 
 BROWSER.webRequest.onBeforeRequest.addListener(webRequest_onBeforeRequest, {
   urls: ["<all_urls>"]
 });
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
   BROWSER.webRequest.onBeforeRequest.removeListener(webRequest_onBeforeRequest);
 });
 
 function tabs_onRemoved(tabId) {
-  if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.tabs -| onRemoved (tabId):", tabId);
+  if (wildfire.VERBOSE) console.log("[background] BROWSER.tabs -| onRemoved (tabId):", tabId);
 
   if (currentContext && currentContext.tabId == tabId) {
     setCurrentContextFromDetails(null);
@@ -6070,539 +6651,885 @@ function tabs_onRemoved(tabId) {
 }
 
 BROWSER.tabs.onRemoved.addListener(tabs_onRemoved);
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
   BROWSER.tabs.onRemoved.removeListener(tabs_onRemoved);
 });
 }).call(this,require("timers").setImmediate)
-},{"./lib":26,"./wildfire":30,"timers":38}],24:[function(require,module,exports){
+},{"./component":38,"./lib":39,"./wildfire":40,"timers":49}],36:[function(require,module,exports){
 "use strict";
 
-exports.for = function (API) {
-  var processor = null;
-  var requestIndex = 0;
+exports.forAPI = function (API) {
+  class RequestObserver {
+    constructor(onRequestHandler) {
+      var self = this;
+      API.on("destroy", function () {
+        self.ensureUnhooked();
+      });
+      var requestIndex = 0;
+      var isHooked = false;
+
+      function onRequest(request) {
+        if (API.VERBOSE) console.log("[http-request-observer] onRequest (request):", request);
+        var requestId = null;
+        var headers = {};
+        request.requestHeaders.forEach(function (header) {
+          if (header.name.toLowerCase() === "x-request-id") {
+            requestId = header.value;
+          }
 
-  function onRequest(request) {
-    var requestId = null;
-    var headers = {};
-    request.requestHeaders.forEach(function (header) {
-      if (header.name.toLowerCase() === "x-request-id") {
-        requestId = header.value;
-      }
+          headers[header.name] = header.value;
+        });
+        requestIndex += 1;
+        var result = onRequestHandler({
+          "id": requestId || "id:" + request.url + ":" + requestIndex,
+          "url": request.url,
+          "hostname": request.url.replace(/^https?:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
+          "port": request.url.replace(/^https?:\/\/[^:]+:?(\d+)?\/.*?$/, "$1") || 80,
+          "method": request.method,
+          "headers": headers
+        });
 
-      headers[header.name] = header.value;
-    });
-    var result = processor({
-      "id": requestId || "id:" + request.url + ":" + requestIndex,
-      "url": request.url,
-      "hostname": request.url.replace(/^https?:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
-      "port": request.url.replace(/^https?:\/\/[^:]+:?(\d+)?\/.*?$/, "$1") || 80,
-      "method": request.method,
-      "headers": headers
-    });
+        if (!result || !result.requestHeaders) {
+          return {};
+        }
 
-    if (!result) {
-      return {};
-    }
+        return {
+          requestHeaders: Object.keys(result.requestHeaders).map(function (name) {
+            return {
+              name: name,
+              value: result.requestHeaders[name]
+            };
+          })
+        };
+      }
 
-    var changes = result;
-    var ret = {};
+      self.ensureHooked = function () {
+        if (!isHooked) {
+          API.BROWSER.webRequest.onBeforeSendHeaders.addListener(onRequest, {
+            urls: ["<all_urls>"]
+          }, ["blocking", "requestHeaders"]);
+          isHooked = true;
+        }
+      };
 
-    if (changes.requestHeaders) {
-      var headers = [];
-      Object.keys(changes.requestHeaders).forEach(function (name) {
-        headers.push({
-          name: name,
-          value: changes.requestHeaders[name]
-        });
-      });
-      ret.requestHeaders = headers;
+      self.ensureUnhooked = function () {
+        API.BROWSER.webRequest.onBeforeSendHeaders.removeListener(onRequest);
+        isHooked = false;
+      };
     }
 
-    return ret;
   }
 
-  API.BROWSER.webRequest.onBeforeSendHeaders.addListener(onRequest, {
-    urls: ["<all_urls>"]
-  }, ["blocking", "requestHeaders"]);
-  API.on("destroy", function () {
-    API.BROWSER.webRequest.onBeforeSendHeaders.removeListener(onRequest);
-  });
-  return {
-    register: function (_processor) {
-      processor = _processor;
-    }
-  };
+  return RequestObserver;
 };
-},{}],25:[function(require,module,exports){
+},{}],37:[function(require,module,exports){
 "use strict";
 
-exports.for = function (API) {
-  var pageUrlByTabId = {};
-  var pageTimestampByTabId = {};
-
-  function onHeadersReceived(response) {
-    var pageUrl = response.documentUrl || response.url;
-    var pageTimeStamp = response.timeStamp;
+exports.forAPI = function (API) {
+  class ResponseObserver {
+    constructor(onResponseHandler) {
+      var self = this;
+      API.on("destroy", function () {
+        self.ensureUnhooked();
+      });
+      var isHooked = false;
+      var pageUrlByTabId = {};
+      var pageTimestampByTabId = {};
+
+      function onHeadersReceived(response) {
+        if (API.VERBOSE) console.log("[http-response-observer] onHeadersReceived (response):", response);
+        var pageUrl = response.documentUrl || response.url;
+        var pageTimeStamp = response.timeStamp;
+
+        if (response.parentFrameId !== -1) {
+          pageUrl = pageUrlByTabId[response.tabId] || null;
+          pageTimeStamp = pageTimestampByTabId[response.tabId] || null;
+        } else if (response.type === "main_frame") {
+            pageUrlByTabId[response.tabId] = pageUrl;
+            pageTimestampByTabId[response.tabId] = pageTimeStamp;
+          }
 
-    if (response.parentFrameId !== -1) {
-      pageUrl = pageUrlByTabId[response.tabId] || null;
-      pageTimeStamp = pageTimestampByTabId[response.tabId] || null;
-    } else if (response.type === "main_frame") {
-        pageUrlByTabId[response.tabId] = pageUrl;
-        pageTimestampByTabId[response.tabId] = pageTimeStamp;
+        onResponseHandler({
+          "request": {
+            "id": response.requestId,
+            "context": {
+              frameId: response.frameId,
+              tabId: response.tabId,
+              url: response.url,
+              hostname: response.url.replace(/^[^:]+:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
+              requestId: response.requestId,
+              requestType: response.type,
+              documentUrl: response.documentUrl,
+              timeStamp: response.timeStamp,
+              pageUrl: pageUrl,
+              pageTimeStamp: pageTimeStamp,
+              pageUid: JSON.stringify({
+                url: pageUrl,
+                tabId: response.tabId
+              }),
+              requestUid: JSON.stringify({
+                url: response.url,
+                timeStamp: response.timeStamp,
+                frameId: response.frameId,
+                tabId: response.tabId
+              })
+            }
+          },
+          "status": response.statusCode,
+          "headers": response.responseHeaders
+        });
       }
 
-    API.emit("http.response", {
-      "request": {
-        "id": response.requestId,
-        "context": {
-          frameId: response.frameId,
-          tabId: response.tabId,
-          url: response.url,
-          hostname: response.url.replace(/^[^:]+:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
-          requestId: response.requestId,
-          requestType: response.type,
-          documentUrl: response.documentUrl,
-          timeStamp: response.timeStamp,
-          pageUrl: pageUrl,
-          pageTimeStamp: pageTimeStamp,
-          pageUid: JSON.stringify({
-            url: pageUrl,
-            tabId: response.tabId
-          }),
-          requestUid: JSON.stringify({
-            url: response.url,
-            timeStamp: response.timeStamp,
-            frameId: response.frameId,
-            tabId: response.tabId
-          })
+      self.ensureHooked = function () {
+        if (!isHooked) {
+          API.BROWSER.webRequest.onHeadersReceived.addListener(onHeadersReceived, {
+            urls: ["<all_urls>"]
+          }, ["responseHeaders"]);
+          isHooked = true;
         }
-      },
-      "status": response.statusCode,
-      "headers": response.responseHeaders
-    });
+      };
+
+      self.ensureUnhooked = function () {
+        API.BROWSER.webRequest.onHeadersReceived.removeListener(onHeadersReceived);
+        isHooked = false;
+        pageUrlByTabId = {};
+        pageTimestampByTabId = {};
+      };
+    }
+
   }
 
-  API.BROWSER.webRequest.onHeadersReceived.addListener(onHeadersReceived, {
-    urls: ["<all_urls>"]
-  }, ["responseHeaders"]);
-  API.on("destroy", function () {
-    API.BROWSER.webRequest.onHeadersReceived.removeListener(onHeadersReceived);
-  });
-  return {};
+  return ResponseObserver;
 };
-},{}],26:[function(require,module,exports){
+},{}],38:[function(require,module,exports){
+(function (setImmediate){
 "use strict";
 
-exports.browser = window.crossbrowser;
-},{}],27:[function(require,module,exports){
-"use strict";
+var EVENTS = require("events");
 
-exports.for = function (API) {
-  var transportReceiver1 = API.WILDFIRE.Receiver();
-  transportReceiver1.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
-  transportReceiver1.addListener({
-    onMessageReceived: function (request, message) {
-      message.context = request.context;
+var DEBOUNCE = require('lodash/debounce');
+
+exports.for = function (ctx) {
+  var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
+  events.currentContext = null;
+  setImmediate(function () {
+    onContextMessage(null);
+    broadcastCurrentContext();
+  });
+  var contextChangeAcknowledged = false;
+
+  events.contextChangeAcknowledged = function () {
+    contextChangeAcknowledged = true;
+  };
+
+  function onContextMessage(context) {
+    if (context !== events.currentContext && (!events.currentContext || !context || context.pageUid !== events.currentContext.pageUid)) {
+      events.currentContext = context;
+      contextChangeAcknowledged = false;
+    }
+
+    if (!contextChangeAcknowledged) {
+      events.emit("changed.context", events.currentContext);
+    }
+  }
 
-      if (API.on && API.on.firePHPMessage) {
-        API.on.firePHPMessage(message);
+  events.handleBroadcastMessage = function (message) {
+    try {
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
+
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
+        }
       }
+    } catch (err) {
+      console.error(err);
     }
-  });
-  API.httpHeaderChannel.addReceiver(transportReceiver1);
-  var transportReceiver2 = API.WILDFIRE.Receiver();
-  transportReceiver2.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1");
-  transportReceiver2.addListener({
-    onMessageReceived: function (request, message) {
-      message.context = request.context;
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
+  ctx.browser.storage.onChanged.addListener(function (changes, area) {
+    try {
+      if (!events.currentContext) {
+        return;
+      }
+
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
+
+      for (var item of Object.keys(changes)) {
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
-      if (API.on && API.on.firePHPMessage) {
-        API.on.firePHPMessage(message);
+        if (item.substring(0, prefix.length) === prefix) {
+          var name = item.substring(prefix.length);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
+        }
       }
+    } catch (err) {
+      console.error(err);
     }
   });
-  API.httpHeaderChannel.addReceiver(transportReceiver2);
-  return {};
-};
-},{}],28:[function(require,module,exports){
-"use strict";
+  var broadcastCurrentContext = DEBOUNCE(function () {
+    ctx.browser.runtime.sendMessage({
+      to: "broadcast",
+      event: "currentContext"
+    });
+  }, 250);
 
-exports.for = function (API) {
-  var receivers = {
-    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/controller/0": {},
-    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/plugin/0": {},
-    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/package/0": {},
-    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/selective/0": {},
-    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/request/0": {},
-    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/page/0": {
-      messageHandler: "insightMessage"
-    },
-    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/process/0": {}
+  events.getSetting = async function (name) {
+    if (!events.currentContext) {
+      return Promise.resolve(null);
+    }
+
+    return events._getSettingForHostname(events.currentContext.hostname, name);
   };
-  Object.keys(receivers).forEach(function (uri) {
-    var receiver = API.WILDFIRE.Receiver();
-    receiver.setId(uri);
-    receiver.addListener({
-      onMessageReceived: function (request, message) {
-        message.context = request.context;
 
-        if (receivers[uri].messageHandler && API.on && API.on[receivers[uri].messageHandler]) {
-          API.on[receivers[uri].messageHandler](message);
-        } else {
-          console.log("IGNORING insight MESSAGE:", message);
-        }
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (typeof ctx.browser === "undefined") {
+      return Promise.resolve(null);
+    }
+
+    var key = "hostname[" + hostname + "]." + name;
+    return ctx.browser.storage.local.get(key).then(function (value) {
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
       }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
     });
-    API.httpHeaderChannel.addReceiver(receiver);
-  });
-  return {};
-};
-},{}],29:[function(require,module,exports){
-"use strict";
+  };
 
-var LIB = require("./lib");
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
 
-var domainSettingsCache = {};
-var settingsByHostnameCache = {};
-
-if (LIB.browser) {
-  LIB.browser.storage.onChanged.addListener(function (changes, area) {
-    for (var item of Object.keys(changes)) {
-      if (!/^domain\[.+\]\..+$/.test(item)) continue;
-      domainSettingsCache[item] = changes[item].newValue;
-      var m = item.match(/^domain\[([^\]]+)\]\.(.+)$/);
-
-      if (m) {
-        if (!settingsByHostnameCache[m[1]]) {
-          settingsByHostnameCache[m[1]] = {
-            "enabled": false,
-            "enableUserAgentHeader": false,
-            "enableFirePHPHeader": false,
-            "enableChromeLoggerData": false
-          };
-        }
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
+    if (!events.currentContext) {
+      throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
+    }
 
-        settingsByHostnameCache[m[1]][m[2]] = changes[item].newValue;
+    return events._setSettingForHostname(events.currentContext.hostname, name, value);
+  };
+
+  events._setSettingForHostname = async function (hostname, name, value) {
+    if (typeof ctx.browser === "undefined") {
+      return Promise.resolve(null);
+    }
+
+    return events._getSettingForHostname(hostname, name).then(function (existingValue) {
+      if (value === existingValue) {
+        return;
       }
+
+      var obj = {};
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
+    });
+  };
+
+  events.getGlobalSetting = async function (name) {
+    if (typeof ctx.browser === "undefined") {
+      return null;
     }
-  });
-}
 
-exports.getSetting = function (name) {
-  function get() {
-    if (!LIB.browser) return Promise.resolve(null);
-    return LIB.browser.storage.local.get(name).then(function (value) {
-      var m = name.match(/^domain\[([^\]]+)\]\.(.+)$/);
-
-      if (m) {
-        if (!settingsByHostnameCache[m[1]]) {
-          settingsByHostnameCache[m[1]] = {
-            "enabled": false,
-            "enableUserAgentHeader": false,
-            "enableFirePHPHeader": false,
-            "enableChromeLoggerData": false
-          };
+    var defaultValue;
+
+    if (name === "reloadOnEnable") {
+      defaultValue = true;
+    }
+
+    return ctx.browser.storage.local.get(name).then(function (value) {
+      if (typeof value[name] === "undefined") {
+        if (typeof defaultValue !== "undefined") {
+          return defaultValue;
         }
 
-        settingsByHostnameCache[m[1]][m[2]] = value[name] || false;
+        return null;
       }
 
-      return domainSettingsCache[name] = value[name];
+      return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
     });
-  }
-
-  if (typeof domainSettingsCache[name] === "undefined") {
-    get();
-    return Promise.resolve(false);
-  }
+  };
 
-  return get();
-};
+  events.setGlobalSetting = async function (name, value) {
+    if (typeof ctx.browser === "undefined") {
+      return null;
+    }
 
-exports.setSetting = function (name, value) {
-  if (!LIB.browser) return Promise.resolve(null);
-  return LIB.browser.storage.local.set(name, value);
-};
+    return events.getGlobalSetting(name).then(function (existingValue) {
+      if (value === existingValue) {
+        return;
+      }
 
-exports.getDomainSettingsForDomain = function (domain) {
-  return exports.getSetting("domain[" + domain + "].enabled").then(function (enabled) {
-    return exports.getSetting("domain[" + domain + "].enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return exports.getSetting("domain[" + domain + "].enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return exports.getSetting("domain[" + domain + "].enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return Promise.resolve({
-            "enabled": enabled,
-            "enableUserAgentHeader": enableUserAgentHeader,
-            "enableFirePHPHeader": enableFirePHPHeader,
-            "enableChromeLoggerData": enableChromeLoggerData
-          });
-        });
-      });
+      var obj = {};
+      obj[name] = value;
+      globalSettings[name] = value;
+      return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
     });
-  });
-};
+  };
 
-exports.isEnabledForDomain = function (domain) {
-  return exports.getDomainSettingsForDomain(domain).then(function (settings) {
-    return settings.enabled && (settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData);
-  });
-};
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
 
-exports.getDomainSettingsForRequest = function (request) {
-  return exports.getDomainSettingsForDomain(request.hostname).then(function (settings) {
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
     return settings;
-  });
-};
+  };
 
-exports.getDomainSettingsForRequestSync = function (request) {
-  if (!exports.getDomainSettingsForRequestSync._fetchedOnce) {
-    exports.getDomainSettingsForRequestSync._fetchedOnce = {};
-  }
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
 
-  if (!exports.getDomainSettingsForRequestSync._fetchedOnce[request.hostname]) {
-    exports.getDomainSettingsForRequestSync._fetchedOnce[request.hostname] = true;
-    exports.getDomainSettingsForRequest(request);
-  }
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
 
-  if (!settingsByHostnameCache[request.hostname]) {
-    settingsByHostnameCache[request.hostname] = {
-      "enabled": false,
-      "enableUserAgentHeader": false,
-      "enableFirePHPHeader": false,
-      "enableChromeLoggerData": false
-    };
-  }
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
+  };
+
+  events.reloadBrowser = function () {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "reload",
+      context: {
+        tabId: ctx.browser.devtools.inspectedWindow.tabId
+      }
+    });
+  };
+
+  events.clearConsole = function () {
+    ctx.browser.runtime.sendMessage({
+      to: "broadcast",
+      event: "clear"
+    });
+  };
+
+  events.showView = function (name, args) {
+    if (name === "manage") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "manage"
+      });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
+    }
+  };
+
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
+    }
+  };
+
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
 
-  return settingsByHostnameCache[request.hostname];
+  return events;
 };
-},{"./lib":26}],30:[function(require,module,exports){
+}).call(this,require("timers").setImmediate)
+},{"events":46,"lodash/debounce":29,"timers":49}],39:[function(require,module,exports){
+"use strict";
+
+exports.browser = window.crossbrowser;
+},{}],40:[function(require,module,exports){
 "use strict";
 
 var EVENTS = require("eventemitter2");
 
-var API = module.exports = new EVENTS();
-var BROWSER = typeof browser != "undefined" ? browser : chrome;
-API.console = console;
-API.BROWSER = BROWSER;
-API.WILDFIRE = require("wildfire-for-js");
+var ENCODER = require("insight-for-js/lib/encoder/default");
 
-var REQUEST_OBSERVER = require("./adapters/http-request-observer").for(API);
+exports.Client = function (comp, options) {
+  options = options || {};
+  var API = new EVENTS();
+  API.console = console;
+  API.BROWSER = comp.browser;
+  API.VERBOSE = options.verbose || false;
+  API.WILDFIRE = require("wildfire-for-js");
 
-var RESPONSE_OBSERVER = require("./adapters/http-response-observer").for(API);
+  var REQUEST_OBSERVER = require("./adapters/http-request-observer").forAPI(API);
 
-var ENCODER = require("insight-for-js/lib/encoder/default");
+  var RESPONSE_OBSERVER = require("./adapters/http-response-observer").forAPI(API);
 
-var encoder = ENCODER.Encoder();
-encoder.setOption("maxObjectDepth", 1000);
-encoder.setOption("maxArrayDepth", 1000);
-encoder.setOption("maxOverallDepth", 1000);
+  var forceEnabled = false;
 
-var SETTINGS = require("./settings");
+  API.forcedEnable = function (oo) {
+    forceEnabled = oo;
+  };
 
-var forceEnabled = false;
+  comp.on("changed.context", function () {
+    comp.contextChangeAcknowledged();
+    syncListeners();
+  });
+  comp.on("changed.setting", function () {
+    syncListeners();
+  });
 
-API.forcedEnable = function (oo) {
-  forceEnabled = oo;
-};
+  async function syncListeners() {
+    var enabled = await comp.isEnabled();
 
-API.on.chromeLoggerMessage = function (message, context) {
-  try {
-    var i, ic, j, jc;
+    if (enabled) {
+      ensureListenersHooked();
+    } else {
+      ensureListenersUnhooked();
+    }
+  }
 
-    var _loop = function () {
-      var log = {};
+  function ensureListenersHooked() {
+    requestObserver.ensureHooked();
+    responseObserver.ensureHooked();
+  }
 
-      for (j = 0, jc = message.columns.length; j < jc; j++) {
-        log[message.columns[j]] = message.rows[i][j];
-      }
+  function ensureListenersUnhooked() {
+    requestObserver.ensureUnhooked();
+    responseObserver.ensureUnhooked();
+  }
 
-      var meta = {
-        "msg.preprocessor": "FirePHPCoreCompatibility",
-        "lang.id": "registry.pinf.org/cadorn.org/github/renderers/packages/php/master",
-        "priority": log.type
-      };
+  var encoder = ENCODER.Encoder();
+  encoder.setOption("maxObjectDepth", 1000);
+  encoder.setOption("maxArrayDepth", 1000);
+  encoder.setOption("maxOverallDepth", 1000);
 
-      if (log.backtrace) {
-        var m = log.backtrace.match(/^([^:]+?)(\s*:\s*(\d+))?$/);
+  function onChromeLoggerMessage(message, context) {
+    try {
+      var i, ic, j, jc;
+
+      var _loop = function () {
+        var log = {};
+
+        for (j = 0, jc = message.columns.length; j < jc; j++) {
+          log[message.columns[j]] = message.rows[i][j];
+        }
 
-        if (m) {
-          meta.file = m[1];
+        var meta = {
+          "msg.preprocessor": "FirePHPCoreCompatibility",
+          "lang.id": "registry.pinf.org/cadorn.org/github/renderers/packages/php/master",
+          "priority": log.type
+        };
+
+        if (log.backtrace) {
+          var m = log.backtrace.match(/^([^:]+?)(\s*:\s*(\d+))?$/);
 
-          if (m[3] !== '') {
-            meta.line = parseInt(m[3]);
+          if (m) {
+            meta.file = m[1];
+
+            if (m[3] !== '') {
+              meta.line = parseInt(m[3]);
+            }
           }
         }
-      }
 
-      if (log.log.length === 1) {
-        log.log = log.log[0];
+        if (log.log.length === 1) {
+          log.log = log.log[0];
+        }
+
+        var dataNode = encoder.encode(log.log, {
+          "lang": "php"
+        }, {
+          "jsonEncode": false
+        });
+        var node = dataNode.origin;
+        Object.keys(meta).forEach(function (name) {
+          node.meta[name] = meta[name];
+        });
+        var msg = {
+          "context": context,
+          "sender": "https://github.com/ccampbell/chromelogger",
+          "receiver": "https://gi0.FireConsole.org/rep.js/InsightTree/0.1",
+          "meta": "{}",
+          "data": node
+        };
+        API.emit("message.firephp", msg);
+      };
+
+      for (i = 0, ic = message.rows.length; i < ic; i++) {
+        _loop();
       }
+    } catch (err) {
+      console.error("Error formatting chromelogger message:", err);
+    }
+  }
 
-      var dataNode = encoder.encode(log.log, {
-        "lang": "php"
-      }, {
-        "jsonEncode": false
-      });
-      var node = dataNode.origin;
-      Object.keys(meta).forEach(function (name) {
-        node.meta[name] = meta[name];
-      });
-      var msg = {
-        "context": context,
-        "sender": "https://github.com/ccampbell/chromelogger",
-        "receiver": "https://gi0.FireConsole.org/rep.js/InsightTree/0.1",
-        "meta": "{}",
-        "data": node
-      };
-      API.emit("message.firephp", msg);
+  API.on.transport = async function (message, request) {
+    var info = {
+      request: request,
+      data: JSON.parse(message.data)
     };
+    API.emit("message.transport", info);
+    info.request.context.serverUrl = info.data.url;
+    var url = info.data.url;
 
-    for (i = 0, ic = message.rows.length; i < ic; i++) {
-      _loop();
+    if (url.indexOf("x-insight=transport") !== -1) {
+      return;
     }
-  } catch (err) {
-    console.error("Error formatting chromelogger message:", err);
-  }
-};
 
-API.on.insightMessage = function (message) {
-  API.emit("message.insight", message);
-};
+    if (url.indexOf("?") === -1) {
+      url += "?";
+    } else {
+      url += "&";
+    }
+
+    url += "x-insight=transport";
+    var response = await fetch(url, {
+      method: 'POST',
+      mode: 'same-origin',
+      cache: 'no-cache',
+      credentials: 'include',
+      headers: {
+        'Content-Type': 'application/json',
+        'x-insight': 'transport'
+      },
+      redirect: 'follow',
+      referrer: 'no-referrer',
+      body: JSON.stringify(info.data.payload)
+    });
+    var body = await response.text();
+    console.log("BODY", body);
+    httpHeaderChannel.parseReceived(body, {
+      "id": info.request.id,
+      "url": info.request.context.url,
+      "hostname": info.request.context.hostname,
+      "context": info.request.context,
+      "port": 0,
+      "method": "",
+      "status": "",
+      "contentType": "",
+      "requestHeaders": ""
+    });
+  };
 
-API.on.firePHPMessage = function (message) {
-  API.emit("message.firephp", message);
-};
+  API.callServer = async function (serverUrl, payload) {
+    var url = serverUrl;
 
-API.on.transport = function (info) {
-  API.emit("message.transport", info);
-};
+    if (url.indexOf("?") === -1) {
+      url += "?";
+    } else {
+      url += "&";
+    }
 
-function isEnabled() {
-  return true;
-}
+    url += "x-insight=serve";
+    console.log("Sending", payload, "to", url);
+    var announceMessage = getAnnounceMessageForRequest(serverUrl.replace(/^https?:\/\/([^\/]+)\/.*$/, '$1'));
+    console.log("announceMessage", announceMessage);
+    var headers = {
+      'Content-Type': 'application/json',
+      'x-insight': 'serve'
+    };
 
-var httpHeaderChannel = API.httpHeaderChannel = API.WILDFIRE.HttpHeaderChannel({
-  "enableTransport": false,
-  onError: function (err) {
-    console.error("HttpHeaderChannel error:", err);
-    API.emit("error", err);
-  }
-});
-httpHeaderChannel.setNoReceiverCallback(function (id) {
-  API.console.error("trying to log to unknown receiver (extension): " + id);
-});
-httpHeaderChannel.addListener({
-  afterChannelOpen: function (context) {
-    if (API.VERBOSE) console.log("[wildfire] httpHeaderChannel -| afterChannelOpen (context):", context);
-    API.emit("response", {
-      context: context.context
-    });
-  }
-});
+    if (announceMessage) {
+      announceDispatcher.dispatch(announceMessage);
+      httpHeaderChannel.flush({
+        setMessagePart: function (name, value) {
+          headers[name] = '' + value;
+        },
+        getMessagePart: function (name) {
+          return headers[name];
+        }
+      });
+    }
 
-require("./receivers/firephp").for(API);
+    console.log("headers::", headers);
+    var response = await fetch(url, {
+      method: 'POST',
+      mode: 'same-origin',
+      cache: 'no-cache',
+      credentials: 'include',
+      headers: headers,
+      redirect: 'follow',
+      referrer: 'no-referrer',
+      body: JSON.stringify(payload)
+    });
+    console.log("response in wildfire", response);
+    var body = await response.text();
+    console.log("BODY", body);
+    return body;
+  };
 
-require("./receivers/insight").for(API);
+  var httpHeaderChannel = API.httpHeaderChannel = API.WILDFIRE.HttpHeaderChannel({
+    "enableTransport": false,
+    onError: function (err) {
+      console.error("HttpHeaderChannel error:", err);
+      API.emit("error", err);
+    }
+  });
+  httpHeaderChannel.setNoReceiverCallback(function (id) {
+    API.console.error("trying to log to unknown receiver (extension): " + id);
+  });
+  httpHeaderChannel.addListener({
+    afterChannelOpen: function (context) {
+      if (API.VERBOSE) console.log("[wildfire] httpHeaderChannel -| afterChannelOpen (context):", context);
+      API.emit("response", {
+        context: context.context
+      });
+    }
+  });
+  var transportReceiver1 = API.WILDFIRE.Receiver();
+  transportReceiver1.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
+  transportReceiver1.addListener({
+    onMessageReceived: function (request, message) {
+      message.context = request.context;
+      API.emit("message.firephp", message);
+    }
+  });
+  API.httpHeaderChannel.addReceiver(transportReceiver1);
+  var transportReceiver2 = API.WILDFIRE.Receiver();
+  transportReceiver2.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1");
+  transportReceiver2.addListener({
+    onMessageReceived: function (request, message) {
+      message.context = request.context;
+      API.emit("message.firephp", message);
+    }
+  });
+  API.httpHeaderChannel.addReceiver(transportReceiver2);
+  var receivers = {
+    "http://registry.pinf.org/cadorn.org/wildfire/@meta/receiver/transport/0": {
+      messageHandler: "transport"
+    },
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/controller/0": {},
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/plugin/0": {},
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/package/0": {},
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/selective/0": {
+      messageEvent: "message.insight.selective"
+    },
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/request/0": {
+      messageEvent: "message.insight.request"
+    },
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/page/0": {
+      messageEvent: "message.firephp"
+    },
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/process/0": {},
+    "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/firephp/0": {
+      messageEvent: "message.firephp"
+    }
+  };
+  Object.keys(receivers).forEach(function (uri) {
+    var receiver = API.WILDFIRE.Receiver();
+    receiver.setId(uri);
+    receiver.addListener({
+      onMessageReceived: function (request, message) {
+        try {
+          message.context = request.context;
+
+          if (receivers[uri].messageHandler && API.on && API.on[receivers[uri].messageHandler]) {
+            API.on[receivers[uri].messageHandler](message, request);
+          } else if (receivers[uri].messageEvent) {
+            API.emit(receivers[uri].messageEvent, message);
+          } else {
+            console.log("IGNORING insight MESSAGE:", uri, message);
+          }
+        } catch (err) {
+          API.console.error(err);
+        }
+      }
+    });
+    API.httpHeaderChannel.addReceiver(receiver);
+  });
+  var announceDispatcher = API.WILDFIRE.Dispatcher();
+  announceDispatcher.setProtocol('http://registry.pinf.org/cadorn.org/wildfire/@meta/protocol/announce/0.1.0');
+  announceDispatcher.setChannel(httpHeaderChannel);
 
-var announceDispatcher = API.WILDFIRE.Dispatcher();
-announceDispatcher.setProtocol('http://registry.pinf.org/cadorn.org/wildfire/@meta/protocol/announce/0.1.0');
-announceDispatcher.setChannel(httpHeaderChannel);
+  function getAnnounceMessageForRequest(request) {
+    if (!getAnnounceMessageForRequest._forHostnames) {
+      getAnnounceMessageForRequest._forHostnames = {};
+    }
 
-function getAnnounceMessageForRequest(request) {
-  if (!getAnnounceMessageForRequest._forHostnames) {
-    getAnnounceMessageForRequest._forHostnames = {};
-  }
+    var cache = getAnnounceMessageForRequest._forHostnames;
 
-  var cache = getAnnounceMessageForRequest._forHostnames;
+    if (cache[request.hostname]) {
+      return cache[request.hostname];
+    }
 
-  if (cache[request.hostname]) {
+    cache[request.hostname] = new API.WILDFIRE.Message();
+    cache[request.hostname].setData(JSON.stringify({
+      "authkey": "mykey",
+      "receivers": httpHeaderChannel.receivers.map(function (receiver) {
+        return receiver.getId();
+      })
+    }));
     return cache[request.hostname];
   }
 
-  cache[request.hostname] = new API.WILDFIRE.Message();
-  cache[request.hostname].setData(JSON.stringify({
-    "authkey": "mykey",
-    "receivers": httpHeaderChannel.receivers.map(function (receiver) {
-      return receiver.getId();
-    })
-  }));
-  return cache[request.hostname];
-}
+  var hostnameSettings = {};
+  var requestObserver = new REQUEST_OBSERVER(function (request) {
+    var settings = comp._getHostnameSettingsForSync(request.hostname);
 
-var hostnameSettings = {};
-API.hostnameSettings = hostnameSettings;
-REQUEST_OBSERVER.register(function (request) {
-  if (!isEnabled()) {
-    if (API.VERBOSE) console.log("[wildfire] REQUEST_OBSERVER handler: not enabled");
-    return null;
-  }
+    if (API.VERBOSE) console.log("[wildfire] forceEnabled:", forceEnabled);
+    if (API.VERBOSE) console.log("[wildfire] request domain settings for '" + request.hostname + "':", settings);
+    hostnameSettings[request.hostname] = settings;
 
-  var settings = SETTINGS.getDomainSettingsForRequestSync(request);
-  if (API.VERBOSE) console.log("[wildfire] forceEnabled:", forceEnabled);
-  if (API.VERBOSE) console.log("[wildfire] request domain settings for '" + request.hostname + "':", settings);
-  hostnameSettings[request.hostname] = settings;
+    if (!forceEnabled && !settings.enabled) {
+      return {};
+    }
 
-  if (!forceEnabled && !settings.enabled) {
-    return {};
-  }
+    if (forceEnabled || settings.enableUserAgentHeader) {
+      if (!request.headers["User-Agent"].match(/\sFirePHP\/([\.|\d]*)\s?/)) {
+        request.headers["User-Agent"] = request.headers["User-Agent"] + " FirePHP/0.5";
+      }
+    }
 
-  if (forceEnabled || settings.enableUserAgentHeader) {
-    if (!request.headers["User-Agent"].match(/\sFirePHP\/([\.|\d]*)\s?/)) {
-      request.headers["User-Agent"] = request.headers["User-Agent"] + " FirePHP/0.5";
+    if (forceEnabled || settings.enableFirePHPHeader) {
+      request.headers["X-FirePHP-Version"] = "0.4";
     }
-  }
 
-  if (forceEnabled || settings.enableFirePHPHeader) {
-    request.headers["X-FirePHP-Version"] = "0.4";
-  }
+    if (API.VERBOSE) console.log("[wildfire] updated request headers:", request.headers);
+    var announceMessage = getAnnounceMessageForRequest(request);
 
-  if (API.VERBOSE) console.log("[wildfire] updated request headers:", request.headers);
-  return {
-    requestHeaders: request.headers
-  };
-});
-API.on("http.response", function (response) {
-  if (!isEnabled()) {
-    return;
-  }
+    if (announceMessage) {
+      announceDispatcher.dispatch(announceMessage);
+      httpHeaderChannel.flush({
+        setMessagePart: function (name, value) {
+          request.headers[name] = '' + value;
+        },
+        getMessagePart: function (name) {
+          return request.headers[name];
+        }
+      });
+    }
 
-  var settings = hostnameSettings[response.request.context.hostname];
+    return {
+      requestHeaders: request.headers
+    };
+  });
+  var responseObserver = new RESPONSE_OBSERVER(function (response) {
+    var settings = hostnameSettings[response.request.context.hostname];
 
-  if (!settings || !forceEnabled && !settings.enabled) {
-    return;
-  }
+    if (!settings || !forceEnabled && !settings.enabled) {
+      return;
+    }
 
-  if (API.VERBOSE) console.log("[wildfire] response domain settings for '" + response.request.context.hostname + "':", settings);
-  var chromeLoggerMessage = response.headers.filter(function (header) {
-    return header.name === "X-ChromeLogger-Data";
-  });
+    if (API.VERBOSE) console.log("[wildfire] response domain settings for '" + response.request.context.hostname + "':", settings);
 
-  if (chromeLoggerMessage.length > 0) {
-    chromeLoggerMessage.forEach(function (header) {
-      try {
-        var message = decodeURIComponent(escape(atob(header.value)));
-        message = JSON.parse(message);
-        API.on.chromeLoggerMessage(message, response.request.context);
-      } catch (err) {
-        console.error("header", header);
-        console.error("Error processing message:", err);
+    if (settings.enableChromeLoggerData) {
+      var chromeLoggerMessage = response.headers.filter(function (header) {
+        return header.name === "X-ChromeLogger-Data";
+      });
+
+      if (chromeLoggerMessage.length > 0) {
+        chromeLoggerMessage.forEach(function (header) {
+          try {
+            var message = decodeURIComponent(escape(atob(header.value)));
+            message = JSON.parse(message);
+            onChromeLoggerMessage(message, response.request.context);
+          } catch (err) {
+            console.error("header", header);
+            console.error("Error processing message:", err);
+          }
+        });
       }
-    });
-  }
+    }
 
-  httpHeaderChannel.parseReceived(response.headers, {
-    "id": response.request.id,
-    "url": response.request.url,
-    "hostname": response.request.hostname,
-    "context": response.request.context,
-    "port": response.request.port,
-    "method": response.request.method,
-    "status": response.status,
-    "contentType": response.contentType,
-    "requestHeaders": response.request.headers
+    httpHeaderChannel.parseReceived(response.headers, {
+      "id": response.request.id,
+      "url": response.request.url,
+      "hostname": response.request.hostname,
+      "context": response.request.context,
+      "port": response.request.port,
+      "method": response.request.method,
+      "status": response.status,
+      "contentType": response.contentType,
+      "requestHeaders": response.request.headers
+    });
   });
-});
-},{"./adapters/http-request-observer":24,"./adapters/http-response-observer":25,"./receivers/firephp":27,"./receivers/insight":28,"./settings":29,"eventemitter2":1,"insight-for-js/lib/encoder/default":31,"wildfire-for-js":13}],31:[function(require,module,exports){
+  return API;
+};
+},{"./adapters/http-request-observer":36,"./adapters/http-response-observer":37,"eventemitter2":22,"insight-for-js/lib/encoder/default":41,"wildfire-for-js":12}],41:[function(require,module,exports){
 
 var UTIL = require("fp-modules-for-nodejs/lib/util");
 var JSON = require("fp-modules-for-nodejs/lib/json");
@@ -7013,11 +7940,11 @@ Encoder.prototype.encodeObject = function(meta, object, objectDepth, arrayDepth,
 
     return ret;
 }
-},{"fp-modules-for-nodejs/lib/json":32,"fp-modules-for-nodejs/lib/util":33}],32:[function(require,module,exports){
-arguments[4][16][0].apply(exports,arguments)
-},{"dup":16}],33:[function(require,module,exports){
-arguments[4][22][0].apply(exports,arguments)
-},{"dup":22}],34:[function(require,module,exports){
+},{"fp-modules-for-nodejs/lib/json":42,"fp-modules-for-nodejs/lib/util":43}],42:[function(require,module,exports){
+arguments[4][15][0].apply(exports,arguments)
+},{"dup":15}],43:[function(require,module,exports){
+arguments[4][21][0].apply(exports,arguments)
+},{"dup":21}],44:[function(require,module,exports){
 'use strict'
 
 exports.byteLength = byteLength
@@ -7170,7 +8097,7 @@ function fromByteArray (uint8) {
   return parts.join('')
 }
 
-},{}],35:[function(require,module,exports){
+},{}],45:[function(require,module,exports){
 /*!
  * The buffer module from node.js, for the browser.
  *
@@ -8949,7 +9876,532 @@ function numberIsNaN (obj) {
   return obj !== obj // eslint-disable-line no-self-compare
 }
 
-},{"base64-js":34,"ieee754":36}],36:[function(require,module,exports){
+},{"base64-js":44,"ieee754":47}],46:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var objectCreate = Object.create || objectCreatePolyfill
+var objectKeys = Object.keys || objectKeysPolyfill
+var bind = Function.prototype.bind || functionBindPolyfill
+
+function EventEmitter() {
+  if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
+    this._events = objectCreate(null);
+    this._eventsCount = 0;
+  }
+
+  this._maxListeners = this._maxListeners || undefined;
+}
+module.exports = EventEmitter;
+
+// Backwards-compat with node 0.10.x
+EventEmitter.EventEmitter = EventEmitter;
+
+EventEmitter.prototype._events = undefined;
+EventEmitter.prototype._maxListeners = undefined;
+
+// By default EventEmitters will print a warning if more than 10 listeners are
+// added to it. This is a useful default which helps finding memory leaks.
+var defaultMaxListeners = 10;
+
+var hasDefineProperty;
+try {
+  var o = {};
+  if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
+  hasDefineProperty = o.x === 0;
+} catch (err) { hasDefineProperty = false }
+if (hasDefineProperty) {
+  Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
+    enumerable: true,
+    get: function() {
+      return defaultMaxListeners;
+    },
+    set: function(arg) {
+      // check whether the input is a positive number (whose value is zero or
+      // greater and not a NaN).
+      if (typeof arg !== 'number' || arg < 0 || arg !== arg)
+        throw new TypeError('"defaultMaxListeners" must be a positive number');
+      defaultMaxListeners = arg;
+    }
+  });
+} else {
+  EventEmitter.defaultMaxListeners = defaultMaxListeners;
+}
+
+// Obviously not all Emitters should be limited to 10. This function allows
+// that to be increased. Set to zero for unlimited.
+EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
+  if (typeof n !== 'number' || n < 0 || isNaN(n))
+    throw new TypeError('"n" argument must be a positive number');
+  this._maxListeners = n;
+  return this;
+};
+
+function $getMaxListeners(that) {
+  if (that._maxListeners === undefined)
+    return EventEmitter.defaultMaxListeners;
+  return that._maxListeners;
+}
+
+EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
+  return $getMaxListeners(this);
+};
+
+// These standalone emit* functions are used to optimize calling of event
+// handlers for fast cases because emit() itself often has a variable number of
+// arguments and can be deoptimized because of that. These functions always have
+// the same number of arguments and thus do not get deoptimized, so the code
+// inside them can execute faster.
+function emitNone(handler, isFn, self) {
+  if (isFn)
+    handler.call(self);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self);
+  }
+}
+function emitOne(handler, isFn, self, arg1) {
+  if (isFn)
+    handler.call(self, arg1);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self, arg1);
+  }
+}
+function emitTwo(handler, isFn, self, arg1, arg2) {
+  if (isFn)
+    handler.call(self, arg1, arg2);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self, arg1, arg2);
+  }
+}
+function emitThree(handler, isFn, self, arg1, arg2, arg3) {
+  if (isFn)
+    handler.call(self, arg1, arg2, arg3);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self, arg1, arg2, arg3);
+  }
+}
+
+function emitMany(handler, isFn, self, args) {
+  if (isFn)
+    handler.apply(self, args);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].apply(self, args);
+  }
+}
+
+EventEmitter.prototype.emit = function emit(type) {
+  var er, handler, len, args, i, events;
+  var doError = (type === 'error');
+
+  events = this._events;
+  if (events)
+    doError = (doError && events.error == null);
+  else if (!doError)
+    return false;
+
+  // If there is no 'error' event listener then throw.
+  if (doError) {
+    if (arguments.length > 1)
+      er = arguments[1];
+    if (er instanceof Error) {
+      throw er; // Unhandled 'error' event
+    } else {
+      // At least give some kind of context to the user
+      var err = new Error('Unhandled "error" event. (' + er + ')');
+      err.context = er;
+      throw err;
+    }
+    return false;
+  }
+
+  handler = events[type];
+
+  if (!handler)
+    return false;
+
+  var isFn = typeof handler === 'function';
+  len = arguments.length;
+  switch (len) {
+      // fast cases
+    case 1:
+      emitNone(handler, isFn, this);
+      break;
+    case 2:
+      emitOne(handler, isFn, this, arguments[1]);
+      break;
+    case 3:
+      emitTwo(handler, isFn, this, arguments[1], arguments[2]);
+      break;
+    case 4:
+      emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
+      break;
+      // slower
+    default:
+      args = new Array(len - 1);
+      for (i = 1; i < len; i++)
+        args[i - 1] = arguments[i];
+      emitMany(handler, isFn, this, args);
+  }
+
+  return true;
+};
+
+function _addListener(target, type, listener, prepend) {
+  var m;
+  var events;
+  var existing;
+
+  if (typeof listener !== 'function')
+    throw new TypeError('"listener" argument must be a function');
+
+  events = target._events;
+  if (!events) {
+    events = target._events = objectCreate(null);
+    target._eventsCount = 0;
+  } else {
+    // To avoid recursion in the case that type === "newListener"! Before
+    // adding it to the listeners, first emit "newListener".
+    if (events.newListener) {
+      target.emit('newListener', type,
+          listener.listener ? listener.listener : listener);
+
+      // Re-assign `events` because a newListener handler could have caused the
+      // this._events to be assigned to a new object
+      events = target._events;
+    }
+    existing = events[type];
+  }
+
+  if (!existing) {
+    // Optimize the case of one listener. Don't need the extra array object.
+    existing = events[type] = listener;
+    ++target._eventsCount;
+  } else {
+    if (typeof existing === 'function') {
+      // Adding the second element, need to change to array.
+      existing = events[type] =
+          prepend ? [listener, existing] : [existing, listener];
+    } else {
+      // If we've already got an array, just append.
+      if (prepend) {
+        existing.unshift(listener);
+      } else {
+        existing.push(listener);
+      }
+    }
+
+    // Check for listener leak
+    if (!existing.warned) {
+      m = $getMaxListeners(target);
+      if (m && m > 0 && existing.length > m) {
+        existing.warned = true;
+        var w = new Error('Possible EventEmitter memory leak detected. ' +
+            existing.length + ' "' + String(type) + '" listeners ' +
+            'added. Use emitter.setMaxListeners() to ' +
+            'increase limit.');
+        w.name = 'MaxListenersExceededWarning';
+        w.emitter = target;
+        w.type = type;
+        w.count = existing.length;
+        if (typeof console === 'object' && console.warn) {
+          console.warn('%s: %s', w.name, w.message);
+        }
+      }
+    }
+  }
+
+  return target;
+}
+
+EventEmitter.prototype.addListener = function addListener(type, listener) {
+  return _addListener(this, type, listener, false);
+};
+
+EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+
+EventEmitter.prototype.prependListener =
+    function prependListener(type, listener) {
+      return _addListener(this, type, listener, true);
+    };
+
+function onceWrapper() {
+  if (!this.fired) {
+    this.target.removeListener(this.type, this.wrapFn);
+    this.fired = true;
+    switch (arguments.length) {
+      case 0:
+        return this.listener.call(this.target);
+      case 1:
+        return this.listener.call(this.target, arguments[0]);
+      case 2:
+        return this.listener.call(this.target, arguments[0], arguments[1]);
+      case 3:
+        return this.listener.call(this.target, arguments[0], arguments[1],
+            arguments[2]);
+      default:
+        var args = new Array(arguments.length);
+        for (var i = 0; i < args.length; ++i)
+          args[i] = arguments[i];
+        this.listener.apply(this.target, args);
+    }
+  }
+}
+
+function _onceWrap(target, type, listener) {
+  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
+  var wrapped = bind.call(onceWrapper, state);
+  wrapped.listener = listener;
+  state.wrapFn = wrapped;
+  return wrapped;
+}
+
+EventEmitter.prototype.once = function once(type, listener) {
+  if (typeof listener !== 'function')
+    throw new TypeError('"listener" argument must be a function');
+  this.on(type, _onceWrap(this, type, listener));
+  return this;
+};
+
+EventEmitter.prototype.prependOnceListener =
+    function prependOnceListener(type, listener) {
+      if (typeof listener !== 'function')
+        throw new TypeError('"listener" argument must be a function');
+      this.prependListener(type, _onceWrap(this, type, listener));
+      return this;
+    };
+
+// Emits a 'removeListener' event if and only if the listener was removed.
+EventEmitter.prototype.removeListener =
+    function removeListener(type, listener) {
+      var list, events, position, i, originalListener;
+
+      if (typeof listener !== 'function')
+        throw new TypeError('"listener" argument must be a function');
+
+      events = this._events;
+      if (!events)
+        return this;
+
+      list = events[type];
+      if (!list)
+        return this;
+
+      if (list === listener || list.listener === listener) {
+        if (--this._eventsCount === 0)
+          this._events = objectCreate(null);
+        else {
+          delete events[type];
+          if (events.removeListener)
+            this.emit('removeListener', type, list.listener || listener);
+        }
+      } else if (typeof list !== 'function') {
+        position = -1;
+
+        for (i = list.length - 1; i >= 0; i--) {
+          if (list[i] === listener || list[i].listener === listener) {
+            originalListener = list[i].listener;
+            position = i;
+            break;
+          }
+        }
+
+        if (position < 0)
+          return this;
+
+        if (position === 0)
+          list.shift();
+        else
+          spliceOne(list, position);
+
+        if (list.length === 1)
+          events[type] = list[0];
+
+        if (events.removeListener)
+          this.emit('removeListener', type, originalListener || listener);
+      }
+
+      return this;
+    };
+
+EventEmitter.prototype.removeAllListeners =
+    function removeAllListeners(type) {
+      var listeners, events, i;
+
+      events = this._events;
+      if (!events)
+        return this;
+
+      // not listening for removeListener, no need to emit
+      if (!events.removeListener) {
+        if (arguments.length === 0) {
+          this._events = objectCreate(null);
+          this._eventsCount = 0;
+        } else if (events[type]) {
+          if (--this._eventsCount === 0)
+            this._events = objectCreate(null);
+          else
+            delete events[type];
+        }
+        return this;
+      }
+
+      // emit removeListener for all listeners on all events
+      if (arguments.length === 0) {
+        var keys = objectKeys(events);
+        var key;
+        for (i = 0; i < keys.length; ++i) {
+          key = keys[i];
+          if (key === 'removeListener') continue;
+          this.removeAllListeners(key);
+        }
+        this.removeAllListeners('removeListener');
+        this._events = objectCreate(null);
+        this._eventsCount = 0;
+        return this;
+      }
+
+      listeners = events[type];
+
+      if (typeof listeners === 'function') {
+        this.removeListener(type, listeners);
+      } else if (listeners) {
+        // LIFO order
+        for (i = listeners.length - 1; i >= 0; i--) {
+          this.removeListener(type, listeners[i]);
+        }
+      }
+
+      return this;
+    };
+
+function _listeners(target, type, unwrap) {
+  var events = target._events;
+
+  if (!events)
+    return [];
+
+  var evlistener = events[type];
+  if (!evlistener)
+    return [];
+
+  if (typeof evlistener === 'function')
+    return unwrap ? [evlistener.listener || evlistener] : [evlistener];
+
+  return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
+}
+
+EventEmitter.prototype.listeners = function listeners(type) {
+  return _listeners(this, type, true);
+};
+
+EventEmitter.prototype.rawListeners = function rawListeners(type) {
+  return _listeners(this, type, false);
+};
+
+EventEmitter.listenerCount = function(emitter, type) {
+  if (typeof emitter.listenerCount === 'function') {
+    return emitter.listenerCount(type);
+  } else {
+    return listenerCount.call(emitter, type);
+  }
+};
+
+EventEmitter.prototype.listenerCount = listenerCount;
+function listenerCount(type) {
+  var events = this._events;
+
+  if (events) {
+    var evlistener = events[type];
+
+    if (typeof evlistener === 'function') {
+      return 1;
+    } else if (evlistener) {
+      return evlistener.length;
+    }
+  }
+
+  return 0;
+}
+
+EventEmitter.prototype.eventNames = function eventNames() {
+  return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
+};
+
+// About 1.5x faster than the two-arg version of Array#splice().
+function spliceOne(list, index) {
+  for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
+    list[i] = list[k];
+  list.pop();
+}
+
+function arrayClone(arr, n) {
+  var copy = new Array(n);
+  for (var i = 0; i < n; ++i)
+    copy[i] = arr[i];
+  return copy;
+}
+
+function unwrapListeners(arr) {
+  var ret = new Array(arr.length);
+  for (var i = 0; i < ret.length; ++i) {
+    ret[i] = arr[i].listener || arr[i];
+  }
+  return ret;
+}
+
+function objectCreatePolyfill(proto) {
+  var F = function() {};
+  F.prototype = proto;
+  return new F;
+}
+function objectKeysPolyfill(obj) {
+  var keys = [];
+  for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
+    keys.push(k);
+  }
+  return k;
+}
+function functionBindPolyfill(context) {
+  var fn = this;
+  return function () {
+    return fn.apply(context, arguments);
+  };
+}
+
+},{}],47:[function(require,module,exports){
 exports.read = function (buffer, offset, isLE, mLen, nBytes) {
   var e, m
   var eLen = (nBytes * 8) - mLen - 1
@@ -9035,7 +10487,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
   buffer[offset + i - d] |= s * 128
 }
 
-},{}],37:[function(require,module,exports){
+},{}],48:[function(require,module,exports){
 // shim for using process in browser
 var process = module.exports = {};
 
@@ -9221,7 +10673,7 @@ process.chdir = function (dir) {
 };
 process.umask = function() { return 0; };
 
-},{}],38:[function(require,module,exports){
+},{}],49:[function(require,module,exports){
 (function (setImmediate,clearImmediate){
 var nextTick = require('process/browser.js').nextTick;
 var apply = Function.prototype.apply;
@@ -9300,7 +10752,7 @@ exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate :
   delete immediateIds[id];
 };
 }).call(this,require("timers").setImmediate,require("timers").clearImmediate)
-},{"process/browser.js":37,"timers":38}]},{},[23])(23)
+},{"process/browser.js":48,"timers":49}]},{},[35])(35)
 });
 
 	});
diff --git a/dist/firephp.build/scripts/devtools/console.rep.css b/dist/firephp.build/scripts/devtools/dist/console.rep.css
similarity index 67%
rename from dist/firephp.build/scripts/devtools/console.rep.css
rename to dist/firephp.build/scripts/devtools/dist/console.rep.css
index 771a1ca..7e0465f 100644
--- a/dist/firephp.build/scripts/devtools/console.rep.css
+++ b/dist/firephp.build/scripts/devtools/dist/console.rep.css
@@ -1,12 +1,12 @@
 
-[_cssid="9051886b96d6421ced5fdf604fe05f9c22afe06c"].console {
+[_cssid="884ff3ac7a6a819ec4185fdb7c2035051c097737"].console {
     overflow-x: hidden;
     overflow-y: auto;
     border-right: 1px solid #dcdcdc;
     height: 100vh;
 }
 
-[_cssid="9051886b96d6421ced5fdf604fe05f9c22afe06c"].console DIV.request {
+[_cssid="884ff3ac7a6a819ec4185fdb7c2035051c097737"].console DIV.request {
     padding: 3px;
     padding-left: 10px;
     padding-right: 10px;
diff --git a/dist/firephp.build/scripts/devtools/console.rep.js b/dist/firephp.build/scripts/devtools/dist/console.rep.js
similarity index 88%
rename from dist/firephp.build/scripts/devtools/console.rep.js
rename to dist/firephp.build/scripts/devtools/dist/console.rep.js
index ee72550..0d9d5cc 100644
--- a/dist/firephp.build/scripts/devtools/console.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/console.rep.js
@@ -313,6 +313,7 @@ function debounce(func, wait, options) {
       }
       if (maxing) {
         // Handle invocations in a tight loop.
+        clearTimeout(timerId);
         timerId = setTimeout(timerExpired, wait);
         return invokeFunc(lastCallTime);
       }
@@ -527,7 +528,7 @@ exports.main = function (JSONREP, node, options) {
     return JSONREP.makeRep('<div class="console">' + code + '</div>', {
       css: {
         ".@": "github.com~0ink~codeblock/codeblock:Codeblock",
-        "_code": "{\"_cssid\":\"9051886b96d6421ced5fdf604fe05f9c22afe06c\",\"repUri\":\"console\"}",
+        "_code": "{\"_cssid\":\"884ff3ac7a6a819ec4185fdb7c2035051c097737\",\"repUri\":\"console\"}",
         "_format": "json",
         "_args": [],
         "_compiled": false
@@ -629,19 +630,20 @@ exports.main = function (JSONREP, node, options) {
                 message.message.context = message.context;
                 var fc = getConsoleForContext(message.context);
                 ensureRequestWrapper(fc, message.context).then(function (api) {
+                  console.log("LOG TO CONSOLE 1", message.message);
+                  message.message.meta = message.message.meta && JSON.parse(message.message.meta) || {};
+                  message.message.meta.console = message.message.meta.console || {};
+                  message.message.meta.console.enableFileInspect = true;
                   api.send(message.message);
                   scrollIfBottom();
                 });
               } else if (message.event === "prepare") {
-                console.log("PREPARE CONSOLE!!!", message.context);
-
                 var _fc = getConsoleForContext(message.context);
 
                 if (!persistLogs) {
                   _fc.getAPI().clear();
                 }
               } else if (message.event === "clear") {
-                console.log("CLEAR CONSOLE!!!", message.context);
                 Object.keys(consoles).forEach(function (id) {
                   if (id == message.context.pageUid) {
                     consoles[id].getAPI().clear();
@@ -649,7 +651,6 @@ exports.main = function (JSONREP, node, options) {
                 });
                 scrollIfBottom();
               } else if (message.event === "destroyContext") {
-                console.log("DESTROY CONTEXT!!!", message.context);
                 Object.keys(consoles).forEach(function (id) {
                   if (id == message.context.pageUid) {
                     WINDOW.FC.fireconsole.destroyConsoleForId(id.replace(/["\{\}]/g, '_'));
@@ -753,6 +754,7 @@ var DEBOUNCE = require('lodash/debounce');
 
 exports.for = function (ctx) {
   var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
   events.currentContext = null;
   setImmediate(function () {
     onContextMessage(null);
@@ -775,38 +777,52 @@ exports.for = function (ctx) {
     }
   }
 
-  ctx.browser.runtime.onMessage.addListener(function (message) {
+  events.handleBroadcastMessage = function (message) {
     try {
-      if (!ctx.browser || !ctx.browser.devtools || !ctx.browser.devtools.inspectedWindow || !message.context || message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId) {
-        return;
-      }
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
 
-      if (message.to === "message-listener") {
-        if (message.event === "currentContext" && typeof message.context !== "undefined") {
-          onContextMessage(message.context);
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
         }
-
-        events.emit("message", message);
       }
     } catch (err) {
       console.error(err);
     }
-  });
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
   ctx.browser.storage.onChanged.addListener(function (changes, area) {
     try {
       if (!events.currentContext) {
         return;
       }
 
-      var prefix = "domain[".concat(events.currentContext.hostname, "].");
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
 
       for (var item of Object.keys(changes)) {
-        if (!/^domain\[.+\]\..+$/.test(item)) continue;
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
         if (item.substring(0, prefix.length) === prefix) {
           var name = item.substring(prefix.length);
-          events.emit("setting." + name, changes[item].newValue);
-          events.emit("changed.setting", name, changes[item].newValue);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
         }
       }
     } catch (err) {
@@ -820,7 +836,7 @@ exports.for = function (ctx) {
     });
   }, 250);
 
-  events.getSetting = function (name) {
+  events.getSetting = async function (name) {
     if (!events.currentContext) {
       return Promise.resolve(null);
     }
@@ -828,21 +844,47 @@ exports.for = function (ctx) {
     return events._getSettingForHostname(events.currentContext.hostname, name);
   };
 
-  events._getSettingForHostname = function (hostname, name) {
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
 
-    var key = "domain[" + hostname + "]." + name;
+    var key = "hostname[" + hostname + "]." + name;
     return ctx.browser.storage.local.get(key).then(function (value) {
-      return value[key] || false;
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setSetting = function (name, value) {
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
     if (!events.currentContext) {
       throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
     }
@@ -850,7 +892,7 @@ exports.for = function (ctx) {
     return events._setSettingForHostname(events.currentContext.hostname, name, value);
   };
 
-  events._setSettingForHostname = function (hostname, name, value) {
+  events._setSettingForHostname = async function (hostname, name, value) {
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
@@ -861,7 +903,9 @@ exports.for = function (ctx) {
       }
 
       var obj = {};
-      obj["domain[" + hostname + "]." + name] = value;
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -869,9 +913,9 @@ exports.for = function (ctx) {
     });
   };
 
-  events.getGlobalSetting = function (name) {
+  events.getGlobalSetting = async function (name) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     var defaultValue;
@@ -890,15 +934,18 @@ exports.for = function (ctx) {
       }
 
       return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setGlobalSetting = function (name, value) {
+  events.setGlobalSetting = async function (name, value) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     return events.getGlobalSetting(name).then(function (existingValue) {
@@ -908,6 +955,7 @@ exports.for = function (ctx) {
 
       var obj = {};
       obj[name] = value;
+      globalSettings[name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -915,14 +963,54 @@ exports.for = function (ctx) {
     });
   };
 
-  events.isConfigured = function () {
-    return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return enableUserAgentHeader || enableFirePHPHeader || enableChromeLoggerData;
-        });
-      });
-    });
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
   };
 
   events.reloadBrowser = function () {
@@ -942,15 +1030,41 @@ exports.for = function (ctx) {
     });
   };
 
-  events.showView = function (name) {
+  events.showView = function (name, args) {
     if (name === "manage") {
       ctx.browser.runtime.sendMessage({
         to: "broadcast",
         event: "manage"
       });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
+    }
+  };
+
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
     }
   };
 
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
   return events;
 };
 }).call(this,require("timers").setImmediate)
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/babel-regenerator-runtime.js b/dist/firephp.build/scripts/devtools/dist/dist/babel-regenerator-runtime.js
new file mode 100644
index 0000000..4da0b72
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/babel-regenerator-runtime.js
@@ -0,0 +1,581 @@
+((function (require, exports, module) {
+var bundle = { require: require, exports: exports, module: module };
+
+(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+"use strict";
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var runtime = function (exports) {
+  "use strict";
+
+  var Op = Object.prototype;
+  var hasOwn = Op.hasOwnProperty;
+  var undefined;
+  var $Symbol = typeof Symbol === "function" ? Symbol : {};
+  var iteratorSymbol = $Symbol.iterator || "@@iterator";
+  var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
+  var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
+
+  function wrap(innerFn, outerFn, self, tryLocsList) {
+    var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
+    var generator = Object.create(protoGenerator.prototype);
+    var context = new Context(tryLocsList || []);
+    generator._invoke = makeInvokeMethod(innerFn, self, context);
+    return generator;
+  }
+
+  exports.wrap = wrap;
+
+  function tryCatch(fn, obj, arg) {
+    try {
+      return {
+        type: "normal",
+        arg: fn.call(obj, arg)
+      };
+    } catch (err) {
+      return {
+        type: "throw",
+        arg: err
+      };
+    }
+  }
+
+  var GenStateSuspendedStart = "suspendedStart";
+  var GenStateSuspendedYield = "suspendedYield";
+  var GenStateExecuting = "executing";
+  var GenStateCompleted = "completed";
+  var ContinueSentinel = {};
+
+  function Generator() {}
+
+  function GeneratorFunction() {}
+
+  function GeneratorFunctionPrototype() {}
+
+  var IteratorPrototype = {};
+
+  IteratorPrototype[iteratorSymbol] = function () {
+    return this;
+  };
+
+  var getProto = Object.getPrototypeOf;
+  var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
+
+  if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
+    IteratorPrototype = NativeIteratorPrototype;
+  }
+
+  var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
+  GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
+  GeneratorFunctionPrototype.constructor = GeneratorFunction;
+  GeneratorFunctionPrototype[toStringTagSymbol] = GeneratorFunction.displayName = "GeneratorFunction";
+
+  function defineIteratorMethods(prototype) {
+    ["next", "throw", "return"].forEach(function (method) {
+      prototype[method] = function (arg) {
+        return this._invoke(method, arg);
+      };
+    });
+  }
+
+  exports.isGeneratorFunction = function (genFun) {
+    var ctor = typeof genFun === "function" && genFun.constructor;
+    return ctor ? ctor === GeneratorFunction || (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
+  };
+
+  exports.mark = function (genFun) {
+    if (Object.setPrototypeOf) {
+      Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
+    } else {
+      genFun.__proto__ = GeneratorFunctionPrototype;
+
+      if (!(toStringTagSymbol in genFun)) {
+        genFun[toStringTagSymbol] = "GeneratorFunction";
+      }
+    }
+
+    genFun.prototype = Object.create(Gp);
+    return genFun;
+  };
+
+  exports.awrap = function (arg) {
+    return {
+      __await: arg
+    };
+  };
+
+  function AsyncIterator(generator) {
+    function invoke(method, arg, resolve, reject) {
+      var record = tryCatch(generator[method], generator, arg);
+
+      if (record.type === "throw") {
+        reject(record.arg);
+      } else {
+        var result = record.arg;
+        var value = result.value;
+
+        if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
+          return Promise.resolve(value.__await).then(function (value) {
+            invoke("next", value, resolve, reject);
+          }, function (err) {
+            invoke("throw", err, resolve, reject);
+          });
+        }
+
+        return Promise.resolve(value).then(function (unwrapped) {
+          result.value = unwrapped;
+          resolve(result);
+        }, function (error) {
+          return invoke("throw", error, resolve, reject);
+        });
+      }
+    }
+
+    var previousPromise;
+
+    function enqueue(method, arg) {
+      function callInvokeWithMethodAndArg() {
+        return new Promise(function (resolve, reject) {
+          invoke(method, arg, resolve, reject);
+        });
+      }
+
+      return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
+    }
+
+    this._invoke = enqueue;
+  }
+
+  defineIteratorMethods(AsyncIterator.prototype);
+
+  AsyncIterator.prototype[asyncIteratorSymbol] = function () {
+    return this;
+  };
+
+  exports.AsyncIterator = AsyncIterator;
+
+  exports.async = function (innerFn, outerFn, self, tryLocsList) {
+    var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList));
+    return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) {
+      return result.done ? result.value : iter.next();
+    });
+  };
+
+  function makeInvokeMethod(innerFn, self, context) {
+    var state = GenStateSuspendedStart;
+    return function invoke(method, arg) {
+      if (state === GenStateExecuting) {
+        throw new Error("Generator is already running");
+      }
+
+      if (state === GenStateCompleted) {
+        if (method === "throw") {
+          throw arg;
+        }
+
+        return doneResult();
+      }
+
+      context.method = method;
+      context.arg = arg;
+
+      while (true) {
+        var delegate = context.delegate;
+
+        if (delegate) {
+          var delegateResult = maybeInvokeDelegate(delegate, context);
+
+          if (delegateResult) {
+            if (delegateResult === ContinueSentinel) continue;
+            return delegateResult;
+          }
+        }
+
+        if (context.method === "next") {
+          context.sent = context._sent = context.arg;
+        } else if (context.method === "throw") {
+          if (state === GenStateSuspendedStart) {
+            state = GenStateCompleted;
+            throw context.arg;
+          }
+
+          context.dispatchException(context.arg);
+        } else if (context.method === "return") {
+          context.abrupt("return", context.arg);
+        }
+
+        state = GenStateExecuting;
+        var record = tryCatch(innerFn, self, context);
+
+        if (record.type === "normal") {
+          state = context.done ? GenStateCompleted : GenStateSuspendedYield;
+
+          if (record.arg === ContinueSentinel) {
+            continue;
+          }
+
+          return {
+            value: record.arg,
+            done: context.done
+          };
+        } else if (record.type === "throw") {
+          state = GenStateCompleted;
+          context.method = "throw";
+          context.arg = record.arg;
+        }
+      }
+    };
+  }
+
+  function maybeInvokeDelegate(delegate, context) {
+    var method = delegate.iterator[context.method];
+
+    if (method === undefined) {
+      context.delegate = null;
+
+      if (context.method === "throw") {
+        if (delegate.iterator["return"]) {
+          context.method = "return";
+          context.arg = undefined;
+          maybeInvokeDelegate(delegate, context);
+
+          if (context.method === "throw") {
+            return ContinueSentinel;
+          }
+        }
+
+        context.method = "throw";
+        context.arg = new TypeError("The iterator does not provide a 'throw' method");
+      }
+
+      return ContinueSentinel;
+    }
+
+    var record = tryCatch(method, delegate.iterator, context.arg);
+
+    if (record.type === "throw") {
+      context.method = "throw";
+      context.arg = record.arg;
+      context.delegate = null;
+      return ContinueSentinel;
+    }
+
+    var info = record.arg;
+
+    if (!info) {
+      context.method = "throw";
+      context.arg = new TypeError("iterator result is not an object");
+      context.delegate = null;
+      return ContinueSentinel;
+    }
+
+    if (info.done) {
+      context[delegate.resultName] = info.value;
+      context.next = delegate.nextLoc;
+
+      if (context.method !== "return") {
+        context.method = "next";
+        context.arg = undefined;
+      }
+    } else {
+      return info;
+    }
+
+    context.delegate = null;
+    return ContinueSentinel;
+  }
+
+  defineIteratorMethods(Gp);
+  Gp[toStringTagSymbol] = "Generator";
+
+  Gp[iteratorSymbol] = function () {
+    return this;
+  };
+
+  Gp.toString = function () {
+    return "[object Generator]";
+  };
+
+  function pushTryEntry(locs) {
+    var entry = {
+      tryLoc: locs[0]
+    };
+
+    if (1 in locs) {
+      entry.catchLoc = locs[1];
+    }
+
+    if (2 in locs) {
+      entry.finallyLoc = locs[2];
+      entry.afterLoc = locs[3];
+    }
+
+    this.tryEntries.push(entry);
+  }
+
+  function resetTryEntry(entry) {
+    var record = entry.completion || {};
+    record.type = "normal";
+    delete record.arg;
+    entry.completion = record;
+  }
+
+  function Context(tryLocsList) {
+    this.tryEntries = [{
+      tryLoc: "root"
+    }];
+    tryLocsList.forEach(pushTryEntry, this);
+    this.reset(true);
+  }
+
+  exports.keys = function (object) {
+    var keys = [];
+
+    for (var key in object) {
+      keys.push(key);
+    }
+
+    keys.reverse();
+    return function next() {
+      while (keys.length) {
+        var key = keys.pop();
+
+        if (key in object) {
+          next.value = key;
+          next.done = false;
+          return next;
+        }
+      }
+
+      next.done = true;
+      return next;
+    };
+  };
+
+  function values(iterable) {
+    if (iterable) {
+      var iteratorMethod = iterable[iteratorSymbol];
+
+      if (iteratorMethod) {
+        return iteratorMethod.call(iterable);
+      }
+
+      if (typeof iterable.next === "function") {
+        return iterable;
+      }
+
+      if (!isNaN(iterable.length)) {
+        var i = -1,
+            next = function next() {
+          while (++i < iterable.length) {
+            if (hasOwn.call(iterable, i)) {
+              next.value = iterable[i];
+              next.done = false;
+              return next;
+            }
+          }
+
+          next.value = undefined;
+          next.done = true;
+          return next;
+        };
+
+        return next.next = next;
+      }
+    }
+
+    return {
+      next: doneResult
+    };
+  }
+
+  exports.values = values;
+
+  function doneResult() {
+    return {
+      value: undefined,
+      done: true
+    };
+  }
+
+  Context.prototype = {
+    constructor: Context,
+    reset: function reset(skipTempReset) {
+      this.prev = 0;
+      this.next = 0;
+      this.sent = this._sent = undefined;
+      this.done = false;
+      this.delegate = null;
+      this.method = "next";
+      this.arg = undefined;
+      this.tryEntries.forEach(resetTryEntry);
+
+      if (!skipTempReset) {
+        for (var name in this) {
+          if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
+            this[name] = undefined;
+          }
+        }
+      }
+    },
+    stop: function stop() {
+      this.done = true;
+      var rootEntry = this.tryEntries[0];
+      var rootRecord = rootEntry.completion;
+
+      if (rootRecord.type === "throw") {
+        throw rootRecord.arg;
+      }
+
+      return this.rval;
+    },
+    dispatchException: function dispatchException(exception) {
+      if (this.done) {
+        throw exception;
+      }
+
+      var context = this;
+
+      function handle(loc, caught) {
+        record.type = "throw";
+        record.arg = exception;
+        context.next = loc;
+
+        if (caught) {
+          context.method = "next";
+          context.arg = undefined;
+        }
+
+        return !!caught;
+      }
+
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+        var record = entry.completion;
+
+        if (entry.tryLoc === "root") {
+          return handle("end");
+        }
+
+        if (entry.tryLoc <= this.prev) {
+          var hasCatch = hasOwn.call(entry, "catchLoc");
+          var hasFinally = hasOwn.call(entry, "finallyLoc");
+
+          if (hasCatch && hasFinally) {
+            if (this.prev < entry.catchLoc) {
+              return handle(entry.catchLoc, true);
+            } else if (this.prev < entry.finallyLoc) {
+              return handle(entry.finallyLoc);
+            }
+          } else if (hasCatch) {
+            if (this.prev < entry.catchLoc) {
+              return handle(entry.catchLoc, true);
+            }
+          } else if (hasFinally) {
+            if (this.prev < entry.finallyLoc) {
+              return handle(entry.finallyLoc);
+            }
+          } else {
+            throw new Error("try statement without catch or finally");
+          }
+        }
+      }
+    },
+    abrupt: function abrupt(type, arg) {
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+
+        if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
+          var finallyEntry = entry;
+          break;
+        }
+      }
+
+      if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
+        finallyEntry = null;
+      }
+
+      var record = finallyEntry ? finallyEntry.completion : {};
+      record.type = type;
+      record.arg = arg;
+
+      if (finallyEntry) {
+        this.method = "next";
+        this.next = finallyEntry.finallyLoc;
+        return ContinueSentinel;
+      }
+
+      return this.complete(record);
+    },
+    complete: function complete(record, afterLoc) {
+      if (record.type === "throw") {
+        throw record.arg;
+      }
+
+      if (record.type === "break" || record.type === "continue") {
+        this.next = record.arg;
+      } else if (record.type === "return") {
+        this.rval = this.arg = record.arg;
+        this.method = "return";
+        this.next = "end";
+      } else if (record.type === "normal" && afterLoc) {
+        this.next = afterLoc;
+      }
+
+      return ContinueSentinel;
+    },
+    finish: function finish(finallyLoc) {
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+
+        if (entry.finallyLoc === finallyLoc) {
+          this.complete(entry.completion, entry.afterLoc);
+          resetTryEntry(entry);
+          return ContinueSentinel;
+        }
+      }
+    },
+    "catch": function _catch(tryLoc) {
+      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+        var entry = this.tryEntries[i];
+
+        if (entry.tryLoc === tryLoc) {
+          var record = entry.completion;
+
+          if (record.type === "throw") {
+            var thrown = record.arg;
+            resetTryEntry(entry);
+          }
+
+          return thrown;
+        }
+      }
+
+      throw new Error("illegal catch attempt");
+    },
+    delegateYield: function delegateYield(iterable, resultName, nextLoc) {
+      this.delegate = {
+        iterator: values(iterable),
+        resultName: resultName,
+        nextLoc: nextLoc
+      };
+
+      if (this.method === "next") {
+        this.arg = undefined;
+      }
+
+      return ContinueSentinel;
+    }
+  };
+  return exports;
+}((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" ? module.exports : {});
+
+try {
+  regeneratorRuntime = runtime;
+} catch (accidentalStrictMode) {
+  Function("r", "regeneratorRuntime = r")(runtime);
+}
+},{}]},{},[1]);
+
+})((typeof require !== "undefined" && require) || undefined, (typeof exports !== "undefined" && exports) || undefined, (typeof module !== "undefined" && module) || undefined))
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/jquery3.min.js b/dist/firephp.build/scripts/devtools/dist/dist/jquery3.min.js
new file mode 100644
index 0000000..47b6397
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/jquery3.min.js
@@ -0,0 +1,2 @@
+/*! jQuery v3.5.0 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t=Object.create(null),V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
diff --git a/dist/firephp.build/scripts/devtools/lib/jsonrep.js b/dist/firephp.build/scripts/devtools/dist/dist/jsonrep.js
similarity index 97%
rename from dist/firephp.build/scripts/devtools/lib/jsonrep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/jsonrep.js
index 7dd0e12..fc9e8f0 100644
--- a/dist/firephp.build/scripts/devtools/lib/jsonrep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/jsonrep.js
@@ -85,7 +85,7 @@ function makeExports(exports) {
     var keys = Object.keys(node);
 
     if (keys.length === 1 && /^@/.test(keys[0])) {
-      uri = keys[0].replace(/^@/, "") + ".rep";
+      uri = "/../" + keys[0].replace(/^@/, "") + ".rep";
       node = node[keys[0]];
     } else {
       if (exports.options.defaultRenderer) {
@@ -103,14 +103,10 @@ function makeExports(exports) {
           }
         });
       } else {
-        uri = "dist/insight.rep.js";
+        uri = "/reps/insight.rep.js";
       }
     }
 
-    if (/^dist\//.test(uri) && exports.options.ourBaseUri) {
-      uri = exports.options.ourBaseUri + "/" + uri;
-    }
-
     return exports.loadRenderer(uri).then(function (renderer) {
       return renderer.main(exports, node, {
         renderer: {
@@ -132,27 +128,30 @@ function makeExports(exports) {
       } else {
         exports.PINF = WINDOW.PINF;
       }
+    }
 
-      if (WINDOW.document) {
+    exports.getBaseUri = function () {
+      if (!exports.options.ourBaseUri && typeof WINDOW.document !== "undefined") {
         if (!exports.options.ourBaseUri) {
           var ourBaseUri = Array.from(WINDOW.document.querySelectorAll('SCRIPT[src]')).filter(function (tag) {
             return /\/jsonrep(\.min)?\.js$/.test(tag.getAttribute("src"));
           });
 
-          if (!ourBaseUri.length) {
-            exports.options.ourBaseUri = "";
-          } else {
-            exports.options.ourBaseUri = ourBaseUri[0].getAttribute("src").split("/").slice(0, -2).join("/");
+          if (ourBaseUri.length) {
+            exports.options.ourBaseUri = ourBaseUri[0].getAttribute("src").replace(/\/[^\/]+$/, '');
           }
         }
       }
-    }
 
-    exports.loadStyle = function (uri) {
-      if (WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
-        uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
+      if (!exports.options.ourBaseUri) {
+        throw new Error("[jsonrep] Could not determine 'ourBaseUri' by looking at 'options' and injected script tags!");
       }
 
+      return exports.options.ourBaseUri;
+    };
+
+    exports.loadStyle = function (uri) {
+      uri = "".concat(exports.getBaseUri(), "/").concat(uri).replace(/\/\.?\//g, '/');
       return new Promise(function (resolve, reject) {
         var link = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "link") : window.document.createElement("link");
         link.rel = "stylesheet";
@@ -168,10 +167,7 @@ function makeExports(exports) {
     };
 
     exports.loadRenderer = function (uri) {
-      if (WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
-        uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
-      }
-
+      uri = "".concat(exports.getBaseUri(), "/").concat(uri).replace(/\/\.?\//g, '/');
       return new Promise(function (resolve, reject) {
         exports.PINF.sandbox(uri, resolve, reject);
       });
@@ -199,7 +195,7 @@ function makeExports(exports) {
             if (block._format === 'json') {
               var cssConfig = JSON.parse(block.getCode());
               el.setAttribute("_cssid", cssConfig._cssid);
-              exports.loadStyle(cssConfig.repUri + '.rep.css');
+              exports.loadStyle('../' + cssConfig.repUri + '.rep.css');
             } else if (block._format === 'css') {
               css = block.getCode();
             }
@@ -258,8 +254,6 @@ function makeExports(exports) {
 
   if (typeof sandbox !== "undefined") {
     init(sandbox);
-  } else if (WINDOW) {
-    WINDOW.jsonrep = init({});
   } else if (typeof exports !== "undefined") {
     init(exports);
   } else {
@@ -384,7 +378,7 @@ Codeblock.prototype.getCode = function () {
 **/
 
 (function (exports) {
- 
+
 // The global `require` for the 'external' (to the loader) environment.
 var Loader = function (global) {
 
@@ -431,7 +425,7 @@ var Loader = function (global) {
 	}
 
 	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
-	function loadInBrowser (uri, loadedCallback) {
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
 		try {
 			// See if we are in a web worker.
 			if (typeof importScripts !== "undefined") {
@@ -459,7 +453,9 @@ var Loader = function (global) {
 				if (ev.type === "load" || readyStates[this.readyState]) {
 					this.onload = this.onreadystatechange = this.onerror = null;
 					loadedCallback(null, function () {
-						element.parentNode.removeChild(element);
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
 					});
 				}
 			}
@@ -572,7 +568,8 @@ var Loader = function (global) {
 										cleanupCallback();
 									}
 								});
-							}
+							},
+							sandboxOptions
 						);
 					}
 				}
@@ -624,7 +621,8 @@ var Loader = function (global) {
 									function () {
 										pending -= 1;
 										finalize();
-									}
+									},
+									sandboxOptions
 								);
 							}
 						}
@@ -1113,7 +1111,7 @@ var Loader = function (global) {
 					loadedCallback(null);
 					return;
 				}
-				return fallbackLoad(uri, loadedCallback);
+				return fallbackLoad(uri, loadedCallback, options);
 			}
 			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
 		}
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/div.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/div.rep.js
new file mode 100644
index 0000000..0c3bd60
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/div.rep.js
@@ -0,0 +1,25 @@
+
+PINF.bundle("", function(require) {
+    require.memoize("/main.js", function(require, exports, module) {
+
+        exports.main = function (JSONREP, node) {
+
+            function makeAttributes () {
+                var attributes = {};
+                if (node.style) {
+                    attributes.style = Object.keys(node.style).map(function (name) {
+                        return name + ':' + node.style[name];
+                    }).join(";");
+                }
+                return Object.keys(attributes).map(function (name) {
+                    return name + '="' + attributes[name].replace(/"/g, '\\"') + '"';
+                }).join(" ");
+            }
+
+            return JSONREP.markupNode(node.innerHTML).then(function (html) {
+
+                return ('<div ' + makeAttributes() + '>' + html + '</div>');
+            });
+        }
+	});
+});
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/golden-layout.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/golden-layout.rep.js
new file mode 100644
index 0000000..b1c1921
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/golden-layout.rep.js
@@ -0,0 +1,96 @@
+
+PINF.bundle("", function(require) {
+
+    require.memoize("/main.js", function(require, exports, module) {
+
+        function ensureDependencies (done) {
+
+            var link = document.createElement("link");
+            link.href = "//golden-layout.com/files/latest/css/goldenlayout-base.css";
+            link.type = "text/css";
+            link.rel = "stylesheet";
+            document.getElementsByTagName("head")[0].appendChild(link);
+
+            var link = document.createElement("link");
+            link.href = "//golden-layout.com/files/latest/css/goldenlayout-dark-theme.css";
+            link.type = "text/css";
+            link.rel = "stylesheet";
+            document.getElementsByTagName("head")[0].appendChild(link);
+
+            var script = document.createElement('script');
+            script.src = '//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js';
+            script.type = 'text/javascript';
+            script.onload = function () {
+                var script = document.createElement('script');
+                script.src = '//golden-layout.com/files/latest/js/goldenlayout.min.js';
+                script.type = 'text/javascript';
+                script.onload = function () {
+                    return done();
+                }
+                document.getElementsByTagName('head')[0].appendChild(script);
+            },
+            document.getElementsByTagName('head')[0].appendChild(script);
+        }
+
+        exports.main = function (JSONREP, node) {
+
+            return JSONREP.makeRep({
+                html: '<div style="width: 100%; height: 100%; color: #ffffff;"></div>',
+                css: [
+                    '[__dbid][__dtid="insight.domplate.reps/default/string"] {',
+                    '    color: inherit;',
+                    '}'
+                ].join("\n"),                    
+                on: {
+                    mount: function (el) {
+
+                        ensureDependencies(function () {
+
+                            var myLayout = new window.GoldenLayout(node, el);
+
+                            myLayout.registerComponent('example', function (container, state) {
+
+                                var node = {};
+                                var key = Object.keys(state).filter(function (key) {
+                                    return /^@/.test(key);
+                                });
+                                if (key.length === 1) {
+                                    node[key] = state[key];
+                                } else
+                                if (key.length > 1) {
+                                    console.error("state", state);
+                                    throw new Error("You need one, and only one, key prefixed by '@'!");
+                                } else {
+                                    node = state;
+                                }
+
+                                JSONREP.markupNode(node).then(function (html) {
+
+                                    container.getElement().html('<div>' + html + '</div>');
+
+
+                                    var el = container.getElement().get(0);
+
+                                    Array.from(el.querySelectorAll('[_repid]')).forEach(function (el) {
+
+                                        var rep = JSONREP.getRepForId(el.getAttribute("_repid"));
+                                        if (
+                                            rep &&
+                                            rep.on &&
+                                            rep.on.mount
+                                        ) {
+                                            rep.on.mount(el);
+                                        }
+                                    });
+                                });        
+                            });
+
+                            myLayout.init();
+                        });
+                    }
+                }
+            });
+        }
+    });
+
+});
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/exports.json b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/exports.json
new file mode 100644
index 0000000..31cfcca
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/exports.json
@@ -0,0 +1,12 @@
+{
+    "js.browser": {
+        "insight-domplate-renderer": {
+            "core": {
+                "path": "./insight-domplate-renderer.browser.js",
+                "summary": "Browser JavaScript source code to load and bootstrap reps.",
+                "size": 91354,
+                "hash": "sha1:f4cf21d714d3768c53133d053dbcd0f1b8b3183f"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/insight-domplate-renderer.browser.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/insight-domplate-renderer.browser.js
new file mode 100644
index 0000000..1bc1eb8
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/insight-domplate-renderer.browser.js
@@ -0,0 +1,2840 @@
+((function (_require, _exports, _module) {
+var bundle = { require: _require, exports: _exports, module: _module };
+var exports = undefined;
+var module = undefined;
+var define = function (deps, init) {
+var exports = init();
+[["insight-domplate-renderer","Renderer"]].forEach(function (expose) {
+if (typeof window !== "undefined") {
+window[expose[0]] = exports[expose[1]];
+} else if (typeof self !== "undefined") {
+self[expose[0]] = exports[expose[1]];
+}
+});
+}; define.amd = true;
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+(function (global){
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var RT = _require_("./rt");
+
+var Renderer = exports.Renderer = _require_("./renderer").Renderer;
+
+function Domplate(exports) {
+  exports.util = _require_("./util");
+
+  exports.loadStyle = function (uri, baseUrl) {
+    var WINDOW = window;
+
+    if (typeof baseUrl === 'undefined' && WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
+      uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
+    } else if (typeof baseUrl !== 'undefined') {
+      uri = [baseUrl, uri].join("/").replace(/\/\.?\//g, "/");
+    }
+
+    return new Promise(function (resolve, reject) {
+      var link = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "link") : window.document.createElement("link");
+      link.rel = "stylesheet";
+      link.href = uri;
+
+      link.onload = function () {
+        resolve();
+      };
+
+      var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+      head.appendChild(link);
+    });
+  };
+
+  exports.EVAL = {
+    compileMarkup: function compileMarkup(code, context) {
+      return context.compiled(context);
+    },
+    compileDOM: function compileDOM(code, context) {
+      return context.compiled(context);
+    }
+  };
+  exports.tags = {};
+  exports.tags._domplate_ = exports;
+
+  var DomplateTag = exports.DomplateTag = function DomplateTag(tagName) {
+    this.tagName = tagName;
+  };
+
+  function DomplateEmbed() {}
+
+  function DomplateLoop() {}
+
+  function DomplateIf() {}
+
+  function copyArray(oldArray) {
+    var ary = [];
+    if (oldArray) for (var i = 0; i < oldArray.length; ++i) {
+      ary.push(oldArray[i]);
+    }
+    return ary;
+  }
+
+  function copyObject(l, r) {
+    var m = {};
+    extend(m, l);
+    extend(m, r);
+    return m;
+  }
+
+  function extend(l, r) {
+    for (var n in r) {
+      l[n] = r[n];
+    }
+  }
+
+  var womb = null;
+
+  var domplate = exports.domplate = function () {
+    var lastSubject;
+
+    for (var i = 0; i < arguments.length; ++i) {
+      lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
+    }
+
+    for (var name in lastSubject) {
+      var val = lastSubject[name];
+      if (isTag(val)) val.tag.subject = lastSubject;
+    }
+
+    return lastSubject;
+  };
+
+  domplate.context = function (context, fn) {
+    var lastContext = domplate.lastContext;
+    domplate.topContext = context;
+    fn.apply(context);
+    domplate.topContext = lastContext;
+  };
+
+  exports.tags.TAG = function () {
+    var embed = new DomplateEmbed();
+    return embed.merge(arguments);
+  };
+
+  exports.tags.FOR = domplate.FOR = function () {
+    var loop = new DomplateLoop();
+    return loop.merge(arguments);
+  };
+
+  exports.tags.IF = domplate.IF = function () {
+    var loop = new DomplateIf();
+    return loop.merge(arguments);
+  };
+
+  DomplateTag.prototype = {
+    merge: function merge(args, oldTag) {
+      if (oldTag) this.tagName = oldTag.tagName;
+      this.context = oldTag ? oldTag.context : null;
+      this.subject = oldTag ? oldTag.subject : null;
+      this.attrs = oldTag ? copyObject(oldTag.attrs) : {};
+      this.classes = oldTag ? copyObject(oldTag.classes) : {};
+      this.props = oldTag ? copyObject(oldTag.props) : null;
+      this.listeners = oldTag ? copyArray(oldTag.listeners) : null;
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args.length ? args[0] : null;
+      var hasAttrs = _typeof(attrs) == "object" && !isTag(attrs);
+      this.resources = {};
+      this.children = [];
+      if (domplate.topContext) this.context = domplate.topContext;
+      if (args.length) parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
+      if (hasAttrs) this.parseAttrs(attrs);
+      return creator(this, DomplateTag);
+    },
+    parseAttrs: function parseAttrs(args) {
+      for (var name in args) {
+        var val = parseValue(args[name]);
+        readPartNames(val, this.vars);
+
+        if (name.indexOf("on") === 0) {
+          var eventName = name.substr(2);
+          if (!this.listeners) this.listeners = [];
+          this.listeners.push(eventName, val);
+        } else if (name[0] === "_" && name[1] !== "_") {
+          var propName = name.substr(1);
+          if (!this.props) this.props = {};
+          this.props[propName] = val;
+        } else if (name[0] === "$") {
+          var className = name.substr(1);
+          if (!this.classes) this.classes = {};
+          this.classes[className] = val;
+        } else {
+          if (name === "class" && this.attrs.hasOwnProperty(name)) this.attrs[name] += " " + val;else this.attrs[name] = val;
+        }
+      }
+    },
+    compile: function compile() {
+      if (this.renderMarkup) {
+        return;
+      }
+
+      if (this.subject._resources) {
+        this.resources = this.subject._resources();
+      }
+
+      this.compileMarkup();
+      this.compileDOM();
+    },
+    compileMarkup: function compileMarkup() {
+      this.markupArgs = [];
+      var topBlock = [],
+          topOuts = [],
+          blocks = [],
+          info = {
+        args: this.markupArgs,
+        argIndex: 0
+      };
+      this.generateMarkup(topBlock, topOuts, blocks, info, true);
+      this.addCode(topBlock, topOuts, blocks);
+      var fnBlock = ['(function (__code__, __context__, __in__, __out__'];
+
+      for (var i = 0; i < info.argIndex; ++i) {
+        fnBlock.push(', s', i);
+      }
+
+      fnBlock.push(') {');
+      if (this.subject) fnBlock.push('  with (this) {');
+      if (this.context) fnBlock.push('  with (__context__) {');
+      fnBlock.push('  with (__in__) {');
+      fnBlock.push.apply(fnBlock, blocks);
+      if (this.subject) fnBlock.push('  }');
+      if (this.context) fnBlock.push('  }');
+      fnBlock.push('}})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderMarkup = exports.EVAL.compileMarkup(js, RT.makeMarkupRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__markup
+      }));
+    },
+    getVarNames: function getVarNames(args) {
+      if (this.vars) args.push.apply(args, this.vars);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.getVarNames(args);else if (child instanceof Parts) {
+          for (var i = 0; i < child.parts.length; ++i) {
+            if (child.parts[i] instanceof Variables) {
+              var name = child.parts[i].names[0];
+              var names = name.split(".");
+              args.push(names[0]);
+            }
+          }
+        }
+      }
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info, topNode) {
+      topBlock.push(',"<', this.tagName, '"');
+
+      if (topNode) {
+        if (this.subject.__dbid) this.attrs['__dbid'] = this.subject.__dbid;
+        if (this.subject.__dtid) this.attrs['__dtid'] = this.subject.__dtid;
+      }
+
+      for (var name in this.attrs) {
+        if (name != "class") {
+          var val = this.attrs[name];
+          topBlock.push(', " ', name, '=\\""');
+          addParts(val, ',', topBlock, info, true);
+          topBlock.push(', "\\""');
+        }
+      }
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          readPartNames(this.listeners[i + 1], topOuts);
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          readPartNames(this.props[name], topOuts);
+        }
+      }
+
+      if (this.attrs.class || this.classes && Object.keys(this.classes).length > 0) {
+        topBlock.push(', " class=\\""');
+        if (this.attrs.hasOwnProperty("class")) addParts(this.attrs["class"], ',', topBlock, info, true);
+        topBlock.push(', " "');
+
+        for (var name in this.classes) {
+          topBlock.push(', (');
+          addParts(this.classes[name], '', topBlock, info);
+          topBlock.push(' ? "', name, '" + " " : "")');
+        }
+
+        topBlock.push(', "\\""');
+      }
+
+      if (this.tagName == "br") {
+        topBlock.push(',"/>"');
+      } else {
+        topBlock.push(',">"');
+        this.generateChildMarkup(topBlock, topOuts, blocks, info);
+        topBlock.push(',"</', this.tagName, '>"');
+      }
+    },
+    generateChildMarkup: function generateChildMarkup(topBlock, topOuts, blocks, info) {
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.generateMarkup(topBlock, topOuts, blocks, info);else addParts(child, ',', topBlock, info, true);
+      }
+    },
+    addCode: function addCode(topBlock, topOuts, blocks) {
+      if (topBlock.length) blocks.push('    __code__.push(""', topBlock.join(""), ');');
+      if (topOuts.length) blocks.push('__out__.push(', topOuts.join(","), ');');
+      topBlock.splice(0, topBlock.length);
+      topOuts.splice(0, topOuts.length);
+    },
+    addLocals: function addLocals(blocks) {
+      var varNames = [];
+      this.getVarNames(varNames);
+      var map = {};
+
+      for (var i = 0; i < varNames.length; ++i) {
+        var name = varNames[i];
+        if (map.hasOwnProperty(name)) continue;
+        map[name] = 1;
+        var names = name.split(".");
+        blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';');
+      }
+    },
+    compileDOM: function compileDOM() {
+      var path = [];
+      var blocks = [];
+      this.domArgs = [];
+      path.embedIndex = 0;
+      path.loopIndex = 0;
+      path.ifIndex = 0;
+      path.staticIndex = 0;
+      path.renderIndex = 0;
+      var nodeCount = this.generateDOM(path, blocks, this.domArgs);
+      var fnBlock = ['(function (root, context, o'];
+
+      for (var i = 0; i < path.staticIndex; ++i) {
+        fnBlock.push(', ', 's' + i);
+      }
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        fnBlock.push(', ', 'd' + i);
+      }
+
+      fnBlock.push(') {');
+
+      for (var i = 0; i < path.loopIndex; ++i) {
+        fnBlock.push('  var l', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.ifIndex; ++i) {
+        fnBlock.push('  var if_', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.embedIndex; ++i) {
+        fnBlock.push('  var e', i, ' = 0;');
+      }
+
+      if (this.subject) {
+        fnBlock.push('  with (this) {');
+      }
+
+      if (this.context) {
+        fnBlock.push('    with (context) {');
+      }
+
+      fnBlock.push(blocks.join(""));
+      if (this.context) fnBlock.push('    }');
+      if (this.subject) fnBlock.push('  }');
+      fnBlock.push('  return ', nodeCount, ';');
+      fnBlock.push('})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderDOM = exports.EVAL.compileDOM(js, RT.makeDOMRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__dom
+      }));
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      if (this.listeners || this.props) this.generateNodePath(path, blocks);
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          var val = this.listeners[i + 1];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);');
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          var val = this.props[name];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.', name, ' = ', arg, ';');
+        }
+      }
+
+      this.generateChildDOM(path, blocks, args);
+      return 1;
+    },
+    generateNodePath: function generateNodePath(path, blocks) {
+      blocks.push("        node = __path__(root, o");
+
+      for (var i = 0; i < path.length; ++i) {
+        blocks.push(",", path[i]);
+      }
+
+      blocks.push(");");
+    },
+    generateChildDOM: function generateChildDOM(path, blocks, args) {
+      path.push(0);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) path[path.length - 1] += '+' + child.tag.generateDOM(path, blocks, args);else path[path.length - 1] += '+1';
+      }
+
+      path.pop();
+    }
+  };
+  DomplateEmbed.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.value = oldTag ? oldTag.value : parseValue(args[0]);
+      this.attrs = oldTag ? oldTag.attrs : {};
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args[1];
+
+      for (var name in attrs) {
+        var val = parseValue(attrs[name]);
+        this.attrs[name] = val;
+        readPartNames(val, this.vars);
+      }
+
+      var retval = creator(this, DomplateEmbed);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.value instanceof Parts) names.push(this.value.parts[0].name);
+      if (this.vars) names.push.apply(names, this.vars);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('__link__(');
+      addParts(this.value, '', blocks, info);
+      blocks.push(', __code__, __out__, {');
+      var lastName = null;
+
+      for (var name in this.attrs) {
+        if (lastName) blocks.push(',');
+        lastName = name;
+        var val = this.attrs[name];
+        blocks.push('"', name, '":');
+        addParts(val, '', blocks, info);
+      }
+
+      blocks.push('});');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var embedName = 'e' + path.embedIndex++;
+      this.generateNodePath(path, blocks);
+      var valueName = 'd' + path.renderIndex++;
+      var argsName = 'd' + path.renderIndex++;
+      blocks.push('        ', embedName + ' = __link__(node, ', valueName, ', ', argsName, ');');
+      return embedName;
+    }
+  });
+  DomplateLoop.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.varName = oldTag ? oldTag.varName : args[0];
+      this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 2);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateLoop);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.iter instanceof Parts) names.push(this.iter.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var iterName;
+
+      if (this.iter instanceof Parts) {
+        var part = this.iter.parts[0];
+        iterName = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            iterName = part.format[i] + "(" + iterName + ")";
+          }
+        }
+      } else {
+        iterName = this.iter;
+      }
+
+      blocks.push('    __loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('    }]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var iterName = 'd' + path.renderIndex++;
+      var counterName = 'i' + path.loopIndex;
+      var loopName = 'l' + path.loopIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+      var basePath = path[path.length - 1];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        path[path.length - 1] = basePath + '+' + loopName + '+' + nodeCount;
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      path[path.length - 1] = basePath + '+' + loopName;
+      blocks.push('      ', loopName, ' = __loop__.apply(this, [', iterName, ', function(', counterName, ',', loopName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('        return ', nodeCount, ';');
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return loopName;
+    }
+  });
+  DomplateIf.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.booleanVar = oldTag ? oldTag.booleanVar : parseValue(args[0]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 1);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateIf);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.booleanVar instanceof Parts) names.push(this.booleanVar.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var expr;
+
+      if (this.booleanVar instanceof Parts) {
+        var part = this.booleanVar.parts[0];
+        expr = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            expr = part.format[i] + "(" + expr + ")";
+          }
+        }
+      } else {
+        expr = this.booleanVar;
+      }
+
+      blocks.push('__if__.apply(this, [', expr, ', __out__, function(__out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('}]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var controlName = 'd' + path.renderIndex++;
+      var ifName = 'if_' + path.ifIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      blocks.push('      ', ifName, ' = __if__.apply(this, [', controlName, ', function(', ifName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return controlName;
+    }
+  });
+
+  function Variables(names, format) {
+    this.names = names;
+    this.format = format;
+  }
+
+  function Parts(parts) {
+    this.parts = parts;
+  }
+
+  function parseParts(str) {
+    var index = 0;
+    var parts = [];
+    var m;
+    var re = /\$([_A-Za-z][$_A-Za-z0-9.,|]*)/g;
+
+    while (m = re.exec(str)) {
+      var pre = str.substr(index, re.lastIndex - m[0].length - index);
+      if (pre) parts.push(pre);
+      var segs = m[1].split("|");
+      var vars = segs[0].split(",$");
+      parts.push(new Variables(vars, segs.splice(1)));
+      index = re.lastIndex;
+    }
+
+    if (!index) {
+      return str;
+    }
+
+    var post = str.substr(index);
+    if (post) parts.push(post);
+    var retval = new Parts(parts);
+    return retval;
+  }
+
+  function parseValue(val) {
+    return typeof val == 'string' ? parseParts(val) : val;
+  }
+
+  function parseChildren(args, offset, vars, children) {
+    for (var i = offset; i < args.length; ++i) {
+      var val = parseValue(args[i]);
+      children.push(val);
+      readPartNames(val, vars);
+    }
+  }
+
+  function readPartNames(val, vars) {
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+        if (part instanceof Variables) vars.push(part.names[0]);
+      }
+    }
+  }
+
+  function generateArg(val, path, args) {
+    if (val instanceof Parts) {
+      var vals = [];
+
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var varName = 'd' + path.renderIndex++;
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              varName = part.format[j] + '(' + varName + ')';
+            }
+          }
+
+          vals.push(varName);
+        } else vals.push('"' + part.replace(/"/g, '\\"') + '"');
+      }
+
+      return vals.join('+');
+    } else {
+      args.push(val);
+      return 's' + path.staticIndex++;
+    }
+  }
+
+  function addParts(val, delim, block, info, escapeIt) {
+    var vals = [];
+
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var partName = part.names.join(",");
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              partName = part.format[j] + "(" + partName + ")";
+            }
+          }
+
+          if (escapeIt) vals.push("__escape__(" + partName + ")");else vals.push(partName);
+        } else vals.push('"' + part + '"');
+      }
+    } else if (isTag(val)) {
+      info.args.push(val);
+      vals.push('s' + info.argIndex++);
+    } else vals.push('"' + val + '"');
+
+    var parts = vals.join(delim);
+    if (parts) block.push(delim, parts);
+  }
+
+  function isTag(obj) {
+    return (typeof obj == "function" || obj instanceof Function) && !!obj.tag;
+  }
+
+  function creator(tag, cons) {
+    var fn = function fn() {
+      var tag = arguments.callee.tag;
+      var cons = arguments.callee.cons;
+      var newTag = new cons();
+      return newTag.merge(arguments, tag);
+    };
+
+    fn.tag = tag;
+    fn.cons = cons;
+    extend(fn, Renderer);
+    return fn;
+  }
+
+  function defineTags() {
+    Array.from(arguments).forEach(function (tagName) {
+      var fnName = tagName.toUpperCase();
+
+      exports.tags[fnName] = function () {
+        var newTag = new this._domplate_.DomplateTag(tagName);
+        return newTag.merge(arguments);
+      };
+    });
+  }
+
+  defineTags("a", "button", "br", "canvas", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select", "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul");
+}
+
+exports.domplate = {};
+Domplate(exports.domplate);
+
+exports.domplate.ensureLoader = function () {
+  if (typeof window !== "undefined" && window.PINF) {
+    return window.PINF;
+  }
+
+  var PINF = _require_("pinf-loader-js/loader.browser");
+
+  return window.PINF;
+};
+
+exports.domplate.loadRep = function (url, options, successCallback, errorCallback) {
+  if (typeof options === "function") {
+    errorCallback = successCallback;
+    successCallback = options;
+    options = {};
+  }
+
+  var PINF = exports.domplate.ensureLoader();
+  return PINF.sandbox(url + ".rep", function (sandbox) {
+    var rep = sandbox.main(exports.domplate, options);
+    successCallback(rep);
+  }, errorCallback);
+};
+},{"./renderer":2,"./rt":3,"./util":4,"pinf-loader-js/loader.browser":6}],2:[function(_require_,module,exports){
+
+var Renderer = exports.Renderer = {
+  checkDebug: function checkDebug() {},
+  renderHTML: function renderHTML(args, outputs, self) {
+    var code = [];
+    var markupArgs = [code, this.tag.context ? this.tag.context : null, args, outputs];
+    markupArgs.push.apply(markupArgs, this.tag.markupArgs);
+    this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
+
+    if (this.tag.resources && this.tag.subject._resourceListener) {
+      this.tag.subject._resourceListener.register(this.tag.resources);
+    }
+
+    return code.join("");
+  },
+  insertRows: function insertRows(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var table = doc.createElement("table");
+    table.innerHTML = html;
+    var tbody = table.firstChild;
+    var parent = before.localName == "TR" ? before.parentNode : before;
+    var after = before.localName == "TR" ? before.nextSibling : null;
+    var firstRow = tbody.firstChild,
+        lastRow;
+
+    while (tbody.firstChild) {
+      lastRow = tbody.firstChild;
+      if (after) parent.insertBefore(lastRow, after);else parent.appendChild(lastRow);
+    }
+
+    var offset = 0;
+
+    if (before.localName == "TR") {
+      var node = firstRow.parentNode.firstChild;
+
+      for (; node && node != firstRow; node = node.nextSibling) {
+        ++offset;
+      }
+    }
+
+    var domArgs = [firstRow, this.tag.context, offset];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return [firstRow, lastRow];
+  },
+  insertAfter: function insertAfter(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var range = doc.createRange();
+    range.selectNode(doc.body);
+    var frag = range.createContextualFragment(html);
+    var root = frag.firstChild;
+    if (before.nextSibling) before.parentNode.insertBefore(frag, before.nextSibling);else before.parentNode.appendChild(frag);
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject ? this.tag.subject : null, domArgs);
+    return root;
+  },
+  replace: function replace(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var root;
+
+    if (parent.nodeType == 1) {
+      parent.innerHTML = html;
+      root = parent.firstChild;
+    } else {
+      if (!parent || parent.nodeType != 9) parent = document;
+      if (!womb || womb.ownerDocument != parent) womb = parent.createElement("div");
+      womb.innerHTML = html;
+      root = womb.firstChild;
+    }
+
+    var domArgs = [root, this.tag.context ? this.tag.context : null, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  append: function append(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    if (!womb || womb.ownerDocument != parent.ownerDocument) womb = parent.ownerDocument.createElement("div");
+    womb.innerHTML = html;
+    root = womb.firstChild;
+
+    while (womb.firstChild) {
+      parent.appendChild(womb.firstChild);
+    }
+
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  render: function render(args, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    return html;
+  }
+};
+},{}],3:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+function ArrayIterator(array) {
+  var index = -1;
+
+  this.next = function () {
+    if (++index >= array.length) throw StopIteration;
+    return array[index];
+  };
+}
+
+function StopIteration() {}
+
+exports.makeMarkupRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__link__ = function (tag, code, outputs, args) {
+    if (!tag) {
+      return;
+    }
+
+    if (!tag.tag) {
+      return;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return;
+    }
+
+    tag.tag.compile();
+
+    if (self.resources && tag.tag.resources && tag.tag.resources !== self.resources) {
+      for (var key in tag.tag.resources) {
+        self.resources[key] = tag.tag.resources[key];
+      }
+    }
+
+    var tagOutputs = [];
+    var markupArgs = [code, tag.tag.context ? tag.tag.context : null, args, tagOutputs];
+    markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
+    tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
+    outputs.push(tag);
+    outputs.push(tagOutputs);
+  };
+
+  exports.__escape__ = function (value) {
+    function replaceChars(ch) {
+      switch (ch) {
+        case "<":
+          return "&lt;";
+
+        case ">":
+          return "&gt;";
+
+        case "&":
+          return "&amp;";
+
+        case "'":
+          return "&#39;";
+
+        case '"':
+          return "&quot;";
+      }
+
+      return "?";
+    }
+
+    ;
+    return String(value).replace(/[<>&"']/g, replaceChars);
+  };
+
+  exports.__loop__ = function (iter, outputs, fn) {
+    var iterOuts = [];
+    outputs.push(iterOuts);
+
+    if (iter instanceof Array || typeof iter === "array" || Array.isArray(iter)) {
+      iter = new ArrayIterator(iter);
+    }
+
+    try {
+      if (!iter || !iter.next) {
+        console.error("Cannot iterate loop", iter, _typeof(iter), outputs, fn);
+        throw new Error("Cannot iterate loop as iter.next() method is not defined");
+      }
+
+      while (1) {
+        var value = iter.next();
+        var itemOuts = [0, 0];
+        iterOuts.push(itemOuts);
+        fn.apply(this, [value, itemOuts]);
+      }
+    } catch (exc) {
+      if (exc != StopIteration) throw exc;
+    }
+  };
+
+  exports.__if__ = function (booleanVar, outputs, fn) {
+    var ifControl = [];
+    outputs.push(ifControl);
+
+    if (booleanVar) {
+      ifControl.push(1);
+      fn.apply(this, [ifControl]);
+    } else {
+      ifControl.push(0);
+    }
+  };
+
+  return exports;
+};
+
+exports.makeDOMRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__bind__ = function (object, fn) {
+    return function (event) {
+      return fn.apply(object, [event]);
+    };
+  };
+
+  exports.__link__ = function (node, tag, args) {
+    if (!tag) {
+      return 0;
+    }
+
+    if (!tag.tag) {
+      return 0;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return 0;
+    }
+
+    tag.tag.compile();
+    var domArgs = [node, tag.tag.context ? tag.tag.context : null, 0];
+    domArgs.push.apply(domArgs, tag.tag.domArgs);
+    domArgs.push.apply(domArgs, args);
+    var oo = tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
+    return oo;
+  };
+
+  exports.__loop__ = function (iter, fn) {
+    if (!Array.isArray(iter)) {
+      return 0;
+    }
+
+    var nodeCount = 0;
+
+    for (var i = 0; i < iter.length; ++i) {
+      iter[i][0] = i;
+      iter[i][1] = nodeCount;
+      nodeCount += fn.apply(this, iter[i]);
+    }
+
+    return nodeCount;
+  };
+
+  exports.__if__ = function (control, fn) {
+    if (control && control[0]) {
+      fn.apply(this, [0, control[1]]);
+    } else {}
+  };
+
+  exports.__path__ = function (parent, offset) {
+    var root = parent;
+
+    for (var i = 2; i < arguments.length; ++i) {
+      var index = arguments[i];
+      if (i == 3) index += offset;
+
+      if (index == -1) {
+        parent = parent.parentNode;
+      } else {
+        parent = parent.childNodes[index];
+      }
+    }
+
+    return parent;
+  };
+
+  return exports;
+};
+},{}],4:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var FBTrace = {};
+exports.merge = _require_("deepmerge");
+
+exports.escapeNewLines = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+};
+
+exports.stripNewLines = function (value) {
+  return typeof value == "string" ? value.replace(/[\r\n]/gm, " ") : value;
+};
+
+exports.escapeJS = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n").replace('"', '\\"', "g");
+};
+
+exports.cropString = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  var halfLimit = limit / 2;
+  halfLimit -= 2;
+  if (text.length > limit) return text.substr(0, halfLimit) + alterText + text.substr(text.length - halfLimit);else return text;
+};
+
+exports.cropStringLeft = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  limit -= alterText.length;
+  if (text.length > limit) return alterText + text.substr(text.length - limit);else return text;
+};
+
+exports.hasClass = function (node, name) {
+  if (!node || node.nodeType != 1) return false;else {
+    for (var i = 1; i < arguments.length; ++i) {
+      var name = arguments[i];
+      var re = new RegExp("(^|\\s)" + name + "($|\\s)");
+      if (!re.exec(node.getAttribute("class"))) return false;
+    }
+
+    return true;
+  }
+};
+
+exports.setClass = function (node, name) {
+  if (node && !exports.hasClass(node, name)) node.className += " " + name;
+};
+
+exports.getClassValue = function (node, name) {
+  var re = new RegExp(name + "-([^ ]+)");
+  var m = re.exec(node.className);
+  return m ? m[1] : "";
+};
+
+exports.removeClass = function (node, name) {
+  if (node && node.className) {
+    var index = node.className.indexOf(name);
+
+    if (index >= 0) {
+      var size = name.length;
+      node.className = node.className.substr(0, index - 1) + node.className.substr(index + size);
+    }
+  }
+};
+
+exports.toggleClass = function (elt, name) {
+  if (exports.hasClass(elt, name)) exports.removeClass(elt, name);else exports.setClass(elt, name);
+};
+
+exports.setClassTimed = function (elt, name, context, timeout) {
+  if (!timeout) timeout = 1300;
+  if (elt.__setClassTimeout) context.clearTimeout(elt.__setClassTimeout);else exports.setClass(elt, name);
+
+  if (!exports.isVisible(elt)) {
+    if (elt.__invisibleAtSetPoint) elt.__invisibleAtSetPoint--;else elt.__invisibleAtSetPoint = 5;
+  } else {
+    delete elt.__invisibleAtSetPoint;
+  }
+
+  elt.__setClassTimeout = context.setTimeout(function () {
+    delete elt.__setClassTimeout;
+    if (elt.__invisibleAtSetPoint) exports.setClassTimed(elt, name, context, timeout);else {
+      delete elt.__invisibleAtSetPoint;
+      exports.removeClass(elt, name);
+    }
+  }, timeout);
+};
+
+exports.cancelClassTimed = function (elt, name, context) {
+  if (elt.__setClassTimeout) {
+    exports.removeClass(elt, name);
+    context.clearTimeout(elt.__setClassTimeout);
+    delete elt.__setClassTimeout;
+  }
+};
+
+exports.$ = function (id, doc) {
+  if (doc) return doc.getElementById(id);else return document.getElementById(id);
+};
+
+exports.getChildByClass = function (node) {
+  for (var i = 1; i < arguments.length; ++i) {
+    var className = arguments[i];
+    var child = node.firstChild;
+    node = null;
+
+    for (; child; child = child.nextSibling) {
+      if (exports.hasClass(child, className)) {
+        node = child;
+        break;
+      }
+    }
+  }
+
+  return node;
+};
+
+exports.getAncestorByClass = function (node, className) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (exports.hasClass(parent, className)) return parent;
+  }
+
+  return null;
+};
+
+exports.getElementByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  var elements = node.getElementsByClassName(className);
+  return elements[0];
+};
+
+exports.getElementsByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  return node.getElementsByClassName(className);
+};
+
+exports.getElementsByAttribute = function (node, attrName, attrValue) {
+  function iteratorHelper(node, attrName, attrValue, result) {
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      if (child.getAttribute(attrName) == attrValue) result.push(child);
+      iteratorHelper(child, attrName, attrValue, result);
+    }
+  }
+
+  var result = [];
+  iteratorHelper(node, attrName, attrValue, result);
+  return result;
+};
+
+exports.isAncestor = function (node, potentialAncestor) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (parent == potentialAncestor) return true;
+  }
+
+  return false;
+};
+
+exports.getNextElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.nextSibling;
+  }
+
+  return node;
+};
+
+exports.getPreviousElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.previousSibling;
+  }
+
+  return node;
+};
+
+exports.getBody = function (doc) {
+  if (doc.body) return doc.body;
+  var body = doc.getElementsByTagName("body")[0];
+  if (body) return body;
+  return doc.documentElement;
+};
+
+exports.findNextDown = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (criteria(child)) return child;
+    var next = exports.findNextDown(child, criteria);
+    if (next) return next;
+  }
+};
+
+exports.findPreviousUp = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.lastChild; child; child = child.previousSibling) {
+    var next = exports.findPreviousUp(child, criteria);
+    if (next) return next;
+    if (criteria(child)) return child;
+  }
+};
+
+exports.findNext = function (node, criteria, upOnly, maxRoot) {
+  if (!node) return null;
+
+  if (!upOnly) {
+    var next = exports.findNextDown(node, criteria);
+    if (next) return next;
+  }
+
+  for (var sib = node.nextSibling; sib; sib = sib.nextSibling) {
+    if (criteria(sib)) return sib;
+    var next = exports.findNextDown(sib, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) return exports.findNext(node.parentNode, criteria, true);
+};
+
+exports.findPrevious = function (node, criteria, downOnly, maxRoot) {
+  if (!node) return null;
+
+  for (var sib = node.previousSibling; sib; sib = sib.previousSibling) {
+    var prev = exports.findPreviousUp(sib, criteria);
+    if (prev) return prev;
+    if (criteria(sib)) return sib;
+  }
+
+  if (!downOnly) {
+    var next = exports.findPreviousUp(node, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) {
+    if (criteria(node.parentNode)) return node.parentNode;
+    return exports.findPrevious(node.parentNode, criteria, true);
+  }
+};
+
+exports.getNextByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findNext(root, iter);
+};
+
+exports.getPreviousByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findPrevious(root, iter);
+};
+
+exports.hasChildElements = function (node) {
+  if (node.contentDocument) return true;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (child.nodeType == 1) return true;
+  }
+
+  return false;
+};
+
+exports.isElement = function (o) {
+  try {
+    return o && o instanceof Element;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.isNode = function (o) {
+  try {
+    return o && o instanceof Node;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.cancelEvent = function (event) {
+  event.stopPropagation();
+  event.preventDefault();
+};
+
+exports.isLeftClick = function (event) {
+  return event.button == 0 && exports.noKeyModifiers(event);
+};
+
+exports.isMiddleClick = function (event) {
+  return event.button == 1 && exports.noKeyModifiers(event);
+};
+
+exports.isRightClick = function (event) {
+  return event.button == 2 && exports.noKeyModifiers(event);
+};
+
+exports.noKeyModifiers = function (event) {
+  return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
+};
+
+exports.isControlClick = function (event) {
+  return event.button == 0 && exports.isControl(event);
+};
+
+exports.isShiftClick = function (event) {
+  return event.button == 0 && exports.isShift(event);
+};
+
+exports.isControl = function (event) {
+  return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
+};
+
+exports.isControlShift = function (event) {
+  return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
+};
+
+exports.isShift = function (event) {
+  return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
+};
+
+exports.bind = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments));
+  };
+};
+
+exports.bindFixed = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, args);
+  };
+};
+
+exports.extend = function (l, r) {
+  var newOb = {};
+
+  for (var n in l) {
+    newOb[n] = l[n];
+  }
+
+  for (var n in r) {
+    newOb[n] = r[n];
+  }
+
+  return newOb;
+};
+
+exports.keys = function (map) {
+  var keys = [];
+
+  try {
+    for (var name in map) {
+      keys.push(name);
+    }
+  } catch (exc) {}
+
+  return keys;
+};
+
+exports.values = function (map) {
+  var values = [];
+
+  try {
+    for (var name in map) {
+      try {
+        values.push(map[name]);
+      } catch (exc) {
+        if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+      }
+    }
+  } catch (exc) {
+    if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+  }
+
+  return values;
+};
+
+exports.remove = function (list, item) {
+  for (var i = 0; i < list.length; ++i) {
+    if (list[i] == item) {
+      list.splice(i, 1);
+      break;
+    }
+  }
+};
+
+exports.sliceArray = function (array, index) {
+  var slice = [];
+
+  for (var i = index; i < array.length; ++i) {
+    slice.push(array[i]);
+  }
+
+  return slice;
+};
+
+function cloneArray(array, fn) {
+  var newArray = [];
+  if (fn) for (var i = 0; i < array.length; ++i) {
+    newArray.push(fn(array[i]));
+  } else for (var i = 0; i < array.length; ++i) {
+    newArray.push(array[i]);
+  }
+  return newArray;
+}
+
+function extendArray(array, array2) {
+  var newArray = [];
+  newArray.push.apply(newArray, array);
+  newArray.push.apply(newArray, array2);
+  return newArray;
+}
+
+exports.extendArray = extendArray;
+exports.cloneArray = cloneArray;
+
+function arrayInsert(array, index, other) {
+  for (var i = 0; i < other.length; ++i) {
+    array.splice(i + index, 0, other[i]);
+  }
+
+  return array;
+}
+
+exports.arrayInsert = arrayInsert;
+
+exports.isArrayLike = function (object) {
+  return Object.prototype.toString.call(object) == "[object Array]" || exports.isArguments(object);
+};
+
+exports.isArguments = function (object) {
+  if (Object.prototype.toString.call(object) == "[object Arguments]") return true;
+  if (!_typeof(object) == "object" || !Object.prototype.hasOwnProperty.call(object, 'callee') || !object.callee || Object.prototype.toString.call(object.callee) !== '[object Function]' || typeof object.length != 'number') return false;
+
+  for (var name in object) {
+    if (name === 'callee' || name === 'length') return false;
+  }
+
+  return true;
+};
+},{"deepmerge":5}],5:[function(_require_,module,exports){
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.deepmerge = factory());
+}(this, (function () { 'use strict';
+
+var isMergeableObject = function isMergeableObject(value) {
+	return isNonNullObject(value)
+		&& !isSpecial(value)
+};
+
+function isNonNullObject(value) {
+	return !!value && typeof value === 'object'
+}
+
+function isSpecial(value) {
+	var stringValue = Object.prototype.toString.call(value);
+
+	return stringValue === '[object RegExp]'
+		|| stringValue === '[object Date]'
+		|| isReactElement(value)
+}
+
+// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
+var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
+var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
+
+function isReactElement(value) {
+	return value.$$typeof === REACT_ELEMENT_TYPE
+}
+
+function emptyTarget(val) {
+	return Array.isArray(val) ? [] : {}
+}
+
+function cloneUnlessOtherwiseSpecified(value, options) {
+	return (options.clone !== false && options.isMergeableObject(value))
+		? deepmerge(emptyTarget(value), value, options)
+		: value
+}
+
+function defaultArrayMerge(target, source, options) {
+	return target.concat(source).map(function(element) {
+		return cloneUnlessOtherwiseSpecified(element, options)
+	})
+}
+
+function mergeObject(target, source, options) {
+	var destination = {};
+	if (options.isMergeableObject(target)) {
+		Object.keys(target).forEach(function(key) {
+			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
+		});
+	}
+	Object.keys(source).forEach(function(key) {
+		if (!options.isMergeableObject(source[key]) || !target[key]) {
+			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
+		} else {
+			destination[key] = deepmerge(target[key], source[key], options);
+		}
+	});
+	return destination
+}
+
+function deepmerge(target, source, options) {
+	options = options || {};
+	options.arrayMerge = options.arrayMerge || defaultArrayMerge;
+	options.isMergeableObject = options.isMergeableObject || isMergeableObject;
+
+	var sourceIsArray = Array.isArray(source);
+	var targetIsArray = Array.isArray(target);
+	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
+
+	if (!sourceAndTargetTypesMatch) {
+		return cloneUnlessOtherwiseSpecified(source, options)
+	} else if (sourceIsArray) {
+		return options.arrayMerge(target, source, options)
+	} else {
+		return mergeObject(target, source, options)
+	}
+}
+
+deepmerge.all = function deepmergeAll(array, options) {
+	if (!Array.isArray(array)) {
+		throw new Error('first argument should be an array')
+	}
+
+	return array.reduce(function(prev, next) {
+		return deepmerge(prev, next, options)
+	}, {})
+};
+
+var deepmerge_1 = deepmerge;
+
+return deepmerge_1;
+
+})));
+
+},{}],6:[function(_require_,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+exports.PINF = (function (global) {
+
+	if (!global || typeof global !== "object") {
+		throw new Error("No root object scope provided!");
+	}
+
+	// If `PINF` gloabl already exists, don't do anything to change it.
+	if (typeof global.PINF !== "undefined") {
+		return global.PINF;
+	}
+
+	const LOADER = _require_('./loader');
+
+	const PINF = LOADER.Loader({
+		document: global.document
+	});
+
+	global.PINF = PINF;
+
+	// Attach postMessage handler to listen for sandbox load triggers.
+	// This is useful in Web Workers where only the loader must be loaded and
+	// sandboxes can then be loaded like this:
+	//    worker.postMessage(URIJS("notify://pinf-loader-js/sandbox/load").addSearch("uri", uri).toString())
+	if (typeof global.addEventListener === "function") {
+		global.addEventListener("message", function (event) {
+			var m = null;
+			if (
+				typeof event.data === "string" &&
+				(m = event.data.match(/^notify:\/\/pinf-loader-js\/sandbox\/load\?uri=(.+)$/)) &&
+				(m = decodeURIComponent(m[1])) &&
+				// SECURITY: Only allow URIs that begin with `/` so that scripts may NOT
+				//           be loaded cross-domain this way. If this was allowed one could
+				//           load malicious code simply by posting a message to this window.
+				/^\/[^\/]/.test(m)
+			) {
+				return PINF.sandbox(m, function (sandbox) {
+		            sandbox.main();
+					if (typeof global.postMessage === "function") {
+						global.postMessage(event.data.replace("/load?", "/loaded?"));
+					}
+		        }, function (err) {
+		        	// TODO: Post error back to main frame instead of throwing?
+		        	throw err;
+		        });
+			}
+		}, false);
+	}
+
+	return global.PINF;
+}(
+	typeof window !== "undefined" ?
+		// Used in the browser
+		window :
+		typeof self !== "undefined" ?
+			// Used in web worker
+			self :
+			// No root scope variable found
+			null
+));
+
+},{"./loader":7}],7:[function(_require_,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+(function (exports) {
+
+// The global `require` for the 'external' (to the loader) environment.
+var Loader = function (global) {
+
+	var loadedBundles = [],
+		// @see https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L69
+		readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 },
+		lastModule = null,
+		headTag = null;
+
+	// For older browsers that don't have `Object.keys()` (Firefox 3.6)
+	function keys(obj) {
+		var keys = [];
+		for (var key in obj) {
+			keys.push(key);
+		}
+		return keys;
+	}
+
+	// TODO: Use 'Object.create()` in modern browsers
+	function create (proto) {
+		function F() {}
+		F.prototype = proto;
+		return new F();
+	};
+
+	function normalizeSandboxArguments (implementation) {
+		return function (programIdentifier, options, loadedCallback, errorCallback) {
+			/*DEBUG*/ if (typeof options === "function" && typeof loadedCallback === "object") {
+			/*DEBUG*/     throw new Error("Callback before options for `require.sandbox(programIdentifier, options, loadedCallback)`");
+			/*DEBUG*/ }
+			if (typeof options === "function" && !loadedCallback && !errorCallback) {
+				loadedCallback = options;
+				options = {};
+			} else
+			if (typeof options === "function" && typeof loadedCallback === "function" && !errorCallback) {
+				errorCallback = loadedCallback;
+				loadedCallback = options;
+				options = {};
+			} else {
+				options = options || {};
+			}
+			implementation(programIdentifier, options, loadedCallback, errorCallback);
+		};
+	}
+
+	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
+		try {
+			// See if we are in a web worker.
+			if (typeof importScripts !== "undefined") {
+				importScripts(uri.replace(/^\/?\{host\}/, ""));
+				return loadedCallback(null);
+			}
+			var document = global.document;
+			/*DEBUG*/ if (!document) {
+			/*DEBUG*/ 	throw new Error("Unable to get reference to 'document'!");
+			/*DEBUG*/ }
+			var location = document.location;
+			if (/^\/?\{host\}\//.test(uri)) {
+				uri = location.protocol + "//" + location.host + uri.replace(/^\/?\{host\}/, "");
+			} else
+			if (/^\/\//.test(uri)) {
+				uri = location.protocol + uri;
+			}
+			if (!headTag) {
+				headTag = document.getElementsByTagName("head")[0];
+			}
+			var element = document.createElement("script");
+			element.type = "text/javascript";
+			element.onload = element.onreadystatechange = function (ev) {
+				ev = ev || global.event;
+				if (ev.type === "load" || readyStates[this.readyState]) {
+					this.onload = this.onreadystatechange = this.onerror = null;
+					loadedCallback(null, function () {
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
+					});
+				}
+			}
+			element.onerror = function (err) {
+				/*DEBUG*/ console.error(err);
+				return loadedCallback(new Error("Error loading '" + uri + "'"));
+			}
+			element.charset = "utf-8";
+			element.async = true;
+			element.src = uri;
+			element = headTag.insertBefore(element, headTag.firstChild);
+		} catch(err) {
+			loadedCallback(err);
+		}
+	}
+
+	// A set of modules working together.
+	var Sandbox = function (sandboxIdentifier, sandboxOptions, loadedCallback) {
+
+		var moduleInitializers = {},
+			initializedModules = {},
+			/*DEBUG*/ bundleIdentifiers = {},
+			packages = {},
+			loadingBundles = {};
+
+		var sandbox = {
+				id: sandboxIdentifier
+			};
+
+		/*DEBUG*/ function logDebug() {
+		/*DEBUG*/ 	if (sandboxOptions.debug !== true) return;
+		/*DEBUG*/ 	// NOTRE: This does not work in google chrome.
+		/*DEBUG*/ 	//console.log.apply(null, arguments);
+		/*DEBUG*/ 	if (arguments.length === 1) {
+		/*DEBUG*/ 		console.log(arguments[0]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 2) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 3) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 4) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
+		/*DEBUG*/ 	}
+		/*DEBUG*/ }
+
+		function rebaseUri (uri) {
+			if (!sandboxOptions.baseUrl) {
+				return uri;
+			}
+			return sandboxOptions.baseUrl + "/" + uri;
+		}
+
+		function load (bundleIdentifier, packageIdentifier, bundleSubPath, loadedCallback) {
+
+			var loadSandboxIdentifier = sandboxIdentifier;
+			var finalBundleIdentifier = null;
+			var moduleIdentifierPrefix = "";
+			var finalPackageIdentifier = "";
+
+			try {
+				if (packageIdentifier !== "") {
+
+					if (/^@bundle:/.test(packageIdentifier)) {
+						var absPackageIdentifier = packageIdentifier;
+						if (/^@bundle:\./.test(absPackageIdentifier)) {
+							absPackageIdentifier = absPackageIdentifier.replace(/^(@bundle:)\./, "$1" + sandboxIdentifier + "/.");
+						}
+						moduleIdentifierPrefix = packageIdentifier;
+						finalPackageIdentifier = packageIdentifier;
+						bundleIdentifier = absPackageIdentifier.replace(/^@bundle:/, "") + ".js";
+						loadSandboxIdentifier = "";
+						finalBundleIdentifier = "@bundle:" + packageIdentifier.replace(/^@bundle:/, "") + ".js";
+					} else {
+						bundleIdentifier = ("/" + packageIdentifier + "/" + bundleIdentifier).replace(/\/+/g, "/");
+					}
+				}
+
+				if (initializedModules[bundleIdentifier]) {
+					// Module is already loaded and initialized.
+					loadedCallback(null, sandbox);
+				} else {
+					// Module is not initialized.
+					if (loadingBundles[bundleIdentifier]) {
+						// Module is already loading.
+						loadingBundles[bundleIdentifier].push(loadedCallback);
+					} else {
+						// Module is not already loading.
+						loadingBundles[bundleIdentifier] = [];
+
+						bundleIdentifier = (loadSandboxIdentifier + bundleSubPath + bundleIdentifier).replace(/\/$/, ".js");
+
+						bundleIdentifier = bundleIdentifier.replace(/\.php\.js$/, ".php");
+
+						if (!finalBundleIdentifier) {
+							finalBundleIdentifier = bundleIdentifier;
+						}
+
+						// Default to our script-injection browser loader.
+						(sandboxOptions.rootBundleLoader || sandboxOptions.load || loadInBrowser)(
+							rebaseUri(bundleIdentifier),
+							function (err, cleanupCallback) {
+								if (err) return loadedCallback(err);
+								// The rootBundleLoader is only applicable for the first load.
+								delete sandboxOptions.rootBundleLoader;
+								finalizeLoad(moduleIdentifierPrefix, finalBundleIdentifier, finalPackageIdentifier, function () {
+									loadedCallback(null, sandbox);
+									if (cleanupCallback) {
+										cleanupCallback();
+									}
+								});
+							},
+							sandboxOptions
+						);
+					}
+				}
+			} catch(err) {
+				loadedCallback(err);
+			}
+		}
+
+		// Called after a bundle has been loaded. Takes the top bundle off the *loading* stack
+		// and makes the new modules available to the sandbox.
+		function finalizeLoad (moduleIdentifierPrefix, bundleIdentifier, packageIdentifier, loadFinalized) {
+			var pending = 0;
+			function finalize () {
+				if (pending !== 0) {
+					return;
+				}
+				if (loadFinalized) loadFinalized();
+			}
+
+			pending += 1;
+
+			// Assume a consistent statically linked set of modules has been memoized.
+			/*DEBUG*/ if (!loadedBundles[0]) {
+			/*DEBUG*/     throw new Error("No bundle memoized for '" + bundleIdentifier + "'! Check the file to ensure it contains JavaScript and that a bundle is memoized against the correct loader instance.");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[bundleIdentifier] = loadedBundles[0][0];
+			var loadedModuleInitializers = loadedBundles[0][1]({
+				id: sandboxIdentifier
+			});
+			var key;
+			for (key in loadedModuleInitializers) {
+
+				var memoizeKey = moduleIdentifierPrefix + key;
+
+				// If we have a package descriptor add it or merge it on top.
+				if (/^[^\/]*\/package.json$/.test(key)) {
+
+					if (sandboxOptions.rewritePackageDescriptor) {
+						loadedModuleInitializers[key][0] = sandboxOptions.rewritePackageDescriptor(loadedModuleInitializers[key][0], memoizeKey);
+					}
+
+					// Load all dependent resources
+					if (loadedModuleInitializers[key][0].mappings) {
+						for (var alias in loadedModuleInitializers[key][0].mappings) {
+							if (/^@script:\/\//.test(loadedModuleInitializers[key][0].mappings[alias])) {
+								pending += 1;
+								loadInBrowser(
+									rebaseUri(loadedModuleInitializers[key][0].mappings[alias].replace(/^@script:/, "")),
+									function () {
+										pending -= 1;
+										finalize();
+									},
+									sandboxOptions
+								);
+							}
+						}
+					}
+
+					// NOTE: Not quite sure if we should allow agumenting package descriptors.
+					//       When doing nested requires using same package we can either add all
+					//		 mappings (included mappings not needed until further down the tree) to
+					//       the first encounter of the package descriptor or add more mappings as
+					//       needed down the road. We currently support both.
+
+					if (moduleInitializers[memoizeKey]) {
+						// TODO: Keep array of bundle identifiers instead of overwriting existing one?
+						//		 Overwriting may change subsequent bundeling behaviour?
+						moduleInitializers[memoizeKey][0] = bundleIdentifier;
+						// Only augment (instead of replace existing values).
+						if (typeof moduleInitializers[memoizeKey][1].main === "undefined") {
+							moduleInitializers[memoizeKey][1].main = loadedModuleInitializers[key][0].main;
+						}
+						if (loadedModuleInitializers[key][0].mappings) {
+							if (!moduleInitializers[memoizeKey][1].mappings) {
+								moduleInitializers[memoizeKey][1].mappings = {};
+							}
+							for (var alias in loadedModuleInitializers[key][0].mappings) {
+								if (typeof moduleInitializers[memoizeKey][1].mappings[alias] === "undefined") {
+									moduleInitializers[memoizeKey][1].mappings[alias] = loadedModuleInitializers[key][0].mappings[alias];
+								}
+							}
+						}
+					} else {
+						moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+					}
+					// Now that we have a [updated] package descriptor, re-initialize it if we have it already in cache.
+					var packageIdentifier = packageIdentifier || key.split("/").shift();
+					if (packages[packageIdentifier]) {
+						packages[packageIdentifier].init();
+					}
+				}
+				// Only add modules that don't already exist!
+				// TODO: Log warning in debug mode if module already exists.
+				if (typeof moduleInitializers[memoizeKey] === "undefined") {
+					moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+				}				
+			}
+			loadedBundles.shift();
+
+			pending -= 1;
+			finalize();
+
+			return;
+		}
+
+		var Package = function (packageIdentifier) {
+			if (packages[packageIdentifier]) {
+				return packages[packageIdentifier];
+			}
+
+			var pkg = {
+				id: packageIdentifier,
+				descriptor: {},
+				main: "/main.js",
+				mappings: {},
+				directories: {},
+				libPath: ""
+			};
+
+			var parentModule = lastModule;
+
+			pkg.init = function () {
+				var descriptor = (moduleInitializers[packageIdentifier + "/package.json"] && moduleInitializers[packageIdentifier + "/package.json"][1]) || {};
+				if (descriptor) {
+					pkg.descriptor = descriptor;
+					if (typeof descriptor.main === "string") {
+						pkg.main = descriptor.main;
+					}
+					pkg.mappings = descriptor.mappings || pkg.mappings;
+					pkg.directories = descriptor.directories || pkg.directories;
+					// NOTE: We need `lib` directory support so that the source directory structure can be mapped
+					//       into the bundle structure without modification. If this is not done, a module doing a relative _require_
+					//       for a resource outside of the lib directory will not find the file.
+					pkg.libPath = (typeof pkg.directories.lib !== "undefined" && pkg.directories.lib != "") ? pkg.directories.lib + "/" : pkg.libPath;
+				}
+			}
+			pkg.init();
+
+			function normalizeIdentifier (identifier) {
+				// If we have a period (".") in the basename we want an absolute path from
+				// the root of the package. Otherwise a relative path to the "lib" directory.
+				if (identifier.split("/").pop().indexOf(".") === -1) {
+					// We have a module relative to the "lib" directory of the package.
+					identifier = identifier + ".js";
+				} else
+				if (!/^\//.test(identifier)) {
+					// We want an absolute path for the module from the root of the package.
+					identifier = "/" + identifier;
+				}
+				return identifier;
+			}
+
+			var Module = function (moduleIdentifier, parentModule) {
+
+				var moduleIdentifierSegment = null;
+				if (/^@bundle:/.test(moduleIdentifier)) {
+					moduleIdentifierSegment = moduleIdentifier.replace(packageIdentifier, "").replace(/\/[^\/]*$/, "").split("/");
+				} else {
+					moduleIdentifierSegment = moduleIdentifier.replace(/\/[^\/]*$/, "").split("/");
+				}
+
+				var module = {
+						id: moduleIdentifier,
+						exports: {},
+						parentModule: parentModule,
+						bundle: null,
+						pkg: packageIdentifier
+					};
+
+				function resolveIdentifier (identifier) {
+					if (/\/$/.test(identifier)) {
+						identifier += "index";
+					}
+					lastModule = module;
+					// Check for plugin prefix.
+					var plugin = null;
+					if (/^[^!]*!/.test(identifier)) {
+						var m = identifier.match(/^([^!]*)!(.+)$/);
+						identifier = m[2];
+						plugin = m[1];
+					}
+					function pluginify (id) {
+						if (!plugin) return id;
+						id = new String(id);
+						id.plugin = plugin;
+						return id;
+					}
+					// Check for relative module path to module within same package.
+					if (/^\./.test(identifier)) {
+						var segments = identifier.replace(/^\.\//, "").split("../");
+						identifier = "/" + moduleIdentifierSegment.slice(1, moduleIdentifierSegment.length-segments.length+1).concat(segments[segments.length-1]).join("/");
+						if (identifier === "/.") {
+							return [pkg, pluginify("")];
+						}
+						return [pkg, pluginify(normalizeIdentifier(identifier.replace(/\/\.$/, "/")))];
+					}
+					var splitIdentifier = identifier.split("/");
+					// Check for mapped module path to module within mapped package.
+					if (typeof pkg.mappings[splitIdentifier[0]] !== "undefined") {
+						return [Package(pkg.mappings[splitIdentifier[0]]), pluginify((splitIdentifier.length > 1)?normalizeIdentifier(splitIdentifier.slice(1).join("/")):"")];
+					}
+					/*DEBUG*/ if (!moduleInitializers["/" + normalizeIdentifier(identifier)]) {
+					/*DEBUG*/     throw new Error("Descriptor for package '" + pkg.id + "' in sandbox '" + sandbox.id + "' does not declare 'mappings[\"" + splitIdentifier[0] + "\"]' property nor does sandbox have module memoized at '" + "/" + normalizeIdentifier(identifier) + "' needed to satisfy module path '" + identifier + "' in module '" + moduleIdentifier + "'!");
+					/*DEBUG*/ }
+					return [Package(""), pluginify("/" + normalizeIdentifier(identifier))];
+				}
+
+				// Statically link a module and its dependencies
+				module.require = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require(identifier[1]).exports;
+				};
+
+				module.require.supports = [
+					"ucjs-pinf-0"
+				];
+
+				module.require.id = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require.id(identifier[1]);
+				};
+
+				module.require.async = function (identifier, loadedCallback, errorCallback) {
+					identifier = resolveIdentifier(identifier);					
+					var mi = moduleIdentifier;
+					if (/^\//.test(identifier[0].id)) {
+						mi = "/main.js";
+					}
+					identifier[0].load(identifier[1], module.bundle, function (err, moduleAPI) {
+						if (err) {
+							if (errorCallback) return errorCallback(err);
+							throw err;
+						}
+						loadedCallback(moduleAPI);
+					});
+				};
+
+				module.require.sandbox = normalizeSandboxArguments (function (programIdentifier, options, loadedCallback, errorCallback) {
+					options.load = options.load || sandboxOptions.load;
+					// If the `programIdentifier` is relative it is resolved against the URI of the owning sandbox (not the owning page).
+					if (/^\./.test(programIdentifier))
+					{
+						programIdentifier = sandboxIdentifier + "/" + programIdentifier;
+						// HACK: Temporary hack as zombie (https://github.com/assaf/zombie) does not normalize path before sending to server.
+						programIdentifier = programIdentifier.replace(/\/\.\//g, "/");
+					}
+					return PINF.sandbox(programIdentifier, options, loadedCallback, errorCallback);
+				});
+				module.require.sandbox.id = sandboxIdentifier;
+
+				module.load = function () {
+					module.bundle = moduleInitializers[moduleIdentifier][0];
+					if (typeof moduleInitializers[moduleIdentifier][1] === "function") {
+
+						var moduleInterface = {
+							id: module.id,
+							filename:
+								// The `filename` from the meta info attached to the module.
+								// This is typically where the module was originally found on the filesystem.
+								moduleInitializers[moduleIdentifier][2].filename ||
+								// Fall back to the virtual path of the module in the bundle.
+								// TODO: Insert a delimiter between bundle and module id.
+								(module.bundle.replace(/\.js$/, "") + "/" + module.id).replace(/\/+/g, "/"),
+							exports: {}
+						}
+
+						if (packageIdentifier === "" && pkg.main === moduleIdentifier) {
+							module.require.main = moduleInterface;
+						}
+
+						if (sandboxOptions.onInitModule) {
+							sandboxOptions.onInitModule(moduleInterface, module, pkg, sandbox, {
+								normalizeIdentifier: normalizeIdentifier,
+								resolveIdentifier: resolveIdentifier,
+								finalizeLoad: finalizeLoad,
+								moduleInitializers: moduleInitializers,
+								initializedModules: initializedModules
+							});
+						}
+
+						var exports = moduleInitializers[moduleIdentifier][1].call(exports, module.require, module.exports, moduleInterface);
+						if (
+							typeof moduleInterface.exports !== "undefined" &&
+							(
+								typeof moduleInterface.exports !== "object" ||
+								keys(moduleInterface.exports).length !== 0
+							)
+						) {
+							module.exports = moduleInterface.exports;
+						} else
+						if (typeof exports !== "undefined") {
+							module.exports = exports;
+						}
+					} else
+					if (typeof moduleInitializers[moduleIdentifier][1] === "string") {
+						// TODO: Use more optimal string encoding algorythm to reduce payload size?
+						module.exports = decodeURIComponent(moduleInitializers[moduleIdentifier][1]);
+					} else {
+						module.exports = moduleInitializers[moduleIdentifier][1];
+					}
+				};
+
+				/*DEBUG*/ module.getReport = function () {
+				/*DEBUG*/ 	var exportsCount = 0,
+				/*DEBUG*/ 		key;
+				/*DEBUG*/ 	for (key in module.exports) {
+				/*DEBUG*/ 		exportsCount++;
+				/*DEBUG*/ 	}
+				/*DEBUG*/ 	return {
+				/*DEBUG*/ 		exports: exportsCount
+				/*DEBUG*/ 	};
+				/*DEBUG*/ };
+
+				return module;
+			};
+
+			pkg.load = function (moduleIdentifier, bundleIdentifier, loadedCallback) {
+
+				// If module/bundle to be loaded asynchronously is already memoized we skip the load.
+				if (moduleInitializers[packageIdentifier + (moduleIdentifier || pkg.main)]) {
+					return loadedCallback(null, pkg.require(moduleIdentifier).exports);
+				}
+				var bundleSubPath = bundleIdentifier.substring(sandboxIdentifier.length);
+				load(
+					((!/^\//.test(moduleIdentifier))?"/"+pkg.libPath:"") + moduleIdentifier,
+					packageIdentifier,
+					bundleSubPath.replace(/\.js$/g, ""),
+					function (err) {
+						if (err) return loadedCallback(err);
+						loadedCallback(null, pkg.require(moduleIdentifier).exports);
+					}
+				);
+			}
+
+			pkg.require = function (moduleIdentifier) {
+
+				var plugin = moduleIdentifier.plugin;
+
+				if (moduleIdentifier) {
+					if (!/^\//.test(moduleIdentifier)) {
+						moduleIdentifier = ("/" + ((moduleIdentifier.substring(0, pkg.libPath.length)===pkg.libPath)?"":pkg.libPath)).replace(/\/\.\//, "/") + moduleIdentifier;
+					}
+					moduleIdentifier = packageIdentifier + moduleIdentifier;
+				} else {
+					moduleIdentifier = packageIdentifier + pkg.main;
+				}
+
+				if (
+					!moduleInitializers[moduleIdentifier] &&
+					moduleInitializers[moduleIdentifier.replace(/\.js$/, "/index.js")]
+				) {
+					moduleIdentifier = moduleIdentifier.replace(/\.js$/, "/index.js");
+				}
+
+				// Use a specifically formatted module for requested plugin if available
+				if (
+					plugin &&
+					moduleInitializers[moduleIdentifier + ":" + plugin]
+				) {
+					moduleIdentifier += ":" + plugin;
+				}
+
+				if (!initializedModules[moduleIdentifier]) {
+					/*DEBUG*/ if (!moduleInitializers[moduleIdentifier]) {
+					/*DEBUG*/ 	console.error("[pinf-loader-js]", "moduleInitializers", moduleInitializers);
+					/*DEBUG*/ 	throw new Error("Module '" + moduleIdentifier + "' " + (plugin?"for format '" + plugin + "' ":"") + "not found in sandbox '" + sandbox.id + "'!");
+					/*DEBUG*/ }
+					(initializedModules[moduleIdentifier] = Module(moduleIdentifier, lastModule)).load();
+				}
+
+				var loadingBundlesCallbacks;
+				if (loadingBundles[moduleIdentifier]) {
+					loadingBundlesCallbacks = loadingBundles[moduleIdentifier];
+					delete loadingBundles[moduleIdentifier];
+					for (var i=0 ; i<loadingBundlesCallbacks.length ; i++) {
+						loadingBundlesCallbacks[i](null, sandbox);
+					}
+				}
+
+				// TODO: Do this via plugins registered using sandbox options.
+				// TODO: Cache response so we only process files once.
+
+				var moduleInfo = create(initializedModules[moduleIdentifier]);
+				// RequireJS/AMD international strings plugin using root by default.
+				if (plugin === "i18n") {
+					moduleInfo.exports = moduleInfo.exports.root;
+				}
+
+				return moduleInfo;
+			}
+
+			pkg.require.id = function (moduleIdentifier) {
+				if (!/^\//.test(moduleIdentifier)) {
+					moduleIdentifier = "/" + pkg.libPath + moduleIdentifier;
+				}
+				return (((packageIdentifier !== "")?"/"+packageIdentifier+"/":"") + moduleIdentifier).replace(/\/+/g, "/");
+			}
+
+			/*DEBUG*/ pkg.getReport = function () {
+			/*DEBUG*/ 	return {
+			/*DEBUG*/ 		main: pkg.main,
+			/*DEBUG*/ 		mappings: pkg.mappings,
+			/*DEBUG*/ 		directories: pkg.directories,
+			/*DEBUG*/ 		libPath: pkg.libPath
+			/*DEBUG*/ 	};
+			/*DEBUG*/ }
+
+			if (sandboxOptions.onInitPackage) {
+				sandboxOptions.onInitPackage(pkg, sandbox, {
+					normalizeIdentifier: normalizeIdentifier,
+					finalizeLoad: finalizeLoad,
+					moduleInitializers: moduleInitializers,
+					initializedModules: initializedModules
+				});
+			}
+
+			packages[packageIdentifier] = pkg;
+
+			return pkg;
+		}
+
+		// Get a module and initialize it (statically link its dependencies) if it is not already so
+		sandbox.require = function (moduleIdentifier) {
+			return Package("").require(moduleIdentifier).exports;
+		}
+
+		// Call the 'main' module of the program
+		sandbox.boot = function () {
+			/*DEBUG*/ if (typeof Package("").main !== "string") {
+			/*DEBUG*/ 	throw new Error("No 'main' property declared in '/package.json' in sandbox '" + sandbox.id + "'!");
+			/*DEBUG*/ }
+			return sandbox.require(Package("").main);
+		};
+
+		// Call the 'main' exported function of the main' module of the program
+		sandbox.main = function () {
+			var exports = sandbox.boot();
+			return ((exports.main)?exports.main.apply(null, arguments):exports);
+		};
+
+		/*DEBUG*/ sandbox.getReport = function () {
+		/*DEBUG*/ 	var report = {
+		/*DEBUG*/ 			bundles: {},
+		/*DEBUG*/ 			packages: {},
+		/*DEBUG*/ 			modules: {}
+		/*DEBUG*/ 		},
+		/*DEBUG*/ 		key;
+		/*DEBUG*/ 	for (key in bundleIdentifiers) {
+		/*DEBUG*/ 		report.bundles[key] = bundleIdentifiers[key];
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in packages) {
+		/*DEBUG*/ 		report.packages[key] = packages[key].getReport();
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in moduleInitializers) {
+		/*DEBUG*/ 		if (initializedModules[key]) {
+		/*DEBUG*/ 			report.modules[key] = initializedModules[key].getReport();
+		/*DEBUG*/ 		} else {
+		/*DEBUG*/ 			report.modules[key] = {};
+		/*DEBUG*/ 		}
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	return report;
+		/*DEBUG*/ }
+		/*DEBUG*/ sandbox.reset = function () {
+		/*DEBUG*/   moduleInitializers = {};
+		/*DEBUG*/   initializedModules = {};
+		/*DEBUG*/   bundleIdentifiers = {};
+		/*DEBUG*/   packages = {};
+		/*DEBUG*/   loadingBundles = {};
+		/*DEBUG*/ }
+
+		load((sandboxIdentifier.indexOf("?") === -1) ? ".js" : "", "", "", loadedCallback);
+
+		return sandbox;
+	};
+
+	var
+		/*DEBUG*/ bundleIdentifiers = {},
+		sandboxes = {};
+
+	var Require = function (bundle) {
+		var self = this;
+
+		// Address a specific sandbox or currently loading sandbox if initial load.
+		var bundleHandler = function (uid, callback) {
+			/*DEBUG*/ if (uid && bundleIdentifiers[uid]) {
+			/*DEBUG*/ 	throw new Error("You cannot split require.bundle(UID) calls where UID is constant!");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[uid] = true;
+			loadedBundles.push([uid, function (sandbox) {
+				var moduleInitializers = {},
+					req = new Require(uid);
+				delete req.bundle;
+				req.sandbox = sandbox;
+				// Store raw module in loading bundle
+				req.memoize = function (moduleIdentifier, moduleInitializer, moduleMeta) {
+					moduleInitializers[
+						moduleIdentifier +
+						// NOTE: This feature may be elevated to a new function argument to 'memoize' if it proves to be prevalent.
+						(
+							(
+								moduleMeta &&
+								moduleMeta.variation
+							) ? ":" + moduleMeta.variation : ""
+						)
+					] = [moduleInitializer, moduleMeta || {}];
+				}
+				callback(req, global || null);
+				return moduleInitializers;
+			}]);
+		}
+		var activeBundleHandler = bundleHandler;
+		this.bundle = function () {
+			return activeBundleHandler.apply(null, arguments);
+		}
+		this.setActiveBundleHandler = function (handler) {
+			var oldHandler = activeBundleHandler;
+			activeBundleHandler = handler;
+			return oldHandler;
+		}
+	}
+
+	var PINF = new Require();
+
+	// TODO: @see URL_TO_SPEC
+	PINF.supports = [
+		"ucjs-pinf-0"
+	];
+
+	// Create a new environment to memoize modules to.
+	// If relative, the `programIdentifier` is resolved against the URI of the owning page (this is only for the global _require_).
+	PINF.sandbox = normalizeSandboxArguments(function (programIdentifier, options, loadedCallback, errorCallback) {
+		if (typeof programIdentifier === "function") {
+			options = options || {};
+			var bundle = programIdentifier;
+			var fallbackLoad = options.load || loadInBrowser;
+			options.load = function (uri, loadedCallback) {
+				if (uri === (programIdentifier + ".js")) {
+					PINF.bundle("", bundle);
+					loadedCallback(null);
+					return;
+				}
+				return fallbackLoad(uri, loadedCallback, options);
+			}
+			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
+		}
+		var sandboxIdentifier = programIdentifier.replace(/\.js$/, "");
+		return sandboxes[sandboxIdentifier] = Sandbox(sandboxIdentifier, options, function (err, sandbox) {
+			if (err) {
+				if (errorCallback) return errorCallback(err);
+				throw err;
+			}
+			loadedCallback(sandbox);
+		});
+	});
+
+	PINF.Loader = Loader;
+
+	/*DEBUG*/ PINF.getReport = function () {
+	/*DEBUG*/ 	var report = {
+	/*DEBUG*/ 			sandboxes: {}
+	/*DEBUG*/ 		};
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		report.sandboxes[key] = sandboxes[key].getReport();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	return report;
+	/*DEBUG*/ }
+	/*DEBUG*/ PINF.reset = function () {
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		sandboxes[key].reset();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	sandboxes = {};
+	/*DEBUG*/ 	bundleIdentifiers = {};
+	/*DEBUG*/ 	loadedBundles = [];
+	/*DEBUG*/ }
+
+	return PINF;
+}
+
+if (exports) exports.Loader = Loader;
+
+})(typeof exports !== "undefined" ? exports : null);
+
+},{}]},{},[1])(1)
+});
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],2:[function(require,module,exports){
+"use strict";
+
+var WINDOW = window;
+
+var DOMPLATE = require("domplate/dist/domplate.js").domplate;
+
+function Renderer(options) {
+  var self = this;
+  var loader = options.loader || new exports.Loader(options);
+  self.domplate = DOMPLATE;
+
+  function InsightDomplateContext() {
+    var self = this;
+    self.repForNode = loader.repForNode.bind(loader);
+    self.wrapperRepForNode = loader.wrapperRepForNode.bind(loader);
+
+    self.dispatchEvent = function (name, args) {
+      if (options.onEvent) {
+        try {
+          options.onEvent(name, args);
+        } catch (err) {
+          err.message += "(while dispatching event with name '" + name + "')";
+          err.stack[0] += "(while dispatching event with name '" + name + "')";
+          throw err;
+        }
+      }
+    };
+
+    self.forNode = function (rootNode) {
+      var context = Object.create(self);
+
+      context.getInstanceNode = function (node) {
+        if (!rootNode.instances || !rootNode.instances[node.value]) {
+          console.error("node", node);
+          throw new Error("Object instance for reference '" + node.value + "' not found in 'instances'!");
+        }
+
+        return rootNode.instances[node.value];
+      };
+
+      return context;
+    };
+  }
+
+  var context = new InsightDomplateContext();
+
+  function ensureRepsForNodeLoaded(node) {
+    try {
+      var traverse = function traverse(node) {
+        if (node.type) {
+          loadTypes["default/" + node.type] = true;
+        }
+
+        if (node.meta) {
+          if (node.meta["encoder.trimmed"]) {
+            loadTypes["default/trimmed"] = true;
+          } else if (node.meta.renderer === "structures/table") {
+              loadTypes["default/table"] = true;
+              loadTypes["default/string"] = true;
+              node.type = "table";
+            } else if (node.meta.renderer === "structures/trace") {
+                loadTypes["default/trace"] = true;
+                loadTypes["default/string"] = true;
+                node.type = "trace";
+              } else if (node.meta["lang"] && node.meta["lang.type"]) {
+                if (node.meta["lang"] === "php") {
+                  if (node.meta["lang.type"] === "array") {
+                    if (node.value[0] && Array.isArray(node.value[0])) {
+                      loadTypes["php/array-associative"] = true;
+                      node.value.forEach(function (pair) {
+                        traverse(pair[0]);
+                        traverse(pair[1]);
+                      });
+                    } else {
+                      loadTypes["php/array-indexed"] = true;
+                      node.value.forEach(function (node) {
+                        traverse(node);
+                      });
+                    }
+                  } else if (node.meta["lang.type"] === "map") {
+                    loadTypes["php/array-associative"] = true;
+                    node.value.forEach(function (pair) {
+                      traverse(pair[0]);
+                      traverse(pair[1]);
+                    });
+                  } else if (node.meta["lang.type"] === "exception") {
+                    loadTypes["php/exception"] = true;
+                    loadTypes["default/string"] = true;
+
+                    if (node.value.title) {
+                      traverse(node.value.title);
+                    }
+
+                    if (node.value.stack) {
+                      node.value.stack.forEach(function (frame) {
+                        frame.args.forEach(function (arg) {
+                          traverse(arg);
+                        });
+                      });
+                    }
+                  } else {
+                    loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
+
+                    if (node.meta["lang.type"] === "table") {
+                      loadTypes["default/string"] = true;
+                    } else if (node.meta["lang.type"] === "trace") {
+                      loadTypes["default/string"] = true;
+                    }
+                  }
+                } else {
+                  loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
+
+                  if (node.meta["lang.type"] === "table") {
+                    loadTypes["default/string"] = true;
+                  } else if (node.meta["lang.type"] === "trace") {
+                    loadTypes["default/string"] = true;
+                  } else if (node.meta["lang.type"] === "pathtree") {
+                    loadTypes["default/string"] = true;
+                  } else if (node.meta["lang.type"] === "optiontree") {
+                    loadTypes["default/string"] = true;
+                  }
+                }
+              }
+
+          if (node.meta.wrapper) {
+            loadTypes[node.meta.wrapper] = true;
+
+            if (node.meta.wrapper === "wrappers/request") {
+              if (node.value.title) {
+                traverse(node.value.title);
+              }
+            }
+          }
+        }
+
+        if (node.value !== null && typeof node.value !== 'undefined') {
+          var type = node.type || node.meta["lang.type"];
+
+          if (type === "array") {
+            node.value.forEach(function (node) {
+              traverse(node);
+            });
+          } else if (type === "dictionary") {
+            Object.keys(node.value).forEach(function (key) {
+              traverse(node.value[key]);
+            });
+          } else if (type === "map") {
+            node.value.forEach(function (pair) {
+              traverse(pair[0]);
+              traverse(pair[1]);
+            });
+          } else if (type === "reference") {
+            if (node.value.instance) {
+              traverse(node.value.instance);
+            } else if (node.instances && typeof node.value === "number") {
+              traverse(node.instances[node.value]);
+            } else if (typeof node.getInstance === 'function') {
+              traverse(node.getInstance());
+            }
+          } else if (type === "table") {
+            if (node.value.title) {
+              traverse(node.value.title);
+            }
+
+            if (node.value.header) {
+              node.value.header.forEach(function (node) {
+                traverse(node);
+              });
+            }
+
+            if (node.value.body) {
+              node.value.body.forEach(function (row) {
+                row.forEach(function (cell) {
+                  traverse(cell);
+                });
+              });
+            }
+          } else if (type === "trace") {
+            if (node.value.title) {
+              traverse(node.value.title);
+            }
+
+            if (node.value.stack) {
+              node.value.stack.forEach(function (frame) {
+                frame.args.forEach(function (arg) {
+                  traverse(arg);
+                });
+              });
+            }
+          }
+        }
+      };
+
+      var loadTypes = {};
+      traverse(node);
+      return Promise.all(Object.keys(loadTypes).map(function (type) {
+        type = type.split("/");
+        var repUri = loader.repUriForType(type[0], type[1]);
+        return loader.ensureRepForUri(repUri).then(function () {
+          return null;
+        });
+      }));
+    } catch (err) {
+      console.error('Error checking node:', node);
+      throw err;
+    }
+  }
+
+  self.renderNodeInto = function (node, selectorOrElement, options) {
+    options = options || {};
+    var el = typeof selectorOrElement === 'string' && document.querySelector(selectorOrElement) || selectorOrElement;
+
+    if (!el) {
+      throw new Error("Could not find element for selector '" + selectorOrElement + "'!");
+    }
+
+    return ensureRepsForNodeLoaded(node).then(function () {
+      var wrapperRep = context.wrapperRepForNode(node);
+
+      if (wrapperRep) {
+        if (!wrapperRep[options.tagName || 'tag']) {
+          console.error("node", node);
+          console.error("wrapperRep", wrapperRep);
+          throw new Error("Could not get tag '".concat(options.tagName || 'tag', "' from wrapper!"));
+        }
+
+        wrapperRep[options.tagName || 'tag'].replace({
+          context: context.forNode(node),
+          node: node
+        }, el);
+        return;
+      }
+
+      var rep = context.repForNode(node);
+      rep[options.tagName || 'tag'].replace({
+        context: context.forNode(node),
+        node: node
+      }, el);
+    });
+  };
+}
+
+exports.Renderer = Renderer;
+
+function Loader(options) {
+  var self = this;
+
+  if (!options.repsBaseUrl) {
+    throw new Error("'options.repsBaseUrl' not set!");
+  }
+
+  var loadingReps = {};
+  var loadedReps = {};
+  self.domplate = DOMPLATE;
+
+  self.ensureRepForUri = function (repUri) {
+    if (!loadingReps[repUri]) {
+      loadingReps[repUri] = new WINDOW.Promise(function (resolve, reject) {
+        var url = options.repsBaseUrl + "/" + repUri;
+        DOMPLATE.loadRep(url, {
+          cssBaseUrl: options.repsBaseUrl.replace(/\/?$/, "/")
+        }, function (rep) {
+          setTimeout(function () {
+            rep.__ensureCssInjected();
+          }, 0);
+          loadedReps[repUri] = rep;
+          resolve(rep);
+        }, function (err) {
+          var error = new Error("Error loading rep for uri '" + repUri + "' from '" + url + "'!");
+          error.previous = err;
+          reject(error);
+        });
+      });
+    }
+
+    return loadingReps[repUri];
+  };
+
+  self.repUriForType = function (lang, type) {
+    type = type || "unknown";
+    return lang + "/" + type;
+  };
+
+  function repUriForNode(node) {
+    var lang = "default";
+    var type = node.type;
+
+    if (node.meta) {
+      if (node.meta["encoder.trimmed"]) {
+        type = "trimmed";
+      } else if (node.meta.renderer === "structures/table") {
+          type = "table";
+        } else if (node.meta.renderer === "structures/trace") {
+            type = "trace";
+          } else if (node.meta["lang"] && node.meta["lang.type"]) {
+            lang = node.meta["lang"];
+            type = node.meta["lang.type"];
+
+            if (lang === "php") {
+              if (type === "array") {
+                if (node.value[0] && Array.isArray(node.value[0])) {
+                  type = "array-associative";
+                } else {
+                  type = "array-indexed";
+                }
+              } else if (type === "map") {
+                type = "array-associative";
+              }
+            }
+          } else if (node.meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers/packages/php/master") {
+            lang = "php";
+            type = node.meta["lang.type"];
+
+            if (node.meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/table") {
+              lang = "default";
+              type = "table";
+            }
+          }
+    }
+
+    if (!type) {
+      console.error("node", node);
+      console.error("lang", lang);
+      throw new Error('Could not determine type for node!');
+    }
+
+    return self.repUriForType(lang, type);
+  }
+
+  self.repForNode = function (node) {
+    var repUri = repUriForNode(node);
+
+    if (!loadedReps[repUri]) {
+      throw new Error("Rep for uri '" + repUri + "' not loaded!");
+    }
+
+    return loadedReps[repUri];
+  };
+
+  self.wrapperRepForNode = function (node) {
+    if (node.meta && node.meta.wrapper) {
+      if (!loadedReps[node.meta.wrapper]) {
+        throw new Error("Wrapper Rep for uri '" + node.meta.wrapper + "' not loaded!");
+      }
+
+      return loadedReps[node.meta.wrapper];
+    }
+
+    return null;
+  };
+}
+
+exports.Loader = Loader;
+},{"domplate/dist/domplate.js":1}]},{},[2])(2)
+});
+
+})((typeof require !== "undefined" && require) || undefined, (typeof exports !== "undefined" && exports) || undefined, (typeof module !== "undefined" && module) || undefined))
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.preview.htm
new file mode 100644
index 0000000..ac8ff30
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.preview.htm
@@ -0,0 +1,40 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="db686dc5e4aff22e09d387a8de9141e496d71de8" __dtid="default/array" class="array "><span>array(</span><div class="element "><span class="value "></span><span class="separator ">,</span></div><div class="element "><span class="value "></span></div><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="db686dc5e4aff22e09d387a8de9141e496d71de8" __dtid="default/array" class="array "><span>array(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="db686dc5e4aff22e09d387a8de9141e496d71de8" __dtid="default/array" class="array "><span>array(</span><span class="element "><span class="value "></span><span class="separator ">,</span></span><span class="element "><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>expandableStub</h2>
+
+
+
+<br/>
+
+<h2>expandedStub</h2>
+
+
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="db686dc5e4aff22e09d387a8de9141e496d71de8" __dtid="default/array"> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.rep.css
similarity index 55%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.rep.css
index 38fffc9..71f3cfc 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.rep.css
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.rep.css
@@ -1,45 +1,45 @@
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > SPAN {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > SPAN {
     color: #9C9C9C;
     font-weight: bold;
 }
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > SPAN.collapsed {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > SPAN.collapsed {
     color: #0000FF;
     font-weight: normal;
     padding-left: 5px;
     padding-right: 5px;
 }
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > SPAN.summary {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > SPAN.summary {
     color: #0000FF;
     font-weight: normal;
     padding-left: 5px;
     padding-right: 5px;
 }
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > DIV.element {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > DIV.element {
     display: block;
     padding-left: 20px;
 }
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > SPAN.element {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > SPAN.element {
     padding-left: 2px;
 }
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > DIV.element.expandable {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > DIV.element.expandable {
     background-image: url(images/twisty-closed.png);
     background-repeat: no-repeat;
     background-position: 6px 2px;
     cursor: pointer;
 }
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > DIV.element.expandable.expanded {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > DIV.element.expandable.expanded {
     background-image: url(images/twisty-open.png);
 }
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > .element > SPAN.value {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > .element > SPAN.value {
 }
 
-SPAN.array[__dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48"] > .element > SPAN.separator {
+SPAN.array[__dbid="db686dc5e4aff22e09d387a8de9141e496d71de8"] > .element > SPAN.separator {
     color: #9C9C9C;
 }
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.rep.js
similarity index 85%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.rep.js
index b971360..917da99 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/array.rep.js
@@ -227,7 +227,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","c8e051a0d8ba6235ed889e184aae731c7e813f48", "\"", " __dtid=\"","insight.domplate.reps/default/array", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Normal), __out__, function(element, __out__) {    __code__.push("","<div", " class=\"","element", " ", (element.expandable ? "expandable" + " " : ""), "\"",">","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,context,element);__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","db686dc5e4aff22e09d387a8de9141e496d71de8", "\"", " __dtid=\"","default/array", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Normal), __out__, function(element, __out__) {    __code__.push("","<div", " class=\"","element", " ", (element.expandable ? "expandable" + " " : ""), "\"",">","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,context,element);__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -236,7 +236,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","c8e051a0d8ba6235ed889e184aae731c7e813f48", "\"", " __dtid=\"","insight.domplate.reps/default/array", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getElementCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","db686dc5e4aff22e09d387a8de9141e496d71de8", "\"", " __dtid=\"","default/array", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getElementCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -245,7 +245,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","c8e051a0d8ba6235ed889e184aae731c7e813f48", "\"", " __dtid=\"","insight.domplate.reps/default/array", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Short), __out__, function(element, __out__) {    __code__.push("","<span", " class=\"","element", " ", "\"",">","<span", " class=\"","value", " ", "\"",">");__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","db686dc5e4aff22e09d387a8de9141e496d71de8", "\"", " __dtid=\"","default/array", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Short), __out__, function(element, __out__) {    __code__.push("","<span", " class=\"","element", " ", "\"",">","<span", " class=\"","value", " ", "\"",">");__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "expandableStub":function (context) {
@@ -272,11 +272,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","c8e051a0d8ba6235ed889e184aae731c7e813f48", "\"", " __dtid=\"","insight.domplate.reps/default/array", "\"",">"," ... ","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","db686dc5e4aff22e09d387a8de9141e496d71de8", "\"", " __dtid=\"","default/array", "\"",">"," ... ","</span>");  }}})
 }
 };
-  rep.__dbid = "c8e051a0d8ba6235ed889e184aae731c7e813f48";
-  rep.__dtid = "insight.domplate.reps/default/array";
+  rep.__dbid = "db686dc5e4aff22e09d387a8de9141e496d71de8";
+  rep.__dtid = "default/array";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.preview.htm
similarity index 56%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.preview.htm
index 577b4c2..2735f62 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="3e909bde186a14516e0c86324d0f008fc71f2b2c" __dtid="insight.domplate.reps/default/constant" class="constant ">Hello World</span>
+<span __dbid="3e909bde186a14516e0c86324d0f008fc71f2b2c" __dtid="default/constant" class="constant ">Hello World</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="3e909bde186a14516e0c86324d0f008fc71f2b2c" __dtid="insight.domplate.reps/default/constant" class="constant ">Hello World</span>
+<span __dbid="3e909bde186a14516e0c86324d0f008fc71f2b2c" __dtid="default/constant" class="constant ">Hello World</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.rep.js
similarity index 92%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.rep.js
index 2e3f164..16c86b2 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/constant.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/constant.rep.js
@@ -54,7 +54,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3e909bde186a14516e0c86324d0f008fc71f2b2c", "\"", " __dtid=\"","insight.domplate.reps/default/constant", "\"", " class=\"","constant", " ", "\"",">",__escape__(node.value),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3e909bde186a14516e0c86324d0f008fc71f2b2c", "\"", " __dtid=\"","default/constant", "\"", " class=\"","constant", " ", "\"",">",__escape__(node.value),"</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -63,11 +63,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3e909bde186a14516e0c86324d0f008fc71f2b2c", "\"", " __dtid=\"","insight.domplate.reps/default/constant", "\"", " class=\"","constant", " ", "\"",">",__escape__(node.value),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3e909bde186a14516e0c86324d0f008fc71f2b2c", "\"", " __dtid=\"","default/constant", "\"", " class=\"","constant", " ", "\"",">",__escape__(node.value),"</span>");  }}})
 }
 };
   rep.__dbid = "3e909bde186a14516e0c86324d0f008fc71f2b2c";
-  rep.__dtid = "insight.domplate.reps/default/constant";
+  rep.__dtid = "default/constant";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.preview.htm
new file mode 100644
index 0000000..9ee2adb
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.preview.htm
@@ -0,0 +1,40 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="default/dictionary" class="dictionary "><span>dictionary(</span><div class="member "><span decorator="" class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></div><div class="member "><span decorator="" class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></div><span>)</span></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="default/dictionary" class="dictionary "><span>dictionary(</span><span class="member "><span class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></span><span class="member "><span class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="default/dictionary" class="dictionary "><span>dictionary(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
+
+<br/>
+
+<h2>expandableStub</h2>
+
+
+
+<br/>
+
+<h2>expandedStub</h2>
+
+
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="default/dictionary" class="more "> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.rep.js
similarity index 85%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.rep.js
index 3f5aed4..edfbf3e 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/dictionary.rep.js
@@ -272,7 +272,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","insight.domplate.reps/default/dictionary", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Normal), __out__, function(member, __out__) {    __code__.push("","<div", " class=\"","member", " ", (member.expandable ? "expandable" + " " : ""), "\"",">","<span", " decorator=\"",__escape__(getMemberNameDecorator(member)), "\"", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,member,context);__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","default/dictionary", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Normal), __out__, function(member, __out__) {    __code__.push("","<div", " class=\"","member", " ", (member.expandable ? "expandable" + " " : ""), "\"",">","<span", " decorator=\"",__escape__(getMemberNameDecorator(member)), "\"", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,member,context);__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -281,7 +281,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","insight.domplate.reps/default/dictionary", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Short), __out__, function(member, __out__) {    __code__.push("","<span", " class=\"","member", " ", "\"",">","<span", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","default/dictionary", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Short), __out__, function(member, __out__) {    __code__.push("","<span", " class=\"","member", " ", "\"",">","<span", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -290,7 +290,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","insight.domplate.reps/default/dictionary", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getMemberCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","default/dictionary", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getMemberCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "expandableStub":function (context) {
@@ -317,11 +317,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","insight.domplate.reps/default/dictionary", "\"", " class=\"","more", " ", "\"",">"," ... ","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","91497e8f62a95c8946215eb31aaa2ffc2bf5cc84", "\"", " __dtid=\"","default/dictionary", "\"", " class=\"","more", " ", "\"",">"," ... ","</span>");  }}})
 }
 };
   rep.__dbid = "91497e8f62a95c8946215eb31aaa2ffc2bf5cc84";
-  rep.__dtid = "insight.domplate.reps/default/dictionary";
+  rep.__dtid = "default/dictionary";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/edit-rule.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/edit-rule.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/edit-rule.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/edit-rule.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/table.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/table.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/table.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/table.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/twisty-closed.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/twisty-closed.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/twisty-closed.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/twisty-closed.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/twisty-open.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/twisty-open.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/images/twisty-open.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/images/twisty-open.png
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.preview.htm
new file mode 100644
index 0000000..1bb5dd7
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.preview.htm
@@ -0,0 +1,28 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="default/map" class="map "><span>map(</span><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></div><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></div><span>)</span></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="default/map" class="map "><span>map(</span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="default/map" class="map "><span>map(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="default/map"> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.rep.js
similarity index 82%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.rep.js
index 626fc4e..93079d8 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/map.rep.js
@@ -230,7 +230,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","insight.domplate.reps/default/map", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Normal), __out__, function(pair, __out__) {    __code__.push("","<div", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","default/map", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Normal), __out__, function(pair, __out__) {    __code__.push("","<div", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -239,7 +239,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","insight.domplate.reps/default/map", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Short), __out__, function(pair, __out__) {    __code__.push("","<span", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","default/map", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Short), __out__, function(pair, __out__) {    __code__.push("","<span", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -248,7 +248,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","insight.domplate.reps/default/map", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getItemCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","default/map", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getItemCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "moreTag":function (context) {
@@ -257,11 +257,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","insight.domplate.reps/default/map", "\"",">"," ... ","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","3dd180ce224195ebd5ae4041dd5384730e8c6534", "\"", " __dtid=\"","default/map", "\"",">"," ... ","</span>");  }}})
 }
 };
   rep.__dbid = "3dd180ce224195ebd5ae4041dd5384730e8c6534";
-  rep.__dtid = "insight.domplate.reps/default/map";
+  rep.__dtid = "default/map";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.preview.htm
similarity index 59%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.preview.htm
index d9f8bc8..5504f10 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.preview.htm
@@ -3,19 +3,19 @@
 
 <h2>tag</h2>
 
-<span __dbid="485215d9752c351011a71bdfb2f63d02f1ffbc0b" __dtid="insight.domplate.reps/default/reference" class="reference "></span>
+<span __dbid="485215d9752c351011a71bdfb2f63d02f1ffbc0b" __dtid="default/reference" class="reference "></span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="485215d9752c351011a71bdfb2f63d02f1ffbc0b" __dtid="insight.domplate.reps/default/reference" class="reference "></span>
+<span __dbid="485215d9752c351011a71bdfb2f63d02f1ffbc0b" __dtid="default/reference" class="reference "></span>
 
 <br/>
 
 <h2>collapsedTag</h2>
 
-<span __dbid="485215d9752c351011a71bdfb2f63d02f1ffbc0b" __dtid="insight.domplate.reps/default/reference" class="reference "></span>
+<span __dbid="485215d9752c351011a71bdfb2f63d02f1ffbc0b" __dtid="default/reference" class="reference "></span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.rep.js
similarity index 87%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.rep.js
index db45380..1a61728 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/reference.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/reference.rep.js
@@ -98,7 +98,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","485215d9752c351011a71bdfb2f63d02f1ffbc0b", "\"", " __dtid=\"","insight.domplate.reps/default/reference", "\"", " class=\"","reference", " ", "\"",">");__link__(getTag(context,node,CONST_Normal), __code__, __out__, {"context":context,"node":getInstanceNode(context,node)});    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","485215d9752c351011a71bdfb2f63d02f1ffbc0b", "\"", " __dtid=\"","default/reference", "\"", " class=\"","reference", " ", "\"",">");__link__(getTag(context,node,CONST_Normal), __code__, __out__, {"context":context,"node":getInstanceNode(context,node)});    __code__.push("","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -107,7 +107,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","485215d9752c351011a71bdfb2f63d02f1ffbc0b", "\"", " __dtid=\"","insight.domplate.reps/default/reference", "\"", " class=\"","reference", " ", "\"",">");__link__(getTag(context,node,CONST_Collapsed), __code__, __out__, {"context":context,"node":getInstanceNode(context,node)});    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","485215d9752c351011a71bdfb2f63d02f1ffbc0b", "\"", " __dtid=\"","default/reference", "\"", " class=\"","reference", " ", "\"",">");__link__(getTag(context,node,CONST_Collapsed), __code__, __out__, {"context":context,"node":getInstanceNode(context,node)});    __code__.push("","</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -116,11 +116,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","485215d9752c351011a71bdfb2f63d02f1ffbc0b", "\"", " __dtid=\"","insight.domplate.reps/default/reference", "\"", " class=\"","reference", " ", "\"",">");__link__(getTag(context,node,CONST_Collapsed), __code__, __out__, {"context":context,"node":getInstanceNode(context,node)});    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","485215d9752c351011a71bdfb2f63d02f1ffbc0b", "\"", " __dtid=\"","default/reference", "\"", " class=\"","reference", " ", "\"",">");__link__(getTag(context,node,CONST_Collapsed), __code__, __out__, {"context":context,"node":getInstanceNode(context,node)});    __code__.push("","</span>");  }}})
 }
 };
   rep.__dbid = "485215d9752c351011a71bdfb2f63d02f1ffbc0b";
-  rep.__dtid = "insight.domplate.reps/default/reference";
+  rep.__dtid = "default/reference";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.preview.htm
new file mode 100644
index 0000000..bc151cd
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.preview.htm
@@ -0,0 +1,22 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="62e0cc04c396104049de2fe8bd98aa71d8459ae5" __dtid="default/string" class="string ">Hello World</span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="62e0cc04c396104049de2fe8bd98aa71d8459ae5" __dtid="default/string" class="string ">Hello World</span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="62e0cc04c396104049de2fe8bd98aa71d8459ae5" __dtid="default/string" class="string ">Hello World</span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.rep.css
new file mode 100644
index 0000000..b8f5fc1
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.rep.css
@@ -0,0 +1,4 @@
+
+SPAN[__dbid="62e0cc04c396104049de2fe8bd98aa71d8459ae5"] {
+    color: black;
+}
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.rep.js
similarity index 74%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.rep.js
index e088844..25221f6 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/string.rep.js
@@ -20,9 +20,15 @@ function impl(domplate) {
     shortTag: T.SPAN({
       "class": "string"
     }, "$node|getValue"),
+    collapsedTag: T.SPAN({
+      "class": "string"
+    }, "$node|cropNode"),
     getValue: function getValue(node) {
       if (!node.parentNode || node.meta && typeof node.meta["string.trim.enabled"] !== "undefined" && node.meta["string.trim.enabled"] === false) return node.value;else return this.cropString(node.value);
     },
+    cropNode: function cropNode(node) {
+      return this.cropString(node.value);
+    },
     cropString: function cropString(text, limit) {
       text = text + "";
       limit = limit || 50;
@@ -76,6 +82,16 @@ var __link__ = context.__link__;
 var __loop__ = context.__loop__;
 return (function (root, context, o) {  with (this) {  }  return 1;})
 }
+,
+"collapsedTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o) {  with (this) {  }  return 1;})
+}
 };
   rep.__markup = {
 "tag":function (context) {
@@ -84,7 +100,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","bc26bd236f1fdd412abbeac376be6d7301b0432e", "\"", " __dtid=\"","insight.domplate.reps/default/string", "\"", " class=\"","string", " ", "\"",">");    __loop__.apply(this, [lineIterator(node.value), __out__, function(line, __out__) {    __code__.push("",__escape__(line.value));__if__.apply(this, [line.more, __out__, function(__out__) {    __code__.push("","<br","/>");}]);    }]);    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","62e0cc04c396104049de2fe8bd98aa71d8459ae5", "\"", " __dtid=\"","default/string", "\"", " class=\"","string", " ", "\"",">");    __loop__.apply(this, [lineIterator(node.value), __out__, function(line, __out__) {    __code__.push("",__escape__(line.value));__if__.apply(this, [line.more, __out__, function(__out__) {    __code__.push("","<br","/>");}]);    }]);    __code__.push("","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -93,11 +109,20 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","bc26bd236f1fdd412abbeac376be6d7301b0432e", "\"", " __dtid=\"","insight.domplate.reps/default/string", "\"", " class=\"","string", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","62e0cc04c396104049de2fe8bd98aa71d8459ae5", "\"", " __dtid=\"","default/string", "\"", " class=\"","string", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+}
+,
+"collapsedTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","62e0cc04c396104049de2fe8bd98aa71d8459ae5", "\"", " __dtid=\"","default/string", "\"", " class=\"","string", " ", "\"",">",__escape__(cropNode(node)),"</span>");  }}})
 }
 };
-  rep.__dbid = "bc26bd236f1fdd412abbeac376be6d7301b0432e";
-  rep.__dtid = "insight.domplate.reps/default/string";
+  rep.__dbid = "62e0cc04c396104049de2fe8bd98aa71d8459ae5";
+  rep.__dtid = "default/string";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.preview.htm
new file mode 100644
index 0000000..2dca779
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.preview.htm
@@ -0,0 +1,16 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<div __dbid="39ff1ebd3d2ba584e6cc7b584b5b45791c136b55" __dtid="default/table" class="table "><table cellpadding="3" cellspacing="0"><tbody><tr class=" "><th class="header "></th><th class="header "></th></tr><tr><td class="cell "></td><td class="cell "></td></tr><tr><td class="cell "></td><td class="cell "></td></tr></tbody></table></div>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="39ff1ebd3d2ba584e6cc7b584b5b45791c136b55" __dtid="default/table" class="table "></span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.rep.js
similarity index 84%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.rep.js
index 2369e6d..fb0eb10 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/table.rep.js
@@ -185,7 +185,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","39ff1ebd3d2ba584e6cc7b584b5b45791c136b55", "\"", " __dtid=\"","insight.domplate.reps/default/table", "\"", " class=\"","table", " ", "\"",">","<table", " cellpadding=\"","3", "\"", " cellspacing=\"","0", "\"",">","<tbody",">","<tr", " class=\"",__escape__(getHeaderClass(node)), " ", "\"",">");    __loop__.apply(this, [getHeaders(context,node), __out__, function(column, __out__) {    __code__.push("","<th", " class=\"","header", " ", "\"",">");__link__(column.tag, __code__, __out__, {"node":column.node});    __code__.push("","</th>");    }]);__if__.apply(this, [hasNoHeader(node), __out__, function(__out__) {    __code__.push("","<th",">","</th>");}]);    __code__.push("","</tr>");    __loop__.apply(this, [getRows(node), __out__, function(row, __out__) {    __code__.push("","<tr",">");    __loop__.apply(this, [getCells(context,row), __out__, function(cell, __out__) {    __code__.push("","<td", " class=\"","cell", " ", "\"",">");__out__.push(onCellClick,cell.node);__link__(cell.tag, __code__, __out__, {"node":cell.node,"context":context});    __code__.push("","</td>");    }]);    __code__.push("","</tr>");    }]);    __code__.push("","</tbody>","</table>","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","39ff1ebd3d2ba584e6cc7b584b5b45791c136b55", "\"", " __dtid=\"","default/table", "\"", " class=\"","table", " ", "\"",">","<table", " cellpadding=\"","3", "\"", " cellspacing=\"","0", "\"",">","<tbody",">","<tr", " class=\"",__escape__(getHeaderClass(node)), " ", "\"",">");    __loop__.apply(this, [getHeaders(context,node), __out__, function(column, __out__) {    __code__.push("","<th", " class=\"","header", " ", "\"",">");__link__(column.tag, __code__, __out__, {"node":column.node});    __code__.push("","</th>");    }]);__if__.apply(this, [hasNoHeader(node), __out__, function(__out__) {    __code__.push("","<th",">","</th>");}]);    __code__.push("","</tr>");    __loop__.apply(this, [getRows(node), __out__, function(row, __out__) {    __code__.push("","<tr",">");    __loop__.apply(this, [getCells(context,row), __out__, function(cell, __out__) {    __code__.push("","<td", " class=\"","cell", " ", "\"",">");__out__.push(onCellClick,cell.node);__link__(cell.tag, __code__, __out__, {"node":cell.node,"context":context});    __code__.push("","</td>");    }]);    __code__.push("","</tr>");    }]);    __code__.push("","</tbody>","</table>","</div>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -194,11 +194,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","39ff1ebd3d2ba584e6cc7b584b5b45791c136b55", "\"", " __dtid=\"","insight.domplate.reps/default/table", "\"", " class=\"","table", " ", "\"",">");__link__(getTitleTag(context,node), __code__, __out__, {"node":getTitleNode(node),"context":context});    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","39ff1ebd3d2ba584e6cc7b584b5b45791c136b55", "\"", " __dtid=\"","default/table", "\"", " class=\"","table", " ", "\"",">");__link__(getTitleTag(context,node), __code__, __out__, {"node":getTitleNode(node),"context":context});    __code__.push("","</span>");  }}})
 }
 };
   rep.__dbid = "39ff1ebd3d2ba584e6cc7b584b5b45791c136b55";
-  rep.__dtid = "insight.domplate.reps/default/table";
+  rep.__dtid = "default/table";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.preview.htm
new file mode 100644
index 0000000..2cb55ef
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.preview.htm
@@ -0,0 +1,16 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<div __dbid="02ce81bf834058ce5150d7be4d01e547baa84a2c" __dtid="default/trace" class="structures-trace "><table cellpadding="3" cellspacing="0"><tbody><tr><th class="header-file ">File</th><th class="header-line ">Line</th><th class="header-inst ">Instruction</th></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">10</td><td class="cell-inst "><div>Class(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">5</td><td class="cell-inst "><div>Function(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr></tbody></table></div>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="02ce81bf834058ce5150d7be4d01e547baa84a2c" __dtid="default/trace" class="structures-trace "></span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.rep.js
similarity index 86%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.rep.js
index b1b90f3..4b80727 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trace.rep.js
@@ -257,7 +257,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","02ce81bf834058ce5150d7be4d01e547baa84a2c", "\"", " __dtid=\"","insight.domplate.reps/default/trace", "\"", " class=\"","structures-trace", " ", "\"",">","<table", " cellpadding=\"","3", "\"", " cellspacing=\"","0", "\"",">","<tbody",">","<tr",">","<th", " class=\"","header-file", " ", "\"",">","File","</th>","<th", " class=\"","header-line", " ", "\"",">","Line","</th>","<th", " class=\"","header-inst", " ", "\"",">","Instruction","</th>","</tr>");__out__.push(node);    __loop__.apply(this, [getCallList(node), __out__, function(frame, __out__) {    __code__.push("","<tr",">","<td", " class=\"","cell-file", " ", "\"",">",__escape__(frame.file),"</td>","<td", " class=\"","cell-line", " ", "\"",">",__escape__(frame.line),"</td>","<td", " class=\"","cell-inst", " ", "\"",">","<div",">",__escape__(getFrameLabel(frame)),"(");__out__.push(frame.node,onFileClick,onFileClick);    __loop__.apply(this, [argIterator(context,frame), __out__, function(arg, __out__) {    __code__.push("","<div", " class=\"","arg", " ", "\"",">");__out__.push(onArgClick,arg.node);__link__(arg.tag, __code__, __out__, {"node":arg.node,"context":context});__if__.apply(this, [arg.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("",")","</div>","</td>","</tr>");    }]);    __code__.push("","</tbody>","</table>","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","02ce81bf834058ce5150d7be4d01e547baa84a2c", "\"", " __dtid=\"","default/trace", "\"", " class=\"","structures-trace", " ", "\"",">","<table", " cellpadding=\"","3", "\"", " cellspacing=\"","0", "\"",">","<tbody",">","<tr",">","<th", " class=\"","header-file", " ", "\"",">","File","</th>","<th", " class=\"","header-line", " ", "\"",">","Line","</th>","<th", " class=\"","header-inst", " ", "\"",">","Instruction","</th>","</tr>");__out__.push(node);    __loop__.apply(this, [getCallList(node), __out__, function(frame, __out__) {    __code__.push("","<tr",">","<td", " class=\"","cell-file", " ", "\"",">",__escape__(frame.file),"</td>","<td", " class=\"","cell-line", " ", "\"",">",__escape__(frame.line),"</td>","<td", " class=\"","cell-inst", " ", "\"",">","<div",">",__escape__(getFrameLabel(frame)),"(");__out__.push(frame.node,onFileClick,onFileClick);    __loop__.apply(this, [argIterator(context,frame), __out__, function(arg, __out__) {    __code__.push("","<div", " class=\"","arg", " ", "\"",">");__out__.push(onArgClick,arg.node);__link__(arg.tag, __code__, __out__, {"node":arg.node,"context":context});__if__.apply(this, [arg.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("",")","</div>","</td>","</tr>");    }]);    __code__.push("","</tbody>","</table>","</div>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -266,11 +266,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","02ce81bf834058ce5150d7be4d01e547baa84a2c", "\"", " __dtid=\"","insight.domplate.reps/default/trace", "\"", " class=\"","structures-trace", " ", "\"",">");__out__.push(node);__link__(getCaptionTag(context,node), __code__, __out__, {"node":getCaptionNode(node)});    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","02ce81bf834058ce5150d7be4d01e547baa84a2c", "\"", " __dtid=\"","default/trace", "\"", " class=\"","structures-trace", " ", "\"",">");__out__.push(node);__link__(getCaptionTag(context,node), __code__, __out__, {"node":getCaptionNode(node)});    __code__.push("","</span>");  }}})
 }
 };
   rep.__dbid = "02ce81bf834058ce5150d7be4d01e547baa84a2c";
-  rep.__dtid = "insight.domplate.reps/default/trace";
+  rep.__dtid = "default/trace";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.preview.htm
similarity index 58%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.preview.htm
index c3d24e9..1b52c91 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="5ee0a11f1b7ce8d99318df96e48d8aae775a3482" __dtid="insight.domplate.reps/default/trimmed" class="trimmed ">Trimmed!</span>
+<span __dbid="5ee0a11f1b7ce8d99318df96e48d8aae775a3482" __dtid="default/trimmed" class="trimmed ">Trimmed!</span>
 
 <br/>
 
 <h2>collapsedTag</h2>
 
-<span __dbid="5ee0a11f1b7ce8d99318df96e48d8aae775a3482" __dtid="insight.domplate.reps/default/trimmed" class="trimmed ">Trimmed!</span>
+<span __dbid="5ee0a11f1b7ce8d99318df96e48d8aae775a3482" __dtid="default/trimmed" class="trimmed ">Trimmed!</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.rep.js
similarity index 92%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.rep.js
index c71eeb5..7e50a9e 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trimmed.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/trimmed.rep.js
@@ -57,7 +57,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ee0a11f1b7ce8d99318df96e48d8aae775a3482", "\"", " __dtid=\"","insight.domplate.reps/default/trimmed", "\"", " class=\"","trimmed", " ", "\"",">",__escape__(getNotice(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ee0a11f1b7ce8d99318df96e48d8aae775a3482", "\"", " __dtid=\"","default/trimmed", "\"", " class=\"","trimmed", " ", "\"",">",__escape__(getNotice(node)),"</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -66,11 +66,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ee0a11f1b7ce8d99318df96e48d8aae775a3482", "\"", " __dtid=\"","insight.domplate.reps/default/trimmed", "\"", " class=\"","trimmed", " ", "\"",">",__escape__(getNotice(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ee0a11f1b7ce8d99318df96e48d8aae775a3482", "\"", " __dtid=\"","default/trimmed", "\"", " class=\"","trimmed", " ", "\"",">",__escape__(getNotice(node)),"</span>");  }}})
 }
 };
   rep.__dbid = "5ee0a11f1b7ce8d99318df96e48d8aae775a3482";
-  rep.__dtid = "insight.domplate.reps/default/trimmed";
+  rep.__dtid = "default/trimmed";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.preview.htm
similarity index 57%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.preview.htm
index bbff65e..a218699 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="28f0cad32e3f8c4b4123b834007ba004ed6057b2" __dtid="insight.domplate.reps/default/unknown" class="unknown ">Hello World</span>
+<span __dbid="28f0cad32e3f8c4b4123b834007ba004ed6057b2" __dtid="default/unknown" class="unknown ">Hello World</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="28f0cad32e3f8c4b4123b834007ba004ed6057b2" __dtid="insight.domplate.reps/default/unknown" class="unknown ">Hello World</span>
+<span __dbid="28f0cad32e3f8c4b4123b834007ba004ed6057b2" __dtid="default/unknown" class="unknown ">Hello World</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.rep.js
similarity index 92%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.rep.js
index 4db214e..6804b42 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/unknown.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/default/unknown.rep.js
@@ -54,7 +54,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","28f0cad32e3f8c4b4123b834007ba004ed6057b2", "\"", " __dtid=\"","insight.domplate.reps/default/unknown", "\"", " class=\"","unknown", " ", "\"",">",__escape__(node.value),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","28f0cad32e3f8c4b4123b834007ba004ed6057b2", "\"", " __dtid=\"","default/unknown", "\"", " class=\"","unknown", " ", "\"",">",__escape__(node.value),"</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -63,11 +63,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","28f0cad32e3f8c4b4123b834007ba004ed6057b2", "\"", " __dtid=\"","insight.domplate.reps/default/unknown", "\"", " class=\"","unknown", " ", "\"",">",__escape__(node.value),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","28f0cad32e3f8c4b4123b834007ba004ed6057b2", "\"", " __dtid=\"","default/unknown", "\"", " class=\"","unknown", " ", "\"",">",__escape__(node.value),"</span>");  }}})
 }
 };
   rep.__dbid = "28f0cad32e3f8c4b4123b834007ba004ed6057b2";
-  rep.__dtid = "insight.domplate.reps/default/unknown";
+  rep.__dtid = "default/unknown";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate-eval.browser.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate-eval.browser.js
new file mode 100644
index 0000000..7382467
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate-eval.browser.js
@@ -0,0 +1,2513 @@
+((function (_require, _exports, _module) {
+var bundle = { require: _require, exports: _exports, module: _module };
+var exports = undefined;
+var module = undefined;
+var define = function (deps, init) {
+var exports = init();
+[["domplate","domplate"]].forEach(function (expose) {
+if (typeof window !== "undefined") {
+window[expose[0]] = exports[expose[1]];
+} else if (typeof self !== "undefined") {
+self[expose[0]] = exports[expose[1]];
+}
+});
+}; define.amd = true;
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+
+module.exports = require("./domplate");
+module.exports.domplate.EVAL = require("./explicit-unsafe-eval");
+},{"./domplate":2,"./explicit-unsafe-eval":3}],2:[function(require,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var RT = require("./rt");
+
+var Renderer = exports.Renderer = require("./renderer").Renderer;
+
+function Domplate(exports) {
+  exports.util = require("./util");
+
+  exports.loadStyle = function (uri, baseUrl) {
+    var WINDOW = window;
+
+    if (typeof baseUrl === 'undefined' && WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
+      uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
+    } else if (typeof baseUrl !== 'undefined') {
+      uri = [baseUrl, uri].join("/").replace(/\/\.?\//g, "/");
+    }
+
+    return new Promise(function (resolve, reject) {
+      var link = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "link") : window.document.createElement("link");
+      link.rel = "stylesheet";
+      link.href = uri;
+
+      link.onload = function () {
+        resolve();
+      };
+
+      var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+      head.appendChild(link);
+    });
+  };
+
+  exports.EVAL = {
+    compileMarkup: function compileMarkup(code, context) {
+      return context.compiled(context);
+    },
+    compileDOM: function compileDOM(code, context) {
+      return context.compiled(context);
+    }
+  };
+  exports.tags = {};
+  exports.tags._domplate_ = exports;
+
+  var DomplateTag = exports.DomplateTag = function DomplateTag(tagName) {
+    this.tagName = tagName;
+  };
+
+  function DomplateEmbed() {}
+
+  function DomplateLoop() {}
+
+  function DomplateIf() {}
+
+  function copyArray(oldArray) {
+    var ary = [];
+    if (oldArray) for (var i = 0; i < oldArray.length; ++i) {
+      ary.push(oldArray[i]);
+    }
+    return ary;
+  }
+
+  function copyObject(l, r) {
+    var m = {};
+    extend(m, l);
+    extend(m, r);
+    return m;
+  }
+
+  function extend(l, r) {
+    for (var n in r) {
+      l[n] = r[n];
+    }
+  }
+
+  var womb = null;
+
+  var domplate = exports.domplate = function () {
+    var lastSubject;
+
+    for (var i = 0; i < arguments.length; ++i) {
+      lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
+    }
+
+    for (var name in lastSubject) {
+      var val = lastSubject[name];
+      if (isTag(val)) val.tag.subject = lastSubject;
+    }
+
+    return lastSubject;
+  };
+
+  domplate.context = function (context, fn) {
+    var lastContext = domplate.lastContext;
+    domplate.topContext = context;
+    fn.apply(context);
+    domplate.topContext = lastContext;
+  };
+
+  exports.tags.TAG = function () {
+    var embed = new DomplateEmbed();
+    return embed.merge(arguments);
+  };
+
+  exports.tags.FOR = domplate.FOR = function () {
+    var loop = new DomplateLoop();
+    return loop.merge(arguments);
+  };
+
+  exports.tags.IF = domplate.IF = function () {
+    var loop = new DomplateIf();
+    return loop.merge(arguments);
+  };
+
+  DomplateTag.prototype = {
+    merge: function merge(args, oldTag) {
+      if (oldTag) this.tagName = oldTag.tagName;
+      this.context = oldTag ? oldTag.context : null;
+      this.subject = oldTag ? oldTag.subject : null;
+      this.attrs = oldTag ? copyObject(oldTag.attrs) : {};
+      this.classes = oldTag ? copyObject(oldTag.classes) : {};
+      this.props = oldTag ? copyObject(oldTag.props) : null;
+      this.listeners = oldTag ? copyArray(oldTag.listeners) : null;
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args.length ? args[0] : null;
+      var hasAttrs = _typeof(attrs) == "object" && !isTag(attrs);
+      this.resources = {};
+      this.children = [];
+      if (domplate.topContext) this.context = domplate.topContext;
+      if (args.length) parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
+      if (hasAttrs) this.parseAttrs(attrs);
+      return creator(this, DomplateTag);
+    },
+    parseAttrs: function parseAttrs(args) {
+      for (var name in args) {
+        var val = parseValue(args[name]);
+        readPartNames(val, this.vars);
+
+        if (name.indexOf("on") === 0) {
+          var eventName = name.substr(2);
+          if (!this.listeners) this.listeners = [];
+          this.listeners.push(eventName, val);
+        } else if (name[0] === "_" && name[1] !== "_") {
+          var propName = name.substr(1);
+          if (!this.props) this.props = {};
+          this.props[propName] = val;
+        } else if (name[0] === "$") {
+          var className = name.substr(1);
+          if (!this.classes) this.classes = {};
+          this.classes[className] = val;
+        } else {
+          if (name === "class" && this.attrs.hasOwnProperty(name)) this.attrs[name] += " " + val;else this.attrs[name] = val;
+        }
+      }
+    },
+    compile: function compile() {
+      if (this.renderMarkup) {
+        return;
+      }
+
+      if (this.subject._resources) {
+        this.resources = this.subject._resources();
+      }
+
+      this.compileMarkup();
+      this.compileDOM();
+    },
+    compileMarkup: function compileMarkup() {
+      this.markupArgs = [];
+      var topBlock = [],
+          topOuts = [],
+          blocks = [],
+          info = {
+        args: this.markupArgs,
+        argIndex: 0
+      };
+      this.generateMarkup(topBlock, topOuts, blocks, info, true);
+      this.addCode(topBlock, topOuts, blocks);
+      var fnBlock = ['(function (__code__, __context__, __in__, __out__'];
+
+      for (var i = 0; i < info.argIndex; ++i) {
+        fnBlock.push(', s', i);
+      }
+
+      fnBlock.push(') {');
+      if (this.subject) fnBlock.push('  with (this) {');
+      if (this.context) fnBlock.push('  with (__context__) {');
+      fnBlock.push('  with (__in__) {');
+      fnBlock.push.apply(fnBlock, blocks);
+      if (this.subject) fnBlock.push('  }');
+      if (this.context) fnBlock.push('  }');
+      fnBlock.push('}})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderMarkup = exports.EVAL.compileMarkup(js, RT.makeMarkupRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__markup
+      }));
+    },
+    getVarNames: function getVarNames(args) {
+      if (this.vars) args.push.apply(args, this.vars);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.getVarNames(args);else if (child instanceof Parts) {
+          for (var i = 0; i < child.parts.length; ++i) {
+            if (child.parts[i] instanceof Variables) {
+              var name = child.parts[i].names[0];
+              var names = name.split(".");
+              args.push(names[0]);
+            }
+          }
+        }
+      }
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info, topNode) {
+      topBlock.push(',"<', this.tagName, '"');
+
+      if (topNode) {
+        if (this.subject.__dbid) this.attrs['__dbid'] = this.subject.__dbid;
+        if (this.subject.__dtid) this.attrs['__dtid'] = this.subject.__dtid;
+      }
+
+      for (var name in this.attrs) {
+        if (name != "class") {
+          var val = this.attrs[name];
+          topBlock.push(', " ', name, '=\\""');
+          addParts(val, ',', topBlock, info, true);
+          topBlock.push(', "\\""');
+        }
+      }
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          readPartNames(this.listeners[i + 1], topOuts);
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          readPartNames(this.props[name], topOuts);
+        }
+      }
+
+      if (this.attrs.class || this.classes && Object.keys(this.classes).length > 0) {
+        topBlock.push(', " class=\\""');
+        if (this.attrs.hasOwnProperty("class")) addParts(this.attrs["class"], ',', topBlock, info, true);
+        topBlock.push(', " "');
+
+        for (var name in this.classes) {
+          topBlock.push(', (');
+          addParts(this.classes[name], '', topBlock, info);
+          topBlock.push(' ? "', name, '" + " " : "")');
+        }
+
+        topBlock.push(', "\\""');
+      }
+
+      if (this.tagName == "br") {
+        topBlock.push(',"/>"');
+      } else {
+        topBlock.push(',">"');
+        this.generateChildMarkup(topBlock, topOuts, blocks, info);
+        topBlock.push(',"</', this.tagName, '>"');
+      }
+    },
+    generateChildMarkup: function generateChildMarkup(topBlock, topOuts, blocks, info) {
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.generateMarkup(topBlock, topOuts, blocks, info);else addParts(child, ',', topBlock, info, true);
+      }
+    },
+    addCode: function addCode(topBlock, topOuts, blocks) {
+      if (topBlock.length) blocks.push('    __code__.push(""', topBlock.join(""), ');');
+      if (topOuts.length) blocks.push('__out__.push(', topOuts.join(","), ');');
+      topBlock.splice(0, topBlock.length);
+      topOuts.splice(0, topOuts.length);
+    },
+    addLocals: function addLocals(blocks) {
+      var varNames = [];
+      this.getVarNames(varNames);
+      var map = {};
+
+      for (var i = 0; i < varNames.length; ++i) {
+        var name = varNames[i];
+        if (map.hasOwnProperty(name)) continue;
+        map[name] = 1;
+        var names = name.split(".");
+        blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';');
+      }
+    },
+    compileDOM: function compileDOM() {
+      var path = [];
+      var blocks = [];
+      this.domArgs = [];
+      path.embedIndex = 0;
+      path.loopIndex = 0;
+      path.ifIndex = 0;
+      path.staticIndex = 0;
+      path.renderIndex = 0;
+      var nodeCount = this.generateDOM(path, blocks, this.domArgs);
+      var fnBlock = ['(function (root, context, o'];
+
+      for (var i = 0; i < path.staticIndex; ++i) {
+        fnBlock.push(', ', 's' + i);
+      }
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        fnBlock.push(', ', 'd' + i);
+      }
+
+      fnBlock.push(') {');
+
+      for (var i = 0; i < path.loopIndex; ++i) {
+        fnBlock.push('  var l', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.ifIndex; ++i) {
+        fnBlock.push('  var if_', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.embedIndex; ++i) {
+        fnBlock.push('  var e', i, ' = 0;');
+      }
+
+      if (this.subject) {
+        fnBlock.push('  with (this) {');
+      }
+
+      if (this.context) {
+        fnBlock.push('    with (context) {');
+      }
+
+      fnBlock.push(blocks.join(""));
+      if (this.context) fnBlock.push('    }');
+      if (this.subject) fnBlock.push('  }');
+      fnBlock.push('  return ', nodeCount, ';');
+      fnBlock.push('})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderDOM = exports.EVAL.compileDOM(js, RT.makeDOMRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__dom
+      }));
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      if (this.listeners || this.props) this.generateNodePath(path, blocks);
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          var val = this.listeners[i + 1];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);');
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          var val = this.props[name];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.', name, ' = ', arg, ';');
+        }
+      }
+
+      this.generateChildDOM(path, blocks, args);
+      return 1;
+    },
+    generateNodePath: function generateNodePath(path, blocks) {
+      blocks.push("        node = __path__(root, o");
+
+      for (var i = 0; i < path.length; ++i) {
+        blocks.push(",", path[i]);
+      }
+
+      blocks.push(");");
+    },
+    generateChildDOM: function generateChildDOM(path, blocks, args) {
+      path.push(0);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) path[path.length - 1] += '+' + child.tag.generateDOM(path, blocks, args);else path[path.length - 1] += '+1';
+      }
+
+      path.pop();
+    }
+  };
+  DomplateEmbed.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.value = oldTag ? oldTag.value : parseValue(args[0]);
+      this.attrs = oldTag ? oldTag.attrs : {};
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args[1];
+
+      for (var name in attrs) {
+        var val = parseValue(attrs[name]);
+        this.attrs[name] = val;
+        readPartNames(val, this.vars);
+      }
+
+      var retval = creator(this, DomplateEmbed);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.value instanceof Parts) names.push(this.value.parts[0].name);
+      if (this.vars) names.push.apply(names, this.vars);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('__link__(');
+      addParts(this.value, '', blocks, info);
+      blocks.push(', __code__, __out__, {');
+      var lastName = null;
+
+      for (var name in this.attrs) {
+        if (lastName) blocks.push(',');
+        lastName = name;
+        var val = this.attrs[name];
+        blocks.push('"', name, '":');
+        addParts(val, '', blocks, info);
+      }
+
+      blocks.push('});');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var embedName = 'e' + path.embedIndex++;
+      this.generateNodePath(path, blocks);
+      var valueName = 'd' + path.renderIndex++;
+      var argsName = 'd' + path.renderIndex++;
+      blocks.push('        ', embedName + ' = __link__(node, ', valueName, ', ', argsName, ');');
+      return embedName;
+    }
+  });
+  DomplateLoop.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.varName = oldTag ? oldTag.varName : args[0];
+      this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 2);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateLoop);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.iter instanceof Parts) names.push(this.iter.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var iterName;
+
+      if (this.iter instanceof Parts) {
+        var part = this.iter.parts[0];
+        iterName = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            iterName = part.format[i] + "(" + iterName + ")";
+          }
+        }
+      } else {
+        iterName = this.iter;
+      }
+
+      blocks.push('    __loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('    }]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var iterName = 'd' + path.renderIndex++;
+      var counterName = 'i' + path.loopIndex;
+      var loopName = 'l' + path.loopIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+      var basePath = path[path.length - 1];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        path[path.length - 1] = basePath + '+' + loopName + '+' + nodeCount;
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      path[path.length - 1] = basePath + '+' + loopName;
+      blocks.push('      ', loopName, ' = __loop__.apply(this, [', iterName, ', function(', counterName, ',', loopName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('        return ', nodeCount, ';');
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return loopName;
+    }
+  });
+  DomplateIf.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.booleanVar = oldTag ? oldTag.booleanVar : parseValue(args[0]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 1);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateIf);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.booleanVar instanceof Parts) names.push(this.booleanVar.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var expr;
+
+      if (this.booleanVar instanceof Parts) {
+        var part = this.booleanVar.parts[0];
+        expr = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            expr = part.format[i] + "(" + expr + ")";
+          }
+        }
+      } else {
+        expr = this.booleanVar;
+      }
+
+      blocks.push('__if__.apply(this, [', expr, ', __out__, function(__out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('}]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var controlName = 'd' + path.renderIndex++;
+      var ifName = 'if_' + path.ifIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      blocks.push('      ', ifName, ' = __if__.apply(this, [', controlName, ', function(', ifName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return controlName;
+    }
+  });
+
+  function Variables(names, format) {
+    this.names = names;
+    this.format = format;
+  }
+
+  function Parts(parts) {
+    this.parts = parts;
+  }
+
+  function parseParts(str) {
+    var index = 0;
+    var parts = [];
+    var m;
+    var re = /\$([_A-Za-z][$_A-Za-z0-9.,|]*)/g;
+
+    while (m = re.exec(str)) {
+      var pre = str.substr(index, re.lastIndex - m[0].length - index);
+      if (pre) parts.push(pre);
+      var segs = m[1].split("|");
+      var vars = segs[0].split(",$");
+      parts.push(new Variables(vars, segs.splice(1)));
+      index = re.lastIndex;
+    }
+
+    if (!index) {
+      return str;
+    }
+
+    var post = str.substr(index);
+    if (post) parts.push(post);
+    var retval = new Parts(parts);
+    return retval;
+  }
+
+  function parseValue(val) {
+    return typeof val == 'string' ? parseParts(val) : val;
+  }
+
+  function parseChildren(args, offset, vars, children) {
+    for (var i = offset; i < args.length; ++i) {
+      var val = parseValue(args[i]);
+      children.push(val);
+      readPartNames(val, vars);
+    }
+  }
+
+  function readPartNames(val, vars) {
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+        if (part instanceof Variables) vars.push(part.names[0]);
+      }
+    }
+  }
+
+  function generateArg(val, path, args) {
+    if (val instanceof Parts) {
+      var vals = [];
+
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var varName = 'd' + path.renderIndex++;
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              varName = part.format[j] + '(' + varName + ')';
+            }
+          }
+
+          vals.push(varName);
+        } else vals.push('"' + part.replace(/"/g, '\\"') + '"');
+      }
+
+      return vals.join('+');
+    } else {
+      args.push(val);
+      return 's' + path.staticIndex++;
+    }
+  }
+
+  function addParts(val, delim, block, info, escapeIt) {
+    var vals = [];
+
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var partName = part.names.join(",");
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              partName = part.format[j] + "(" + partName + ")";
+            }
+          }
+
+          if (escapeIt) vals.push("__escape__(" + partName + ")");else vals.push(partName);
+        } else vals.push('"' + part + '"');
+      }
+    } else if (isTag(val)) {
+      info.args.push(val);
+      vals.push('s' + info.argIndex++);
+    } else vals.push('"' + val + '"');
+
+    var parts = vals.join(delim);
+    if (parts) block.push(delim, parts);
+  }
+
+  function isTag(obj) {
+    return (typeof obj == "function" || obj instanceof Function) && !!obj.tag;
+  }
+
+  function creator(tag, cons) {
+    var fn = function fn() {
+      var tag = arguments.callee.tag;
+      var cons = arguments.callee.cons;
+      var newTag = new cons();
+      return newTag.merge(arguments, tag);
+    };
+
+    fn.tag = tag;
+    fn.cons = cons;
+    extend(fn, Renderer);
+    return fn;
+  }
+
+  function defineTags() {
+    Array.from(arguments).forEach(function (tagName) {
+      var fnName = tagName.toUpperCase();
+
+      exports.tags[fnName] = function () {
+        var newTag = new this._domplate_.DomplateTag(tagName);
+        return newTag.merge(arguments);
+      };
+    });
+  }
+
+  defineTags("a", "button", "br", "canvas", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select", "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul");
+}
+
+exports.domplate = {};
+Domplate(exports.domplate);
+
+exports.domplate.ensureLoader = function () {
+  if (typeof window !== "undefined" && window.PINF) {
+    return window.PINF;
+  }
+
+  var PINF = require("pinf-loader-js/loader.browser");
+
+  return window.PINF;
+};
+
+exports.domplate.loadRep = function (url, options, successCallback, errorCallback) {
+  if (typeof options === "function") {
+    errorCallback = successCallback;
+    successCallback = options;
+    options = {};
+  }
+
+  var PINF = exports.domplate.ensureLoader();
+  return PINF.sandbox(url + ".rep", function (sandbox) {
+    var rep = sandbox.main(exports.domplate, options);
+    successCallback(rep);
+  }, errorCallback);
+};
+},{"./renderer":4,"./rt":5,"./util":6,"pinf-loader-js/loader.browser":8}],3:[function(require,module,exports){
+
+exports.compileMarkup = function (code, context) {
+  var DomplateDebug = context.DomplateDebug;
+  var __escape__ = context.__escape__;
+  var __if__ = context.__if__;
+  var __loop__ = context.__loop__;
+  var __link__ = context.__link__;
+
+  if (exports.onMarkupCode) {
+    exports.onMarkupCode(code);
+  }
+
+  return eval(code);
+};
+
+exports.compileDOM = function (code, context) {
+  var DomplateDebug = context.DomplateDebug;
+  var __path__ = context.__path__;
+  var __bind__ = context.__bind__;
+  var __if__ = context.__if__;
+  var __link__ = context.__link__;
+  var __loop__ = context.__loop__;
+
+  if (exports.onDOMCode) {
+    exports.onDOMCode(code);
+  }
+
+  return eval(code);
+};
+},{}],4:[function(require,module,exports){
+
+var Renderer = exports.Renderer = {
+  checkDebug: function checkDebug() {},
+  renderHTML: function renderHTML(args, outputs, self) {
+    var code = [];
+    var markupArgs = [code, this.tag.context ? this.tag.context : null, args, outputs];
+    markupArgs.push.apply(markupArgs, this.tag.markupArgs);
+    this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
+
+    if (this.tag.resources && this.tag.subject._resourceListener) {
+      this.tag.subject._resourceListener.register(this.tag.resources);
+    }
+
+    return code.join("");
+  },
+  insertRows: function insertRows(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var table = doc.createElement("table");
+    table.innerHTML = html;
+    var tbody = table.firstChild;
+    var parent = before.localName == "TR" ? before.parentNode : before;
+    var after = before.localName == "TR" ? before.nextSibling : null;
+    var firstRow = tbody.firstChild,
+        lastRow;
+
+    while (tbody.firstChild) {
+      lastRow = tbody.firstChild;
+      if (after) parent.insertBefore(lastRow, after);else parent.appendChild(lastRow);
+    }
+
+    var offset = 0;
+
+    if (before.localName == "TR") {
+      var node = firstRow.parentNode.firstChild;
+
+      for (; node && node != firstRow; node = node.nextSibling) {
+        ++offset;
+      }
+    }
+
+    var domArgs = [firstRow, this.tag.context, offset];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return [firstRow, lastRow];
+  },
+  insertAfter: function insertAfter(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var range = doc.createRange();
+    range.selectNode(doc.body);
+    var frag = range.createContextualFragment(html);
+    var root = frag.firstChild;
+    if (before.nextSibling) before.parentNode.insertBefore(frag, before.nextSibling);else before.parentNode.appendChild(frag);
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject ? this.tag.subject : null, domArgs);
+    return root;
+  },
+  replace: function replace(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var root;
+
+    if (parent.nodeType == 1) {
+      parent.innerHTML = html;
+      root = parent.firstChild;
+    } else {
+      if (!parent || parent.nodeType != 9) parent = document;
+      if (!womb || womb.ownerDocument != parent) womb = parent.createElement("div");
+      womb.innerHTML = html;
+      root = womb.firstChild;
+    }
+
+    var domArgs = [root, this.tag.context ? this.tag.context : null, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  append: function append(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    if (!womb || womb.ownerDocument != parent.ownerDocument) womb = parent.ownerDocument.createElement("div");
+    womb.innerHTML = html;
+    root = womb.firstChild;
+
+    while (womb.firstChild) {
+      parent.appendChild(womb.firstChild);
+    }
+
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  render: function render(args, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    return html;
+  }
+};
+},{}],5:[function(require,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+function ArrayIterator(array) {
+  var index = -1;
+
+  this.next = function () {
+    if (++index >= array.length) throw StopIteration;
+    return array[index];
+  };
+}
+
+function StopIteration() {}
+
+exports.makeMarkupRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__link__ = function (tag, code, outputs, args) {
+    if (!tag) {
+      return;
+    }
+
+    if (!tag.tag) {
+      return;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return;
+    }
+
+    tag.tag.compile();
+
+    if (self.resources && tag.tag.resources && tag.tag.resources !== self.resources) {
+      for (var key in tag.tag.resources) {
+        self.resources[key] = tag.tag.resources[key];
+      }
+    }
+
+    var tagOutputs = [];
+    var markupArgs = [code, tag.tag.context ? tag.tag.context : null, args, tagOutputs];
+    markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
+    tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
+    outputs.push(tag);
+    outputs.push(tagOutputs);
+  };
+
+  exports.__escape__ = function (value) {
+    function replaceChars(ch) {
+      switch (ch) {
+        case "<":
+          return "&lt;";
+
+        case ">":
+          return "&gt;";
+
+        case "&":
+          return "&amp;";
+
+        case "'":
+          return "&#39;";
+
+        case '"':
+          return "&quot;";
+      }
+
+      return "?";
+    }
+
+    ;
+    return String(value).replace(/[<>&"']/g, replaceChars);
+  };
+
+  exports.__loop__ = function (iter, outputs, fn) {
+    var iterOuts = [];
+    outputs.push(iterOuts);
+
+    if (iter instanceof Array || typeof iter === "array" || Array.isArray(iter)) {
+      iter = new ArrayIterator(iter);
+    }
+
+    try {
+      if (!iter || !iter.next) {
+        console.error("Cannot iterate loop", iter, _typeof(iter), outputs, fn);
+        throw new Error("Cannot iterate loop as iter.next() method is not defined");
+      }
+
+      while (1) {
+        var value = iter.next();
+        var itemOuts = [0, 0];
+        iterOuts.push(itemOuts);
+        fn.apply(this, [value, itemOuts]);
+      }
+    } catch (exc) {
+      if (exc != StopIteration) throw exc;
+    }
+  };
+
+  exports.__if__ = function (booleanVar, outputs, fn) {
+    var ifControl = [];
+    outputs.push(ifControl);
+
+    if (booleanVar) {
+      ifControl.push(1);
+      fn.apply(this, [ifControl]);
+    } else {
+      ifControl.push(0);
+    }
+  };
+
+  return exports;
+};
+
+exports.makeDOMRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__bind__ = function (object, fn) {
+    return function (event) {
+      return fn.apply(object, [event]);
+    };
+  };
+
+  exports.__link__ = function (node, tag, args) {
+    if (!tag) {
+      return 0;
+    }
+
+    if (!tag.tag) {
+      return 0;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return 0;
+    }
+
+    tag.tag.compile();
+    var domArgs = [node, tag.tag.context ? tag.tag.context : null, 0];
+    domArgs.push.apply(domArgs, tag.tag.domArgs);
+    domArgs.push.apply(domArgs, args);
+    var oo = tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
+    return oo;
+  };
+
+  exports.__loop__ = function (iter, fn) {
+    if (!Array.isArray(iter)) {
+      return 0;
+    }
+
+    var nodeCount = 0;
+
+    for (var i = 0; i < iter.length; ++i) {
+      iter[i][0] = i;
+      iter[i][1] = nodeCount;
+      nodeCount += fn.apply(this, iter[i]);
+    }
+
+    return nodeCount;
+  };
+
+  exports.__if__ = function (control, fn) {
+    if (control && control[0]) {
+      fn.apply(this, [0, control[1]]);
+    } else {}
+  };
+
+  exports.__path__ = function (parent, offset) {
+    var root = parent;
+
+    for (var i = 2; i < arguments.length; ++i) {
+      var index = arguments[i];
+      if (i == 3) index += offset;
+
+      if (index == -1) {
+        parent = parent.parentNode;
+      } else {
+        parent = parent.childNodes[index];
+      }
+    }
+
+    return parent;
+  };
+
+  return exports;
+};
+},{}],6:[function(require,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var FBTrace = {};
+exports.merge = require("deepmerge");
+
+exports.escapeNewLines = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+};
+
+exports.stripNewLines = function (value) {
+  return typeof value == "string" ? value.replace(/[\r\n]/gm, " ") : value;
+};
+
+exports.escapeJS = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n").replace('"', '\\"', "g");
+};
+
+exports.cropString = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  var halfLimit = limit / 2;
+  halfLimit -= 2;
+  if (text.length > limit) return text.substr(0, halfLimit) + alterText + text.substr(text.length - halfLimit);else return text;
+};
+
+exports.cropStringLeft = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  limit -= alterText.length;
+  if (text.length > limit) return alterText + text.substr(text.length - limit);else return text;
+};
+
+exports.hasClass = function (node, name) {
+  if (!node || node.nodeType != 1) return false;else {
+    for (var i = 1; i < arguments.length; ++i) {
+      var name = arguments[i];
+      var re = new RegExp("(^|\\s)" + name + "($|\\s)");
+      if (!re.exec(node.getAttribute("class"))) return false;
+    }
+
+    return true;
+  }
+};
+
+exports.setClass = function (node, name) {
+  if (node && !exports.hasClass(node, name)) node.className += " " + name;
+};
+
+exports.getClassValue = function (node, name) {
+  var re = new RegExp(name + "-([^ ]+)");
+  var m = re.exec(node.className);
+  return m ? m[1] : "";
+};
+
+exports.removeClass = function (node, name) {
+  if (node && node.className) {
+    var index = node.className.indexOf(name);
+
+    if (index >= 0) {
+      var size = name.length;
+      node.className = node.className.substr(0, index - 1) + node.className.substr(index + size);
+    }
+  }
+};
+
+exports.toggleClass = function (elt, name) {
+  if (exports.hasClass(elt, name)) exports.removeClass(elt, name);else exports.setClass(elt, name);
+};
+
+exports.setClassTimed = function (elt, name, context, timeout) {
+  if (!timeout) timeout = 1300;
+  if (elt.__setClassTimeout) context.clearTimeout(elt.__setClassTimeout);else exports.setClass(elt, name);
+
+  if (!exports.isVisible(elt)) {
+    if (elt.__invisibleAtSetPoint) elt.__invisibleAtSetPoint--;else elt.__invisibleAtSetPoint = 5;
+  } else {
+    delete elt.__invisibleAtSetPoint;
+  }
+
+  elt.__setClassTimeout = context.setTimeout(function () {
+    delete elt.__setClassTimeout;
+    if (elt.__invisibleAtSetPoint) exports.setClassTimed(elt, name, context, timeout);else {
+      delete elt.__invisibleAtSetPoint;
+      exports.removeClass(elt, name);
+    }
+  }, timeout);
+};
+
+exports.cancelClassTimed = function (elt, name, context) {
+  if (elt.__setClassTimeout) {
+    exports.removeClass(elt, name);
+    context.clearTimeout(elt.__setClassTimeout);
+    delete elt.__setClassTimeout;
+  }
+};
+
+exports.$ = function (id, doc) {
+  if (doc) return doc.getElementById(id);else return document.getElementById(id);
+};
+
+exports.getChildByClass = function (node) {
+  for (var i = 1; i < arguments.length; ++i) {
+    var className = arguments[i];
+    var child = node.firstChild;
+    node = null;
+
+    for (; child; child = child.nextSibling) {
+      if (exports.hasClass(child, className)) {
+        node = child;
+        break;
+      }
+    }
+  }
+
+  return node;
+};
+
+exports.getAncestorByClass = function (node, className) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (exports.hasClass(parent, className)) return parent;
+  }
+
+  return null;
+};
+
+exports.getElementByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  var elements = node.getElementsByClassName(className);
+  return elements[0];
+};
+
+exports.getElementsByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  return node.getElementsByClassName(className);
+};
+
+exports.getElementsByAttribute = function (node, attrName, attrValue) {
+  function iteratorHelper(node, attrName, attrValue, result) {
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      if (child.getAttribute(attrName) == attrValue) result.push(child);
+      iteratorHelper(child, attrName, attrValue, result);
+    }
+  }
+
+  var result = [];
+  iteratorHelper(node, attrName, attrValue, result);
+  return result;
+};
+
+exports.isAncestor = function (node, potentialAncestor) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (parent == potentialAncestor) return true;
+  }
+
+  return false;
+};
+
+exports.getNextElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.nextSibling;
+  }
+
+  return node;
+};
+
+exports.getPreviousElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.previousSibling;
+  }
+
+  return node;
+};
+
+exports.getBody = function (doc) {
+  if (doc.body) return doc.body;
+  var body = doc.getElementsByTagName("body")[0];
+  if (body) return body;
+  return doc.documentElement;
+};
+
+exports.findNextDown = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (criteria(child)) return child;
+    var next = exports.findNextDown(child, criteria);
+    if (next) return next;
+  }
+};
+
+exports.findPreviousUp = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.lastChild; child; child = child.previousSibling) {
+    var next = exports.findPreviousUp(child, criteria);
+    if (next) return next;
+    if (criteria(child)) return child;
+  }
+};
+
+exports.findNext = function (node, criteria, upOnly, maxRoot) {
+  if (!node) return null;
+
+  if (!upOnly) {
+    var next = exports.findNextDown(node, criteria);
+    if (next) return next;
+  }
+
+  for (var sib = node.nextSibling; sib; sib = sib.nextSibling) {
+    if (criteria(sib)) return sib;
+    var next = exports.findNextDown(sib, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) return exports.findNext(node.parentNode, criteria, true);
+};
+
+exports.findPrevious = function (node, criteria, downOnly, maxRoot) {
+  if (!node) return null;
+
+  for (var sib = node.previousSibling; sib; sib = sib.previousSibling) {
+    var prev = exports.findPreviousUp(sib, criteria);
+    if (prev) return prev;
+    if (criteria(sib)) return sib;
+  }
+
+  if (!downOnly) {
+    var next = exports.findPreviousUp(node, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) {
+    if (criteria(node.parentNode)) return node.parentNode;
+    return exports.findPrevious(node.parentNode, criteria, true);
+  }
+};
+
+exports.getNextByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findNext(root, iter);
+};
+
+exports.getPreviousByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findPrevious(root, iter);
+};
+
+exports.hasChildElements = function (node) {
+  if (node.contentDocument) return true;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (child.nodeType == 1) return true;
+  }
+
+  return false;
+};
+
+exports.isElement = function (o) {
+  try {
+    return o && o instanceof Element;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.isNode = function (o) {
+  try {
+    return o && o instanceof Node;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.cancelEvent = function (event) {
+  event.stopPropagation();
+  event.preventDefault();
+};
+
+exports.isLeftClick = function (event) {
+  return event.button == 0 && exports.noKeyModifiers(event);
+};
+
+exports.isMiddleClick = function (event) {
+  return event.button == 1 && exports.noKeyModifiers(event);
+};
+
+exports.isRightClick = function (event) {
+  return event.button == 2 && exports.noKeyModifiers(event);
+};
+
+exports.noKeyModifiers = function (event) {
+  return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
+};
+
+exports.isControlClick = function (event) {
+  return event.button == 0 && exports.isControl(event);
+};
+
+exports.isShiftClick = function (event) {
+  return event.button == 0 && exports.isShift(event);
+};
+
+exports.isControl = function (event) {
+  return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
+};
+
+exports.isControlShift = function (event) {
+  return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
+};
+
+exports.isShift = function (event) {
+  return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
+};
+
+exports.bind = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments));
+  };
+};
+
+exports.bindFixed = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, args);
+  };
+};
+
+exports.extend = function (l, r) {
+  var newOb = {};
+
+  for (var n in l) {
+    newOb[n] = l[n];
+  }
+
+  for (var n in r) {
+    newOb[n] = r[n];
+  }
+
+  return newOb;
+};
+
+exports.keys = function (map) {
+  var keys = [];
+
+  try {
+    for (var name in map) {
+      keys.push(name);
+    }
+  } catch (exc) {}
+
+  return keys;
+};
+
+exports.values = function (map) {
+  var values = [];
+
+  try {
+    for (var name in map) {
+      try {
+        values.push(map[name]);
+      } catch (exc) {
+        if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+      }
+    }
+  } catch (exc) {
+    if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+  }
+
+  return values;
+};
+
+exports.remove = function (list, item) {
+  for (var i = 0; i < list.length; ++i) {
+    if (list[i] == item) {
+      list.splice(i, 1);
+      break;
+    }
+  }
+};
+
+exports.sliceArray = function (array, index) {
+  var slice = [];
+
+  for (var i = index; i < array.length; ++i) {
+    slice.push(array[i]);
+  }
+
+  return slice;
+};
+
+function cloneArray(array, fn) {
+  var newArray = [];
+  if (fn) for (var i = 0; i < array.length; ++i) {
+    newArray.push(fn(array[i]));
+  } else for (var i = 0; i < array.length; ++i) {
+    newArray.push(array[i]);
+  }
+  return newArray;
+}
+
+function extendArray(array, array2) {
+  var newArray = [];
+  newArray.push.apply(newArray, array);
+  newArray.push.apply(newArray, array2);
+  return newArray;
+}
+
+exports.extendArray = extendArray;
+exports.cloneArray = cloneArray;
+
+function arrayInsert(array, index, other) {
+  for (var i = 0; i < other.length; ++i) {
+    array.splice(i + index, 0, other[i]);
+  }
+
+  return array;
+}
+
+exports.arrayInsert = arrayInsert;
+
+exports.isArrayLike = function (object) {
+  return Object.prototype.toString.call(object) == "[object Array]" || exports.isArguments(object);
+};
+
+exports.isArguments = function (object) {
+  if (Object.prototype.toString.call(object) == "[object Arguments]") return true;
+  if (!_typeof(object) == "object" || !Object.prototype.hasOwnProperty.call(object, 'callee') || !object.callee || Object.prototype.toString.call(object.callee) !== '[object Function]' || typeof object.length != 'number') return false;
+
+  for (var name in object) {
+    if (name === 'callee' || name === 'length') return false;
+  }
+
+  return true;
+};
+},{"deepmerge":7}],7:[function(require,module,exports){
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.deepmerge = factory());
+}(this, (function () { 'use strict';
+
+var isMergeableObject = function isMergeableObject(value) {
+	return isNonNullObject(value)
+		&& !isSpecial(value)
+};
+
+function isNonNullObject(value) {
+	return !!value && typeof value === 'object'
+}
+
+function isSpecial(value) {
+	var stringValue = Object.prototype.toString.call(value);
+
+	return stringValue === '[object RegExp]'
+		|| stringValue === '[object Date]'
+		|| isReactElement(value)
+}
+
+// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
+var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
+var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
+
+function isReactElement(value) {
+	return value.$$typeof === REACT_ELEMENT_TYPE
+}
+
+function emptyTarget(val) {
+	return Array.isArray(val) ? [] : {}
+}
+
+function cloneUnlessOtherwiseSpecified(value, options) {
+	return (options.clone !== false && options.isMergeableObject(value))
+		? deepmerge(emptyTarget(value), value, options)
+		: value
+}
+
+function defaultArrayMerge(target, source, options) {
+	return target.concat(source).map(function(element) {
+		return cloneUnlessOtherwiseSpecified(element, options)
+	})
+}
+
+function mergeObject(target, source, options) {
+	var destination = {};
+	if (options.isMergeableObject(target)) {
+		Object.keys(target).forEach(function(key) {
+			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
+		});
+	}
+	Object.keys(source).forEach(function(key) {
+		if (!options.isMergeableObject(source[key]) || !target[key]) {
+			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
+		} else {
+			destination[key] = deepmerge(target[key], source[key], options);
+		}
+	});
+	return destination
+}
+
+function deepmerge(target, source, options) {
+	options = options || {};
+	options.arrayMerge = options.arrayMerge || defaultArrayMerge;
+	options.isMergeableObject = options.isMergeableObject || isMergeableObject;
+
+	var sourceIsArray = Array.isArray(source);
+	var targetIsArray = Array.isArray(target);
+	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
+
+	if (!sourceAndTargetTypesMatch) {
+		return cloneUnlessOtherwiseSpecified(source, options)
+	} else if (sourceIsArray) {
+		return options.arrayMerge(target, source, options)
+	} else {
+		return mergeObject(target, source, options)
+	}
+}
+
+deepmerge.all = function deepmergeAll(array, options) {
+	if (!Array.isArray(array)) {
+		throw new Error('first argument should be an array')
+	}
+
+	return array.reduce(function(prev, next) {
+		return deepmerge(prev, next, options)
+	}, {})
+};
+
+var deepmerge_1 = deepmerge;
+
+return deepmerge_1;
+
+})));
+
+},{}],8:[function(require,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+exports.PINF = (function (global) {
+
+	if (!global || typeof global !== "object") {
+		throw new Error("No root object scope provided!");
+	}
+
+	// If `PINF` gloabl already exists, don't do anything to change it.
+	if (typeof global.PINF !== "undefined") {
+		return global.PINF;
+	}
+
+	const LOADER = require('./loader');
+
+	const PINF = LOADER.Loader({
+		document: global.document
+	});
+
+	global.PINF = PINF;
+
+	// Attach postMessage handler to listen for sandbox load triggers.
+	// This is useful in Web Workers where only the loader must be loaded and
+	// sandboxes can then be loaded like this:
+	//    worker.postMessage(URIJS("notify://pinf-loader-js/sandbox/load").addSearch("uri", uri).toString())
+	if (typeof global.addEventListener === "function") {
+		global.addEventListener("message", function (event) {
+			var m = null;
+			if (
+				typeof event.data === "string" &&
+				(m = event.data.match(/^notify:\/\/pinf-loader-js\/sandbox\/load\?uri=(.+)$/)) &&
+				(m = decodeURIComponent(m[1])) &&
+				// SECURITY: Only allow URIs that begin with `/` so that scripts may NOT
+				//           be loaded cross-domain this way. If this was allowed one could
+				//           load malicious code simply by posting a message to this window.
+				/^\/[^\/]/.test(m)
+			) {
+				return PINF.sandbox(m, function (sandbox) {
+		            sandbox.main();
+					if (typeof global.postMessage === "function") {
+						global.postMessage(event.data.replace("/load?", "/loaded?"));
+					}
+		        }, function (err) {
+		        	// TODO: Post error back to main frame instead of throwing?
+		        	throw err;
+		        });
+			}
+		}, false);
+	}
+
+	return global.PINF;
+}(
+	typeof window !== "undefined" ?
+		// Used in the browser
+		window :
+		typeof self !== "undefined" ?
+			// Used in web worker
+			self :
+			// No root scope variable found
+			null
+));
+
+},{"./loader":9}],9:[function(require,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+(function (exports) {
+
+// The global `require` for the 'external' (to the loader) environment.
+var Loader = function (global) {
+
+	var loadedBundles = [],
+		// @see https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L69
+		readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 },
+		lastModule = null,
+		headTag = null;
+
+	// For older browsers that don't have `Object.keys()` (Firefox 3.6)
+	function keys(obj) {
+		var keys = [];
+		for (var key in obj) {
+			keys.push(key);
+		}
+		return keys;
+	}
+
+	// TODO: Use 'Object.create()` in modern browsers
+	function create (proto) {
+		function F() {}
+		F.prototype = proto;
+		return new F();
+	};
+
+	function normalizeSandboxArguments (implementation) {
+		return function (programIdentifier, options, loadedCallback, errorCallback) {
+			/*DEBUG*/ if (typeof options === "function" && typeof loadedCallback === "object") {
+			/*DEBUG*/     throw new Error("Callback before options for `require.sandbox(programIdentifier, options, loadedCallback)`");
+			/*DEBUG*/ }
+			if (typeof options === "function" && !loadedCallback && !errorCallback) {
+				loadedCallback = options;
+				options = {};
+			} else
+			if (typeof options === "function" && typeof loadedCallback === "function" && !errorCallback) {
+				errorCallback = loadedCallback;
+				loadedCallback = options;
+				options = {};
+			} else {
+				options = options || {};
+			}
+			implementation(programIdentifier, options, loadedCallback, errorCallback);
+		};
+	}
+
+	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
+		try {
+			// See if we are in a web worker.
+			if (typeof importScripts !== "undefined") {
+				importScripts(uri.replace(/^\/?\{host\}/, ""));
+				return loadedCallback(null);
+			}
+			var document = global.document;
+			/*DEBUG*/ if (!document) {
+			/*DEBUG*/ 	throw new Error("Unable to get reference to 'document'!");
+			/*DEBUG*/ }
+			var location = document.location;
+			if (/^\/?\{host\}\//.test(uri)) {
+				uri = location.protocol + "//" + location.host + uri.replace(/^\/?\{host\}/, "");
+			} else
+			if (/^\/\//.test(uri)) {
+				uri = location.protocol + uri;
+			}
+			if (!headTag) {
+				headTag = document.getElementsByTagName("head")[0];
+			}
+			var element = document.createElement("script");
+			element.type = "text/javascript";
+			element.onload = element.onreadystatechange = function (ev) {
+				ev = ev || global.event;
+				if (ev.type === "load" || readyStates[this.readyState]) {
+					this.onload = this.onreadystatechange = this.onerror = null;
+					loadedCallback(null, function () {
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
+					});
+				}
+			}
+			element.onerror = function (err) {
+				/*DEBUG*/ console.error(err);
+				return loadedCallback(new Error("Error loading '" + uri + "'"));
+			}
+			element.charset = "utf-8";
+			element.async = true;
+			element.src = uri;
+			element = headTag.insertBefore(element, headTag.firstChild);
+		} catch(err) {
+			loadedCallback(err);
+		}
+	}
+
+	// A set of modules working together.
+	var Sandbox = function (sandboxIdentifier, sandboxOptions, loadedCallback) {
+
+		var moduleInitializers = {},
+			initializedModules = {},
+			/*DEBUG*/ bundleIdentifiers = {},
+			packages = {},
+			loadingBundles = {};
+
+		var sandbox = {
+				id: sandboxIdentifier
+			};
+
+		/*DEBUG*/ function logDebug() {
+		/*DEBUG*/ 	if (sandboxOptions.debug !== true) return;
+		/*DEBUG*/ 	// NOTRE: This does not work in google chrome.
+		/*DEBUG*/ 	//console.log.apply(null, arguments);
+		/*DEBUG*/ 	if (arguments.length === 1) {
+		/*DEBUG*/ 		console.log(arguments[0]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 2) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 3) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 4) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
+		/*DEBUG*/ 	}
+		/*DEBUG*/ }
+
+		function rebaseUri (uri) {
+			if (!sandboxOptions.baseUrl) {
+				return uri;
+			}
+			return sandboxOptions.baseUrl + "/" + uri;
+		}
+
+		function load (bundleIdentifier, packageIdentifier, bundleSubPath, loadedCallback) {
+
+			var loadSandboxIdentifier = sandboxIdentifier;
+			var finalBundleIdentifier = null;
+			var moduleIdentifierPrefix = "";
+			var finalPackageIdentifier = "";
+
+			try {
+				if (packageIdentifier !== "") {
+
+					if (/^@bundle:/.test(packageIdentifier)) {
+						var absPackageIdentifier = packageIdentifier;
+						if (/^@bundle:\./.test(absPackageIdentifier)) {
+							absPackageIdentifier = absPackageIdentifier.replace(/^(@bundle:)\./, "$1" + sandboxIdentifier + "/.");
+						}
+						moduleIdentifierPrefix = packageIdentifier;
+						finalPackageIdentifier = packageIdentifier;
+						bundleIdentifier = absPackageIdentifier.replace(/^@bundle:/, "") + ".js";
+						loadSandboxIdentifier = "";
+						finalBundleIdentifier = "@bundle:" + packageIdentifier.replace(/^@bundle:/, "") + ".js";
+					} else {
+						bundleIdentifier = ("/" + packageIdentifier + "/" + bundleIdentifier).replace(/\/+/g, "/");
+					}
+				}
+
+				if (initializedModules[bundleIdentifier]) {
+					// Module is already loaded and initialized.
+					loadedCallback(null, sandbox);
+				} else {
+					// Module is not initialized.
+					if (loadingBundles[bundleIdentifier]) {
+						// Module is already loading.
+						loadingBundles[bundleIdentifier].push(loadedCallback);
+					} else {
+						// Module is not already loading.
+						loadingBundles[bundleIdentifier] = [];
+
+						bundleIdentifier = (loadSandboxIdentifier + bundleSubPath + bundleIdentifier).replace(/\/$/, ".js");
+
+						bundleIdentifier = bundleIdentifier.replace(/\.php\.js$/, ".php");
+
+						if (!finalBundleIdentifier) {
+							finalBundleIdentifier = bundleIdentifier;
+						}
+
+						// Default to our script-injection browser loader.
+						(sandboxOptions.rootBundleLoader || sandboxOptions.load || loadInBrowser)(
+							rebaseUri(bundleIdentifier),
+							function (err, cleanupCallback) {
+								if (err) return loadedCallback(err);
+								// The rootBundleLoader is only applicable for the first load.
+								delete sandboxOptions.rootBundleLoader;
+								finalizeLoad(moduleIdentifierPrefix, finalBundleIdentifier, finalPackageIdentifier, function () {
+									loadedCallback(null, sandbox);
+									if (cleanupCallback) {
+										cleanupCallback();
+									}
+								});
+							},
+							sandboxOptions
+						);
+					}
+				}
+			} catch(err) {
+				loadedCallback(err);
+			}
+		}
+
+		// Called after a bundle has been loaded. Takes the top bundle off the *loading* stack
+		// and makes the new modules available to the sandbox.
+		function finalizeLoad (moduleIdentifierPrefix, bundleIdentifier, packageIdentifier, loadFinalized) {
+			var pending = 0;
+			function finalize () {
+				if (pending !== 0) {
+					return;
+				}
+				if (loadFinalized) loadFinalized();
+			}
+
+			pending += 1;
+
+			// Assume a consistent statically linked set of modules has been memoized.
+			/*DEBUG*/ if (!loadedBundles[0]) {
+			/*DEBUG*/     throw new Error("No bundle memoized for '" + bundleIdentifier + "'! Check the file to ensure it contains JavaScript and that a bundle is memoized against the correct loader instance.");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[bundleIdentifier] = loadedBundles[0][0];
+			var loadedModuleInitializers = loadedBundles[0][1]({
+				id: sandboxIdentifier
+			});
+			var key;
+			for (key in loadedModuleInitializers) {
+
+				var memoizeKey = moduleIdentifierPrefix + key;
+
+				// If we have a package descriptor add it or merge it on top.
+				if (/^[^\/]*\/package.json$/.test(key)) {
+
+					if (sandboxOptions.rewritePackageDescriptor) {
+						loadedModuleInitializers[key][0] = sandboxOptions.rewritePackageDescriptor(loadedModuleInitializers[key][0], memoizeKey);
+					}
+
+					// Load all dependent resources
+					if (loadedModuleInitializers[key][0].mappings) {
+						for (var alias in loadedModuleInitializers[key][0].mappings) {
+							if (/^@script:\/\//.test(loadedModuleInitializers[key][0].mappings[alias])) {
+								pending += 1;
+								loadInBrowser(
+									rebaseUri(loadedModuleInitializers[key][0].mappings[alias].replace(/^@script:/, "")),
+									function () {
+										pending -= 1;
+										finalize();
+									},
+									sandboxOptions
+								);
+							}
+						}
+					}
+
+					// NOTE: Not quite sure if we should allow agumenting package descriptors.
+					//       When doing nested requires using same package we can either add all
+					//		 mappings (included mappings not needed until further down the tree) to
+					//       the first encounter of the package descriptor or add more mappings as
+					//       needed down the road. We currently support both.
+
+					if (moduleInitializers[memoizeKey]) {
+						// TODO: Keep array of bundle identifiers instead of overwriting existing one?
+						//		 Overwriting may change subsequent bundeling behaviour?
+						moduleInitializers[memoizeKey][0] = bundleIdentifier;
+						// Only augment (instead of replace existing values).
+						if (typeof moduleInitializers[memoizeKey][1].main === "undefined") {
+							moduleInitializers[memoizeKey][1].main = loadedModuleInitializers[key][0].main;
+						}
+						if (loadedModuleInitializers[key][0].mappings) {
+							if (!moduleInitializers[memoizeKey][1].mappings) {
+								moduleInitializers[memoizeKey][1].mappings = {};
+							}
+							for (var alias in loadedModuleInitializers[key][0].mappings) {
+								if (typeof moduleInitializers[memoizeKey][1].mappings[alias] === "undefined") {
+									moduleInitializers[memoizeKey][1].mappings[alias] = loadedModuleInitializers[key][0].mappings[alias];
+								}
+							}
+						}
+					} else {
+						moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+					}
+					// Now that we have a [updated] package descriptor, re-initialize it if we have it already in cache.
+					var packageIdentifier = packageIdentifier || key.split("/").shift();
+					if (packages[packageIdentifier]) {
+						packages[packageIdentifier].init();
+					}
+				}
+				// Only add modules that don't already exist!
+				// TODO: Log warning in debug mode if module already exists.
+				if (typeof moduleInitializers[memoizeKey] === "undefined") {
+					moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+				}				
+			}
+			loadedBundles.shift();
+
+			pending -= 1;
+			finalize();
+
+			return;
+		}
+
+		var Package = function (packageIdentifier) {
+			if (packages[packageIdentifier]) {
+				return packages[packageIdentifier];
+			}
+
+			var pkg = {
+				id: packageIdentifier,
+				descriptor: {},
+				main: "/main.js",
+				mappings: {},
+				directories: {},
+				libPath: ""
+			};
+
+			var parentModule = lastModule;
+
+			pkg.init = function () {
+				var descriptor = (moduleInitializers[packageIdentifier + "/package.json"] && moduleInitializers[packageIdentifier + "/package.json"][1]) || {};
+				if (descriptor) {
+					pkg.descriptor = descriptor;
+					if (typeof descriptor.main === "string") {
+						pkg.main = descriptor.main;
+					}
+					pkg.mappings = descriptor.mappings || pkg.mappings;
+					pkg.directories = descriptor.directories || pkg.directories;
+					// NOTE: We need `lib` directory support so that the source directory structure can be mapped
+					//       into the bundle structure without modification. If this is not done, a module doing a relative require
+					//       for a resource outside of the lib directory will not find the file.
+					pkg.libPath = (typeof pkg.directories.lib !== "undefined" && pkg.directories.lib != "") ? pkg.directories.lib + "/" : pkg.libPath;
+				}
+			}
+			pkg.init();
+
+			function normalizeIdentifier (identifier) {
+				// If we have a period (".") in the basename we want an absolute path from
+				// the root of the package. Otherwise a relative path to the "lib" directory.
+				if (identifier.split("/").pop().indexOf(".") === -1) {
+					// We have a module relative to the "lib" directory of the package.
+					identifier = identifier + ".js";
+				} else
+				if (!/^\//.test(identifier)) {
+					// We want an absolute path for the module from the root of the package.
+					identifier = "/" + identifier;
+				}
+				return identifier;
+			}
+
+			var Module = function (moduleIdentifier, parentModule) {
+
+				var moduleIdentifierSegment = null;
+				if (/^@bundle:/.test(moduleIdentifier)) {
+					moduleIdentifierSegment = moduleIdentifier.replace(packageIdentifier, "").replace(/\/[^\/]*$/, "").split("/");
+				} else {
+					moduleIdentifierSegment = moduleIdentifier.replace(/\/[^\/]*$/, "").split("/");
+				}
+
+				var module = {
+						id: moduleIdentifier,
+						exports: {},
+						parentModule: parentModule,
+						bundle: null,
+						pkg: packageIdentifier
+					};
+
+				function resolveIdentifier (identifier) {
+					if (/\/$/.test(identifier)) {
+						identifier += "index";
+					}
+					lastModule = module;
+					// Check for plugin prefix.
+					var plugin = null;
+					if (/^[^!]*!/.test(identifier)) {
+						var m = identifier.match(/^([^!]*)!(.+)$/);
+						identifier = m[2];
+						plugin = m[1];
+					}
+					function pluginify (id) {
+						if (!plugin) return id;
+						id = new String(id);
+						id.plugin = plugin;
+						return id;
+					}
+					// Check for relative module path to module within same package.
+					if (/^\./.test(identifier)) {
+						var segments = identifier.replace(/^\.\//, "").split("../");
+						identifier = "/" + moduleIdentifierSegment.slice(1, moduleIdentifierSegment.length-segments.length+1).concat(segments[segments.length-1]).join("/");
+						if (identifier === "/.") {
+							return [pkg, pluginify("")];
+						}
+						return [pkg, pluginify(normalizeIdentifier(identifier.replace(/\/\.$/, "/")))];
+					}
+					var splitIdentifier = identifier.split("/");
+					// Check for mapped module path to module within mapped package.
+					if (typeof pkg.mappings[splitIdentifier[0]] !== "undefined") {
+						return [Package(pkg.mappings[splitIdentifier[0]]), pluginify((splitIdentifier.length > 1)?normalizeIdentifier(splitIdentifier.slice(1).join("/")):"")];
+					}
+					/*DEBUG*/ if (!moduleInitializers["/" + normalizeIdentifier(identifier)]) {
+					/*DEBUG*/     throw new Error("Descriptor for package '" + pkg.id + "' in sandbox '" + sandbox.id + "' does not declare 'mappings[\"" + splitIdentifier[0] + "\"]' property nor does sandbox have module memoized at '" + "/" + normalizeIdentifier(identifier) + "' needed to satisfy module path '" + identifier + "' in module '" + moduleIdentifier + "'!");
+					/*DEBUG*/ }
+					return [Package(""), pluginify("/" + normalizeIdentifier(identifier))];
+				}
+
+				// Statically link a module and its dependencies
+				module.require = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require(identifier[1]).exports;
+				};
+
+				module.require.supports = [
+					"ucjs-pinf-0"
+				];
+
+				module.require.id = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require.id(identifier[1]);
+				};
+
+				module.require.async = function (identifier, loadedCallback, errorCallback) {
+					identifier = resolveIdentifier(identifier);					
+					var mi = moduleIdentifier;
+					if (/^\//.test(identifier[0].id)) {
+						mi = "/main.js";
+					}
+					identifier[0].load(identifier[1], module.bundle, function (err, moduleAPI) {
+						if (err) {
+							if (errorCallback) return errorCallback(err);
+							throw err;
+						}
+						loadedCallback(moduleAPI);
+					});
+				};
+
+				module.require.sandbox = normalizeSandboxArguments (function (programIdentifier, options, loadedCallback, errorCallback) {
+					options.load = options.load || sandboxOptions.load;
+					// If the `programIdentifier` is relative it is resolved against the URI of the owning sandbox (not the owning page).
+					if (/^\./.test(programIdentifier))
+					{
+						programIdentifier = sandboxIdentifier + "/" + programIdentifier;
+						// HACK: Temporary hack as zombie (https://github.com/assaf/zombie) does not normalize path before sending to server.
+						programIdentifier = programIdentifier.replace(/\/\.\//g, "/");
+					}
+					return PINF.sandbox(programIdentifier, options, loadedCallback, errorCallback);
+				});
+				module.require.sandbox.id = sandboxIdentifier;
+
+				module.load = function () {
+					module.bundle = moduleInitializers[moduleIdentifier][0];
+					if (typeof moduleInitializers[moduleIdentifier][1] === "function") {
+
+						var moduleInterface = {
+							id: module.id,
+							filename:
+								// The `filename` from the meta info attached to the module.
+								// This is typically where the module was originally found on the filesystem.
+								moduleInitializers[moduleIdentifier][2].filename ||
+								// Fall back to the virtual path of the module in the bundle.
+								// TODO: Insert a delimiter between bundle and module id.
+								(module.bundle.replace(/\.js$/, "") + "/" + module.id).replace(/\/+/g, "/"),
+							exports: {}
+						}
+
+						if (packageIdentifier === "" && pkg.main === moduleIdentifier) {
+							module.require.main = moduleInterface;
+						}
+
+						if (sandboxOptions.onInitModule) {
+							sandboxOptions.onInitModule(moduleInterface, module, pkg, sandbox, {
+								normalizeIdentifier: normalizeIdentifier,
+								resolveIdentifier: resolveIdentifier,
+								finalizeLoad: finalizeLoad,
+								moduleInitializers: moduleInitializers,
+								initializedModules: initializedModules
+							});
+						}
+
+						var exports = moduleInitializers[moduleIdentifier][1].call(exports, module.require, module.exports, moduleInterface);
+						if (
+							typeof moduleInterface.exports !== "undefined" &&
+							(
+								typeof moduleInterface.exports !== "object" ||
+								keys(moduleInterface.exports).length !== 0
+							)
+						) {
+							module.exports = moduleInterface.exports;
+						} else
+						if (typeof exports !== "undefined") {
+							module.exports = exports;
+						}
+					} else
+					if (typeof moduleInitializers[moduleIdentifier][1] === "string") {
+						// TODO: Use more optimal string encoding algorythm to reduce payload size?
+						module.exports = decodeURIComponent(moduleInitializers[moduleIdentifier][1]);
+					} else {
+						module.exports = moduleInitializers[moduleIdentifier][1];
+					}
+				};
+
+				/*DEBUG*/ module.getReport = function () {
+				/*DEBUG*/ 	var exportsCount = 0,
+				/*DEBUG*/ 		key;
+				/*DEBUG*/ 	for (key in module.exports) {
+				/*DEBUG*/ 		exportsCount++;
+				/*DEBUG*/ 	}
+				/*DEBUG*/ 	return {
+				/*DEBUG*/ 		exports: exportsCount
+				/*DEBUG*/ 	};
+				/*DEBUG*/ };
+
+				return module;
+			};
+
+			pkg.load = function (moduleIdentifier, bundleIdentifier, loadedCallback) {
+
+				// If module/bundle to be loaded asynchronously is already memoized we skip the load.
+				if (moduleInitializers[packageIdentifier + (moduleIdentifier || pkg.main)]) {
+					return loadedCallback(null, pkg.require(moduleIdentifier).exports);
+				}
+				var bundleSubPath = bundleIdentifier.substring(sandboxIdentifier.length);
+				load(
+					((!/^\//.test(moduleIdentifier))?"/"+pkg.libPath:"") + moduleIdentifier,
+					packageIdentifier,
+					bundleSubPath.replace(/\.js$/g, ""),
+					function (err) {
+						if (err) return loadedCallback(err);
+						loadedCallback(null, pkg.require(moduleIdentifier).exports);
+					}
+				);
+			}
+
+			pkg.require = function (moduleIdentifier) {
+
+				var plugin = moduleIdentifier.plugin;
+
+				if (moduleIdentifier) {
+					if (!/^\//.test(moduleIdentifier)) {
+						moduleIdentifier = ("/" + ((moduleIdentifier.substring(0, pkg.libPath.length)===pkg.libPath)?"":pkg.libPath)).replace(/\/\.\//, "/") + moduleIdentifier;
+					}
+					moduleIdentifier = packageIdentifier + moduleIdentifier;
+				} else {
+					moduleIdentifier = packageIdentifier + pkg.main;
+				}
+
+				if (
+					!moduleInitializers[moduleIdentifier] &&
+					moduleInitializers[moduleIdentifier.replace(/\.js$/, "/index.js")]
+				) {
+					moduleIdentifier = moduleIdentifier.replace(/\.js$/, "/index.js");
+				}
+
+				// Use a specifically formatted module for requested plugin if available
+				if (
+					plugin &&
+					moduleInitializers[moduleIdentifier + ":" + plugin]
+				) {
+					moduleIdentifier += ":" + plugin;
+				}
+
+				if (!initializedModules[moduleIdentifier]) {
+					/*DEBUG*/ if (!moduleInitializers[moduleIdentifier]) {
+					/*DEBUG*/ 	console.error("[pinf-loader-js]", "moduleInitializers", moduleInitializers);
+					/*DEBUG*/ 	throw new Error("Module '" + moduleIdentifier + "' " + (plugin?"for format '" + plugin + "' ":"") + "not found in sandbox '" + sandbox.id + "'!");
+					/*DEBUG*/ }
+					(initializedModules[moduleIdentifier] = Module(moduleIdentifier, lastModule)).load();
+				}
+
+				var loadingBundlesCallbacks;
+				if (loadingBundles[moduleIdentifier]) {
+					loadingBundlesCallbacks = loadingBundles[moduleIdentifier];
+					delete loadingBundles[moduleIdentifier];
+					for (var i=0 ; i<loadingBundlesCallbacks.length ; i++) {
+						loadingBundlesCallbacks[i](null, sandbox);
+					}
+				}
+
+				// TODO: Do this via plugins registered using sandbox options.
+				// TODO: Cache response so we only process files once.
+
+				var moduleInfo = create(initializedModules[moduleIdentifier]);
+				// RequireJS/AMD international strings plugin using root by default.
+				if (plugin === "i18n") {
+					moduleInfo.exports = moduleInfo.exports.root;
+				}
+
+				return moduleInfo;
+			}
+
+			pkg.require.id = function (moduleIdentifier) {
+				if (!/^\//.test(moduleIdentifier)) {
+					moduleIdentifier = "/" + pkg.libPath + moduleIdentifier;
+				}
+				return (((packageIdentifier !== "")?"/"+packageIdentifier+"/":"") + moduleIdentifier).replace(/\/+/g, "/");
+			}
+
+			/*DEBUG*/ pkg.getReport = function () {
+			/*DEBUG*/ 	return {
+			/*DEBUG*/ 		main: pkg.main,
+			/*DEBUG*/ 		mappings: pkg.mappings,
+			/*DEBUG*/ 		directories: pkg.directories,
+			/*DEBUG*/ 		libPath: pkg.libPath
+			/*DEBUG*/ 	};
+			/*DEBUG*/ }
+
+			if (sandboxOptions.onInitPackage) {
+				sandboxOptions.onInitPackage(pkg, sandbox, {
+					normalizeIdentifier: normalizeIdentifier,
+					finalizeLoad: finalizeLoad,
+					moduleInitializers: moduleInitializers,
+					initializedModules: initializedModules
+				});
+			}
+
+			packages[packageIdentifier] = pkg;
+
+			return pkg;
+		}
+
+		// Get a module and initialize it (statically link its dependencies) if it is not already so
+		sandbox.require = function (moduleIdentifier) {
+			return Package("").require(moduleIdentifier).exports;
+		}
+
+		// Call the 'main' module of the program
+		sandbox.boot = function () {
+			/*DEBUG*/ if (typeof Package("").main !== "string") {
+			/*DEBUG*/ 	throw new Error("No 'main' property declared in '/package.json' in sandbox '" + sandbox.id + "'!");
+			/*DEBUG*/ }
+			return sandbox.require(Package("").main);
+		};
+
+		// Call the 'main' exported function of the main' module of the program
+		sandbox.main = function () {
+			var exports = sandbox.boot();
+			return ((exports.main)?exports.main.apply(null, arguments):exports);
+		};
+
+		/*DEBUG*/ sandbox.getReport = function () {
+		/*DEBUG*/ 	var report = {
+		/*DEBUG*/ 			bundles: {},
+		/*DEBUG*/ 			packages: {},
+		/*DEBUG*/ 			modules: {}
+		/*DEBUG*/ 		},
+		/*DEBUG*/ 		key;
+		/*DEBUG*/ 	for (key in bundleIdentifiers) {
+		/*DEBUG*/ 		report.bundles[key] = bundleIdentifiers[key];
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in packages) {
+		/*DEBUG*/ 		report.packages[key] = packages[key].getReport();
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in moduleInitializers) {
+		/*DEBUG*/ 		if (initializedModules[key]) {
+		/*DEBUG*/ 			report.modules[key] = initializedModules[key].getReport();
+		/*DEBUG*/ 		} else {
+		/*DEBUG*/ 			report.modules[key] = {};
+		/*DEBUG*/ 		}
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	return report;
+		/*DEBUG*/ }
+		/*DEBUG*/ sandbox.reset = function () {
+		/*DEBUG*/   moduleInitializers = {};
+		/*DEBUG*/   initializedModules = {};
+		/*DEBUG*/   bundleIdentifiers = {};
+		/*DEBUG*/   packages = {};
+		/*DEBUG*/   loadingBundles = {};
+		/*DEBUG*/ }
+
+		load((sandboxIdentifier.indexOf("?") === -1) ? ".js" : "", "", "", loadedCallback);
+
+		return sandbox;
+	};
+
+	var
+		/*DEBUG*/ bundleIdentifiers = {},
+		sandboxes = {};
+
+	var Require = function (bundle) {
+		var self = this;
+
+		// Address a specific sandbox or currently loading sandbox if initial load.
+		var bundleHandler = function (uid, callback) {
+			/*DEBUG*/ if (uid && bundleIdentifiers[uid]) {
+			/*DEBUG*/ 	throw new Error("You cannot split require.bundle(UID) calls where UID is constant!");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[uid] = true;
+			loadedBundles.push([uid, function (sandbox) {
+				var moduleInitializers = {},
+					req = new Require(uid);
+				delete req.bundle;
+				req.sandbox = sandbox;
+				// Store raw module in loading bundle
+				req.memoize = function (moduleIdentifier, moduleInitializer, moduleMeta) {
+					moduleInitializers[
+						moduleIdentifier +
+						// NOTE: This feature may be elevated to a new function argument to 'memoize' if it proves to be prevalent.
+						(
+							(
+								moduleMeta &&
+								moduleMeta.variation
+							) ? ":" + moduleMeta.variation : ""
+						)
+					] = [moduleInitializer, moduleMeta || {}];
+				}
+				callback(req, global || null);
+				return moduleInitializers;
+			}]);
+		}
+		var activeBundleHandler = bundleHandler;
+		this.bundle = function () {
+			return activeBundleHandler.apply(null, arguments);
+		}
+		this.setActiveBundleHandler = function (handler) {
+			var oldHandler = activeBundleHandler;
+			activeBundleHandler = handler;
+			return oldHandler;
+		}
+	}
+
+	var PINF = new Require();
+
+	// TODO: @see URL_TO_SPEC
+	PINF.supports = [
+		"ucjs-pinf-0"
+	];
+
+	// Create a new environment to memoize modules to.
+	// If relative, the `programIdentifier` is resolved against the URI of the owning page (this is only for the global require).
+	PINF.sandbox = normalizeSandboxArguments(function (programIdentifier, options, loadedCallback, errorCallback) {
+		if (typeof programIdentifier === "function") {
+			options = options || {};
+			var bundle = programIdentifier;
+			var fallbackLoad = options.load || loadInBrowser;
+			options.load = function (uri, loadedCallback) {
+				if (uri === (programIdentifier + ".js")) {
+					PINF.bundle("", bundle);
+					loadedCallback(null);
+					return;
+				}
+				return fallbackLoad(uri, loadedCallback, options);
+			}
+			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
+		}
+		var sandboxIdentifier = programIdentifier.replace(/\.js$/, "");
+		return sandboxes[sandboxIdentifier] = Sandbox(sandboxIdentifier, options, function (err, sandbox) {
+			if (err) {
+				if (errorCallback) return errorCallback(err);
+				throw err;
+			}
+			loadedCallback(sandbox);
+		});
+	});
+
+	PINF.Loader = Loader;
+
+	/*DEBUG*/ PINF.getReport = function () {
+	/*DEBUG*/ 	var report = {
+	/*DEBUG*/ 			sandboxes: {}
+	/*DEBUG*/ 		};
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		report.sandboxes[key] = sandboxes[key].getReport();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	return report;
+	/*DEBUG*/ }
+	/*DEBUG*/ PINF.reset = function () {
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		sandboxes[key].reset();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	sandboxes = {};
+	/*DEBUG*/ 	bundleIdentifiers = {};
+	/*DEBUG*/ 	loadedBundles = [];
+	/*DEBUG*/ }
+
+	return PINF;
+}
+
+if (exports) exports.Loader = Loader;
+
+})(typeof exports !== "undefined" ? exports : null);
+
+},{}]},{},[1])(1)
+});
+
+})((typeof require !== "undefined" && require) || undefined, (typeof exports !== "undefined" && exports) || undefined, (typeof module !== "undefined" && module) || undefined))
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate-eval.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate-eval.js
new file mode 100644
index 0000000..53215b8
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate-eval.js
@@ -0,0 +1,2497 @@
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_require_,module,exports){
+
+module.exports = _require_("./domplate");
+module.exports.domplate.EVAL = _require_("./explicit-unsafe-eval");
+},{"./domplate":2,"./explicit-unsafe-eval":3}],2:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var RT = _require_("./rt");
+
+var Renderer = exports.Renderer = _require_("./renderer").Renderer;
+
+function Domplate(exports) {
+  exports.util = _require_("./util");
+
+  exports.loadStyle = function (uri, baseUrl) {
+    var WINDOW = window;
+
+    if (typeof baseUrl === 'undefined' && WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
+      uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
+    } else if (typeof baseUrl !== 'undefined') {
+      uri = [baseUrl, uri].join("/").replace(/\/\.?\//g, "/");
+    }
+
+    return new Promise(function (resolve, reject) {
+      var link = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "link") : window.document.createElement("link");
+      link.rel = "stylesheet";
+      link.href = uri;
+
+      link.onload = function () {
+        resolve();
+      };
+
+      var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+      head.appendChild(link);
+    });
+  };
+
+  exports.EVAL = {
+    compileMarkup: function compileMarkup(code, context) {
+      return context.compiled(context);
+    },
+    compileDOM: function compileDOM(code, context) {
+      return context.compiled(context);
+    }
+  };
+  exports.tags = {};
+  exports.tags._domplate_ = exports;
+
+  var DomplateTag = exports.DomplateTag = function DomplateTag(tagName) {
+    this.tagName = tagName;
+  };
+
+  function DomplateEmbed() {}
+
+  function DomplateLoop() {}
+
+  function DomplateIf() {}
+
+  function copyArray(oldArray) {
+    var ary = [];
+    if (oldArray) for (var i = 0; i < oldArray.length; ++i) {
+      ary.push(oldArray[i]);
+    }
+    return ary;
+  }
+
+  function copyObject(l, r) {
+    var m = {};
+    extend(m, l);
+    extend(m, r);
+    return m;
+  }
+
+  function extend(l, r) {
+    for (var n in r) {
+      l[n] = r[n];
+    }
+  }
+
+  var womb = null;
+
+  var domplate = exports.domplate = function () {
+    var lastSubject;
+
+    for (var i = 0; i < arguments.length; ++i) {
+      lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
+    }
+
+    for (var name in lastSubject) {
+      var val = lastSubject[name];
+      if (isTag(val)) val.tag.subject = lastSubject;
+    }
+
+    return lastSubject;
+  };
+
+  domplate.context = function (context, fn) {
+    var lastContext = domplate.lastContext;
+    domplate.topContext = context;
+    fn.apply(context);
+    domplate.topContext = lastContext;
+  };
+
+  exports.tags.TAG = function () {
+    var embed = new DomplateEmbed();
+    return embed.merge(arguments);
+  };
+
+  exports.tags.FOR = domplate.FOR = function () {
+    var loop = new DomplateLoop();
+    return loop.merge(arguments);
+  };
+
+  exports.tags.IF = domplate.IF = function () {
+    var loop = new DomplateIf();
+    return loop.merge(arguments);
+  };
+
+  DomplateTag.prototype = {
+    merge: function merge(args, oldTag) {
+      if (oldTag) this.tagName = oldTag.tagName;
+      this.context = oldTag ? oldTag.context : null;
+      this.subject = oldTag ? oldTag.subject : null;
+      this.attrs = oldTag ? copyObject(oldTag.attrs) : {};
+      this.classes = oldTag ? copyObject(oldTag.classes) : {};
+      this.props = oldTag ? copyObject(oldTag.props) : null;
+      this.listeners = oldTag ? copyArray(oldTag.listeners) : null;
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args.length ? args[0] : null;
+      var hasAttrs = _typeof(attrs) == "object" && !isTag(attrs);
+      this.resources = {};
+      this.children = [];
+      if (domplate.topContext) this.context = domplate.topContext;
+      if (args.length) parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
+      if (hasAttrs) this.parseAttrs(attrs);
+      return creator(this, DomplateTag);
+    },
+    parseAttrs: function parseAttrs(args) {
+      for (var name in args) {
+        var val = parseValue(args[name]);
+        readPartNames(val, this.vars);
+
+        if (name.indexOf("on") === 0) {
+          var eventName = name.substr(2);
+          if (!this.listeners) this.listeners = [];
+          this.listeners.push(eventName, val);
+        } else if (name[0] === "_" && name[1] !== "_") {
+          var propName = name.substr(1);
+          if (!this.props) this.props = {};
+          this.props[propName] = val;
+        } else if (name[0] === "$") {
+          var className = name.substr(1);
+          if (!this.classes) this.classes = {};
+          this.classes[className] = val;
+        } else {
+          if (name === "class" && this.attrs.hasOwnProperty(name)) this.attrs[name] += " " + val;else this.attrs[name] = val;
+        }
+      }
+    },
+    compile: function compile() {
+      if (this.renderMarkup) {
+        return;
+      }
+
+      if (this.subject._resources) {
+        this.resources = this.subject._resources();
+      }
+
+      this.compileMarkup();
+      this.compileDOM();
+    },
+    compileMarkup: function compileMarkup() {
+      this.markupArgs = [];
+      var topBlock = [],
+          topOuts = [],
+          blocks = [],
+          info = {
+        args: this.markupArgs,
+        argIndex: 0
+      };
+      this.generateMarkup(topBlock, topOuts, blocks, info, true);
+      this.addCode(topBlock, topOuts, blocks);
+      var fnBlock = ['(function (__code__, __context__, __in__, __out__'];
+
+      for (var i = 0; i < info.argIndex; ++i) {
+        fnBlock.push(', s', i);
+      }
+
+      fnBlock.push(') {');
+      if (this.subject) fnBlock.push('  with (this) {');
+      if (this.context) fnBlock.push('  with (__context__) {');
+      fnBlock.push('  with (__in__) {');
+      fnBlock.push.apply(fnBlock, blocks);
+      if (this.subject) fnBlock.push('  }');
+      if (this.context) fnBlock.push('  }');
+      fnBlock.push('}})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderMarkup = exports.EVAL.compileMarkup(js, RT.makeMarkupRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__markup
+      }));
+    },
+    getVarNames: function getVarNames(args) {
+      if (this.vars) args.push.apply(args, this.vars);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.getVarNames(args);else if (child instanceof Parts) {
+          for (var i = 0; i < child.parts.length; ++i) {
+            if (child.parts[i] instanceof Variables) {
+              var name = child.parts[i].names[0];
+              var names = name.split(".");
+              args.push(names[0]);
+            }
+          }
+        }
+      }
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info, topNode) {
+      topBlock.push(',"<', this.tagName, '"');
+
+      if (topNode) {
+        if (this.subject.__dbid) this.attrs['__dbid'] = this.subject.__dbid;
+        if (this.subject.__dtid) this.attrs['__dtid'] = this.subject.__dtid;
+      }
+
+      for (var name in this.attrs) {
+        if (name != "class") {
+          var val = this.attrs[name];
+          topBlock.push(', " ', name, '=\\""');
+          addParts(val, ',', topBlock, info, true);
+          topBlock.push(', "\\""');
+        }
+      }
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          readPartNames(this.listeners[i + 1], topOuts);
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          readPartNames(this.props[name], topOuts);
+        }
+      }
+
+      if (this.attrs.class || this.classes && Object.keys(this.classes).length > 0) {
+        topBlock.push(', " class=\\""');
+        if (this.attrs.hasOwnProperty("class")) addParts(this.attrs["class"], ',', topBlock, info, true);
+        topBlock.push(', " "');
+
+        for (var name in this.classes) {
+          topBlock.push(', (');
+          addParts(this.classes[name], '', topBlock, info);
+          topBlock.push(' ? "', name, '" + " " : "")');
+        }
+
+        topBlock.push(', "\\""');
+      }
+
+      if (this.tagName == "br") {
+        topBlock.push(',"/>"');
+      } else {
+        topBlock.push(',">"');
+        this.generateChildMarkup(topBlock, topOuts, blocks, info);
+        topBlock.push(',"</', this.tagName, '>"');
+      }
+    },
+    generateChildMarkup: function generateChildMarkup(topBlock, topOuts, blocks, info) {
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.generateMarkup(topBlock, topOuts, blocks, info);else addParts(child, ',', topBlock, info, true);
+      }
+    },
+    addCode: function addCode(topBlock, topOuts, blocks) {
+      if (topBlock.length) blocks.push('    __code__.push(""', topBlock.join(""), ');');
+      if (topOuts.length) blocks.push('__out__.push(', topOuts.join(","), ');');
+      topBlock.splice(0, topBlock.length);
+      topOuts.splice(0, topOuts.length);
+    },
+    addLocals: function addLocals(blocks) {
+      var varNames = [];
+      this.getVarNames(varNames);
+      var map = {};
+
+      for (var i = 0; i < varNames.length; ++i) {
+        var name = varNames[i];
+        if (map.hasOwnProperty(name)) continue;
+        map[name] = 1;
+        var names = name.split(".");
+        blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';');
+      }
+    },
+    compileDOM: function compileDOM() {
+      var path = [];
+      var blocks = [];
+      this.domArgs = [];
+      path.embedIndex = 0;
+      path.loopIndex = 0;
+      path.ifIndex = 0;
+      path.staticIndex = 0;
+      path.renderIndex = 0;
+      var nodeCount = this.generateDOM(path, blocks, this.domArgs);
+      var fnBlock = ['(function (root, context, o'];
+
+      for (var i = 0; i < path.staticIndex; ++i) {
+        fnBlock.push(', ', 's' + i);
+      }
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        fnBlock.push(', ', 'd' + i);
+      }
+
+      fnBlock.push(') {');
+
+      for (var i = 0; i < path.loopIndex; ++i) {
+        fnBlock.push('  var l', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.ifIndex; ++i) {
+        fnBlock.push('  var if_', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.embedIndex; ++i) {
+        fnBlock.push('  var e', i, ' = 0;');
+      }
+
+      if (this.subject) {
+        fnBlock.push('  with (this) {');
+      }
+
+      if (this.context) {
+        fnBlock.push('    with (context) {');
+      }
+
+      fnBlock.push(blocks.join(""));
+      if (this.context) fnBlock.push('    }');
+      if (this.subject) fnBlock.push('  }');
+      fnBlock.push('  return ', nodeCount, ';');
+      fnBlock.push('})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderDOM = exports.EVAL.compileDOM(js, RT.makeDOMRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__dom
+      }));
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      if (this.listeners || this.props) this.generateNodePath(path, blocks);
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          var val = this.listeners[i + 1];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);');
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          var val = this.props[name];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.', name, ' = ', arg, ';');
+        }
+      }
+
+      this.generateChildDOM(path, blocks, args);
+      return 1;
+    },
+    generateNodePath: function generateNodePath(path, blocks) {
+      blocks.push("        node = __path__(root, o");
+
+      for (var i = 0; i < path.length; ++i) {
+        blocks.push(",", path[i]);
+      }
+
+      blocks.push(");");
+    },
+    generateChildDOM: function generateChildDOM(path, blocks, args) {
+      path.push(0);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) path[path.length - 1] += '+' + child.tag.generateDOM(path, blocks, args);else path[path.length - 1] += '+1';
+      }
+
+      path.pop();
+    }
+  };
+  DomplateEmbed.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.value = oldTag ? oldTag.value : parseValue(args[0]);
+      this.attrs = oldTag ? oldTag.attrs : {};
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args[1];
+
+      for (var name in attrs) {
+        var val = parseValue(attrs[name]);
+        this.attrs[name] = val;
+        readPartNames(val, this.vars);
+      }
+
+      var retval = creator(this, DomplateEmbed);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.value instanceof Parts) names.push(this.value.parts[0].name);
+      if (this.vars) names.push.apply(names, this.vars);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('__link__(');
+      addParts(this.value, '', blocks, info);
+      blocks.push(', __code__, __out__, {');
+      var lastName = null;
+
+      for (var name in this.attrs) {
+        if (lastName) blocks.push(',');
+        lastName = name;
+        var val = this.attrs[name];
+        blocks.push('"', name, '":');
+        addParts(val, '', blocks, info);
+      }
+
+      blocks.push('});');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var embedName = 'e' + path.embedIndex++;
+      this.generateNodePath(path, blocks);
+      var valueName = 'd' + path.renderIndex++;
+      var argsName = 'd' + path.renderIndex++;
+      blocks.push('        ', embedName + ' = __link__(node, ', valueName, ', ', argsName, ');');
+      return embedName;
+    }
+  });
+  DomplateLoop.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.varName = oldTag ? oldTag.varName : args[0];
+      this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 2);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateLoop);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.iter instanceof Parts) names.push(this.iter.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var iterName;
+
+      if (this.iter instanceof Parts) {
+        var part = this.iter.parts[0];
+        iterName = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            iterName = part.format[i] + "(" + iterName + ")";
+          }
+        }
+      } else {
+        iterName = this.iter;
+      }
+
+      blocks.push('    __loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('    }]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var iterName = 'd' + path.renderIndex++;
+      var counterName = 'i' + path.loopIndex;
+      var loopName = 'l' + path.loopIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+      var basePath = path[path.length - 1];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        path[path.length - 1] = basePath + '+' + loopName + '+' + nodeCount;
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      path[path.length - 1] = basePath + '+' + loopName;
+      blocks.push('      ', loopName, ' = __loop__.apply(this, [', iterName, ', function(', counterName, ',', loopName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('        return ', nodeCount, ';');
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return loopName;
+    }
+  });
+  DomplateIf.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.booleanVar = oldTag ? oldTag.booleanVar : parseValue(args[0]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 1);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateIf);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.booleanVar instanceof Parts) names.push(this.booleanVar.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var expr;
+
+      if (this.booleanVar instanceof Parts) {
+        var part = this.booleanVar.parts[0];
+        expr = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            expr = part.format[i] + "(" + expr + ")";
+          }
+        }
+      } else {
+        expr = this.booleanVar;
+      }
+
+      blocks.push('__if__.apply(this, [', expr, ', __out__, function(__out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('}]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var controlName = 'd' + path.renderIndex++;
+      var ifName = 'if_' + path.ifIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      blocks.push('      ', ifName, ' = __if__.apply(this, [', controlName, ', function(', ifName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return controlName;
+    }
+  });
+
+  function Variables(names, format) {
+    this.names = names;
+    this.format = format;
+  }
+
+  function Parts(parts) {
+    this.parts = parts;
+  }
+
+  function parseParts(str) {
+    var index = 0;
+    var parts = [];
+    var m;
+    var re = /\$([_A-Za-z][$_A-Za-z0-9.,|]*)/g;
+
+    while (m = re.exec(str)) {
+      var pre = str.substr(index, re.lastIndex - m[0].length - index);
+      if (pre) parts.push(pre);
+      var segs = m[1].split("|");
+      var vars = segs[0].split(",$");
+      parts.push(new Variables(vars, segs.splice(1)));
+      index = re.lastIndex;
+    }
+
+    if (!index) {
+      return str;
+    }
+
+    var post = str.substr(index);
+    if (post) parts.push(post);
+    var retval = new Parts(parts);
+    return retval;
+  }
+
+  function parseValue(val) {
+    return typeof val == 'string' ? parseParts(val) : val;
+  }
+
+  function parseChildren(args, offset, vars, children) {
+    for (var i = offset; i < args.length; ++i) {
+      var val = parseValue(args[i]);
+      children.push(val);
+      readPartNames(val, vars);
+    }
+  }
+
+  function readPartNames(val, vars) {
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+        if (part instanceof Variables) vars.push(part.names[0]);
+      }
+    }
+  }
+
+  function generateArg(val, path, args) {
+    if (val instanceof Parts) {
+      var vals = [];
+
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var varName = 'd' + path.renderIndex++;
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              varName = part.format[j] + '(' + varName + ')';
+            }
+          }
+
+          vals.push(varName);
+        } else vals.push('"' + part.replace(/"/g, '\\"') + '"');
+      }
+
+      return vals.join('+');
+    } else {
+      args.push(val);
+      return 's' + path.staticIndex++;
+    }
+  }
+
+  function addParts(val, delim, block, info, escapeIt) {
+    var vals = [];
+
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var partName = part.names.join(",");
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              partName = part.format[j] + "(" + partName + ")";
+            }
+          }
+
+          if (escapeIt) vals.push("__escape__(" + partName + ")");else vals.push(partName);
+        } else vals.push('"' + part + '"');
+      }
+    } else if (isTag(val)) {
+      info.args.push(val);
+      vals.push('s' + info.argIndex++);
+    } else vals.push('"' + val + '"');
+
+    var parts = vals.join(delim);
+    if (parts) block.push(delim, parts);
+  }
+
+  function isTag(obj) {
+    return (typeof obj == "function" || obj instanceof Function) && !!obj.tag;
+  }
+
+  function creator(tag, cons) {
+    var fn = function fn() {
+      var tag = arguments.callee.tag;
+      var cons = arguments.callee.cons;
+      var newTag = new cons();
+      return newTag.merge(arguments, tag);
+    };
+
+    fn.tag = tag;
+    fn.cons = cons;
+    extend(fn, Renderer);
+    return fn;
+  }
+
+  function defineTags() {
+    Array.from(arguments).forEach(function (tagName) {
+      var fnName = tagName.toUpperCase();
+
+      exports.tags[fnName] = function () {
+        var newTag = new this._domplate_.DomplateTag(tagName);
+        return newTag.merge(arguments);
+      };
+    });
+  }
+
+  defineTags("a", "button", "br", "canvas", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select", "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul");
+}
+
+exports.domplate = {};
+Domplate(exports.domplate);
+
+exports.domplate.ensureLoader = function () {
+  if (typeof window !== "undefined" && window.PINF) {
+    return window.PINF;
+  }
+
+  var PINF = _require_("pinf-loader-js/loader.browser");
+
+  return window.PINF;
+};
+
+exports.domplate.loadRep = function (url, options, successCallback, errorCallback) {
+  if (typeof options === "function") {
+    errorCallback = successCallback;
+    successCallback = options;
+    options = {};
+  }
+
+  var PINF = exports.domplate.ensureLoader();
+  return PINF.sandbox(url + ".rep", function (sandbox) {
+    var rep = sandbox.main(exports.domplate, options);
+    successCallback(rep);
+  }, errorCallback);
+};
+},{"./renderer":4,"./rt":5,"./util":6,"pinf-loader-js/loader.browser":8}],3:[function(_require_,module,exports){
+
+exports.compileMarkup = function (code, context) {
+  var DomplateDebug = context.DomplateDebug;
+  var __escape__ = context.__escape__;
+  var __if__ = context.__if__;
+  var __loop__ = context.__loop__;
+  var __link__ = context.__link__;
+
+  if (exports.onMarkupCode) {
+    exports.onMarkupCode(code);
+  }
+
+  return eval(code);
+};
+
+exports.compileDOM = function (code, context) {
+  var DomplateDebug = context.DomplateDebug;
+  var __path__ = context.__path__;
+  var __bind__ = context.__bind__;
+  var __if__ = context.__if__;
+  var __link__ = context.__link__;
+  var __loop__ = context.__loop__;
+
+  if (exports.onDOMCode) {
+    exports.onDOMCode(code);
+  }
+
+  return eval(code);
+};
+},{}],4:[function(_require_,module,exports){
+
+var Renderer = exports.Renderer = {
+  checkDebug: function checkDebug() {},
+  renderHTML: function renderHTML(args, outputs, self) {
+    var code = [];
+    var markupArgs = [code, this.tag.context ? this.tag.context : null, args, outputs];
+    markupArgs.push.apply(markupArgs, this.tag.markupArgs);
+    this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
+
+    if (this.tag.resources && this.tag.subject._resourceListener) {
+      this.tag.subject._resourceListener.register(this.tag.resources);
+    }
+
+    return code.join("");
+  },
+  insertRows: function insertRows(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var table = doc.createElement("table");
+    table.innerHTML = html;
+    var tbody = table.firstChild;
+    var parent = before.localName == "TR" ? before.parentNode : before;
+    var after = before.localName == "TR" ? before.nextSibling : null;
+    var firstRow = tbody.firstChild,
+        lastRow;
+
+    while (tbody.firstChild) {
+      lastRow = tbody.firstChild;
+      if (after) parent.insertBefore(lastRow, after);else parent.appendChild(lastRow);
+    }
+
+    var offset = 0;
+
+    if (before.localName == "TR") {
+      var node = firstRow.parentNode.firstChild;
+
+      for (; node && node != firstRow; node = node.nextSibling) {
+        ++offset;
+      }
+    }
+
+    var domArgs = [firstRow, this.tag.context, offset];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return [firstRow, lastRow];
+  },
+  insertAfter: function insertAfter(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var range = doc.createRange();
+    range.selectNode(doc.body);
+    var frag = range.createContextualFragment(html);
+    var root = frag.firstChild;
+    if (before.nextSibling) before.parentNode.insertBefore(frag, before.nextSibling);else before.parentNode.appendChild(frag);
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject ? this.tag.subject : null, domArgs);
+    return root;
+  },
+  replace: function replace(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var root;
+
+    if (parent.nodeType == 1) {
+      parent.innerHTML = html;
+      root = parent.firstChild;
+    } else {
+      if (!parent || parent.nodeType != 9) parent = document;
+      if (!womb || womb.ownerDocument != parent) womb = parent.createElement("div");
+      womb.innerHTML = html;
+      root = womb.firstChild;
+    }
+
+    var domArgs = [root, this.tag.context ? this.tag.context : null, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  append: function append(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    if (!womb || womb.ownerDocument != parent.ownerDocument) womb = parent.ownerDocument.createElement("div");
+    womb.innerHTML = html;
+    root = womb.firstChild;
+
+    while (womb.firstChild) {
+      parent.appendChild(womb.firstChild);
+    }
+
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  render: function render(args, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    return html;
+  }
+};
+},{}],5:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+function ArrayIterator(array) {
+  var index = -1;
+
+  this.next = function () {
+    if (++index >= array.length) throw StopIteration;
+    return array[index];
+  };
+}
+
+function StopIteration() {}
+
+exports.makeMarkupRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__link__ = function (tag, code, outputs, args) {
+    if (!tag) {
+      return;
+    }
+
+    if (!tag.tag) {
+      return;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return;
+    }
+
+    tag.tag.compile();
+
+    if (self.resources && tag.tag.resources && tag.tag.resources !== self.resources) {
+      for (var key in tag.tag.resources) {
+        self.resources[key] = tag.tag.resources[key];
+      }
+    }
+
+    var tagOutputs = [];
+    var markupArgs = [code, tag.tag.context ? tag.tag.context : null, args, tagOutputs];
+    markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
+    tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
+    outputs.push(tag);
+    outputs.push(tagOutputs);
+  };
+
+  exports.__escape__ = function (value) {
+    function replaceChars(ch) {
+      switch (ch) {
+        case "<":
+          return "&lt;";
+
+        case ">":
+          return "&gt;";
+
+        case "&":
+          return "&amp;";
+
+        case "'":
+          return "&#39;";
+
+        case '"':
+          return "&quot;";
+      }
+
+      return "?";
+    }
+
+    ;
+    return String(value).replace(/[<>&"']/g, replaceChars);
+  };
+
+  exports.__loop__ = function (iter, outputs, fn) {
+    var iterOuts = [];
+    outputs.push(iterOuts);
+
+    if (iter instanceof Array || typeof iter === "array" || Array.isArray(iter)) {
+      iter = new ArrayIterator(iter);
+    }
+
+    try {
+      if (!iter || !iter.next) {
+        console.error("Cannot iterate loop", iter, _typeof(iter), outputs, fn);
+        throw new Error("Cannot iterate loop as iter.next() method is not defined");
+      }
+
+      while (1) {
+        var value = iter.next();
+        var itemOuts = [0, 0];
+        iterOuts.push(itemOuts);
+        fn.apply(this, [value, itemOuts]);
+      }
+    } catch (exc) {
+      if (exc != StopIteration) throw exc;
+    }
+  };
+
+  exports.__if__ = function (booleanVar, outputs, fn) {
+    var ifControl = [];
+    outputs.push(ifControl);
+
+    if (booleanVar) {
+      ifControl.push(1);
+      fn.apply(this, [ifControl]);
+    } else {
+      ifControl.push(0);
+    }
+  };
+
+  return exports;
+};
+
+exports.makeDOMRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__bind__ = function (object, fn) {
+    return function (event) {
+      return fn.apply(object, [event]);
+    };
+  };
+
+  exports.__link__ = function (node, tag, args) {
+    if (!tag) {
+      return 0;
+    }
+
+    if (!tag.tag) {
+      return 0;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return 0;
+    }
+
+    tag.tag.compile();
+    var domArgs = [node, tag.tag.context ? tag.tag.context : null, 0];
+    domArgs.push.apply(domArgs, tag.tag.domArgs);
+    domArgs.push.apply(domArgs, args);
+    var oo = tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
+    return oo;
+  };
+
+  exports.__loop__ = function (iter, fn) {
+    if (!Array.isArray(iter)) {
+      return 0;
+    }
+
+    var nodeCount = 0;
+
+    for (var i = 0; i < iter.length; ++i) {
+      iter[i][0] = i;
+      iter[i][1] = nodeCount;
+      nodeCount += fn.apply(this, iter[i]);
+    }
+
+    return nodeCount;
+  };
+
+  exports.__if__ = function (control, fn) {
+    if (control && control[0]) {
+      fn.apply(this, [0, control[1]]);
+    } else {}
+  };
+
+  exports.__path__ = function (parent, offset) {
+    var root = parent;
+
+    for (var i = 2; i < arguments.length; ++i) {
+      var index = arguments[i];
+      if (i == 3) index += offset;
+
+      if (index == -1) {
+        parent = parent.parentNode;
+      } else {
+        parent = parent.childNodes[index];
+      }
+    }
+
+    return parent;
+  };
+
+  return exports;
+};
+},{}],6:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var FBTrace = {};
+exports.merge = _require_("deepmerge");
+
+exports.escapeNewLines = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+};
+
+exports.stripNewLines = function (value) {
+  return typeof value == "string" ? value.replace(/[\r\n]/gm, " ") : value;
+};
+
+exports.escapeJS = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n").replace('"', '\\"', "g");
+};
+
+exports.cropString = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  var halfLimit = limit / 2;
+  halfLimit -= 2;
+  if (text.length > limit) return text.substr(0, halfLimit) + alterText + text.substr(text.length - halfLimit);else return text;
+};
+
+exports.cropStringLeft = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  limit -= alterText.length;
+  if (text.length > limit) return alterText + text.substr(text.length - limit);else return text;
+};
+
+exports.hasClass = function (node, name) {
+  if (!node || node.nodeType != 1) return false;else {
+    for (var i = 1; i < arguments.length; ++i) {
+      var name = arguments[i];
+      var re = new RegExp("(^|\\s)" + name + "($|\\s)");
+      if (!re.exec(node.getAttribute("class"))) return false;
+    }
+
+    return true;
+  }
+};
+
+exports.setClass = function (node, name) {
+  if (node && !exports.hasClass(node, name)) node.className += " " + name;
+};
+
+exports.getClassValue = function (node, name) {
+  var re = new RegExp(name + "-([^ ]+)");
+  var m = re.exec(node.className);
+  return m ? m[1] : "";
+};
+
+exports.removeClass = function (node, name) {
+  if (node && node.className) {
+    var index = node.className.indexOf(name);
+
+    if (index >= 0) {
+      var size = name.length;
+      node.className = node.className.substr(0, index - 1) + node.className.substr(index + size);
+    }
+  }
+};
+
+exports.toggleClass = function (elt, name) {
+  if (exports.hasClass(elt, name)) exports.removeClass(elt, name);else exports.setClass(elt, name);
+};
+
+exports.setClassTimed = function (elt, name, context, timeout) {
+  if (!timeout) timeout = 1300;
+  if (elt.__setClassTimeout) context.clearTimeout(elt.__setClassTimeout);else exports.setClass(elt, name);
+
+  if (!exports.isVisible(elt)) {
+    if (elt.__invisibleAtSetPoint) elt.__invisibleAtSetPoint--;else elt.__invisibleAtSetPoint = 5;
+  } else {
+    delete elt.__invisibleAtSetPoint;
+  }
+
+  elt.__setClassTimeout = context.setTimeout(function () {
+    delete elt.__setClassTimeout;
+    if (elt.__invisibleAtSetPoint) exports.setClassTimed(elt, name, context, timeout);else {
+      delete elt.__invisibleAtSetPoint;
+      exports.removeClass(elt, name);
+    }
+  }, timeout);
+};
+
+exports.cancelClassTimed = function (elt, name, context) {
+  if (elt.__setClassTimeout) {
+    exports.removeClass(elt, name);
+    context.clearTimeout(elt.__setClassTimeout);
+    delete elt.__setClassTimeout;
+  }
+};
+
+exports.$ = function (id, doc) {
+  if (doc) return doc.getElementById(id);else return document.getElementById(id);
+};
+
+exports.getChildByClass = function (node) {
+  for (var i = 1; i < arguments.length; ++i) {
+    var className = arguments[i];
+    var child = node.firstChild;
+    node = null;
+
+    for (; child; child = child.nextSibling) {
+      if (exports.hasClass(child, className)) {
+        node = child;
+        break;
+      }
+    }
+  }
+
+  return node;
+};
+
+exports.getAncestorByClass = function (node, className) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (exports.hasClass(parent, className)) return parent;
+  }
+
+  return null;
+};
+
+exports.getElementByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  var elements = node.getElementsByClassName(className);
+  return elements[0];
+};
+
+exports.getElementsByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  return node.getElementsByClassName(className);
+};
+
+exports.getElementsByAttribute = function (node, attrName, attrValue) {
+  function iteratorHelper(node, attrName, attrValue, result) {
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      if (child.getAttribute(attrName) == attrValue) result.push(child);
+      iteratorHelper(child, attrName, attrValue, result);
+    }
+  }
+
+  var result = [];
+  iteratorHelper(node, attrName, attrValue, result);
+  return result;
+};
+
+exports.isAncestor = function (node, potentialAncestor) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (parent == potentialAncestor) return true;
+  }
+
+  return false;
+};
+
+exports.getNextElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.nextSibling;
+  }
+
+  return node;
+};
+
+exports.getPreviousElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.previousSibling;
+  }
+
+  return node;
+};
+
+exports.getBody = function (doc) {
+  if (doc.body) return doc.body;
+  var body = doc.getElementsByTagName("body")[0];
+  if (body) return body;
+  return doc.documentElement;
+};
+
+exports.findNextDown = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (criteria(child)) return child;
+    var next = exports.findNextDown(child, criteria);
+    if (next) return next;
+  }
+};
+
+exports.findPreviousUp = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.lastChild; child; child = child.previousSibling) {
+    var next = exports.findPreviousUp(child, criteria);
+    if (next) return next;
+    if (criteria(child)) return child;
+  }
+};
+
+exports.findNext = function (node, criteria, upOnly, maxRoot) {
+  if (!node) return null;
+
+  if (!upOnly) {
+    var next = exports.findNextDown(node, criteria);
+    if (next) return next;
+  }
+
+  for (var sib = node.nextSibling; sib; sib = sib.nextSibling) {
+    if (criteria(sib)) return sib;
+    var next = exports.findNextDown(sib, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) return exports.findNext(node.parentNode, criteria, true);
+};
+
+exports.findPrevious = function (node, criteria, downOnly, maxRoot) {
+  if (!node) return null;
+
+  for (var sib = node.previousSibling; sib; sib = sib.previousSibling) {
+    var prev = exports.findPreviousUp(sib, criteria);
+    if (prev) return prev;
+    if (criteria(sib)) return sib;
+  }
+
+  if (!downOnly) {
+    var next = exports.findPreviousUp(node, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) {
+    if (criteria(node.parentNode)) return node.parentNode;
+    return exports.findPrevious(node.parentNode, criteria, true);
+  }
+};
+
+exports.getNextByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findNext(root, iter);
+};
+
+exports.getPreviousByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findPrevious(root, iter);
+};
+
+exports.hasChildElements = function (node) {
+  if (node.contentDocument) return true;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (child.nodeType == 1) return true;
+  }
+
+  return false;
+};
+
+exports.isElement = function (o) {
+  try {
+    return o && o instanceof Element;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.isNode = function (o) {
+  try {
+    return o && o instanceof Node;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.cancelEvent = function (event) {
+  event.stopPropagation();
+  event.preventDefault();
+};
+
+exports.isLeftClick = function (event) {
+  return event.button == 0 && exports.noKeyModifiers(event);
+};
+
+exports.isMiddleClick = function (event) {
+  return event.button == 1 && exports.noKeyModifiers(event);
+};
+
+exports.isRightClick = function (event) {
+  return event.button == 2 && exports.noKeyModifiers(event);
+};
+
+exports.noKeyModifiers = function (event) {
+  return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
+};
+
+exports.isControlClick = function (event) {
+  return event.button == 0 && exports.isControl(event);
+};
+
+exports.isShiftClick = function (event) {
+  return event.button == 0 && exports.isShift(event);
+};
+
+exports.isControl = function (event) {
+  return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
+};
+
+exports.isControlShift = function (event) {
+  return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
+};
+
+exports.isShift = function (event) {
+  return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
+};
+
+exports.bind = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments));
+  };
+};
+
+exports.bindFixed = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, args);
+  };
+};
+
+exports.extend = function (l, r) {
+  var newOb = {};
+
+  for (var n in l) {
+    newOb[n] = l[n];
+  }
+
+  for (var n in r) {
+    newOb[n] = r[n];
+  }
+
+  return newOb;
+};
+
+exports.keys = function (map) {
+  var keys = [];
+
+  try {
+    for (var name in map) {
+      keys.push(name);
+    }
+  } catch (exc) {}
+
+  return keys;
+};
+
+exports.values = function (map) {
+  var values = [];
+
+  try {
+    for (var name in map) {
+      try {
+        values.push(map[name]);
+      } catch (exc) {
+        if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+      }
+    }
+  } catch (exc) {
+    if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+  }
+
+  return values;
+};
+
+exports.remove = function (list, item) {
+  for (var i = 0; i < list.length; ++i) {
+    if (list[i] == item) {
+      list.splice(i, 1);
+      break;
+    }
+  }
+};
+
+exports.sliceArray = function (array, index) {
+  var slice = [];
+
+  for (var i = index; i < array.length; ++i) {
+    slice.push(array[i]);
+  }
+
+  return slice;
+};
+
+function cloneArray(array, fn) {
+  var newArray = [];
+  if (fn) for (var i = 0; i < array.length; ++i) {
+    newArray.push(fn(array[i]));
+  } else for (var i = 0; i < array.length; ++i) {
+    newArray.push(array[i]);
+  }
+  return newArray;
+}
+
+function extendArray(array, array2) {
+  var newArray = [];
+  newArray.push.apply(newArray, array);
+  newArray.push.apply(newArray, array2);
+  return newArray;
+}
+
+exports.extendArray = extendArray;
+exports.cloneArray = cloneArray;
+
+function arrayInsert(array, index, other) {
+  for (var i = 0; i < other.length; ++i) {
+    array.splice(i + index, 0, other[i]);
+  }
+
+  return array;
+}
+
+exports.arrayInsert = arrayInsert;
+
+exports.isArrayLike = function (object) {
+  return Object.prototype.toString.call(object) == "[object Array]" || exports.isArguments(object);
+};
+
+exports.isArguments = function (object) {
+  if (Object.prototype.toString.call(object) == "[object Arguments]") return true;
+  if (!_typeof(object) == "object" || !Object.prototype.hasOwnProperty.call(object, 'callee') || !object.callee || Object.prototype.toString.call(object.callee) !== '[object Function]' || typeof object.length != 'number') return false;
+
+  for (var name in object) {
+    if (name === 'callee' || name === 'length') return false;
+  }
+
+  return true;
+};
+},{"deepmerge":7}],7:[function(_require_,module,exports){
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.deepmerge = factory());
+}(this, (function () { 'use strict';
+
+var isMergeableObject = function isMergeableObject(value) {
+	return isNonNullObject(value)
+		&& !isSpecial(value)
+};
+
+function isNonNullObject(value) {
+	return !!value && typeof value === 'object'
+}
+
+function isSpecial(value) {
+	var stringValue = Object.prototype.toString.call(value);
+
+	return stringValue === '[object RegExp]'
+		|| stringValue === '[object Date]'
+		|| isReactElement(value)
+}
+
+// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
+var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
+var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
+
+function isReactElement(value) {
+	return value.$$typeof === REACT_ELEMENT_TYPE
+}
+
+function emptyTarget(val) {
+	return Array.isArray(val) ? [] : {}
+}
+
+function cloneUnlessOtherwiseSpecified(value, options) {
+	return (options.clone !== false && options.isMergeableObject(value))
+		? deepmerge(emptyTarget(value), value, options)
+		: value
+}
+
+function defaultArrayMerge(target, source, options) {
+	return target.concat(source).map(function(element) {
+		return cloneUnlessOtherwiseSpecified(element, options)
+	})
+}
+
+function mergeObject(target, source, options) {
+	var destination = {};
+	if (options.isMergeableObject(target)) {
+		Object.keys(target).forEach(function(key) {
+			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
+		});
+	}
+	Object.keys(source).forEach(function(key) {
+		if (!options.isMergeableObject(source[key]) || !target[key]) {
+			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
+		} else {
+			destination[key] = deepmerge(target[key], source[key], options);
+		}
+	});
+	return destination
+}
+
+function deepmerge(target, source, options) {
+	options = options || {};
+	options.arrayMerge = options.arrayMerge || defaultArrayMerge;
+	options.isMergeableObject = options.isMergeableObject || isMergeableObject;
+
+	var sourceIsArray = Array.isArray(source);
+	var targetIsArray = Array.isArray(target);
+	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
+
+	if (!sourceAndTargetTypesMatch) {
+		return cloneUnlessOtherwiseSpecified(source, options)
+	} else if (sourceIsArray) {
+		return options.arrayMerge(target, source, options)
+	} else {
+		return mergeObject(target, source, options)
+	}
+}
+
+deepmerge.all = function deepmergeAll(array, options) {
+	if (!Array.isArray(array)) {
+		throw new Error('first argument should be an array')
+	}
+
+	return array.reduce(function(prev, next) {
+		return deepmerge(prev, next, options)
+	}, {})
+};
+
+var deepmerge_1 = deepmerge;
+
+return deepmerge_1;
+
+})));
+
+},{}],8:[function(_require_,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+exports.PINF = (function (global) {
+
+	if (!global || typeof global !== "object") {
+		throw new Error("No root object scope provided!");
+	}
+
+	// If `PINF` gloabl already exists, don't do anything to change it.
+	if (typeof global.PINF !== "undefined") {
+		return global.PINF;
+	}
+
+	const LOADER = _require_('./loader');
+
+	const PINF = LOADER.Loader({
+		document: global.document
+	});
+
+	global.PINF = PINF;
+
+	// Attach postMessage handler to listen for sandbox load triggers.
+	// This is useful in Web Workers where only the loader must be loaded and
+	// sandboxes can then be loaded like this:
+	//    worker.postMessage(URIJS("notify://pinf-loader-js/sandbox/load").addSearch("uri", uri).toString())
+	if (typeof global.addEventListener === "function") {
+		global.addEventListener("message", function (event) {
+			var m = null;
+			if (
+				typeof event.data === "string" &&
+				(m = event.data.match(/^notify:\/\/pinf-loader-js\/sandbox\/load\?uri=(.+)$/)) &&
+				(m = decodeURIComponent(m[1])) &&
+				// SECURITY: Only allow URIs that begin with `/` so that scripts may NOT
+				//           be loaded cross-domain this way. If this was allowed one could
+				//           load malicious code simply by posting a message to this window.
+				/^\/[^\/]/.test(m)
+			) {
+				return PINF.sandbox(m, function (sandbox) {
+		            sandbox.main();
+					if (typeof global.postMessage === "function") {
+						global.postMessage(event.data.replace("/load?", "/loaded?"));
+					}
+		        }, function (err) {
+		        	// TODO: Post error back to main frame instead of throwing?
+		        	throw err;
+		        });
+			}
+		}, false);
+	}
+
+	return global.PINF;
+}(
+	typeof window !== "undefined" ?
+		// Used in the browser
+		window :
+		typeof self !== "undefined" ?
+			// Used in web worker
+			self :
+			// No root scope variable found
+			null
+));
+
+},{"./loader":9}],9:[function(_require_,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+(function (exports) {
+
+// The global `require` for the 'external' (to the loader) environment.
+var Loader = function (global) {
+
+	var loadedBundles = [],
+		// @see https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L69
+		readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 },
+		lastModule = null,
+		headTag = null;
+
+	// For older browsers that don't have `Object.keys()` (Firefox 3.6)
+	function keys(obj) {
+		var keys = [];
+		for (var key in obj) {
+			keys.push(key);
+		}
+		return keys;
+	}
+
+	// TODO: Use 'Object.create()` in modern browsers
+	function create (proto) {
+		function F() {}
+		F.prototype = proto;
+		return new F();
+	};
+
+	function normalizeSandboxArguments (implementation) {
+		return function (programIdentifier, options, loadedCallback, errorCallback) {
+			/*DEBUG*/ if (typeof options === "function" && typeof loadedCallback === "object") {
+			/*DEBUG*/     throw new Error("Callback before options for `require.sandbox(programIdentifier, options, loadedCallback)`");
+			/*DEBUG*/ }
+			if (typeof options === "function" && !loadedCallback && !errorCallback) {
+				loadedCallback = options;
+				options = {};
+			} else
+			if (typeof options === "function" && typeof loadedCallback === "function" && !errorCallback) {
+				errorCallback = loadedCallback;
+				loadedCallback = options;
+				options = {};
+			} else {
+				options = options || {};
+			}
+			implementation(programIdentifier, options, loadedCallback, errorCallback);
+		};
+	}
+
+	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
+		try {
+			// See if we are in a web worker.
+			if (typeof importScripts !== "undefined") {
+				importScripts(uri.replace(/^\/?\{host\}/, ""));
+				return loadedCallback(null);
+			}
+			var document = global.document;
+			/*DEBUG*/ if (!document) {
+			/*DEBUG*/ 	throw new Error("Unable to get reference to 'document'!");
+			/*DEBUG*/ }
+			var location = document.location;
+			if (/^\/?\{host\}\//.test(uri)) {
+				uri = location.protocol + "//" + location.host + uri.replace(/^\/?\{host\}/, "");
+			} else
+			if (/^\/\//.test(uri)) {
+				uri = location.protocol + uri;
+			}
+			if (!headTag) {
+				headTag = document.getElementsByTagName("head")[0];
+			}
+			var element = document.createElement("script");
+			element.type = "text/javascript";
+			element.onload = element.onreadystatechange = function (ev) {
+				ev = ev || global.event;
+				if (ev.type === "load" || readyStates[this.readyState]) {
+					this.onload = this.onreadystatechange = this.onerror = null;
+					loadedCallback(null, function () {
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
+					});
+				}
+			}
+			element.onerror = function (err) {
+				/*DEBUG*/ console.error(err);
+				return loadedCallback(new Error("Error loading '" + uri + "'"));
+			}
+			element.charset = "utf-8";
+			element.async = true;
+			element.src = uri;
+			element = headTag.insertBefore(element, headTag.firstChild);
+		} catch(err) {
+			loadedCallback(err);
+		}
+	}
+
+	// A set of modules working together.
+	var Sandbox = function (sandboxIdentifier, sandboxOptions, loadedCallback) {
+
+		var moduleInitializers = {},
+			initializedModules = {},
+			/*DEBUG*/ bundleIdentifiers = {},
+			packages = {},
+			loadingBundles = {};
+
+		var sandbox = {
+				id: sandboxIdentifier
+			};
+
+		/*DEBUG*/ function logDebug() {
+		/*DEBUG*/ 	if (sandboxOptions.debug !== true) return;
+		/*DEBUG*/ 	// NOTRE: This does not work in google chrome.
+		/*DEBUG*/ 	//console.log.apply(null, arguments);
+		/*DEBUG*/ 	if (arguments.length === 1) {
+		/*DEBUG*/ 		console.log(arguments[0]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 2) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 3) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 4) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
+		/*DEBUG*/ 	}
+		/*DEBUG*/ }
+
+		function rebaseUri (uri) {
+			if (!sandboxOptions.baseUrl) {
+				return uri;
+			}
+			return sandboxOptions.baseUrl + "/" + uri;
+		}
+
+		function load (bundleIdentifier, packageIdentifier, bundleSubPath, loadedCallback) {
+
+			var loadSandboxIdentifier = sandboxIdentifier;
+			var finalBundleIdentifier = null;
+			var moduleIdentifierPrefix = "";
+			var finalPackageIdentifier = "";
+
+			try {
+				if (packageIdentifier !== "") {
+
+					if (/^@bundle:/.test(packageIdentifier)) {
+						var absPackageIdentifier = packageIdentifier;
+						if (/^@bundle:\./.test(absPackageIdentifier)) {
+							absPackageIdentifier = absPackageIdentifier.replace(/^(@bundle:)\./, "$1" + sandboxIdentifier + "/.");
+						}
+						moduleIdentifierPrefix = packageIdentifier;
+						finalPackageIdentifier = packageIdentifier;
+						bundleIdentifier = absPackageIdentifier.replace(/^@bundle:/, "") + ".js";
+						loadSandboxIdentifier = "";
+						finalBundleIdentifier = "@bundle:" + packageIdentifier.replace(/^@bundle:/, "") + ".js";
+					} else {
+						bundleIdentifier = ("/" + packageIdentifier + "/" + bundleIdentifier).replace(/\/+/g, "/");
+					}
+				}
+
+				if (initializedModules[bundleIdentifier]) {
+					// Module is already loaded and initialized.
+					loadedCallback(null, sandbox);
+				} else {
+					// Module is not initialized.
+					if (loadingBundles[bundleIdentifier]) {
+						// Module is already loading.
+						loadingBundles[bundleIdentifier].push(loadedCallback);
+					} else {
+						// Module is not already loading.
+						loadingBundles[bundleIdentifier] = [];
+
+						bundleIdentifier = (loadSandboxIdentifier + bundleSubPath + bundleIdentifier).replace(/\/$/, ".js");
+
+						bundleIdentifier = bundleIdentifier.replace(/\.php\.js$/, ".php");
+
+						if (!finalBundleIdentifier) {
+							finalBundleIdentifier = bundleIdentifier;
+						}
+
+						// Default to our script-injection browser loader.
+						(sandboxOptions.rootBundleLoader || sandboxOptions.load || loadInBrowser)(
+							rebaseUri(bundleIdentifier),
+							function (err, cleanupCallback) {
+								if (err) return loadedCallback(err);
+								// The rootBundleLoader is only applicable for the first load.
+								delete sandboxOptions.rootBundleLoader;
+								finalizeLoad(moduleIdentifierPrefix, finalBundleIdentifier, finalPackageIdentifier, function () {
+									loadedCallback(null, sandbox);
+									if (cleanupCallback) {
+										cleanupCallback();
+									}
+								});
+							},
+							sandboxOptions
+						);
+					}
+				}
+			} catch(err) {
+				loadedCallback(err);
+			}
+		}
+
+		// Called after a bundle has been loaded. Takes the top bundle off the *loading* stack
+		// and makes the new modules available to the sandbox.
+		function finalizeLoad (moduleIdentifierPrefix, bundleIdentifier, packageIdentifier, loadFinalized) {
+			var pending = 0;
+			function finalize () {
+				if (pending !== 0) {
+					return;
+				}
+				if (loadFinalized) loadFinalized();
+			}
+
+			pending += 1;
+
+			// Assume a consistent statically linked set of modules has been memoized.
+			/*DEBUG*/ if (!loadedBundles[0]) {
+			/*DEBUG*/     throw new Error("No bundle memoized for '" + bundleIdentifier + "'! Check the file to ensure it contains JavaScript and that a bundle is memoized against the correct loader instance.");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[bundleIdentifier] = loadedBundles[0][0];
+			var loadedModuleInitializers = loadedBundles[0][1]({
+				id: sandboxIdentifier
+			});
+			var key;
+			for (key in loadedModuleInitializers) {
+
+				var memoizeKey = moduleIdentifierPrefix + key;
+
+				// If we have a package descriptor add it or merge it on top.
+				if (/^[^\/]*\/package.json$/.test(key)) {
+
+					if (sandboxOptions.rewritePackageDescriptor) {
+						loadedModuleInitializers[key][0] = sandboxOptions.rewritePackageDescriptor(loadedModuleInitializers[key][0], memoizeKey);
+					}
+
+					// Load all dependent resources
+					if (loadedModuleInitializers[key][0].mappings) {
+						for (var alias in loadedModuleInitializers[key][0].mappings) {
+							if (/^@script:\/\//.test(loadedModuleInitializers[key][0].mappings[alias])) {
+								pending += 1;
+								loadInBrowser(
+									rebaseUri(loadedModuleInitializers[key][0].mappings[alias].replace(/^@script:/, "")),
+									function () {
+										pending -= 1;
+										finalize();
+									},
+									sandboxOptions
+								);
+							}
+						}
+					}
+
+					// NOTE: Not quite sure if we should allow agumenting package descriptors.
+					//       When doing nested requires using same package we can either add all
+					//		 mappings (included mappings not needed until further down the tree) to
+					//       the first encounter of the package descriptor or add more mappings as
+					//       needed down the road. We currently support both.
+
+					if (moduleInitializers[memoizeKey]) {
+						// TODO: Keep array of bundle identifiers instead of overwriting existing one?
+						//		 Overwriting may change subsequent bundeling behaviour?
+						moduleInitializers[memoizeKey][0] = bundleIdentifier;
+						// Only augment (instead of replace existing values).
+						if (typeof moduleInitializers[memoizeKey][1].main === "undefined") {
+							moduleInitializers[memoizeKey][1].main = loadedModuleInitializers[key][0].main;
+						}
+						if (loadedModuleInitializers[key][0].mappings) {
+							if (!moduleInitializers[memoizeKey][1].mappings) {
+								moduleInitializers[memoizeKey][1].mappings = {};
+							}
+							for (var alias in loadedModuleInitializers[key][0].mappings) {
+								if (typeof moduleInitializers[memoizeKey][1].mappings[alias] === "undefined") {
+									moduleInitializers[memoizeKey][1].mappings[alias] = loadedModuleInitializers[key][0].mappings[alias];
+								}
+							}
+						}
+					} else {
+						moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+					}
+					// Now that we have a [updated] package descriptor, re-initialize it if we have it already in cache.
+					var packageIdentifier = packageIdentifier || key.split("/").shift();
+					if (packages[packageIdentifier]) {
+						packages[packageIdentifier].init();
+					}
+				}
+				// Only add modules that don't already exist!
+				// TODO: Log warning in debug mode if module already exists.
+				if (typeof moduleInitializers[memoizeKey] === "undefined") {
+					moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+				}				
+			}
+			loadedBundles.shift();
+
+			pending -= 1;
+			finalize();
+
+			return;
+		}
+
+		var Package = function (packageIdentifier) {
+			if (packages[packageIdentifier]) {
+				return packages[packageIdentifier];
+			}
+
+			var pkg = {
+				id: packageIdentifier,
+				descriptor: {},
+				main: "/main.js",
+				mappings: {},
+				directories: {},
+				libPath: ""
+			};
+
+			var parentModule = lastModule;
+
+			pkg.init = function () {
+				var descriptor = (moduleInitializers[packageIdentifier + "/package.json"] && moduleInitializers[packageIdentifier + "/package.json"][1]) || {};
+				if (descriptor) {
+					pkg.descriptor = descriptor;
+					if (typeof descriptor.main === "string") {
+						pkg.main = descriptor.main;
+					}
+					pkg.mappings = descriptor.mappings || pkg.mappings;
+					pkg.directories = descriptor.directories || pkg.directories;
+					// NOTE: We need `lib` directory support so that the source directory structure can be mapped
+					//       into the bundle structure without modification. If this is not done, a module doing a relative _require_
+					//       for a resource outside of the lib directory will not find the file.
+					pkg.libPath = (typeof pkg.directories.lib !== "undefined" && pkg.directories.lib != "") ? pkg.directories.lib + "/" : pkg.libPath;
+				}
+			}
+			pkg.init();
+
+			function normalizeIdentifier (identifier) {
+				// If we have a period (".") in the basename we want an absolute path from
+				// the root of the package. Otherwise a relative path to the "lib" directory.
+				if (identifier.split("/").pop().indexOf(".") === -1) {
+					// We have a module relative to the "lib" directory of the package.
+					identifier = identifier + ".js";
+				} else
+				if (!/^\//.test(identifier)) {
+					// We want an absolute path for the module from the root of the package.
+					identifier = "/" + identifier;
+				}
+				return identifier;
+			}
+
+			var Module = function (moduleIdentifier, parentModule) {
+
+				var moduleIdentifierSegment = null;
+				if (/^@bundle:/.test(moduleIdentifier)) {
+					moduleIdentifierSegment = moduleIdentifier.replace(packageIdentifier, "").replace(/\/[^\/]*$/, "").split("/");
+				} else {
+					moduleIdentifierSegment = moduleIdentifier.replace(/\/[^\/]*$/, "").split("/");
+				}
+
+				var module = {
+						id: moduleIdentifier,
+						exports: {},
+						parentModule: parentModule,
+						bundle: null,
+						pkg: packageIdentifier
+					};
+
+				function resolveIdentifier (identifier) {
+					if (/\/$/.test(identifier)) {
+						identifier += "index";
+					}
+					lastModule = module;
+					// Check for plugin prefix.
+					var plugin = null;
+					if (/^[^!]*!/.test(identifier)) {
+						var m = identifier.match(/^([^!]*)!(.+)$/);
+						identifier = m[2];
+						plugin = m[1];
+					}
+					function pluginify (id) {
+						if (!plugin) return id;
+						id = new String(id);
+						id.plugin = plugin;
+						return id;
+					}
+					// Check for relative module path to module within same package.
+					if (/^\./.test(identifier)) {
+						var segments = identifier.replace(/^\.\//, "").split("../");
+						identifier = "/" + moduleIdentifierSegment.slice(1, moduleIdentifierSegment.length-segments.length+1).concat(segments[segments.length-1]).join("/");
+						if (identifier === "/.") {
+							return [pkg, pluginify("")];
+						}
+						return [pkg, pluginify(normalizeIdentifier(identifier.replace(/\/\.$/, "/")))];
+					}
+					var splitIdentifier = identifier.split("/");
+					// Check for mapped module path to module within mapped package.
+					if (typeof pkg.mappings[splitIdentifier[0]] !== "undefined") {
+						return [Package(pkg.mappings[splitIdentifier[0]]), pluginify((splitIdentifier.length > 1)?normalizeIdentifier(splitIdentifier.slice(1).join("/")):"")];
+					}
+					/*DEBUG*/ if (!moduleInitializers["/" + normalizeIdentifier(identifier)]) {
+					/*DEBUG*/     throw new Error("Descriptor for package '" + pkg.id + "' in sandbox '" + sandbox.id + "' does not declare 'mappings[\"" + splitIdentifier[0] + "\"]' property nor does sandbox have module memoized at '" + "/" + normalizeIdentifier(identifier) + "' needed to satisfy module path '" + identifier + "' in module '" + moduleIdentifier + "'!");
+					/*DEBUG*/ }
+					return [Package(""), pluginify("/" + normalizeIdentifier(identifier))];
+				}
+
+				// Statically link a module and its dependencies
+				module.require = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require(identifier[1]).exports;
+				};
+
+				module.require.supports = [
+					"ucjs-pinf-0"
+				];
+
+				module.require.id = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require.id(identifier[1]);
+				};
+
+				module.require.async = function (identifier, loadedCallback, errorCallback) {
+					identifier = resolveIdentifier(identifier);					
+					var mi = moduleIdentifier;
+					if (/^\//.test(identifier[0].id)) {
+						mi = "/main.js";
+					}
+					identifier[0].load(identifier[1], module.bundle, function (err, moduleAPI) {
+						if (err) {
+							if (errorCallback) return errorCallback(err);
+							throw err;
+						}
+						loadedCallback(moduleAPI);
+					});
+				};
+
+				module.require.sandbox = normalizeSandboxArguments (function (programIdentifier, options, loadedCallback, errorCallback) {
+					options.load = options.load || sandboxOptions.load;
+					// If the `programIdentifier` is relative it is resolved against the URI of the owning sandbox (not the owning page).
+					if (/^\./.test(programIdentifier))
+					{
+						programIdentifier = sandboxIdentifier + "/" + programIdentifier;
+						// HACK: Temporary hack as zombie (https://github.com/assaf/zombie) does not normalize path before sending to server.
+						programIdentifier = programIdentifier.replace(/\/\.\//g, "/");
+					}
+					return PINF.sandbox(programIdentifier, options, loadedCallback, errorCallback);
+				});
+				module.require.sandbox.id = sandboxIdentifier;
+
+				module.load = function () {
+					module.bundle = moduleInitializers[moduleIdentifier][0];
+					if (typeof moduleInitializers[moduleIdentifier][1] === "function") {
+
+						var moduleInterface = {
+							id: module.id,
+							filename:
+								// The `filename` from the meta info attached to the module.
+								// This is typically where the module was originally found on the filesystem.
+								moduleInitializers[moduleIdentifier][2].filename ||
+								// Fall back to the virtual path of the module in the bundle.
+								// TODO: Insert a delimiter between bundle and module id.
+								(module.bundle.replace(/\.js$/, "") + "/" + module.id).replace(/\/+/g, "/"),
+							exports: {}
+						}
+
+						if (packageIdentifier === "" && pkg.main === moduleIdentifier) {
+							module.require.main = moduleInterface;
+						}
+
+						if (sandboxOptions.onInitModule) {
+							sandboxOptions.onInitModule(moduleInterface, module, pkg, sandbox, {
+								normalizeIdentifier: normalizeIdentifier,
+								resolveIdentifier: resolveIdentifier,
+								finalizeLoad: finalizeLoad,
+								moduleInitializers: moduleInitializers,
+								initializedModules: initializedModules
+							});
+						}
+
+						var exports = moduleInitializers[moduleIdentifier][1].call(exports, module.require, module.exports, moduleInterface);
+						if (
+							typeof moduleInterface.exports !== "undefined" &&
+							(
+								typeof moduleInterface.exports !== "object" ||
+								keys(moduleInterface.exports).length !== 0
+							)
+						) {
+							module.exports = moduleInterface.exports;
+						} else
+						if (typeof exports !== "undefined") {
+							module.exports = exports;
+						}
+					} else
+					if (typeof moduleInitializers[moduleIdentifier][1] === "string") {
+						// TODO: Use more optimal string encoding algorythm to reduce payload size?
+						module.exports = decodeURIComponent(moduleInitializers[moduleIdentifier][1]);
+					} else {
+						module.exports = moduleInitializers[moduleIdentifier][1];
+					}
+				};
+
+				/*DEBUG*/ module.getReport = function () {
+				/*DEBUG*/ 	var exportsCount = 0,
+				/*DEBUG*/ 		key;
+				/*DEBUG*/ 	for (key in module.exports) {
+				/*DEBUG*/ 		exportsCount++;
+				/*DEBUG*/ 	}
+				/*DEBUG*/ 	return {
+				/*DEBUG*/ 		exports: exportsCount
+				/*DEBUG*/ 	};
+				/*DEBUG*/ };
+
+				return module;
+			};
+
+			pkg.load = function (moduleIdentifier, bundleIdentifier, loadedCallback) {
+
+				// If module/bundle to be loaded asynchronously is already memoized we skip the load.
+				if (moduleInitializers[packageIdentifier + (moduleIdentifier || pkg.main)]) {
+					return loadedCallback(null, pkg.require(moduleIdentifier).exports);
+				}
+				var bundleSubPath = bundleIdentifier.substring(sandboxIdentifier.length);
+				load(
+					((!/^\//.test(moduleIdentifier))?"/"+pkg.libPath:"") + moduleIdentifier,
+					packageIdentifier,
+					bundleSubPath.replace(/\.js$/g, ""),
+					function (err) {
+						if (err) return loadedCallback(err);
+						loadedCallback(null, pkg.require(moduleIdentifier).exports);
+					}
+				);
+			}
+
+			pkg.require = function (moduleIdentifier) {
+
+				var plugin = moduleIdentifier.plugin;
+
+				if (moduleIdentifier) {
+					if (!/^\//.test(moduleIdentifier)) {
+						moduleIdentifier = ("/" + ((moduleIdentifier.substring(0, pkg.libPath.length)===pkg.libPath)?"":pkg.libPath)).replace(/\/\.\//, "/") + moduleIdentifier;
+					}
+					moduleIdentifier = packageIdentifier + moduleIdentifier;
+				} else {
+					moduleIdentifier = packageIdentifier + pkg.main;
+				}
+
+				if (
+					!moduleInitializers[moduleIdentifier] &&
+					moduleInitializers[moduleIdentifier.replace(/\.js$/, "/index.js")]
+				) {
+					moduleIdentifier = moduleIdentifier.replace(/\.js$/, "/index.js");
+				}
+
+				// Use a specifically formatted module for requested plugin if available
+				if (
+					plugin &&
+					moduleInitializers[moduleIdentifier + ":" + plugin]
+				) {
+					moduleIdentifier += ":" + plugin;
+				}
+
+				if (!initializedModules[moduleIdentifier]) {
+					/*DEBUG*/ if (!moduleInitializers[moduleIdentifier]) {
+					/*DEBUG*/ 	console.error("[pinf-loader-js]", "moduleInitializers", moduleInitializers);
+					/*DEBUG*/ 	throw new Error("Module '" + moduleIdentifier + "' " + (plugin?"for format '" + plugin + "' ":"") + "not found in sandbox '" + sandbox.id + "'!");
+					/*DEBUG*/ }
+					(initializedModules[moduleIdentifier] = Module(moduleIdentifier, lastModule)).load();
+				}
+
+				var loadingBundlesCallbacks;
+				if (loadingBundles[moduleIdentifier]) {
+					loadingBundlesCallbacks = loadingBundles[moduleIdentifier];
+					delete loadingBundles[moduleIdentifier];
+					for (var i=0 ; i<loadingBundlesCallbacks.length ; i++) {
+						loadingBundlesCallbacks[i](null, sandbox);
+					}
+				}
+
+				// TODO: Do this via plugins registered using sandbox options.
+				// TODO: Cache response so we only process files once.
+
+				var moduleInfo = create(initializedModules[moduleIdentifier]);
+				// RequireJS/AMD international strings plugin using root by default.
+				if (plugin === "i18n") {
+					moduleInfo.exports = moduleInfo.exports.root;
+				}
+
+				return moduleInfo;
+			}
+
+			pkg.require.id = function (moduleIdentifier) {
+				if (!/^\//.test(moduleIdentifier)) {
+					moduleIdentifier = "/" + pkg.libPath + moduleIdentifier;
+				}
+				return (((packageIdentifier !== "")?"/"+packageIdentifier+"/":"") + moduleIdentifier).replace(/\/+/g, "/");
+			}
+
+			/*DEBUG*/ pkg.getReport = function () {
+			/*DEBUG*/ 	return {
+			/*DEBUG*/ 		main: pkg.main,
+			/*DEBUG*/ 		mappings: pkg.mappings,
+			/*DEBUG*/ 		directories: pkg.directories,
+			/*DEBUG*/ 		libPath: pkg.libPath
+			/*DEBUG*/ 	};
+			/*DEBUG*/ }
+
+			if (sandboxOptions.onInitPackage) {
+				sandboxOptions.onInitPackage(pkg, sandbox, {
+					normalizeIdentifier: normalizeIdentifier,
+					finalizeLoad: finalizeLoad,
+					moduleInitializers: moduleInitializers,
+					initializedModules: initializedModules
+				});
+			}
+
+			packages[packageIdentifier] = pkg;
+
+			return pkg;
+		}
+
+		// Get a module and initialize it (statically link its dependencies) if it is not already so
+		sandbox.require = function (moduleIdentifier) {
+			return Package("").require(moduleIdentifier).exports;
+		}
+
+		// Call the 'main' module of the program
+		sandbox.boot = function () {
+			/*DEBUG*/ if (typeof Package("").main !== "string") {
+			/*DEBUG*/ 	throw new Error("No 'main' property declared in '/package.json' in sandbox '" + sandbox.id + "'!");
+			/*DEBUG*/ }
+			return sandbox.require(Package("").main);
+		};
+
+		// Call the 'main' exported function of the main' module of the program
+		sandbox.main = function () {
+			var exports = sandbox.boot();
+			return ((exports.main)?exports.main.apply(null, arguments):exports);
+		};
+
+		/*DEBUG*/ sandbox.getReport = function () {
+		/*DEBUG*/ 	var report = {
+		/*DEBUG*/ 			bundles: {},
+		/*DEBUG*/ 			packages: {},
+		/*DEBUG*/ 			modules: {}
+		/*DEBUG*/ 		},
+		/*DEBUG*/ 		key;
+		/*DEBUG*/ 	for (key in bundleIdentifiers) {
+		/*DEBUG*/ 		report.bundles[key] = bundleIdentifiers[key];
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in packages) {
+		/*DEBUG*/ 		report.packages[key] = packages[key].getReport();
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in moduleInitializers) {
+		/*DEBUG*/ 		if (initializedModules[key]) {
+		/*DEBUG*/ 			report.modules[key] = initializedModules[key].getReport();
+		/*DEBUG*/ 		} else {
+		/*DEBUG*/ 			report.modules[key] = {};
+		/*DEBUG*/ 		}
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	return report;
+		/*DEBUG*/ }
+		/*DEBUG*/ sandbox.reset = function () {
+		/*DEBUG*/   moduleInitializers = {};
+		/*DEBUG*/   initializedModules = {};
+		/*DEBUG*/   bundleIdentifiers = {};
+		/*DEBUG*/   packages = {};
+		/*DEBUG*/   loadingBundles = {};
+		/*DEBUG*/ }
+
+		load((sandboxIdentifier.indexOf("?") === -1) ? ".js" : "", "", "", loadedCallback);
+
+		return sandbox;
+	};
+
+	var
+		/*DEBUG*/ bundleIdentifiers = {},
+		sandboxes = {};
+
+	var Require = function (bundle) {
+		var self = this;
+
+		// Address a specific sandbox or currently loading sandbox if initial load.
+		var bundleHandler = function (uid, callback) {
+			/*DEBUG*/ if (uid && bundleIdentifiers[uid]) {
+			/*DEBUG*/ 	throw new Error("You cannot split require.bundle(UID) calls where UID is constant!");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[uid] = true;
+			loadedBundles.push([uid, function (sandbox) {
+				var moduleInitializers = {},
+					req = new Require(uid);
+				delete req.bundle;
+				req.sandbox = sandbox;
+				// Store raw module in loading bundle
+				req.memoize = function (moduleIdentifier, moduleInitializer, moduleMeta) {
+					moduleInitializers[
+						moduleIdentifier +
+						// NOTE: This feature may be elevated to a new function argument to 'memoize' if it proves to be prevalent.
+						(
+							(
+								moduleMeta &&
+								moduleMeta.variation
+							) ? ":" + moduleMeta.variation : ""
+						)
+					] = [moduleInitializer, moduleMeta || {}];
+				}
+				callback(req, global || null);
+				return moduleInitializers;
+			}]);
+		}
+		var activeBundleHandler = bundleHandler;
+		this.bundle = function () {
+			return activeBundleHandler.apply(null, arguments);
+		}
+		this.setActiveBundleHandler = function (handler) {
+			var oldHandler = activeBundleHandler;
+			activeBundleHandler = handler;
+			return oldHandler;
+		}
+	}
+
+	var PINF = new Require();
+
+	// TODO: @see URL_TO_SPEC
+	PINF.supports = [
+		"ucjs-pinf-0"
+	];
+
+	// Create a new environment to memoize modules to.
+	// If relative, the `programIdentifier` is resolved against the URI of the owning page (this is only for the global _require_).
+	PINF.sandbox = normalizeSandboxArguments(function (programIdentifier, options, loadedCallback, errorCallback) {
+		if (typeof programIdentifier === "function") {
+			options = options || {};
+			var bundle = programIdentifier;
+			var fallbackLoad = options.load || loadInBrowser;
+			options.load = function (uri, loadedCallback) {
+				if (uri === (programIdentifier + ".js")) {
+					PINF.bundle("", bundle);
+					loadedCallback(null);
+					return;
+				}
+				return fallbackLoad(uri, loadedCallback, options);
+			}
+			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
+		}
+		var sandboxIdentifier = programIdentifier.replace(/\.js$/, "");
+		return sandboxes[sandboxIdentifier] = Sandbox(sandboxIdentifier, options, function (err, sandbox) {
+			if (err) {
+				if (errorCallback) return errorCallback(err);
+				throw err;
+			}
+			loadedCallback(sandbox);
+		});
+	});
+
+	PINF.Loader = Loader;
+
+	/*DEBUG*/ PINF.getReport = function () {
+	/*DEBUG*/ 	var report = {
+	/*DEBUG*/ 			sandboxes: {}
+	/*DEBUG*/ 		};
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		report.sandboxes[key] = sandboxes[key].getReport();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	return report;
+	/*DEBUG*/ }
+	/*DEBUG*/ PINF.reset = function () {
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		sandboxes[key].reset();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	sandboxes = {};
+	/*DEBUG*/ 	bundleIdentifiers = {};
+	/*DEBUG*/ 	loadedBundles = [];
+	/*DEBUG*/ }
+
+	return PINF;
+}
+
+if (exports) exports.Loader = Loader;
+
+})(typeof exports !== "undefined" ? exports : null);
+
+},{}]},{},[1])(1)
+});
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate.browser.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate.browser.js
new file mode 100644
index 0000000..98e23e8
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate.browser.js
@@ -0,0 +1,2479 @@
+((function (_require, _exports, _module) {
+var bundle = { require: _require, exports: _exports, module: _module };
+var exports = undefined;
+var module = undefined;
+var define = function (deps, init) {
+var exports = init();
+[["domplate","domplate"]].forEach(function (expose) {
+if (typeof window !== "undefined") {
+window[expose[0]] = exports[expose[1]];
+} else if (typeof self !== "undefined") {
+self[expose[0]] = exports[expose[1]];
+}
+});
+}; define.amd = true;
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var RT = require("./rt");
+
+var Renderer = exports.Renderer = require("./renderer").Renderer;
+
+function Domplate(exports) {
+  exports.util = require("./util");
+
+  exports.loadStyle = function (uri, baseUrl) {
+    var WINDOW = window;
+
+    if (typeof baseUrl === 'undefined' && WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
+      uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
+    } else if (typeof baseUrl !== 'undefined') {
+      uri = [baseUrl, uri].join("/").replace(/\/\.?\//g, "/");
+    }
+
+    return new Promise(function (resolve, reject) {
+      var link = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "link") : window.document.createElement("link");
+      link.rel = "stylesheet";
+      link.href = uri;
+
+      link.onload = function () {
+        resolve();
+      };
+
+      var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+      head.appendChild(link);
+    });
+  };
+
+  exports.EVAL = {
+    compileMarkup: function compileMarkup(code, context) {
+      return context.compiled(context);
+    },
+    compileDOM: function compileDOM(code, context) {
+      return context.compiled(context);
+    }
+  };
+  exports.tags = {};
+  exports.tags._domplate_ = exports;
+
+  var DomplateTag = exports.DomplateTag = function DomplateTag(tagName) {
+    this.tagName = tagName;
+  };
+
+  function DomplateEmbed() {}
+
+  function DomplateLoop() {}
+
+  function DomplateIf() {}
+
+  function copyArray(oldArray) {
+    var ary = [];
+    if (oldArray) for (var i = 0; i < oldArray.length; ++i) {
+      ary.push(oldArray[i]);
+    }
+    return ary;
+  }
+
+  function copyObject(l, r) {
+    var m = {};
+    extend(m, l);
+    extend(m, r);
+    return m;
+  }
+
+  function extend(l, r) {
+    for (var n in r) {
+      l[n] = r[n];
+    }
+  }
+
+  var womb = null;
+
+  var domplate = exports.domplate = function () {
+    var lastSubject;
+
+    for (var i = 0; i < arguments.length; ++i) {
+      lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
+    }
+
+    for (var name in lastSubject) {
+      var val = lastSubject[name];
+      if (isTag(val)) val.tag.subject = lastSubject;
+    }
+
+    return lastSubject;
+  };
+
+  domplate.context = function (context, fn) {
+    var lastContext = domplate.lastContext;
+    domplate.topContext = context;
+    fn.apply(context);
+    domplate.topContext = lastContext;
+  };
+
+  exports.tags.TAG = function () {
+    var embed = new DomplateEmbed();
+    return embed.merge(arguments);
+  };
+
+  exports.tags.FOR = domplate.FOR = function () {
+    var loop = new DomplateLoop();
+    return loop.merge(arguments);
+  };
+
+  exports.tags.IF = domplate.IF = function () {
+    var loop = new DomplateIf();
+    return loop.merge(arguments);
+  };
+
+  DomplateTag.prototype = {
+    merge: function merge(args, oldTag) {
+      if (oldTag) this.tagName = oldTag.tagName;
+      this.context = oldTag ? oldTag.context : null;
+      this.subject = oldTag ? oldTag.subject : null;
+      this.attrs = oldTag ? copyObject(oldTag.attrs) : {};
+      this.classes = oldTag ? copyObject(oldTag.classes) : {};
+      this.props = oldTag ? copyObject(oldTag.props) : null;
+      this.listeners = oldTag ? copyArray(oldTag.listeners) : null;
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args.length ? args[0] : null;
+      var hasAttrs = _typeof(attrs) == "object" && !isTag(attrs);
+      this.resources = {};
+      this.children = [];
+      if (domplate.topContext) this.context = domplate.topContext;
+      if (args.length) parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
+      if (hasAttrs) this.parseAttrs(attrs);
+      return creator(this, DomplateTag);
+    },
+    parseAttrs: function parseAttrs(args) {
+      for (var name in args) {
+        var val = parseValue(args[name]);
+        readPartNames(val, this.vars);
+
+        if (name.indexOf("on") === 0) {
+          var eventName = name.substr(2);
+          if (!this.listeners) this.listeners = [];
+          this.listeners.push(eventName, val);
+        } else if (name[0] === "_" && name[1] !== "_") {
+          var propName = name.substr(1);
+          if (!this.props) this.props = {};
+          this.props[propName] = val;
+        } else if (name[0] === "$") {
+          var className = name.substr(1);
+          if (!this.classes) this.classes = {};
+          this.classes[className] = val;
+        } else {
+          if (name === "class" && this.attrs.hasOwnProperty(name)) this.attrs[name] += " " + val;else this.attrs[name] = val;
+        }
+      }
+    },
+    compile: function compile() {
+      if (this.renderMarkup) {
+        return;
+      }
+
+      if (this.subject._resources) {
+        this.resources = this.subject._resources();
+      }
+
+      this.compileMarkup();
+      this.compileDOM();
+    },
+    compileMarkup: function compileMarkup() {
+      this.markupArgs = [];
+      var topBlock = [],
+          topOuts = [],
+          blocks = [],
+          info = {
+        args: this.markupArgs,
+        argIndex: 0
+      };
+      this.generateMarkup(topBlock, topOuts, blocks, info, true);
+      this.addCode(topBlock, topOuts, blocks);
+      var fnBlock = ['(function (__code__, __context__, __in__, __out__'];
+
+      for (var i = 0; i < info.argIndex; ++i) {
+        fnBlock.push(', s', i);
+      }
+
+      fnBlock.push(') {');
+      if (this.subject) fnBlock.push('  with (this) {');
+      if (this.context) fnBlock.push('  with (__context__) {');
+      fnBlock.push('  with (__in__) {');
+      fnBlock.push.apply(fnBlock, blocks);
+      if (this.subject) fnBlock.push('  }');
+      if (this.context) fnBlock.push('  }');
+      fnBlock.push('}})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderMarkup = exports.EVAL.compileMarkup(js, RT.makeMarkupRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__markup
+      }));
+    },
+    getVarNames: function getVarNames(args) {
+      if (this.vars) args.push.apply(args, this.vars);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.getVarNames(args);else if (child instanceof Parts) {
+          for (var i = 0; i < child.parts.length; ++i) {
+            if (child.parts[i] instanceof Variables) {
+              var name = child.parts[i].names[0];
+              var names = name.split(".");
+              args.push(names[0]);
+            }
+          }
+        }
+      }
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info, topNode) {
+      topBlock.push(',"<', this.tagName, '"');
+
+      if (topNode) {
+        if (this.subject.__dbid) this.attrs['__dbid'] = this.subject.__dbid;
+        if (this.subject.__dtid) this.attrs['__dtid'] = this.subject.__dtid;
+      }
+
+      for (var name in this.attrs) {
+        if (name != "class") {
+          var val = this.attrs[name];
+          topBlock.push(', " ', name, '=\\""');
+          addParts(val, ',', topBlock, info, true);
+          topBlock.push(', "\\""');
+        }
+      }
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          readPartNames(this.listeners[i + 1], topOuts);
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          readPartNames(this.props[name], topOuts);
+        }
+      }
+
+      if (this.attrs.class || this.classes && Object.keys(this.classes).length > 0) {
+        topBlock.push(', " class=\\""');
+        if (this.attrs.hasOwnProperty("class")) addParts(this.attrs["class"], ',', topBlock, info, true);
+        topBlock.push(', " "');
+
+        for (var name in this.classes) {
+          topBlock.push(', (');
+          addParts(this.classes[name], '', topBlock, info);
+          topBlock.push(' ? "', name, '" + " " : "")');
+        }
+
+        topBlock.push(', "\\""');
+      }
+
+      if (this.tagName == "br") {
+        topBlock.push(',"/>"');
+      } else {
+        topBlock.push(',">"');
+        this.generateChildMarkup(topBlock, topOuts, blocks, info);
+        topBlock.push(',"</', this.tagName, '>"');
+      }
+    },
+    generateChildMarkup: function generateChildMarkup(topBlock, topOuts, blocks, info) {
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.generateMarkup(topBlock, topOuts, blocks, info);else addParts(child, ',', topBlock, info, true);
+      }
+    },
+    addCode: function addCode(topBlock, topOuts, blocks) {
+      if (topBlock.length) blocks.push('    __code__.push(""', topBlock.join(""), ');');
+      if (topOuts.length) blocks.push('__out__.push(', topOuts.join(","), ');');
+      topBlock.splice(0, topBlock.length);
+      topOuts.splice(0, topOuts.length);
+    },
+    addLocals: function addLocals(blocks) {
+      var varNames = [];
+      this.getVarNames(varNames);
+      var map = {};
+
+      for (var i = 0; i < varNames.length; ++i) {
+        var name = varNames[i];
+        if (map.hasOwnProperty(name)) continue;
+        map[name] = 1;
+        var names = name.split(".");
+        blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';');
+      }
+    },
+    compileDOM: function compileDOM() {
+      var path = [];
+      var blocks = [];
+      this.domArgs = [];
+      path.embedIndex = 0;
+      path.loopIndex = 0;
+      path.ifIndex = 0;
+      path.staticIndex = 0;
+      path.renderIndex = 0;
+      var nodeCount = this.generateDOM(path, blocks, this.domArgs);
+      var fnBlock = ['(function (root, context, o'];
+
+      for (var i = 0; i < path.staticIndex; ++i) {
+        fnBlock.push(', ', 's' + i);
+      }
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        fnBlock.push(', ', 'd' + i);
+      }
+
+      fnBlock.push(') {');
+
+      for (var i = 0; i < path.loopIndex; ++i) {
+        fnBlock.push('  var l', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.ifIndex; ++i) {
+        fnBlock.push('  var if_', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.embedIndex; ++i) {
+        fnBlock.push('  var e', i, ' = 0;');
+      }
+
+      if (this.subject) {
+        fnBlock.push('  with (this) {');
+      }
+
+      if (this.context) {
+        fnBlock.push('    with (context) {');
+      }
+
+      fnBlock.push(blocks.join(""));
+      if (this.context) fnBlock.push('    }');
+      if (this.subject) fnBlock.push('  }');
+      fnBlock.push('  return ', nodeCount, ';');
+      fnBlock.push('})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderDOM = exports.EVAL.compileDOM(js, RT.makeDOMRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__dom
+      }));
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      if (this.listeners || this.props) this.generateNodePath(path, blocks);
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          var val = this.listeners[i + 1];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);');
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          var val = this.props[name];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.', name, ' = ', arg, ';');
+        }
+      }
+
+      this.generateChildDOM(path, blocks, args);
+      return 1;
+    },
+    generateNodePath: function generateNodePath(path, blocks) {
+      blocks.push("        node = __path__(root, o");
+
+      for (var i = 0; i < path.length; ++i) {
+        blocks.push(",", path[i]);
+      }
+
+      blocks.push(");");
+    },
+    generateChildDOM: function generateChildDOM(path, blocks, args) {
+      path.push(0);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) path[path.length - 1] += '+' + child.tag.generateDOM(path, blocks, args);else path[path.length - 1] += '+1';
+      }
+
+      path.pop();
+    }
+  };
+  DomplateEmbed.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.value = oldTag ? oldTag.value : parseValue(args[0]);
+      this.attrs = oldTag ? oldTag.attrs : {};
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args[1];
+
+      for (var name in attrs) {
+        var val = parseValue(attrs[name]);
+        this.attrs[name] = val;
+        readPartNames(val, this.vars);
+      }
+
+      var retval = creator(this, DomplateEmbed);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.value instanceof Parts) names.push(this.value.parts[0].name);
+      if (this.vars) names.push.apply(names, this.vars);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('__link__(');
+      addParts(this.value, '', blocks, info);
+      blocks.push(', __code__, __out__, {');
+      var lastName = null;
+
+      for (var name in this.attrs) {
+        if (lastName) blocks.push(',');
+        lastName = name;
+        var val = this.attrs[name];
+        blocks.push('"', name, '":');
+        addParts(val, '', blocks, info);
+      }
+
+      blocks.push('});');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var embedName = 'e' + path.embedIndex++;
+      this.generateNodePath(path, blocks);
+      var valueName = 'd' + path.renderIndex++;
+      var argsName = 'd' + path.renderIndex++;
+      blocks.push('        ', embedName + ' = __link__(node, ', valueName, ', ', argsName, ');');
+      return embedName;
+    }
+  });
+  DomplateLoop.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.varName = oldTag ? oldTag.varName : args[0];
+      this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 2);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateLoop);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.iter instanceof Parts) names.push(this.iter.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var iterName;
+
+      if (this.iter instanceof Parts) {
+        var part = this.iter.parts[0];
+        iterName = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            iterName = part.format[i] + "(" + iterName + ")";
+          }
+        }
+      } else {
+        iterName = this.iter;
+      }
+
+      blocks.push('    __loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('    }]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var iterName = 'd' + path.renderIndex++;
+      var counterName = 'i' + path.loopIndex;
+      var loopName = 'l' + path.loopIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+      var basePath = path[path.length - 1];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        path[path.length - 1] = basePath + '+' + loopName + '+' + nodeCount;
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      path[path.length - 1] = basePath + '+' + loopName;
+      blocks.push('      ', loopName, ' = __loop__.apply(this, [', iterName, ', function(', counterName, ',', loopName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('        return ', nodeCount, ';');
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return loopName;
+    }
+  });
+  DomplateIf.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.booleanVar = oldTag ? oldTag.booleanVar : parseValue(args[0]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 1);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateIf);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.booleanVar instanceof Parts) names.push(this.booleanVar.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var expr;
+
+      if (this.booleanVar instanceof Parts) {
+        var part = this.booleanVar.parts[0];
+        expr = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            expr = part.format[i] + "(" + expr + ")";
+          }
+        }
+      } else {
+        expr = this.booleanVar;
+      }
+
+      blocks.push('__if__.apply(this, [', expr, ', __out__, function(__out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('}]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var controlName = 'd' + path.renderIndex++;
+      var ifName = 'if_' + path.ifIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      blocks.push('      ', ifName, ' = __if__.apply(this, [', controlName, ', function(', ifName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return controlName;
+    }
+  });
+
+  function Variables(names, format) {
+    this.names = names;
+    this.format = format;
+  }
+
+  function Parts(parts) {
+    this.parts = parts;
+  }
+
+  function parseParts(str) {
+    var index = 0;
+    var parts = [];
+    var m;
+    var re = /\$([_A-Za-z][$_A-Za-z0-9.,|]*)/g;
+
+    while (m = re.exec(str)) {
+      var pre = str.substr(index, re.lastIndex - m[0].length - index);
+      if (pre) parts.push(pre);
+      var segs = m[1].split("|");
+      var vars = segs[0].split(",$");
+      parts.push(new Variables(vars, segs.splice(1)));
+      index = re.lastIndex;
+    }
+
+    if (!index) {
+      return str;
+    }
+
+    var post = str.substr(index);
+    if (post) parts.push(post);
+    var retval = new Parts(parts);
+    return retval;
+  }
+
+  function parseValue(val) {
+    return typeof val == 'string' ? parseParts(val) : val;
+  }
+
+  function parseChildren(args, offset, vars, children) {
+    for (var i = offset; i < args.length; ++i) {
+      var val = parseValue(args[i]);
+      children.push(val);
+      readPartNames(val, vars);
+    }
+  }
+
+  function readPartNames(val, vars) {
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+        if (part instanceof Variables) vars.push(part.names[0]);
+      }
+    }
+  }
+
+  function generateArg(val, path, args) {
+    if (val instanceof Parts) {
+      var vals = [];
+
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var varName = 'd' + path.renderIndex++;
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              varName = part.format[j] + '(' + varName + ')';
+            }
+          }
+
+          vals.push(varName);
+        } else vals.push('"' + part.replace(/"/g, '\\"') + '"');
+      }
+
+      return vals.join('+');
+    } else {
+      args.push(val);
+      return 's' + path.staticIndex++;
+    }
+  }
+
+  function addParts(val, delim, block, info, escapeIt) {
+    var vals = [];
+
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var partName = part.names.join(",");
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              partName = part.format[j] + "(" + partName + ")";
+            }
+          }
+
+          if (escapeIt) vals.push("__escape__(" + partName + ")");else vals.push(partName);
+        } else vals.push('"' + part + '"');
+      }
+    } else if (isTag(val)) {
+      info.args.push(val);
+      vals.push('s' + info.argIndex++);
+    } else vals.push('"' + val + '"');
+
+    var parts = vals.join(delim);
+    if (parts) block.push(delim, parts);
+  }
+
+  function isTag(obj) {
+    return (typeof obj == "function" || obj instanceof Function) && !!obj.tag;
+  }
+
+  function creator(tag, cons) {
+    var fn = function fn() {
+      var tag = arguments.callee.tag;
+      var cons = arguments.callee.cons;
+      var newTag = new cons();
+      return newTag.merge(arguments, tag);
+    };
+
+    fn.tag = tag;
+    fn.cons = cons;
+    extend(fn, Renderer);
+    return fn;
+  }
+
+  function defineTags() {
+    Array.from(arguments).forEach(function (tagName) {
+      var fnName = tagName.toUpperCase();
+
+      exports.tags[fnName] = function () {
+        var newTag = new this._domplate_.DomplateTag(tagName);
+        return newTag.merge(arguments);
+      };
+    });
+  }
+
+  defineTags("a", "button", "br", "canvas", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select", "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul");
+}
+
+exports.domplate = {};
+Domplate(exports.domplate);
+
+exports.domplate.ensureLoader = function () {
+  if (typeof window !== "undefined" && window.PINF) {
+    return window.PINF;
+  }
+
+  var PINF = require("pinf-loader-js/loader.browser");
+
+  return window.PINF;
+};
+
+exports.domplate.loadRep = function (url, options, successCallback, errorCallback) {
+  if (typeof options === "function") {
+    errorCallback = successCallback;
+    successCallback = options;
+    options = {};
+  }
+
+  var PINF = exports.domplate.ensureLoader();
+  return PINF.sandbox(url + ".rep", function (sandbox) {
+    var rep = sandbox.main(exports.domplate, options);
+    successCallback(rep);
+  }, errorCallback);
+};
+},{"./renderer":2,"./rt":3,"./util":4,"pinf-loader-js/loader.browser":6}],2:[function(require,module,exports){
+
+var Renderer = exports.Renderer = {
+  checkDebug: function checkDebug() {},
+  renderHTML: function renderHTML(args, outputs, self) {
+    var code = [];
+    var markupArgs = [code, this.tag.context ? this.tag.context : null, args, outputs];
+    markupArgs.push.apply(markupArgs, this.tag.markupArgs);
+    this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
+
+    if (this.tag.resources && this.tag.subject._resourceListener) {
+      this.tag.subject._resourceListener.register(this.tag.resources);
+    }
+
+    return code.join("");
+  },
+  insertRows: function insertRows(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var table = doc.createElement("table");
+    table.innerHTML = html;
+    var tbody = table.firstChild;
+    var parent = before.localName == "TR" ? before.parentNode : before;
+    var after = before.localName == "TR" ? before.nextSibling : null;
+    var firstRow = tbody.firstChild,
+        lastRow;
+
+    while (tbody.firstChild) {
+      lastRow = tbody.firstChild;
+      if (after) parent.insertBefore(lastRow, after);else parent.appendChild(lastRow);
+    }
+
+    var offset = 0;
+
+    if (before.localName == "TR") {
+      var node = firstRow.parentNode.firstChild;
+
+      for (; node && node != firstRow; node = node.nextSibling) {
+        ++offset;
+      }
+    }
+
+    var domArgs = [firstRow, this.tag.context, offset];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return [firstRow, lastRow];
+  },
+  insertAfter: function insertAfter(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var range = doc.createRange();
+    range.selectNode(doc.body);
+    var frag = range.createContextualFragment(html);
+    var root = frag.firstChild;
+    if (before.nextSibling) before.parentNode.insertBefore(frag, before.nextSibling);else before.parentNode.appendChild(frag);
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject ? this.tag.subject : null, domArgs);
+    return root;
+  },
+  replace: function replace(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var root;
+
+    if (parent.nodeType == 1) {
+      parent.innerHTML = html;
+      root = parent.firstChild;
+    } else {
+      if (!parent || parent.nodeType != 9) parent = document;
+      if (!womb || womb.ownerDocument != parent) womb = parent.createElement("div");
+      womb.innerHTML = html;
+      root = womb.firstChild;
+    }
+
+    var domArgs = [root, this.tag.context ? this.tag.context : null, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  append: function append(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    if (!womb || womb.ownerDocument != parent.ownerDocument) womb = parent.ownerDocument.createElement("div");
+    womb.innerHTML = html;
+    root = womb.firstChild;
+
+    while (womb.firstChild) {
+      parent.appendChild(womb.firstChild);
+    }
+
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  render: function render(args, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    return html;
+  }
+};
+},{}],3:[function(require,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+function ArrayIterator(array) {
+  var index = -1;
+
+  this.next = function () {
+    if (++index >= array.length) throw StopIteration;
+    return array[index];
+  };
+}
+
+function StopIteration() {}
+
+exports.makeMarkupRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__link__ = function (tag, code, outputs, args) {
+    if (!tag) {
+      return;
+    }
+
+    if (!tag.tag) {
+      return;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return;
+    }
+
+    tag.tag.compile();
+
+    if (self.resources && tag.tag.resources && tag.tag.resources !== self.resources) {
+      for (var key in tag.tag.resources) {
+        self.resources[key] = tag.tag.resources[key];
+      }
+    }
+
+    var tagOutputs = [];
+    var markupArgs = [code, tag.tag.context ? tag.tag.context : null, args, tagOutputs];
+    markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
+    tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
+    outputs.push(tag);
+    outputs.push(tagOutputs);
+  };
+
+  exports.__escape__ = function (value) {
+    function replaceChars(ch) {
+      switch (ch) {
+        case "<":
+          return "&lt;";
+
+        case ">":
+          return "&gt;";
+
+        case "&":
+          return "&amp;";
+
+        case "'":
+          return "&#39;";
+
+        case '"':
+          return "&quot;";
+      }
+
+      return "?";
+    }
+
+    ;
+    return String(value).replace(/[<>&"']/g, replaceChars);
+  };
+
+  exports.__loop__ = function (iter, outputs, fn) {
+    var iterOuts = [];
+    outputs.push(iterOuts);
+
+    if (iter instanceof Array || typeof iter === "array" || Array.isArray(iter)) {
+      iter = new ArrayIterator(iter);
+    }
+
+    try {
+      if (!iter || !iter.next) {
+        console.error("Cannot iterate loop", iter, _typeof(iter), outputs, fn);
+        throw new Error("Cannot iterate loop as iter.next() method is not defined");
+      }
+
+      while (1) {
+        var value = iter.next();
+        var itemOuts = [0, 0];
+        iterOuts.push(itemOuts);
+        fn.apply(this, [value, itemOuts]);
+      }
+    } catch (exc) {
+      if (exc != StopIteration) throw exc;
+    }
+  };
+
+  exports.__if__ = function (booleanVar, outputs, fn) {
+    var ifControl = [];
+    outputs.push(ifControl);
+
+    if (booleanVar) {
+      ifControl.push(1);
+      fn.apply(this, [ifControl]);
+    } else {
+      ifControl.push(0);
+    }
+  };
+
+  return exports;
+};
+
+exports.makeDOMRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__bind__ = function (object, fn) {
+    return function (event) {
+      return fn.apply(object, [event]);
+    };
+  };
+
+  exports.__link__ = function (node, tag, args) {
+    if (!tag) {
+      return 0;
+    }
+
+    if (!tag.tag) {
+      return 0;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return 0;
+    }
+
+    tag.tag.compile();
+    var domArgs = [node, tag.tag.context ? tag.tag.context : null, 0];
+    domArgs.push.apply(domArgs, tag.tag.domArgs);
+    domArgs.push.apply(domArgs, args);
+    var oo = tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
+    return oo;
+  };
+
+  exports.__loop__ = function (iter, fn) {
+    if (!Array.isArray(iter)) {
+      return 0;
+    }
+
+    var nodeCount = 0;
+
+    for (var i = 0; i < iter.length; ++i) {
+      iter[i][0] = i;
+      iter[i][1] = nodeCount;
+      nodeCount += fn.apply(this, iter[i]);
+    }
+
+    return nodeCount;
+  };
+
+  exports.__if__ = function (control, fn) {
+    if (control && control[0]) {
+      fn.apply(this, [0, control[1]]);
+    } else {}
+  };
+
+  exports.__path__ = function (parent, offset) {
+    var root = parent;
+
+    for (var i = 2; i < arguments.length; ++i) {
+      var index = arguments[i];
+      if (i == 3) index += offset;
+
+      if (index == -1) {
+        parent = parent.parentNode;
+      } else {
+        parent = parent.childNodes[index];
+      }
+    }
+
+    return parent;
+  };
+
+  return exports;
+};
+},{}],4:[function(require,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var FBTrace = {};
+exports.merge = require("deepmerge");
+
+exports.escapeNewLines = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+};
+
+exports.stripNewLines = function (value) {
+  return typeof value == "string" ? value.replace(/[\r\n]/gm, " ") : value;
+};
+
+exports.escapeJS = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n").replace('"', '\\"', "g");
+};
+
+exports.cropString = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  var halfLimit = limit / 2;
+  halfLimit -= 2;
+  if (text.length > limit) return text.substr(0, halfLimit) + alterText + text.substr(text.length - halfLimit);else return text;
+};
+
+exports.cropStringLeft = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  limit -= alterText.length;
+  if (text.length > limit) return alterText + text.substr(text.length - limit);else return text;
+};
+
+exports.hasClass = function (node, name) {
+  if (!node || node.nodeType != 1) return false;else {
+    for (var i = 1; i < arguments.length; ++i) {
+      var name = arguments[i];
+      var re = new RegExp("(^|\\s)" + name + "($|\\s)");
+      if (!re.exec(node.getAttribute("class"))) return false;
+    }
+
+    return true;
+  }
+};
+
+exports.setClass = function (node, name) {
+  if (node && !exports.hasClass(node, name)) node.className += " " + name;
+};
+
+exports.getClassValue = function (node, name) {
+  var re = new RegExp(name + "-([^ ]+)");
+  var m = re.exec(node.className);
+  return m ? m[1] : "";
+};
+
+exports.removeClass = function (node, name) {
+  if (node && node.className) {
+    var index = node.className.indexOf(name);
+
+    if (index >= 0) {
+      var size = name.length;
+      node.className = node.className.substr(0, index - 1) + node.className.substr(index + size);
+    }
+  }
+};
+
+exports.toggleClass = function (elt, name) {
+  if (exports.hasClass(elt, name)) exports.removeClass(elt, name);else exports.setClass(elt, name);
+};
+
+exports.setClassTimed = function (elt, name, context, timeout) {
+  if (!timeout) timeout = 1300;
+  if (elt.__setClassTimeout) context.clearTimeout(elt.__setClassTimeout);else exports.setClass(elt, name);
+
+  if (!exports.isVisible(elt)) {
+    if (elt.__invisibleAtSetPoint) elt.__invisibleAtSetPoint--;else elt.__invisibleAtSetPoint = 5;
+  } else {
+    delete elt.__invisibleAtSetPoint;
+  }
+
+  elt.__setClassTimeout = context.setTimeout(function () {
+    delete elt.__setClassTimeout;
+    if (elt.__invisibleAtSetPoint) exports.setClassTimed(elt, name, context, timeout);else {
+      delete elt.__invisibleAtSetPoint;
+      exports.removeClass(elt, name);
+    }
+  }, timeout);
+};
+
+exports.cancelClassTimed = function (elt, name, context) {
+  if (elt.__setClassTimeout) {
+    exports.removeClass(elt, name);
+    context.clearTimeout(elt.__setClassTimeout);
+    delete elt.__setClassTimeout;
+  }
+};
+
+exports.$ = function (id, doc) {
+  if (doc) return doc.getElementById(id);else return document.getElementById(id);
+};
+
+exports.getChildByClass = function (node) {
+  for (var i = 1; i < arguments.length; ++i) {
+    var className = arguments[i];
+    var child = node.firstChild;
+    node = null;
+
+    for (; child; child = child.nextSibling) {
+      if (exports.hasClass(child, className)) {
+        node = child;
+        break;
+      }
+    }
+  }
+
+  return node;
+};
+
+exports.getAncestorByClass = function (node, className) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (exports.hasClass(parent, className)) return parent;
+  }
+
+  return null;
+};
+
+exports.getElementByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  var elements = node.getElementsByClassName(className);
+  return elements[0];
+};
+
+exports.getElementsByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  return node.getElementsByClassName(className);
+};
+
+exports.getElementsByAttribute = function (node, attrName, attrValue) {
+  function iteratorHelper(node, attrName, attrValue, result) {
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      if (child.getAttribute(attrName) == attrValue) result.push(child);
+      iteratorHelper(child, attrName, attrValue, result);
+    }
+  }
+
+  var result = [];
+  iteratorHelper(node, attrName, attrValue, result);
+  return result;
+};
+
+exports.isAncestor = function (node, potentialAncestor) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (parent == potentialAncestor) return true;
+  }
+
+  return false;
+};
+
+exports.getNextElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.nextSibling;
+  }
+
+  return node;
+};
+
+exports.getPreviousElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.previousSibling;
+  }
+
+  return node;
+};
+
+exports.getBody = function (doc) {
+  if (doc.body) return doc.body;
+  var body = doc.getElementsByTagName("body")[0];
+  if (body) return body;
+  return doc.documentElement;
+};
+
+exports.findNextDown = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (criteria(child)) return child;
+    var next = exports.findNextDown(child, criteria);
+    if (next) return next;
+  }
+};
+
+exports.findPreviousUp = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.lastChild; child; child = child.previousSibling) {
+    var next = exports.findPreviousUp(child, criteria);
+    if (next) return next;
+    if (criteria(child)) return child;
+  }
+};
+
+exports.findNext = function (node, criteria, upOnly, maxRoot) {
+  if (!node) return null;
+
+  if (!upOnly) {
+    var next = exports.findNextDown(node, criteria);
+    if (next) return next;
+  }
+
+  for (var sib = node.nextSibling; sib; sib = sib.nextSibling) {
+    if (criteria(sib)) return sib;
+    var next = exports.findNextDown(sib, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) return exports.findNext(node.parentNode, criteria, true);
+};
+
+exports.findPrevious = function (node, criteria, downOnly, maxRoot) {
+  if (!node) return null;
+
+  for (var sib = node.previousSibling; sib; sib = sib.previousSibling) {
+    var prev = exports.findPreviousUp(sib, criteria);
+    if (prev) return prev;
+    if (criteria(sib)) return sib;
+  }
+
+  if (!downOnly) {
+    var next = exports.findPreviousUp(node, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) {
+    if (criteria(node.parentNode)) return node.parentNode;
+    return exports.findPrevious(node.parentNode, criteria, true);
+  }
+};
+
+exports.getNextByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findNext(root, iter);
+};
+
+exports.getPreviousByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findPrevious(root, iter);
+};
+
+exports.hasChildElements = function (node) {
+  if (node.contentDocument) return true;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (child.nodeType == 1) return true;
+  }
+
+  return false;
+};
+
+exports.isElement = function (o) {
+  try {
+    return o && o instanceof Element;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.isNode = function (o) {
+  try {
+    return o && o instanceof Node;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.cancelEvent = function (event) {
+  event.stopPropagation();
+  event.preventDefault();
+};
+
+exports.isLeftClick = function (event) {
+  return event.button == 0 && exports.noKeyModifiers(event);
+};
+
+exports.isMiddleClick = function (event) {
+  return event.button == 1 && exports.noKeyModifiers(event);
+};
+
+exports.isRightClick = function (event) {
+  return event.button == 2 && exports.noKeyModifiers(event);
+};
+
+exports.noKeyModifiers = function (event) {
+  return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
+};
+
+exports.isControlClick = function (event) {
+  return event.button == 0 && exports.isControl(event);
+};
+
+exports.isShiftClick = function (event) {
+  return event.button == 0 && exports.isShift(event);
+};
+
+exports.isControl = function (event) {
+  return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
+};
+
+exports.isControlShift = function (event) {
+  return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
+};
+
+exports.isShift = function (event) {
+  return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
+};
+
+exports.bind = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments));
+  };
+};
+
+exports.bindFixed = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, args);
+  };
+};
+
+exports.extend = function (l, r) {
+  var newOb = {};
+
+  for (var n in l) {
+    newOb[n] = l[n];
+  }
+
+  for (var n in r) {
+    newOb[n] = r[n];
+  }
+
+  return newOb;
+};
+
+exports.keys = function (map) {
+  var keys = [];
+
+  try {
+    for (var name in map) {
+      keys.push(name);
+    }
+  } catch (exc) {}
+
+  return keys;
+};
+
+exports.values = function (map) {
+  var values = [];
+
+  try {
+    for (var name in map) {
+      try {
+        values.push(map[name]);
+      } catch (exc) {
+        if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+      }
+    }
+  } catch (exc) {
+    if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+  }
+
+  return values;
+};
+
+exports.remove = function (list, item) {
+  for (var i = 0; i < list.length; ++i) {
+    if (list[i] == item) {
+      list.splice(i, 1);
+      break;
+    }
+  }
+};
+
+exports.sliceArray = function (array, index) {
+  var slice = [];
+
+  for (var i = index; i < array.length; ++i) {
+    slice.push(array[i]);
+  }
+
+  return slice;
+};
+
+function cloneArray(array, fn) {
+  var newArray = [];
+  if (fn) for (var i = 0; i < array.length; ++i) {
+    newArray.push(fn(array[i]));
+  } else for (var i = 0; i < array.length; ++i) {
+    newArray.push(array[i]);
+  }
+  return newArray;
+}
+
+function extendArray(array, array2) {
+  var newArray = [];
+  newArray.push.apply(newArray, array);
+  newArray.push.apply(newArray, array2);
+  return newArray;
+}
+
+exports.extendArray = extendArray;
+exports.cloneArray = cloneArray;
+
+function arrayInsert(array, index, other) {
+  for (var i = 0; i < other.length; ++i) {
+    array.splice(i + index, 0, other[i]);
+  }
+
+  return array;
+}
+
+exports.arrayInsert = arrayInsert;
+
+exports.isArrayLike = function (object) {
+  return Object.prototype.toString.call(object) == "[object Array]" || exports.isArguments(object);
+};
+
+exports.isArguments = function (object) {
+  if (Object.prototype.toString.call(object) == "[object Arguments]") return true;
+  if (!_typeof(object) == "object" || !Object.prototype.hasOwnProperty.call(object, 'callee') || !object.callee || Object.prototype.toString.call(object.callee) !== '[object Function]' || typeof object.length != 'number') return false;
+
+  for (var name in object) {
+    if (name === 'callee' || name === 'length') return false;
+  }
+
+  return true;
+};
+},{"deepmerge":5}],5:[function(require,module,exports){
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.deepmerge = factory());
+}(this, (function () { 'use strict';
+
+var isMergeableObject = function isMergeableObject(value) {
+	return isNonNullObject(value)
+		&& !isSpecial(value)
+};
+
+function isNonNullObject(value) {
+	return !!value && typeof value === 'object'
+}
+
+function isSpecial(value) {
+	var stringValue = Object.prototype.toString.call(value);
+
+	return stringValue === '[object RegExp]'
+		|| stringValue === '[object Date]'
+		|| isReactElement(value)
+}
+
+// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
+var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
+var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
+
+function isReactElement(value) {
+	return value.$$typeof === REACT_ELEMENT_TYPE
+}
+
+function emptyTarget(val) {
+	return Array.isArray(val) ? [] : {}
+}
+
+function cloneUnlessOtherwiseSpecified(value, options) {
+	return (options.clone !== false && options.isMergeableObject(value))
+		? deepmerge(emptyTarget(value), value, options)
+		: value
+}
+
+function defaultArrayMerge(target, source, options) {
+	return target.concat(source).map(function(element) {
+		return cloneUnlessOtherwiseSpecified(element, options)
+	})
+}
+
+function mergeObject(target, source, options) {
+	var destination = {};
+	if (options.isMergeableObject(target)) {
+		Object.keys(target).forEach(function(key) {
+			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
+		});
+	}
+	Object.keys(source).forEach(function(key) {
+		if (!options.isMergeableObject(source[key]) || !target[key]) {
+			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
+		} else {
+			destination[key] = deepmerge(target[key], source[key], options);
+		}
+	});
+	return destination
+}
+
+function deepmerge(target, source, options) {
+	options = options || {};
+	options.arrayMerge = options.arrayMerge || defaultArrayMerge;
+	options.isMergeableObject = options.isMergeableObject || isMergeableObject;
+
+	var sourceIsArray = Array.isArray(source);
+	var targetIsArray = Array.isArray(target);
+	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
+
+	if (!sourceAndTargetTypesMatch) {
+		return cloneUnlessOtherwiseSpecified(source, options)
+	} else if (sourceIsArray) {
+		return options.arrayMerge(target, source, options)
+	} else {
+		return mergeObject(target, source, options)
+	}
+}
+
+deepmerge.all = function deepmergeAll(array, options) {
+	if (!Array.isArray(array)) {
+		throw new Error('first argument should be an array')
+	}
+
+	return array.reduce(function(prev, next) {
+		return deepmerge(prev, next, options)
+	}, {})
+};
+
+var deepmerge_1 = deepmerge;
+
+return deepmerge_1;
+
+})));
+
+},{}],6:[function(require,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+exports.PINF = (function (global) {
+
+	if (!global || typeof global !== "object") {
+		throw new Error("No root object scope provided!");
+	}
+
+	// If `PINF` gloabl already exists, don't do anything to change it.
+	if (typeof global.PINF !== "undefined") {
+		return global.PINF;
+	}
+
+	const LOADER = require('./loader');
+
+	const PINF = LOADER.Loader({
+		document: global.document
+	});
+
+	global.PINF = PINF;
+
+	// Attach postMessage handler to listen for sandbox load triggers.
+	// This is useful in Web Workers where only the loader must be loaded and
+	// sandboxes can then be loaded like this:
+	//    worker.postMessage(URIJS("notify://pinf-loader-js/sandbox/load").addSearch("uri", uri).toString())
+	if (typeof global.addEventListener === "function") {
+		global.addEventListener("message", function (event) {
+			var m = null;
+			if (
+				typeof event.data === "string" &&
+				(m = event.data.match(/^notify:\/\/pinf-loader-js\/sandbox\/load\?uri=(.+)$/)) &&
+				(m = decodeURIComponent(m[1])) &&
+				// SECURITY: Only allow URIs that begin with `/` so that scripts may NOT
+				//           be loaded cross-domain this way. If this was allowed one could
+				//           load malicious code simply by posting a message to this window.
+				/^\/[^\/]/.test(m)
+			) {
+				return PINF.sandbox(m, function (sandbox) {
+		            sandbox.main();
+					if (typeof global.postMessage === "function") {
+						global.postMessage(event.data.replace("/load?", "/loaded?"));
+					}
+		        }, function (err) {
+		        	// TODO: Post error back to main frame instead of throwing?
+		        	throw err;
+		        });
+			}
+		}, false);
+	}
+
+	return global.PINF;
+}(
+	typeof window !== "undefined" ?
+		// Used in the browser
+		window :
+		typeof self !== "undefined" ?
+			// Used in web worker
+			self :
+			// No root scope variable found
+			null
+));
+
+},{"./loader":7}],7:[function(require,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+(function (exports) {
+
+// The global `require` for the 'external' (to the loader) environment.
+var Loader = function (global) {
+
+	var loadedBundles = [],
+		// @see https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L69
+		readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 },
+		lastModule = null,
+		headTag = null;
+
+	// For older browsers that don't have `Object.keys()` (Firefox 3.6)
+	function keys(obj) {
+		var keys = [];
+		for (var key in obj) {
+			keys.push(key);
+		}
+		return keys;
+	}
+
+	// TODO: Use 'Object.create()` in modern browsers
+	function create (proto) {
+		function F() {}
+		F.prototype = proto;
+		return new F();
+	};
+
+	function normalizeSandboxArguments (implementation) {
+		return function (programIdentifier, options, loadedCallback, errorCallback) {
+			/*DEBUG*/ if (typeof options === "function" && typeof loadedCallback === "object") {
+			/*DEBUG*/     throw new Error("Callback before options for `require.sandbox(programIdentifier, options, loadedCallback)`");
+			/*DEBUG*/ }
+			if (typeof options === "function" && !loadedCallback && !errorCallback) {
+				loadedCallback = options;
+				options = {};
+			} else
+			if (typeof options === "function" && typeof loadedCallback === "function" && !errorCallback) {
+				errorCallback = loadedCallback;
+				loadedCallback = options;
+				options = {};
+			} else {
+				options = options || {};
+			}
+			implementation(programIdentifier, options, loadedCallback, errorCallback);
+		};
+	}
+
+	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
+		try {
+			// See if we are in a web worker.
+			if (typeof importScripts !== "undefined") {
+				importScripts(uri.replace(/^\/?\{host\}/, ""));
+				return loadedCallback(null);
+			}
+			var document = global.document;
+			/*DEBUG*/ if (!document) {
+			/*DEBUG*/ 	throw new Error("Unable to get reference to 'document'!");
+			/*DEBUG*/ }
+			var location = document.location;
+			if (/^\/?\{host\}\//.test(uri)) {
+				uri = location.protocol + "//" + location.host + uri.replace(/^\/?\{host\}/, "");
+			} else
+			if (/^\/\//.test(uri)) {
+				uri = location.protocol + uri;
+			}
+			if (!headTag) {
+				headTag = document.getElementsByTagName("head")[0];
+			}
+			var element = document.createElement("script");
+			element.type = "text/javascript";
+			element.onload = element.onreadystatechange = function (ev) {
+				ev = ev || global.event;
+				if (ev.type === "load" || readyStates[this.readyState]) {
+					this.onload = this.onreadystatechange = this.onerror = null;
+					loadedCallback(null, function () {
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
+					});
+				}
+			}
+			element.onerror = function (err) {
+				/*DEBUG*/ console.error(err);
+				return loadedCallback(new Error("Error loading '" + uri + "'"));
+			}
+			element.charset = "utf-8";
+			element.async = true;
+			element.src = uri;
+			element = headTag.insertBefore(element, headTag.firstChild);
+		} catch(err) {
+			loadedCallback(err);
+		}
+	}
+
+	// A set of modules working together.
+	var Sandbox = function (sandboxIdentifier, sandboxOptions, loadedCallback) {
+
+		var moduleInitializers = {},
+			initializedModules = {},
+			/*DEBUG*/ bundleIdentifiers = {},
+			packages = {},
+			loadingBundles = {};
+
+		var sandbox = {
+				id: sandboxIdentifier
+			};
+
+		/*DEBUG*/ function logDebug() {
+		/*DEBUG*/ 	if (sandboxOptions.debug !== true) return;
+		/*DEBUG*/ 	// NOTRE: This does not work in google chrome.
+		/*DEBUG*/ 	//console.log.apply(null, arguments);
+		/*DEBUG*/ 	if (arguments.length === 1) {
+		/*DEBUG*/ 		console.log(arguments[0]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 2) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 3) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 4) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
+		/*DEBUG*/ 	}
+		/*DEBUG*/ }
+
+		function rebaseUri (uri) {
+			if (!sandboxOptions.baseUrl) {
+				return uri;
+			}
+			return sandboxOptions.baseUrl + "/" + uri;
+		}
+
+		function load (bundleIdentifier, packageIdentifier, bundleSubPath, loadedCallback) {
+
+			var loadSandboxIdentifier = sandboxIdentifier;
+			var finalBundleIdentifier = null;
+			var moduleIdentifierPrefix = "";
+			var finalPackageIdentifier = "";
+
+			try {
+				if (packageIdentifier !== "") {
+
+					if (/^@bundle:/.test(packageIdentifier)) {
+						var absPackageIdentifier = packageIdentifier;
+						if (/^@bundle:\./.test(absPackageIdentifier)) {
+							absPackageIdentifier = absPackageIdentifier.replace(/^(@bundle:)\./, "$1" + sandboxIdentifier + "/.");
+						}
+						moduleIdentifierPrefix = packageIdentifier;
+						finalPackageIdentifier = packageIdentifier;
+						bundleIdentifier = absPackageIdentifier.replace(/^@bundle:/, "") + ".js";
+						loadSandboxIdentifier = "";
+						finalBundleIdentifier = "@bundle:" + packageIdentifier.replace(/^@bundle:/, "") + ".js";
+					} else {
+						bundleIdentifier = ("/" + packageIdentifier + "/" + bundleIdentifier).replace(/\/+/g, "/");
+					}
+				}
+
+				if (initializedModules[bundleIdentifier]) {
+					// Module is already loaded and initialized.
+					loadedCallback(null, sandbox);
+				} else {
+					// Module is not initialized.
+					if (loadingBundles[bundleIdentifier]) {
+						// Module is already loading.
+						loadingBundles[bundleIdentifier].push(loadedCallback);
+					} else {
+						// Module is not already loading.
+						loadingBundles[bundleIdentifier] = [];
+
+						bundleIdentifier = (loadSandboxIdentifier + bundleSubPath + bundleIdentifier).replace(/\/$/, ".js");
+
+						bundleIdentifier = bundleIdentifier.replace(/\.php\.js$/, ".php");
+
+						if (!finalBundleIdentifier) {
+							finalBundleIdentifier = bundleIdentifier;
+						}
+
+						// Default to our script-injection browser loader.
+						(sandboxOptions.rootBundleLoader || sandboxOptions.load || loadInBrowser)(
+							rebaseUri(bundleIdentifier),
+							function (err, cleanupCallback) {
+								if (err) return loadedCallback(err);
+								// The rootBundleLoader is only applicable for the first load.
+								delete sandboxOptions.rootBundleLoader;
+								finalizeLoad(moduleIdentifierPrefix, finalBundleIdentifier, finalPackageIdentifier, function () {
+									loadedCallback(null, sandbox);
+									if (cleanupCallback) {
+										cleanupCallback();
+									}
+								});
+							},
+							sandboxOptions
+						);
+					}
+				}
+			} catch(err) {
+				loadedCallback(err);
+			}
+		}
+
+		// Called after a bundle has been loaded. Takes the top bundle off the *loading* stack
+		// and makes the new modules available to the sandbox.
+		function finalizeLoad (moduleIdentifierPrefix, bundleIdentifier, packageIdentifier, loadFinalized) {
+			var pending = 0;
+			function finalize () {
+				if (pending !== 0) {
+					return;
+				}
+				if (loadFinalized) loadFinalized();
+			}
+
+			pending += 1;
+
+			// Assume a consistent statically linked set of modules has been memoized.
+			/*DEBUG*/ if (!loadedBundles[0]) {
+			/*DEBUG*/     throw new Error("No bundle memoized for '" + bundleIdentifier + "'! Check the file to ensure it contains JavaScript and that a bundle is memoized against the correct loader instance.");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[bundleIdentifier] = loadedBundles[0][0];
+			var loadedModuleInitializers = loadedBundles[0][1]({
+				id: sandboxIdentifier
+			});
+			var key;
+			for (key in loadedModuleInitializers) {
+
+				var memoizeKey = moduleIdentifierPrefix + key;
+
+				// If we have a package descriptor add it or merge it on top.
+				if (/^[^\/]*\/package.json$/.test(key)) {
+
+					if (sandboxOptions.rewritePackageDescriptor) {
+						loadedModuleInitializers[key][0] = sandboxOptions.rewritePackageDescriptor(loadedModuleInitializers[key][0], memoizeKey);
+					}
+
+					// Load all dependent resources
+					if (loadedModuleInitializers[key][0].mappings) {
+						for (var alias in loadedModuleInitializers[key][0].mappings) {
+							if (/^@script:\/\//.test(loadedModuleInitializers[key][0].mappings[alias])) {
+								pending += 1;
+								loadInBrowser(
+									rebaseUri(loadedModuleInitializers[key][0].mappings[alias].replace(/^@script:/, "")),
+									function () {
+										pending -= 1;
+										finalize();
+									},
+									sandboxOptions
+								);
+							}
+						}
+					}
+
+					// NOTE: Not quite sure if we should allow agumenting package descriptors.
+					//       When doing nested requires using same package we can either add all
+					//		 mappings (included mappings not needed until further down the tree) to
+					//       the first encounter of the package descriptor or add more mappings as
+					//       needed down the road. We currently support both.
+
+					if (moduleInitializers[memoizeKey]) {
+						// TODO: Keep array of bundle identifiers instead of overwriting existing one?
+						//		 Overwriting may change subsequent bundeling behaviour?
+						moduleInitializers[memoizeKey][0] = bundleIdentifier;
+						// Only augment (instead of replace existing values).
+						if (typeof moduleInitializers[memoizeKey][1].main === "undefined") {
+							moduleInitializers[memoizeKey][1].main = loadedModuleInitializers[key][0].main;
+						}
+						if (loadedModuleInitializers[key][0].mappings) {
+							if (!moduleInitializers[memoizeKey][1].mappings) {
+								moduleInitializers[memoizeKey][1].mappings = {};
+							}
+							for (var alias in loadedModuleInitializers[key][0].mappings) {
+								if (typeof moduleInitializers[memoizeKey][1].mappings[alias] === "undefined") {
+									moduleInitializers[memoizeKey][1].mappings[alias] = loadedModuleInitializers[key][0].mappings[alias];
+								}
+							}
+						}
+					} else {
+						moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+					}
+					// Now that we have a [updated] package descriptor, re-initialize it if we have it already in cache.
+					var packageIdentifier = packageIdentifier || key.split("/").shift();
+					if (packages[packageIdentifier]) {
+						packages[packageIdentifier].init();
+					}
+				}
+				// Only add modules that don't already exist!
+				// TODO: Log warning in debug mode if module already exists.
+				if (typeof moduleInitializers[memoizeKey] === "undefined") {
+					moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+				}				
+			}
+			loadedBundles.shift();
+
+			pending -= 1;
+			finalize();
+
+			return;
+		}
+
+		var Package = function (packageIdentifier) {
+			if (packages[packageIdentifier]) {
+				return packages[packageIdentifier];
+			}
+
+			var pkg = {
+				id: packageIdentifier,
+				descriptor: {},
+				main: "/main.js",
+				mappings: {},
+				directories: {},
+				libPath: ""
+			};
+
+			var parentModule = lastModule;
+
+			pkg.init = function () {
+				var descriptor = (moduleInitializers[packageIdentifier + "/package.json"] && moduleInitializers[packageIdentifier + "/package.json"][1]) || {};
+				if (descriptor) {
+					pkg.descriptor = descriptor;
+					if (typeof descriptor.main === "string") {
+						pkg.main = descriptor.main;
+					}
+					pkg.mappings = descriptor.mappings || pkg.mappings;
+					pkg.directories = descriptor.directories || pkg.directories;
+					// NOTE: We need `lib` directory support so that the source directory structure can be mapped
+					//       into the bundle structure without modification. If this is not done, a module doing a relative require
+					//       for a resource outside of the lib directory will not find the file.
+					pkg.libPath = (typeof pkg.directories.lib !== "undefined" && pkg.directories.lib != "") ? pkg.directories.lib + "/" : pkg.libPath;
+				}
+			}
+			pkg.init();
+
+			function normalizeIdentifier (identifier) {
+				// If we have a period (".") in the basename we want an absolute path from
+				// the root of the package. Otherwise a relative path to the "lib" directory.
+				if (identifier.split("/").pop().indexOf(".") === -1) {
+					// We have a module relative to the "lib" directory of the package.
+					identifier = identifier + ".js";
+				} else
+				if (!/^\//.test(identifier)) {
+					// We want an absolute path for the module from the root of the package.
+					identifier = "/" + identifier;
+				}
+				return identifier;
+			}
+
+			var Module = function (moduleIdentifier, parentModule) {
+
+				var moduleIdentifierSegment = null;
+				if (/^@bundle:/.test(moduleIdentifier)) {
+					moduleIdentifierSegment = moduleIdentifier.replace(packageIdentifier, "").replace(/\/[^\/]*$/, "").split("/");
+				} else {
+					moduleIdentifierSegment = moduleIdentifier.replace(/\/[^\/]*$/, "").split("/");
+				}
+
+				var module = {
+						id: moduleIdentifier,
+						exports: {},
+						parentModule: parentModule,
+						bundle: null,
+						pkg: packageIdentifier
+					};
+
+				function resolveIdentifier (identifier) {
+					if (/\/$/.test(identifier)) {
+						identifier += "index";
+					}
+					lastModule = module;
+					// Check for plugin prefix.
+					var plugin = null;
+					if (/^[^!]*!/.test(identifier)) {
+						var m = identifier.match(/^([^!]*)!(.+)$/);
+						identifier = m[2];
+						plugin = m[1];
+					}
+					function pluginify (id) {
+						if (!plugin) return id;
+						id = new String(id);
+						id.plugin = plugin;
+						return id;
+					}
+					// Check for relative module path to module within same package.
+					if (/^\./.test(identifier)) {
+						var segments = identifier.replace(/^\.\//, "").split("../");
+						identifier = "/" + moduleIdentifierSegment.slice(1, moduleIdentifierSegment.length-segments.length+1).concat(segments[segments.length-1]).join("/");
+						if (identifier === "/.") {
+							return [pkg, pluginify("")];
+						}
+						return [pkg, pluginify(normalizeIdentifier(identifier.replace(/\/\.$/, "/")))];
+					}
+					var splitIdentifier = identifier.split("/");
+					// Check for mapped module path to module within mapped package.
+					if (typeof pkg.mappings[splitIdentifier[0]] !== "undefined") {
+						return [Package(pkg.mappings[splitIdentifier[0]]), pluginify((splitIdentifier.length > 1)?normalizeIdentifier(splitIdentifier.slice(1).join("/")):"")];
+					}
+					/*DEBUG*/ if (!moduleInitializers["/" + normalizeIdentifier(identifier)]) {
+					/*DEBUG*/     throw new Error("Descriptor for package '" + pkg.id + "' in sandbox '" + sandbox.id + "' does not declare 'mappings[\"" + splitIdentifier[0] + "\"]' property nor does sandbox have module memoized at '" + "/" + normalizeIdentifier(identifier) + "' needed to satisfy module path '" + identifier + "' in module '" + moduleIdentifier + "'!");
+					/*DEBUG*/ }
+					return [Package(""), pluginify("/" + normalizeIdentifier(identifier))];
+				}
+
+				// Statically link a module and its dependencies
+				module.require = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require(identifier[1]).exports;
+				};
+
+				module.require.supports = [
+					"ucjs-pinf-0"
+				];
+
+				module.require.id = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require.id(identifier[1]);
+				};
+
+				module.require.async = function (identifier, loadedCallback, errorCallback) {
+					identifier = resolveIdentifier(identifier);					
+					var mi = moduleIdentifier;
+					if (/^\//.test(identifier[0].id)) {
+						mi = "/main.js";
+					}
+					identifier[0].load(identifier[1], module.bundle, function (err, moduleAPI) {
+						if (err) {
+							if (errorCallback) return errorCallback(err);
+							throw err;
+						}
+						loadedCallback(moduleAPI);
+					});
+				};
+
+				module.require.sandbox = normalizeSandboxArguments (function (programIdentifier, options, loadedCallback, errorCallback) {
+					options.load = options.load || sandboxOptions.load;
+					// If the `programIdentifier` is relative it is resolved against the URI of the owning sandbox (not the owning page).
+					if (/^\./.test(programIdentifier))
+					{
+						programIdentifier = sandboxIdentifier + "/" + programIdentifier;
+						// HACK: Temporary hack as zombie (https://github.com/assaf/zombie) does not normalize path before sending to server.
+						programIdentifier = programIdentifier.replace(/\/\.\//g, "/");
+					}
+					return PINF.sandbox(programIdentifier, options, loadedCallback, errorCallback);
+				});
+				module.require.sandbox.id = sandboxIdentifier;
+
+				module.load = function () {
+					module.bundle = moduleInitializers[moduleIdentifier][0];
+					if (typeof moduleInitializers[moduleIdentifier][1] === "function") {
+
+						var moduleInterface = {
+							id: module.id,
+							filename:
+								// The `filename` from the meta info attached to the module.
+								// This is typically where the module was originally found on the filesystem.
+								moduleInitializers[moduleIdentifier][2].filename ||
+								// Fall back to the virtual path of the module in the bundle.
+								// TODO: Insert a delimiter between bundle and module id.
+								(module.bundle.replace(/\.js$/, "") + "/" + module.id).replace(/\/+/g, "/"),
+							exports: {}
+						}
+
+						if (packageIdentifier === "" && pkg.main === moduleIdentifier) {
+							module.require.main = moduleInterface;
+						}
+
+						if (sandboxOptions.onInitModule) {
+							sandboxOptions.onInitModule(moduleInterface, module, pkg, sandbox, {
+								normalizeIdentifier: normalizeIdentifier,
+								resolveIdentifier: resolveIdentifier,
+								finalizeLoad: finalizeLoad,
+								moduleInitializers: moduleInitializers,
+								initializedModules: initializedModules
+							});
+						}
+
+						var exports = moduleInitializers[moduleIdentifier][1].call(exports, module.require, module.exports, moduleInterface);
+						if (
+							typeof moduleInterface.exports !== "undefined" &&
+							(
+								typeof moduleInterface.exports !== "object" ||
+								keys(moduleInterface.exports).length !== 0
+							)
+						) {
+							module.exports = moduleInterface.exports;
+						} else
+						if (typeof exports !== "undefined") {
+							module.exports = exports;
+						}
+					} else
+					if (typeof moduleInitializers[moduleIdentifier][1] === "string") {
+						// TODO: Use more optimal string encoding algorythm to reduce payload size?
+						module.exports = decodeURIComponent(moduleInitializers[moduleIdentifier][1]);
+					} else {
+						module.exports = moduleInitializers[moduleIdentifier][1];
+					}
+				};
+
+				/*DEBUG*/ module.getReport = function () {
+				/*DEBUG*/ 	var exportsCount = 0,
+				/*DEBUG*/ 		key;
+				/*DEBUG*/ 	for (key in module.exports) {
+				/*DEBUG*/ 		exportsCount++;
+				/*DEBUG*/ 	}
+				/*DEBUG*/ 	return {
+				/*DEBUG*/ 		exports: exportsCount
+				/*DEBUG*/ 	};
+				/*DEBUG*/ };
+
+				return module;
+			};
+
+			pkg.load = function (moduleIdentifier, bundleIdentifier, loadedCallback) {
+
+				// If module/bundle to be loaded asynchronously is already memoized we skip the load.
+				if (moduleInitializers[packageIdentifier + (moduleIdentifier || pkg.main)]) {
+					return loadedCallback(null, pkg.require(moduleIdentifier).exports);
+				}
+				var bundleSubPath = bundleIdentifier.substring(sandboxIdentifier.length);
+				load(
+					((!/^\//.test(moduleIdentifier))?"/"+pkg.libPath:"") + moduleIdentifier,
+					packageIdentifier,
+					bundleSubPath.replace(/\.js$/g, ""),
+					function (err) {
+						if (err) return loadedCallback(err);
+						loadedCallback(null, pkg.require(moduleIdentifier).exports);
+					}
+				);
+			}
+
+			pkg.require = function (moduleIdentifier) {
+
+				var plugin = moduleIdentifier.plugin;
+
+				if (moduleIdentifier) {
+					if (!/^\//.test(moduleIdentifier)) {
+						moduleIdentifier = ("/" + ((moduleIdentifier.substring(0, pkg.libPath.length)===pkg.libPath)?"":pkg.libPath)).replace(/\/\.\//, "/") + moduleIdentifier;
+					}
+					moduleIdentifier = packageIdentifier + moduleIdentifier;
+				} else {
+					moduleIdentifier = packageIdentifier + pkg.main;
+				}
+
+				if (
+					!moduleInitializers[moduleIdentifier] &&
+					moduleInitializers[moduleIdentifier.replace(/\.js$/, "/index.js")]
+				) {
+					moduleIdentifier = moduleIdentifier.replace(/\.js$/, "/index.js");
+				}
+
+				// Use a specifically formatted module for requested plugin if available
+				if (
+					plugin &&
+					moduleInitializers[moduleIdentifier + ":" + plugin]
+				) {
+					moduleIdentifier += ":" + plugin;
+				}
+
+				if (!initializedModules[moduleIdentifier]) {
+					/*DEBUG*/ if (!moduleInitializers[moduleIdentifier]) {
+					/*DEBUG*/ 	console.error("[pinf-loader-js]", "moduleInitializers", moduleInitializers);
+					/*DEBUG*/ 	throw new Error("Module '" + moduleIdentifier + "' " + (plugin?"for format '" + plugin + "' ":"") + "not found in sandbox '" + sandbox.id + "'!");
+					/*DEBUG*/ }
+					(initializedModules[moduleIdentifier] = Module(moduleIdentifier, lastModule)).load();
+				}
+
+				var loadingBundlesCallbacks;
+				if (loadingBundles[moduleIdentifier]) {
+					loadingBundlesCallbacks = loadingBundles[moduleIdentifier];
+					delete loadingBundles[moduleIdentifier];
+					for (var i=0 ; i<loadingBundlesCallbacks.length ; i++) {
+						loadingBundlesCallbacks[i](null, sandbox);
+					}
+				}
+
+				// TODO: Do this via plugins registered using sandbox options.
+				// TODO: Cache response so we only process files once.
+
+				var moduleInfo = create(initializedModules[moduleIdentifier]);
+				// RequireJS/AMD international strings plugin using root by default.
+				if (plugin === "i18n") {
+					moduleInfo.exports = moduleInfo.exports.root;
+				}
+
+				return moduleInfo;
+			}
+
+			pkg.require.id = function (moduleIdentifier) {
+				if (!/^\//.test(moduleIdentifier)) {
+					moduleIdentifier = "/" + pkg.libPath + moduleIdentifier;
+				}
+				return (((packageIdentifier !== "")?"/"+packageIdentifier+"/":"") + moduleIdentifier).replace(/\/+/g, "/");
+			}
+
+			/*DEBUG*/ pkg.getReport = function () {
+			/*DEBUG*/ 	return {
+			/*DEBUG*/ 		main: pkg.main,
+			/*DEBUG*/ 		mappings: pkg.mappings,
+			/*DEBUG*/ 		directories: pkg.directories,
+			/*DEBUG*/ 		libPath: pkg.libPath
+			/*DEBUG*/ 	};
+			/*DEBUG*/ }
+
+			if (sandboxOptions.onInitPackage) {
+				sandboxOptions.onInitPackage(pkg, sandbox, {
+					normalizeIdentifier: normalizeIdentifier,
+					finalizeLoad: finalizeLoad,
+					moduleInitializers: moduleInitializers,
+					initializedModules: initializedModules
+				});
+			}
+
+			packages[packageIdentifier] = pkg;
+
+			return pkg;
+		}
+
+		// Get a module and initialize it (statically link its dependencies) if it is not already so
+		sandbox.require = function (moduleIdentifier) {
+			return Package("").require(moduleIdentifier).exports;
+		}
+
+		// Call the 'main' module of the program
+		sandbox.boot = function () {
+			/*DEBUG*/ if (typeof Package("").main !== "string") {
+			/*DEBUG*/ 	throw new Error("No 'main' property declared in '/package.json' in sandbox '" + sandbox.id + "'!");
+			/*DEBUG*/ }
+			return sandbox.require(Package("").main);
+		};
+
+		// Call the 'main' exported function of the main' module of the program
+		sandbox.main = function () {
+			var exports = sandbox.boot();
+			return ((exports.main)?exports.main.apply(null, arguments):exports);
+		};
+
+		/*DEBUG*/ sandbox.getReport = function () {
+		/*DEBUG*/ 	var report = {
+		/*DEBUG*/ 			bundles: {},
+		/*DEBUG*/ 			packages: {},
+		/*DEBUG*/ 			modules: {}
+		/*DEBUG*/ 		},
+		/*DEBUG*/ 		key;
+		/*DEBUG*/ 	for (key in bundleIdentifiers) {
+		/*DEBUG*/ 		report.bundles[key] = bundleIdentifiers[key];
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in packages) {
+		/*DEBUG*/ 		report.packages[key] = packages[key].getReport();
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in moduleInitializers) {
+		/*DEBUG*/ 		if (initializedModules[key]) {
+		/*DEBUG*/ 			report.modules[key] = initializedModules[key].getReport();
+		/*DEBUG*/ 		} else {
+		/*DEBUG*/ 			report.modules[key] = {};
+		/*DEBUG*/ 		}
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	return report;
+		/*DEBUG*/ }
+		/*DEBUG*/ sandbox.reset = function () {
+		/*DEBUG*/   moduleInitializers = {};
+		/*DEBUG*/   initializedModules = {};
+		/*DEBUG*/   bundleIdentifiers = {};
+		/*DEBUG*/   packages = {};
+		/*DEBUG*/   loadingBundles = {};
+		/*DEBUG*/ }
+
+		load((sandboxIdentifier.indexOf("?") === -1) ? ".js" : "", "", "", loadedCallback);
+
+		return sandbox;
+	};
+
+	var
+		/*DEBUG*/ bundleIdentifiers = {},
+		sandboxes = {};
+
+	var Require = function (bundle) {
+		var self = this;
+
+		// Address a specific sandbox or currently loading sandbox if initial load.
+		var bundleHandler = function (uid, callback) {
+			/*DEBUG*/ if (uid && bundleIdentifiers[uid]) {
+			/*DEBUG*/ 	throw new Error("You cannot split require.bundle(UID) calls where UID is constant!");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[uid] = true;
+			loadedBundles.push([uid, function (sandbox) {
+				var moduleInitializers = {},
+					req = new Require(uid);
+				delete req.bundle;
+				req.sandbox = sandbox;
+				// Store raw module in loading bundle
+				req.memoize = function (moduleIdentifier, moduleInitializer, moduleMeta) {
+					moduleInitializers[
+						moduleIdentifier +
+						// NOTE: This feature may be elevated to a new function argument to 'memoize' if it proves to be prevalent.
+						(
+							(
+								moduleMeta &&
+								moduleMeta.variation
+							) ? ":" + moduleMeta.variation : ""
+						)
+					] = [moduleInitializer, moduleMeta || {}];
+				}
+				callback(req, global || null);
+				return moduleInitializers;
+			}]);
+		}
+		var activeBundleHandler = bundleHandler;
+		this.bundle = function () {
+			return activeBundleHandler.apply(null, arguments);
+		}
+		this.setActiveBundleHandler = function (handler) {
+			var oldHandler = activeBundleHandler;
+			activeBundleHandler = handler;
+			return oldHandler;
+		}
+	}
+
+	var PINF = new Require();
+
+	// TODO: @see URL_TO_SPEC
+	PINF.supports = [
+		"ucjs-pinf-0"
+	];
+
+	// Create a new environment to memoize modules to.
+	// If relative, the `programIdentifier` is resolved against the URI of the owning page (this is only for the global require).
+	PINF.sandbox = normalizeSandboxArguments(function (programIdentifier, options, loadedCallback, errorCallback) {
+		if (typeof programIdentifier === "function") {
+			options = options || {};
+			var bundle = programIdentifier;
+			var fallbackLoad = options.load || loadInBrowser;
+			options.load = function (uri, loadedCallback) {
+				if (uri === (programIdentifier + ".js")) {
+					PINF.bundle("", bundle);
+					loadedCallback(null);
+					return;
+				}
+				return fallbackLoad(uri, loadedCallback, options);
+			}
+			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
+		}
+		var sandboxIdentifier = programIdentifier.replace(/\.js$/, "");
+		return sandboxes[sandboxIdentifier] = Sandbox(sandboxIdentifier, options, function (err, sandbox) {
+			if (err) {
+				if (errorCallback) return errorCallback(err);
+				throw err;
+			}
+			loadedCallback(sandbox);
+		});
+	});
+
+	PINF.Loader = Loader;
+
+	/*DEBUG*/ PINF.getReport = function () {
+	/*DEBUG*/ 	var report = {
+	/*DEBUG*/ 			sandboxes: {}
+	/*DEBUG*/ 		};
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		report.sandboxes[key] = sandboxes[key].getReport();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	return report;
+	/*DEBUG*/ }
+	/*DEBUG*/ PINF.reset = function () {
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		sandboxes[key].reset();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	sandboxes = {};
+	/*DEBUG*/ 	bundleIdentifiers = {};
+	/*DEBUG*/ 	loadedBundles = [];
+	/*DEBUG*/ }
+
+	return PINF;
+}
+
+if (exports) exports.Loader = Loader;
+
+})(typeof exports !== "undefined" ? exports : null);
+
+},{}]},{},[1])(1)
+});
+
+})((typeof require !== "undefined" && require) || undefined, (typeof exports !== "undefined" && exports) || undefined, (typeof module !== "undefined" && module) || undefined))
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate.js
new file mode 100644
index 0000000..fbd628f
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/domplate.js
@@ -0,0 +1,2463 @@
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var RT = _require_("./rt");
+
+var Renderer = exports.Renderer = _require_("./renderer").Renderer;
+
+function Domplate(exports) {
+  exports.util = _require_("./util");
+
+  exports.loadStyle = function (uri, baseUrl) {
+    var WINDOW = window;
+
+    if (typeof baseUrl === 'undefined' && WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
+      uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
+    } else if (typeof baseUrl !== 'undefined') {
+      uri = [baseUrl, uri].join("/").replace(/\/\.?\//g, "/");
+    }
+
+    return new Promise(function (resolve, reject) {
+      var link = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "link") : window.document.createElement("link");
+      link.rel = "stylesheet";
+      link.href = uri;
+
+      link.onload = function () {
+        resolve();
+      };
+
+      var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+      head.appendChild(link);
+    });
+  };
+
+  exports.EVAL = {
+    compileMarkup: function compileMarkup(code, context) {
+      return context.compiled(context);
+    },
+    compileDOM: function compileDOM(code, context) {
+      return context.compiled(context);
+    }
+  };
+  exports.tags = {};
+  exports.tags._domplate_ = exports;
+
+  var DomplateTag = exports.DomplateTag = function DomplateTag(tagName) {
+    this.tagName = tagName;
+  };
+
+  function DomplateEmbed() {}
+
+  function DomplateLoop() {}
+
+  function DomplateIf() {}
+
+  function copyArray(oldArray) {
+    var ary = [];
+    if (oldArray) for (var i = 0; i < oldArray.length; ++i) {
+      ary.push(oldArray[i]);
+    }
+    return ary;
+  }
+
+  function copyObject(l, r) {
+    var m = {};
+    extend(m, l);
+    extend(m, r);
+    return m;
+  }
+
+  function extend(l, r) {
+    for (var n in r) {
+      l[n] = r[n];
+    }
+  }
+
+  var womb = null;
+
+  var domplate = exports.domplate = function () {
+    var lastSubject;
+
+    for (var i = 0; i < arguments.length; ++i) {
+      lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
+    }
+
+    for (var name in lastSubject) {
+      var val = lastSubject[name];
+      if (isTag(val)) val.tag.subject = lastSubject;
+    }
+
+    return lastSubject;
+  };
+
+  domplate.context = function (context, fn) {
+    var lastContext = domplate.lastContext;
+    domplate.topContext = context;
+    fn.apply(context);
+    domplate.topContext = lastContext;
+  };
+
+  exports.tags.TAG = function () {
+    var embed = new DomplateEmbed();
+    return embed.merge(arguments);
+  };
+
+  exports.tags.FOR = domplate.FOR = function () {
+    var loop = new DomplateLoop();
+    return loop.merge(arguments);
+  };
+
+  exports.tags.IF = domplate.IF = function () {
+    var loop = new DomplateIf();
+    return loop.merge(arguments);
+  };
+
+  DomplateTag.prototype = {
+    merge: function merge(args, oldTag) {
+      if (oldTag) this.tagName = oldTag.tagName;
+      this.context = oldTag ? oldTag.context : null;
+      this.subject = oldTag ? oldTag.subject : null;
+      this.attrs = oldTag ? copyObject(oldTag.attrs) : {};
+      this.classes = oldTag ? copyObject(oldTag.classes) : {};
+      this.props = oldTag ? copyObject(oldTag.props) : null;
+      this.listeners = oldTag ? copyArray(oldTag.listeners) : null;
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args.length ? args[0] : null;
+      var hasAttrs = _typeof(attrs) == "object" && !isTag(attrs);
+      this.resources = {};
+      this.children = [];
+      if (domplate.topContext) this.context = domplate.topContext;
+      if (args.length) parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
+      if (hasAttrs) this.parseAttrs(attrs);
+      return creator(this, DomplateTag);
+    },
+    parseAttrs: function parseAttrs(args) {
+      for (var name in args) {
+        var val = parseValue(args[name]);
+        readPartNames(val, this.vars);
+
+        if (name.indexOf("on") === 0) {
+          var eventName = name.substr(2);
+          if (!this.listeners) this.listeners = [];
+          this.listeners.push(eventName, val);
+        } else if (name[0] === "_" && name[1] !== "_") {
+          var propName = name.substr(1);
+          if (!this.props) this.props = {};
+          this.props[propName] = val;
+        } else if (name[0] === "$") {
+          var className = name.substr(1);
+          if (!this.classes) this.classes = {};
+          this.classes[className] = val;
+        } else {
+          if (name === "class" && this.attrs.hasOwnProperty(name)) this.attrs[name] += " " + val;else this.attrs[name] = val;
+        }
+      }
+    },
+    compile: function compile() {
+      if (this.renderMarkup) {
+        return;
+      }
+
+      if (this.subject._resources) {
+        this.resources = this.subject._resources();
+      }
+
+      this.compileMarkup();
+      this.compileDOM();
+    },
+    compileMarkup: function compileMarkup() {
+      this.markupArgs = [];
+      var topBlock = [],
+          topOuts = [],
+          blocks = [],
+          info = {
+        args: this.markupArgs,
+        argIndex: 0
+      };
+      this.generateMarkup(topBlock, topOuts, blocks, info, true);
+      this.addCode(topBlock, topOuts, blocks);
+      var fnBlock = ['(function (__code__, __context__, __in__, __out__'];
+
+      for (var i = 0; i < info.argIndex; ++i) {
+        fnBlock.push(', s', i);
+      }
+
+      fnBlock.push(') {');
+      if (this.subject) fnBlock.push('  with (this) {');
+      if (this.context) fnBlock.push('  with (__context__) {');
+      fnBlock.push('  with (__in__) {');
+      fnBlock.push.apply(fnBlock, blocks);
+      if (this.subject) fnBlock.push('  }');
+      if (this.context) fnBlock.push('  }');
+      fnBlock.push('}})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderMarkup = exports.EVAL.compileMarkup(js, RT.makeMarkupRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__markup
+      }));
+    },
+    getVarNames: function getVarNames(args) {
+      if (this.vars) args.push.apply(args, this.vars);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.getVarNames(args);else if (child instanceof Parts) {
+          for (var i = 0; i < child.parts.length; ++i) {
+            if (child.parts[i] instanceof Variables) {
+              var name = child.parts[i].names[0];
+              var names = name.split(".");
+              args.push(names[0]);
+            }
+          }
+        }
+      }
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info, topNode) {
+      topBlock.push(',"<', this.tagName, '"');
+
+      if (topNode) {
+        if (this.subject.__dbid) this.attrs['__dbid'] = this.subject.__dbid;
+        if (this.subject.__dtid) this.attrs['__dtid'] = this.subject.__dtid;
+      }
+
+      for (var name in this.attrs) {
+        if (name != "class") {
+          var val = this.attrs[name];
+          topBlock.push(', " ', name, '=\\""');
+          addParts(val, ',', topBlock, info, true);
+          topBlock.push(', "\\""');
+        }
+      }
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          readPartNames(this.listeners[i + 1], topOuts);
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          readPartNames(this.props[name], topOuts);
+        }
+      }
+
+      if (this.attrs.class || this.classes && Object.keys(this.classes).length > 0) {
+        topBlock.push(', " class=\\""');
+        if (this.attrs.hasOwnProperty("class")) addParts(this.attrs["class"], ',', topBlock, info, true);
+        topBlock.push(', " "');
+
+        for (var name in this.classes) {
+          topBlock.push(', (');
+          addParts(this.classes[name], '', topBlock, info);
+          topBlock.push(' ? "', name, '" + " " : "")');
+        }
+
+        topBlock.push(', "\\""');
+      }
+
+      if (this.tagName == "br") {
+        topBlock.push(',"/>"');
+      } else {
+        topBlock.push(',">"');
+        this.generateChildMarkup(topBlock, topOuts, blocks, info);
+        topBlock.push(',"</', this.tagName, '>"');
+      }
+    },
+    generateChildMarkup: function generateChildMarkup(topBlock, topOuts, blocks, info) {
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) child.tag.generateMarkup(topBlock, topOuts, blocks, info);else addParts(child, ',', topBlock, info, true);
+      }
+    },
+    addCode: function addCode(topBlock, topOuts, blocks) {
+      if (topBlock.length) blocks.push('    __code__.push(""', topBlock.join(""), ');');
+      if (topOuts.length) blocks.push('__out__.push(', topOuts.join(","), ');');
+      topBlock.splice(0, topBlock.length);
+      topOuts.splice(0, topOuts.length);
+    },
+    addLocals: function addLocals(blocks) {
+      var varNames = [];
+      this.getVarNames(varNames);
+      var map = {};
+
+      for (var i = 0; i < varNames.length; ++i) {
+        var name = varNames[i];
+        if (map.hasOwnProperty(name)) continue;
+        map[name] = 1;
+        var names = name.split(".");
+        blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';');
+      }
+    },
+    compileDOM: function compileDOM() {
+      var path = [];
+      var blocks = [];
+      this.domArgs = [];
+      path.embedIndex = 0;
+      path.loopIndex = 0;
+      path.ifIndex = 0;
+      path.staticIndex = 0;
+      path.renderIndex = 0;
+      var nodeCount = this.generateDOM(path, blocks, this.domArgs);
+      var fnBlock = ['(function (root, context, o'];
+
+      for (var i = 0; i < path.staticIndex; ++i) {
+        fnBlock.push(', ', 's' + i);
+      }
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        fnBlock.push(', ', 'd' + i);
+      }
+
+      fnBlock.push(') {');
+
+      for (var i = 0; i < path.loopIndex; ++i) {
+        fnBlock.push('  var l', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.ifIndex; ++i) {
+        fnBlock.push('  var if_', i, ' = 0;');
+      }
+
+      for (var i = 0; i < path.embedIndex; ++i) {
+        fnBlock.push('  var e', i, ' = 0;');
+      }
+
+      if (this.subject) {
+        fnBlock.push('  with (this) {');
+      }
+
+      if (this.context) {
+        fnBlock.push('    with (context) {');
+      }
+
+      fnBlock.push(blocks.join(""));
+      if (this.context) fnBlock.push('    }');
+      if (this.subject) fnBlock.push('  }');
+      fnBlock.push('  return ', nodeCount, ';');
+      fnBlock.push('})');
+      var self = this;
+      var js = fnBlock.join("");
+      js = js.replace('__SELF__JS__', js.replace(/\'/g, '\\\''));
+      this.renderDOM = exports.EVAL.compileDOM(js, RT.makeDOMRuntime(exports.EVAL, {
+        self: self,
+        compiled: this.subject.__dom
+      }));
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      if (this.listeners || this.props) this.generateNodePath(path, blocks);
+
+      if (this.listeners) {
+        for (var i = 0; i < this.listeners.length; i += 2) {
+          var val = this.listeners[i + 1];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);');
+        }
+      }
+
+      if (this.props) {
+        for (var name in this.props) {
+          var val = this.props[name];
+          var arg = generateArg(val, path, args);
+          blocks.push('node.', name, ' = ', arg, ';');
+        }
+      }
+
+      this.generateChildDOM(path, blocks, args);
+      return 1;
+    },
+    generateNodePath: function generateNodePath(path, blocks) {
+      blocks.push("        node = __path__(root, o");
+
+      for (var i = 0; i < path.length; ++i) {
+        blocks.push(",", path[i]);
+      }
+
+      blocks.push(");");
+    },
+    generateChildDOM: function generateChildDOM(path, blocks, args) {
+      path.push(0);
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) path[path.length - 1] += '+' + child.tag.generateDOM(path, blocks, args);else path[path.length - 1] += '+1';
+      }
+
+      path.pop();
+    }
+  };
+  DomplateEmbed.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.value = oldTag ? oldTag.value : parseValue(args[0]);
+      this.attrs = oldTag ? oldTag.attrs : {};
+      this.vars = oldTag ? copyArray(oldTag.vars) : [];
+      var attrs = args[1];
+
+      for (var name in attrs) {
+        var val = parseValue(attrs[name]);
+        this.attrs[name] = val;
+        readPartNames(val, this.vars);
+      }
+
+      var retval = creator(this, DomplateEmbed);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.value instanceof Parts) names.push(this.value.parts[0].name);
+      if (this.vars) names.push.apply(names, this.vars);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('__link__(');
+      addParts(this.value, '', blocks, info);
+      blocks.push(', __code__, __out__, {');
+      var lastName = null;
+
+      for (var name in this.attrs) {
+        if (lastName) blocks.push(',');
+        lastName = name;
+        var val = this.attrs[name];
+        blocks.push('"', name, '":');
+        addParts(val, '', blocks, info);
+      }
+
+      blocks.push('});');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var embedName = 'e' + path.embedIndex++;
+      this.generateNodePath(path, blocks);
+      var valueName = 'd' + path.renderIndex++;
+      var argsName = 'd' + path.renderIndex++;
+      blocks.push('        ', embedName + ' = __link__(node, ', valueName, ', ', argsName, ');');
+      return embedName;
+    }
+  });
+  DomplateLoop.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.varName = oldTag ? oldTag.varName : args[0];
+      this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 2);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateLoop);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.iter instanceof Parts) names.push(this.iter.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var iterName;
+
+      if (this.iter instanceof Parts) {
+        var part = this.iter.parts[0];
+        iterName = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            iterName = part.format[i] + "(" + iterName + ")";
+          }
+        }
+      } else {
+        iterName = this.iter;
+      }
+
+      blocks.push('    __loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('    }]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var iterName = 'd' + path.renderIndex++;
+      var counterName = 'i' + path.loopIndex;
+      var loopName = 'l' + path.loopIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+      var basePath = path[path.length - 1];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        path[path.length - 1] = basePath + '+' + loopName + '+' + nodeCount;
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      path[path.length - 1] = basePath + '+' + loopName;
+      blocks.push('      ', loopName, ' = __loop__.apply(this, [', iterName, ', function(', counterName, ',', loopName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('        return ', nodeCount, ';');
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return loopName;
+    }
+  });
+  DomplateIf.prototype = copyObject(DomplateTag.prototype, {
+    merge: function merge(args, oldTag) {
+      this.booleanVar = oldTag ? oldTag.booleanVar : parseValue(args[0]);
+      this.vars = [];
+      this.children = oldTag ? copyArray(oldTag.children) : [];
+      var offset = Math.min(args.length, 1);
+      parseChildren(args, offset, this.vars, this.children);
+      var retval = creator(this, DomplateIf);
+      return retval;
+    },
+    getVarNames: function getVarNames(names) {
+      if (this.booleanVar instanceof Parts) names.push(this.booleanVar.parts[0].name);
+      DomplateTag.prototype.getVarNames.apply(this, [names]);
+    },
+    generateMarkup: function generateMarkup(topBlock, topOuts, blocks, info) {
+      this.addCode(topBlock, topOuts, blocks);
+      var expr;
+
+      if (this.booleanVar instanceof Parts) {
+        var part = this.booleanVar.parts[0];
+        expr = part.names.join(',');
+
+        if (part.format) {
+          for (var i = 0; i < part.format.length; ++i) {
+            expr = part.format[i] + "(" + expr + ")";
+          }
+        }
+      } else {
+        expr = this.booleanVar;
+      }
+
+      blocks.push('__if__.apply(this, [', expr, ', __out__, function(__out__) {');
+      this.generateChildMarkup(topBlock, topOuts, blocks, info);
+      this.addCode(topBlock, topOuts, blocks);
+      blocks.push('}]);');
+    },
+    generateDOM: function generateDOM(path, blocks, args) {
+      var controlName = 'd' + path.renderIndex++;
+      var ifName = 'if_' + path.ifIndex++;
+      if (!path.length) path.push(-1, 0);
+      var preIndex = path.renderIndex;
+      path.renderIndex = 0;
+      var nodeCount = 0;
+      var subBlocks = [];
+
+      for (var i = 0; i < this.children.length; ++i) {
+        var child = this.children[i];
+        if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);else nodeCount += '+1';
+      }
+
+      blocks.push('      ', ifName, ' = __if__.apply(this, [', controlName, ', function(', ifName);
+
+      for (var i = 0; i < path.renderIndex; ++i) {
+        blocks.push(',d' + i);
+      }
+
+      blocks.push(') {');
+      blocks.push(subBlocks.join(""));
+      blocks.push('      }]);');
+      path.renderIndex = preIndex;
+      return controlName;
+    }
+  });
+
+  function Variables(names, format) {
+    this.names = names;
+    this.format = format;
+  }
+
+  function Parts(parts) {
+    this.parts = parts;
+  }
+
+  function parseParts(str) {
+    var index = 0;
+    var parts = [];
+    var m;
+    var re = /\$([_A-Za-z][$_A-Za-z0-9.,|]*)/g;
+
+    while (m = re.exec(str)) {
+      var pre = str.substr(index, re.lastIndex - m[0].length - index);
+      if (pre) parts.push(pre);
+      var segs = m[1].split("|");
+      var vars = segs[0].split(",$");
+      parts.push(new Variables(vars, segs.splice(1)));
+      index = re.lastIndex;
+    }
+
+    if (!index) {
+      return str;
+    }
+
+    var post = str.substr(index);
+    if (post) parts.push(post);
+    var retval = new Parts(parts);
+    return retval;
+  }
+
+  function parseValue(val) {
+    return typeof val == 'string' ? parseParts(val) : val;
+  }
+
+  function parseChildren(args, offset, vars, children) {
+    for (var i = offset; i < args.length; ++i) {
+      var val = parseValue(args[i]);
+      children.push(val);
+      readPartNames(val, vars);
+    }
+  }
+
+  function readPartNames(val, vars) {
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+        if (part instanceof Variables) vars.push(part.names[0]);
+      }
+    }
+  }
+
+  function generateArg(val, path, args) {
+    if (val instanceof Parts) {
+      var vals = [];
+
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var varName = 'd' + path.renderIndex++;
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              varName = part.format[j] + '(' + varName + ')';
+            }
+          }
+
+          vals.push(varName);
+        } else vals.push('"' + part.replace(/"/g, '\\"') + '"');
+      }
+
+      return vals.join('+');
+    } else {
+      args.push(val);
+      return 's' + path.staticIndex++;
+    }
+  }
+
+  function addParts(val, delim, block, info, escapeIt) {
+    var vals = [];
+
+    if (val instanceof Parts) {
+      for (var i = 0; i < val.parts.length; ++i) {
+        var part = val.parts[i];
+
+        if (part instanceof Variables) {
+          var partName = part.names.join(",");
+
+          if (part.format) {
+            for (var j = 0; j < part.format.length; ++j) {
+              partName = part.format[j] + "(" + partName + ")";
+            }
+          }
+
+          if (escapeIt) vals.push("__escape__(" + partName + ")");else vals.push(partName);
+        } else vals.push('"' + part + '"');
+      }
+    } else if (isTag(val)) {
+      info.args.push(val);
+      vals.push('s' + info.argIndex++);
+    } else vals.push('"' + val + '"');
+
+    var parts = vals.join(delim);
+    if (parts) block.push(delim, parts);
+  }
+
+  function isTag(obj) {
+    return (typeof obj == "function" || obj instanceof Function) && !!obj.tag;
+  }
+
+  function creator(tag, cons) {
+    var fn = function fn() {
+      var tag = arguments.callee.tag;
+      var cons = arguments.callee.cons;
+      var newTag = new cons();
+      return newTag.merge(arguments, tag);
+    };
+
+    fn.tag = tag;
+    fn.cons = cons;
+    extend(fn, Renderer);
+    return fn;
+  }
+
+  function defineTags() {
+    Array.from(arguments).forEach(function (tagName) {
+      var fnName = tagName.toUpperCase();
+
+      exports.tags[fnName] = function () {
+        var newTag = new this._domplate_.DomplateTag(tagName);
+        return newTag.merge(arguments);
+      };
+    });
+  }
+
+  defineTags("a", "button", "br", "canvas", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select", "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul");
+}
+
+exports.domplate = {};
+Domplate(exports.domplate);
+
+exports.domplate.ensureLoader = function () {
+  if (typeof window !== "undefined" && window.PINF) {
+    return window.PINF;
+  }
+
+  var PINF = _require_("pinf-loader-js/loader.browser");
+
+  return window.PINF;
+};
+
+exports.domplate.loadRep = function (url, options, successCallback, errorCallback) {
+  if (typeof options === "function") {
+    errorCallback = successCallback;
+    successCallback = options;
+    options = {};
+  }
+
+  var PINF = exports.domplate.ensureLoader();
+  return PINF.sandbox(url + ".rep", function (sandbox) {
+    var rep = sandbox.main(exports.domplate, options);
+    successCallback(rep);
+  }, errorCallback);
+};
+},{"./renderer":2,"./rt":3,"./util":4,"pinf-loader-js/loader.browser":6}],2:[function(_require_,module,exports){
+
+var Renderer = exports.Renderer = {
+  checkDebug: function checkDebug() {},
+  renderHTML: function renderHTML(args, outputs, self) {
+    var code = [];
+    var markupArgs = [code, this.tag.context ? this.tag.context : null, args, outputs];
+    markupArgs.push.apply(markupArgs, this.tag.markupArgs);
+    this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
+
+    if (this.tag.resources && this.tag.subject._resourceListener) {
+      this.tag.subject._resourceListener.register(this.tag.resources);
+    }
+
+    return code.join("");
+  },
+  insertRows: function insertRows(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var table = doc.createElement("table");
+    table.innerHTML = html;
+    var tbody = table.firstChild;
+    var parent = before.localName == "TR" ? before.parentNode : before;
+    var after = before.localName == "TR" ? before.nextSibling : null;
+    var firstRow = tbody.firstChild,
+        lastRow;
+
+    while (tbody.firstChild) {
+      lastRow = tbody.firstChild;
+      if (after) parent.insertBefore(lastRow, after);else parent.appendChild(lastRow);
+    }
+
+    var offset = 0;
+
+    if (before.localName == "TR") {
+      var node = firstRow.parentNode.firstChild;
+
+      for (; node && node != firstRow; node = node.nextSibling) {
+        ++offset;
+      }
+    }
+
+    var domArgs = [firstRow, this.tag.context, offset];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return [firstRow, lastRow];
+  },
+  insertAfter: function insertAfter(args, before, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var doc = before.ownerDocument;
+    var range = doc.createRange();
+    range.selectNode(doc.body);
+    var frag = range.createContextualFragment(html);
+    var root = frag.firstChild;
+    if (before.nextSibling) before.parentNode.insertBefore(frag, before.nextSibling);else before.parentNode.appendChild(frag);
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject ? this.tag.subject : null, domArgs);
+    return root;
+  },
+  replace: function replace(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    var root;
+
+    if (parent.nodeType == 1) {
+      parent.innerHTML = html;
+      root = parent.firstChild;
+    } else {
+      if (!parent || parent.nodeType != 9) parent = document;
+      if (!womb || womb.ownerDocument != parent) womb = parent.createElement("div");
+      womb.innerHTML = html;
+      root = womb.firstChild;
+    }
+
+    var domArgs = [root, this.tag.context ? this.tag.context : null, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  append: function append(args, parent, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    if (!womb || womb.ownerDocument != parent.ownerDocument) womb = parent.ownerDocument.createElement("div");
+    womb.innerHTML = html;
+    root = womb.firstChild;
+
+    while (womb.firstChild) {
+      parent.appendChild(womb.firstChild);
+    }
+
+    var domArgs = [root, this.tag.context, 0];
+    domArgs.push.apply(domArgs, this.tag.domArgs);
+    domArgs.push.apply(domArgs, outputs);
+    this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
+    return root;
+  },
+  render: function render(args, self) {
+    this.tag.compile();
+    var outputs = [];
+    var html = this.renderHTML(args, outputs, self);
+    return html;
+  }
+};
+},{}],3:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+function ArrayIterator(array) {
+  var index = -1;
+
+  this.next = function () {
+    if (++index >= array.length) throw StopIteration;
+    return array[index];
+  };
+}
+
+function StopIteration() {}
+
+exports.makeMarkupRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__link__ = function (tag, code, outputs, args) {
+    if (!tag) {
+      return;
+    }
+
+    if (!tag.tag) {
+      return;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return;
+    }
+
+    tag.tag.compile();
+
+    if (self.resources && tag.tag.resources && tag.tag.resources !== self.resources) {
+      for (var key in tag.tag.resources) {
+        self.resources[key] = tag.tag.resources[key];
+      }
+    }
+
+    var tagOutputs = [];
+    var markupArgs = [code, tag.tag.context ? tag.tag.context : null, args, tagOutputs];
+    markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
+    tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
+    outputs.push(tag);
+    outputs.push(tagOutputs);
+  };
+
+  exports.__escape__ = function (value) {
+    function replaceChars(ch) {
+      switch (ch) {
+        case "<":
+          return "&lt;";
+
+        case ">":
+          return "&gt;";
+
+        case "&":
+          return "&amp;";
+
+        case "'":
+          return "&#39;";
+
+        case '"':
+          return "&quot;";
+      }
+
+      return "?";
+    }
+
+    ;
+    return String(value).replace(/[<>&"']/g, replaceChars);
+  };
+
+  exports.__loop__ = function (iter, outputs, fn) {
+    var iterOuts = [];
+    outputs.push(iterOuts);
+
+    if (iter instanceof Array || typeof iter === "array" || Array.isArray(iter)) {
+      iter = new ArrayIterator(iter);
+    }
+
+    try {
+      if (!iter || !iter.next) {
+        console.error("Cannot iterate loop", iter, _typeof(iter), outputs, fn);
+        throw new Error("Cannot iterate loop as iter.next() method is not defined");
+      }
+
+      while (1) {
+        var value = iter.next();
+        var itemOuts = [0, 0];
+        iterOuts.push(itemOuts);
+        fn.apply(this, [value, itemOuts]);
+      }
+    } catch (exc) {
+      if (exc != StopIteration) throw exc;
+    }
+  };
+
+  exports.__if__ = function (booleanVar, outputs, fn) {
+    var ifControl = [];
+    outputs.push(ifControl);
+
+    if (booleanVar) {
+      ifControl.push(1);
+      fn.apply(this, [ifControl]);
+    } else {
+      ifControl.push(0);
+    }
+  };
+
+  return exports;
+};
+
+exports.makeDOMRuntime = function (EVAL, context) {
+  var self = context.self;
+  var tagName = null;
+  Object.keys(self.subject).forEach(function (name) {
+    if (self.subject[name].tag === self) {
+      tagName = name;
+    }
+  });
+
+  if (!tagName) {
+    throw new Error("Unable to determine 'tagName'!");
+  }
+
+  var exports = {};
+  exports.compiled = context.compiled && context.compiled[tagName] || null;
+
+  exports.__bind__ = function (object, fn) {
+    return function (event) {
+      return fn.apply(object, [event]);
+    };
+  };
+
+  exports.__link__ = function (node, tag, args) {
+    if (!tag) {
+      return 0;
+    }
+
+    if (!tag.tag) {
+      return 0;
+    }
+
+    if (!exports.compiled && EVAL.onMarkupCode) {
+      return 0;
+    }
+
+    tag.tag.compile();
+    var domArgs = [node, tag.tag.context ? tag.tag.context : null, 0];
+    domArgs.push.apply(domArgs, tag.tag.domArgs);
+    domArgs.push.apply(domArgs, args);
+    var oo = tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
+    return oo;
+  };
+
+  exports.__loop__ = function (iter, fn) {
+    if (!Array.isArray(iter)) {
+      return 0;
+    }
+
+    var nodeCount = 0;
+
+    for (var i = 0; i < iter.length; ++i) {
+      iter[i][0] = i;
+      iter[i][1] = nodeCount;
+      nodeCount += fn.apply(this, iter[i]);
+    }
+
+    return nodeCount;
+  };
+
+  exports.__if__ = function (control, fn) {
+    if (control && control[0]) {
+      fn.apply(this, [0, control[1]]);
+    } else {}
+  };
+
+  exports.__path__ = function (parent, offset) {
+    var root = parent;
+
+    for (var i = 2; i < arguments.length; ++i) {
+      var index = arguments[i];
+      if (i == 3) index += offset;
+
+      if (index == -1) {
+        parent = parent.parentNode;
+      } else {
+        parent = parent.childNodes[index];
+      }
+    }
+
+    return parent;
+  };
+
+  return exports;
+};
+},{}],4:[function(_require_,module,exports){
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var FBTrace = {};
+exports.merge = _require_("deepmerge");
+
+exports.escapeNewLines = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+};
+
+exports.stripNewLines = function (value) {
+  return typeof value == "string" ? value.replace(/[\r\n]/gm, " ") : value;
+};
+
+exports.escapeJS = function (value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n").replace('"', '\\"', "g");
+};
+
+exports.cropString = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  var halfLimit = limit / 2;
+  halfLimit -= 2;
+  if (text.length > limit) return text.substr(0, halfLimit) + alterText + text.substr(text.length - halfLimit);else return text;
+};
+
+exports.cropStringLeft = function (text, limit, alterText) {
+  if (!alterText) alterText = "...";
+  text = text + "";
+  if (!limit) limit = 50;
+  limit -= alterText.length;
+  if (text.length > limit) return alterText + text.substr(text.length - limit);else return text;
+};
+
+exports.hasClass = function (node, name) {
+  if (!node || node.nodeType != 1) return false;else {
+    for (var i = 1; i < arguments.length; ++i) {
+      var name = arguments[i];
+      var re = new RegExp("(^|\\s)" + name + "($|\\s)");
+      if (!re.exec(node.getAttribute("class"))) return false;
+    }
+
+    return true;
+  }
+};
+
+exports.setClass = function (node, name) {
+  if (node && !exports.hasClass(node, name)) node.className += " " + name;
+};
+
+exports.getClassValue = function (node, name) {
+  var re = new RegExp(name + "-([^ ]+)");
+  var m = re.exec(node.className);
+  return m ? m[1] : "";
+};
+
+exports.removeClass = function (node, name) {
+  if (node && node.className) {
+    var index = node.className.indexOf(name);
+
+    if (index >= 0) {
+      var size = name.length;
+      node.className = node.className.substr(0, index - 1) + node.className.substr(index + size);
+    }
+  }
+};
+
+exports.toggleClass = function (elt, name) {
+  if (exports.hasClass(elt, name)) exports.removeClass(elt, name);else exports.setClass(elt, name);
+};
+
+exports.setClassTimed = function (elt, name, context, timeout) {
+  if (!timeout) timeout = 1300;
+  if (elt.__setClassTimeout) context.clearTimeout(elt.__setClassTimeout);else exports.setClass(elt, name);
+
+  if (!exports.isVisible(elt)) {
+    if (elt.__invisibleAtSetPoint) elt.__invisibleAtSetPoint--;else elt.__invisibleAtSetPoint = 5;
+  } else {
+    delete elt.__invisibleAtSetPoint;
+  }
+
+  elt.__setClassTimeout = context.setTimeout(function () {
+    delete elt.__setClassTimeout;
+    if (elt.__invisibleAtSetPoint) exports.setClassTimed(elt, name, context, timeout);else {
+      delete elt.__invisibleAtSetPoint;
+      exports.removeClass(elt, name);
+    }
+  }, timeout);
+};
+
+exports.cancelClassTimed = function (elt, name, context) {
+  if (elt.__setClassTimeout) {
+    exports.removeClass(elt, name);
+    context.clearTimeout(elt.__setClassTimeout);
+    delete elt.__setClassTimeout;
+  }
+};
+
+exports.$ = function (id, doc) {
+  if (doc) return doc.getElementById(id);else return document.getElementById(id);
+};
+
+exports.getChildByClass = function (node) {
+  for (var i = 1; i < arguments.length; ++i) {
+    var className = arguments[i];
+    var child = node.firstChild;
+    node = null;
+
+    for (; child; child = child.nextSibling) {
+      if (exports.hasClass(child, className)) {
+        node = child;
+        break;
+      }
+    }
+  }
+
+  return node;
+};
+
+exports.getAncestorByClass = function (node, className) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (exports.hasClass(parent, className)) return parent;
+  }
+
+  return null;
+};
+
+exports.getElementByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  var elements = node.getElementsByClassName(className);
+  return elements[0];
+};
+
+exports.getElementsByClass = function (node, className) {
+  var args = cloneArray(arguments);
+  args.splice(0, 1);
+  var className = args.join(" ");
+  return node.getElementsByClassName(className);
+};
+
+exports.getElementsByAttribute = function (node, attrName, attrValue) {
+  function iteratorHelper(node, attrName, attrValue, result) {
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      if (child.getAttribute(attrName) == attrValue) result.push(child);
+      iteratorHelper(child, attrName, attrValue, result);
+    }
+  }
+
+  var result = [];
+  iteratorHelper(node, attrName, attrValue, result);
+  return result;
+};
+
+exports.isAncestor = function (node, potentialAncestor) {
+  for (var parent = node; parent; parent = parent.parentNode) {
+    if (parent == potentialAncestor) return true;
+  }
+
+  return false;
+};
+
+exports.getNextElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.nextSibling;
+  }
+
+  return node;
+};
+
+exports.getPreviousElement = function (node) {
+  while (node && node.nodeType != 1) {
+    node = node.previousSibling;
+  }
+
+  return node;
+};
+
+exports.getBody = function (doc) {
+  if (doc.body) return doc.body;
+  var body = doc.getElementsByTagName("body")[0];
+  if (body) return body;
+  return doc.documentElement;
+};
+
+exports.findNextDown = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (criteria(child)) return child;
+    var next = exports.findNextDown(child, criteria);
+    if (next) return next;
+  }
+};
+
+exports.findPreviousUp = function (node, criteria) {
+  if (!node) return null;
+
+  for (var child = node.lastChild; child; child = child.previousSibling) {
+    var next = exports.findPreviousUp(child, criteria);
+    if (next) return next;
+    if (criteria(child)) return child;
+  }
+};
+
+exports.findNext = function (node, criteria, upOnly, maxRoot) {
+  if (!node) return null;
+
+  if (!upOnly) {
+    var next = exports.findNextDown(node, criteria);
+    if (next) return next;
+  }
+
+  for (var sib = node.nextSibling; sib; sib = sib.nextSibling) {
+    if (criteria(sib)) return sib;
+    var next = exports.findNextDown(sib, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) return exports.findNext(node.parentNode, criteria, true);
+};
+
+exports.findPrevious = function (node, criteria, downOnly, maxRoot) {
+  if (!node) return null;
+
+  for (var sib = node.previousSibling; sib; sib = sib.previousSibling) {
+    var prev = exports.findPreviousUp(sib, criteria);
+    if (prev) return prev;
+    if (criteria(sib)) return sib;
+  }
+
+  if (!downOnly) {
+    var next = exports.findPreviousUp(node, criteria);
+    if (next) return next;
+  }
+
+  if (node.parentNode && node.parentNode != maxRoot) {
+    if (criteria(node.parentNode)) return node.parentNode;
+    return exports.findPrevious(node.parentNode, criteria, true);
+  }
+};
+
+exports.getNextByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findNext(root, iter);
+};
+
+exports.getPreviousByClass = function (root, state) {
+  function iter(node) {
+    return node.nodeType == 1 && exports.hasClass(node, state);
+  }
+
+  return exports.findPrevious(root, iter);
+};
+
+exports.hasChildElements = function (node) {
+  if (node.contentDocument) return true;
+
+  for (var child = node.firstChild; child; child = child.nextSibling) {
+    if (child.nodeType == 1) return true;
+  }
+
+  return false;
+};
+
+exports.isElement = function (o) {
+  try {
+    return o && o instanceof Element;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.isNode = function (o) {
+  try {
+    return o && o instanceof Node;
+  } catch (ex) {
+    return false;
+  }
+};
+
+exports.cancelEvent = function (event) {
+  event.stopPropagation();
+  event.preventDefault();
+};
+
+exports.isLeftClick = function (event) {
+  return event.button == 0 && exports.noKeyModifiers(event);
+};
+
+exports.isMiddleClick = function (event) {
+  return event.button == 1 && exports.noKeyModifiers(event);
+};
+
+exports.isRightClick = function (event) {
+  return event.button == 2 && exports.noKeyModifiers(event);
+};
+
+exports.noKeyModifiers = function (event) {
+  return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
+};
+
+exports.isControlClick = function (event) {
+  return event.button == 0 && exports.isControl(event);
+};
+
+exports.isShiftClick = function (event) {
+  return event.button == 0 && exports.isShift(event);
+};
+
+exports.isControl = function (event) {
+  return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
+};
+
+exports.isControlShift = function (event) {
+  return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
+};
+
+exports.isShift = function (event) {
+  return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
+};
+
+exports.bind = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments));
+  };
+};
+
+exports.bindFixed = function () {
+  var args = cloneArray(arguments),
+      fn = args.shift(),
+      object = args.shift();
+  return function () {
+    return fn.apply(object, args);
+  };
+};
+
+exports.extend = function (l, r) {
+  var newOb = {};
+
+  for (var n in l) {
+    newOb[n] = l[n];
+  }
+
+  for (var n in r) {
+    newOb[n] = r[n];
+  }
+
+  return newOb;
+};
+
+exports.keys = function (map) {
+  var keys = [];
+
+  try {
+    for (var name in map) {
+      keys.push(name);
+    }
+  } catch (exc) {}
+
+  return keys;
+};
+
+exports.values = function (map) {
+  var values = [];
+
+  try {
+    for (var name in map) {
+      try {
+        values.push(map[name]);
+      } catch (exc) {
+        if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+      }
+    }
+  } catch (exc) {
+    if (FBTrace.DBG_ERRORS) FBTrace.dumpPropreties("lib.values FAILED ", exc);
+  }
+
+  return values;
+};
+
+exports.remove = function (list, item) {
+  for (var i = 0; i < list.length; ++i) {
+    if (list[i] == item) {
+      list.splice(i, 1);
+      break;
+    }
+  }
+};
+
+exports.sliceArray = function (array, index) {
+  var slice = [];
+
+  for (var i = index; i < array.length; ++i) {
+    slice.push(array[i]);
+  }
+
+  return slice;
+};
+
+function cloneArray(array, fn) {
+  var newArray = [];
+  if (fn) for (var i = 0; i < array.length; ++i) {
+    newArray.push(fn(array[i]));
+  } else for (var i = 0; i < array.length; ++i) {
+    newArray.push(array[i]);
+  }
+  return newArray;
+}
+
+function extendArray(array, array2) {
+  var newArray = [];
+  newArray.push.apply(newArray, array);
+  newArray.push.apply(newArray, array2);
+  return newArray;
+}
+
+exports.extendArray = extendArray;
+exports.cloneArray = cloneArray;
+
+function arrayInsert(array, index, other) {
+  for (var i = 0; i < other.length; ++i) {
+    array.splice(i + index, 0, other[i]);
+  }
+
+  return array;
+}
+
+exports.arrayInsert = arrayInsert;
+
+exports.isArrayLike = function (object) {
+  return Object.prototype.toString.call(object) == "[object Array]" || exports.isArguments(object);
+};
+
+exports.isArguments = function (object) {
+  if (Object.prototype.toString.call(object) == "[object Arguments]") return true;
+  if (!_typeof(object) == "object" || !Object.prototype.hasOwnProperty.call(object, 'callee') || !object.callee || Object.prototype.toString.call(object.callee) !== '[object Function]' || typeof object.length != 'number') return false;
+
+  for (var name in object) {
+    if (name === 'callee' || name === 'length') return false;
+  }
+
+  return true;
+};
+},{"deepmerge":5}],5:[function(_require_,module,exports){
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.deepmerge = factory());
+}(this, (function () { 'use strict';
+
+var isMergeableObject = function isMergeableObject(value) {
+	return isNonNullObject(value)
+		&& !isSpecial(value)
+};
+
+function isNonNullObject(value) {
+	return !!value && typeof value === 'object'
+}
+
+function isSpecial(value) {
+	var stringValue = Object.prototype.toString.call(value);
+
+	return stringValue === '[object RegExp]'
+		|| stringValue === '[object Date]'
+		|| isReactElement(value)
+}
+
+// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
+var canUseSymbol = typeof Symbol === 'function' && Symbol.for;
+var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7;
+
+function isReactElement(value) {
+	return value.$$typeof === REACT_ELEMENT_TYPE
+}
+
+function emptyTarget(val) {
+	return Array.isArray(val) ? [] : {}
+}
+
+function cloneUnlessOtherwiseSpecified(value, options) {
+	return (options.clone !== false && options.isMergeableObject(value))
+		? deepmerge(emptyTarget(value), value, options)
+		: value
+}
+
+function defaultArrayMerge(target, source, options) {
+	return target.concat(source).map(function(element) {
+		return cloneUnlessOtherwiseSpecified(element, options)
+	})
+}
+
+function mergeObject(target, source, options) {
+	var destination = {};
+	if (options.isMergeableObject(target)) {
+		Object.keys(target).forEach(function(key) {
+			destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
+		});
+	}
+	Object.keys(source).forEach(function(key) {
+		if (!options.isMergeableObject(source[key]) || !target[key]) {
+			destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
+		} else {
+			destination[key] = deepmerge(target[key], source[key], options);
+		}
+	});
+	return destination
+}
+
+function deepmerge(target, source, options) {
+	options = options || {};
+	options.arrayMerge = options.arrayMerge || defaultArrayMerge;
+	options.isMergeableObject = options.isMergeableObject || isMergeableObject;
+
+	var sourceIsArray = Array.isArray(source);
+	var targetIsArray = Array.isArray(target);
+	var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
+
+	if (!sourceAndTargetTypesMatch) {
+		return cloneUnlessOtherwiseSpecified(source, options)
+	} else if (sourceIsArray) {
+		return options.arrayMerge(target, source, options)
+	} else {
+		return mergeObject(target, source, options)
+	}
+}
+
+deepmerge.all = function deepmergeAll(array, options) {
+	if (!Array.isArray(array)) {
+		throw new Error('first argument should be an array')
+	}
+
+	return array.reduce(function(prev, next) {
+		return deepmerge(prev, next, options)
+	}, {})
+};
+
+var deepmerge_1 = deepmerge;
+
+return deepmerge_1;
+
+})));
+
+},{}],6:[function(_require_,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+exports.PINF = (function (global) {
+
+	if (!global || typeof global !== "object") {
+		throw new Error("No root object scope provided!");
+	}
+
+	// If `PINF` gloabl already exists, don't do anything to change it.
+	if (typeof global.PINF !== "undefined") {
+		return global.PINF;
+	}
+
+	const LOADER = _require_('./loader');
+
+	const PINF = LOADER.Loader({
+		document: global.document
+	});
+
+	global.PINF = PINF;
+
+	// Attach postMessage handler to listen for sandbox load triggers.
+	// This is useful in Web Workers where only the loader must be loaded and
+	// sandboxes can then be loaded like this:
+	//    worker.postMessage(URIJS("notify://pinf-loader-js/sandbox/load").addSearch("uri", uri).toString())
+	if (typeof global.addEventListener === "function") {
+		global.addEventListener("message", function (event) {
+			var m = null;
+			if (
+				typeof event.data === "string" &&
+				(m = event.data.match(/^notify:\/\/pinf-loader-js\/sandbox\/load\?uri=(.+)$/)) &&
+				(m = decodeURIComponent(m[1])) &&
+				// SECURITY: Only allow URIs that begin with `/` so that scripts may NOT
+				//           be loaded cross-domain this way. If this was allowed one could
+				//           load malicious code simply by posting a message to this window.
+				/^\/[^\/]/.test(m)
+			) {
+				return PINF.sandbox(m, function (sandbox) {
+		            sandbox.main();
+					if (typeof global.postMessage === "function") {
+						global.postMessage(event.data.replace("/load?", "/loaded?"));
+					}
+		        }, function (err) {
+		        	// TODO: Post error back to main frame instead of throwing?
+		        	throw err;
+		        });
+			}
+		}, false);
+	}
+
+	return global.PINF;
+}(
+	typeof window !== "undefined" ?
+		// Used in the browser
+		window :
+		typeof self !== "undefined" ?
+			// Used in web worker
+			self :
+			// No root scope variable found
+			null
+));
+
+},{"./loader":7}],7:[function(_require_,module,exports){
+/**
+ * Author: Christoph Dorn <christoph@christophdorn.com>
+ * License: Zero-Clause BSD - https://opensource.org/licenses/0BSD
+**/
+
+(function (exports) {
+
+// The global `require` for the 'external' (to the loader) environment.
+var Loader = function (global) {
+
+	var loadedBundles = [],
+		// @see https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L69
+		readyStates = { 'loaded': 1, 'interactive': 1, 'complete': 1 },
+		lastModule = null,
+		headTag = null;
+
+	// For older browsers that don't have `Object.keys()` (Firefox 3.6)
+	function keys(obj) {
+		var keys = [];
+		for (var key in obj) {
+			keys.push(key);
+		}
+		return keys;
+	}
+
+	// TODO: Use 'Object.create()` in modern browsers
+	function create (proto) {
+		function F() {}
+		F.prototype = proto;
+		return new F();
+	};
+
+	function normalizeSandboxArguments (implementation) {
+		return function (programIdentifier, options, loadedCallback, errorCallback) {
+			/*DEBUG*/ if (typeof options === "function" && typeof loadedCallback === "object") {
+			/*DEBUG*/     throw new Error("Callback before options for `require.sandbox(programIdentifier, options, loadedCallback)`");
+			/*DEBUG*/ }
+			if (typeof options === "function" && !loadedCallback && !errorCallback) {
+				loadedCallback = options;
+				options = {};
+			} else
+			if (typeof options === "function" && typeof loadedCallback === "function" && !errorCallback) {
+				errorCallback = loadedCallback;
+				loadedCallback = options;
+				options = {};
+			} else {
+				options = options || {};
+			}
+			implementation(programIdentifier, options, loadedCallback, errorCallback);
+		};
+	}
+
+	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
+		try {
+			// See if we are in a web worker.
+			if (typeof importScripts !== "undefined") {
+				importScripts(uri.replace(/^\/?\{host\}/, ""));
+				return loadedCallback(null);
+			}
+			var document = global.document;
+			/*DEBUG*/ if (!document) {
+			/*DEBUG*/ 	throw new Error("Unable to get reference to 'document'!");
+			/*DEBUG*/ }
+			var location = document.location;
+			if (/^\/?\{host\}\//.test(uri)) {
+				uri = location.protocol + "//" + location.host + uri.replace(/^\/?\{host\}/, "");
+			} else
+			if (/^\/\//.test(uri)) {
+				uri = location.protocol + uri;
+			}
+			if (!headTag) {
+				headTag = document.getElementsByTagName("head")[0];
+			}
+			var element = document.createElement("script");
+			element.type = "text/javascript";
+			element.onload = element.onreadystatechange = function (ev) {
+				ev = ev || global.event;
+				if (ev.type === "load" || readyStates[this.readyState]) {
+					this.onload = this.onreadystatechange = this.onerror = null;
+					loadedCallback(null, function () {
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
+					});
+				}
+			}
+			element.onerror = function (err) {
+				/*DEBUG*/ console.error(err);
+				return loadedCallback(new Error("Error loading '" + uri + "'"));
+			}
+			element.charset = "utf-8";
+			element.async = true;
+			element.src = uri;
+			element = headTag.insertBefore(element, headTag.firstChild);
+		} catch(err) {
+			loadedCallback(err);
+		}
+	}
+
+	// A set of modules working together.
+	var Sandbox = function (sandboxIdentifier, sandboxOptions, loadedCallback) {
+
+		var moduleInitializers = {},
+			initializedModules = {},
+			/*DEBUG*/ bundleIdentifiers = {},
+			packages = {},
+			loadingBundles = {};
+
+		var sandbox = {
+				id: sandboxIdentifier
+			};
+
+		/*DEBUG*/ function logDebug() {
+		/*DEBUG*/ 	if (sandboxOptions.debug !== true) return;
+		/*DEBUG*/ 	// NOTRE: This does not work in google chrome.
+		/*DEBUG*/ 	//console.log.apply(null, arguments);
+		/*DEBUG*/ 	if (arguments.length === 1) {
+		/*DEBUG*/ 		console.log(arguments[0]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 2) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 3) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2]);
+		/*DEBUG*/ 	} else
+		/*DEBUG*/ 	if (arguments.length === 4) {
+		/*DEBUG*/ 		console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
+		/*DEBUG*/ 	}
+		/*DEBUG*/ }
+
+		function rebaseUri (uri) {
+			if (!sandboxOptions.baseUrl) {
+				return uri;
+			}
+			return sandboxOptions.baseUrl + "/" + uri;
+		}
+
+		function load (bundleIdentifier, packageIdentifier, bundleSubPath, loadedCallback) {
+
+			var loadSandboxIdentifier = sandboxIdentifier;
+			var finalBundleIdentifier = null;
+			var moduleIdentifierPrefix = "";
+			var finalPackageIdentifier = "";
+
+			try {
+				if (packageIdentifier !== "") {
+
+					if (/^@bundle:/.test(packageIdentifier)) {
+						var absPackageIdentifier = packageIdentifier;
+						if (/^@bundle:\./.test(absPackageIdentifier)) {
+							absPackageIdentifier = absPackageIdentifier.replace(/^(@bundle:)\./, "$1" + sandboxIdentifier + "/.");
+						}
+						moduleIdentifierPrefix = packageIdentifier;
+						finalPackageIdentifier = packageIdentifier;
+						bundleIdentifier = absPackageIdentifier.replace(/^@bundle:/, "") + ".js";
+						loadSandboxIdentifier = "";
+						finalBundleIdentifier = "@bundle:" + packageIdentifier.replace(/^@bundle:/, "") + ".js";
+					} else {
+						bundleIdentifier = ("/" + packageIdentifier + "/" + bundleIdentifier).replace(/\/+/g, "/");
+					}
+				}
+
+				if (initializedModules[bundleIdentifier]) {
+					// Module is already loaded and initialized.
+					loadedCallback(null, sandbox);
+				} else {
+					// Module is not initialized.
+					if (loadingBundles[bundleIdentifier]) {
+						// Module is already loading.
+						loadingBundles[bundleIdentifier].push(loadedCallback);
+					} else {
+						// Module is not already loading.
+						loadingBundles[bundleIdentifier] = [];
+
+						bundleIdentifier = (loadSandboxIdentifier + bundleSubPath + bundleIdentifier).replace(/\/$/, ".js");
+
+						bundleIdentifier = bundleIdentifier.replace(/\.php\.js$/, ".php");
+
+						if (!finalBundleIdentifier) {
+							finalBundleIdentifier = bundleIdentifier;
+						}
+
+						// Default to our script-injection browser loader.
+						(sandboxOptions.rootBundleLoader || sandboxOptions.load || loadInBrowser)(
+							rebaseUri(bundleIdentifier),
+							function (err, cleanupCallback) {
+								if (err) return loadedCallback(err);
+								// The rootBundleLoader is only applicable for the first load.
+								delete sandboxOptions.rootBundleLoader;
+								finalizeLoad(moduleIdentifierPrefix, finalBundleIdentifier, finalPackageIdentifier, function () {
+									loadedCallback(null, sandbox);
+									if (cleanupCallback) {
+										cleanupCallback();
+									}
+								});
+							},
+							sandboxOptions
+						);
+					}
+				}
+			} catch(err) {
+				loadedCallback(err);
+			}
+		}
+
+		// Called after a bundle has been loaded. Takes the top bundle off the *loading* stack
+		// and makes the new modules available to the sandbox.
+		function finalizeLoad (moduleIdentifierPrefix, bundleIdentifier, packageIdentifier, loadFinalized) {
+			var pending = 0;
+			function finalize () {
+				if (pending !== 0) {
+					return;
+				}
+				if (loadFinalized) loadFinalized();
+			}
+
+			pending += 1;
+
+			// Assume a consistent statically linked set of modules has been memoized.
+			/*DEBUG*/ if (!loadedBundles[0]) {
+			/*DEBUG*/     throw new Error("No bundle memoized for '" + bundleIdentifier + "'! Check the file to ensure it contains JavaScript and that a bundle is memoized against the correct loader instance.");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[bundleIdentifier] = loadedBundles[0][0];
+			var loadedModuleInitializers = loadedBundles[0][1]({
+				id: sandboxIdentifier
+			});
+			var key;
+			for (key in loadedModuleInitializers) {
+
+				var memoizeKey = moduleIdentifierPrefix + key;
+
+				// If we have a package descriptor add it or merge it on top.
+				if (/^[^\/]*\/package.json$/.test(key)) {
+
+					if (sandboxOptions.rewritePackageDescriptor) {
+						loadedModuleInitializers[key][0] = sandboxOptions.rewritePackageDescriptor(loadedModuleInitializers[key][0], memoizeKey);
+					}
+
+					// Load all dependent resources
+					if (loadedModuleInitializers[key][0].mappings) {
+						for (var alias in loadedModuleInitializers[key][0].mappings) {
+							if (/^@script:\/\//.test(loadedModuleInitializers[key][0].mappings[alias])) {
+								pending += 1;
+								loadInBrowser(
+									rebaseUri(loadedModuleInitializers[key][0].mappings[alias].replace(/^@script:/, "")),
+									function () {
+										pending -= 1;
+										finalize();
+									},
+									sandboxOptions
+								);
+							}
+						}
+					}
+
+					// NOTE: Not quite sure if we should allow agumenting package descriptors.
+					//       When doing nested requires using same package we can either add all
+					//		 mappings (included mappings not needed until further down the tree) to
+					//       the first encounter of the package descriptor or add more mappings as
+					//       needed down the road. We currently support both.
+
+					if (moduleInitializers[memoizeKey]) {
+						// TODO: Keep array of bundle identifiers instead of overwriting existing one?
+						//		 Overwriting may change subsequent bundeling behaviour?
+						moduleInitializers[memoizeKey][0] = bundleIdentifier;
+						// Only augment (instead of replace existing values).
+						if (typeof moduleInitializers[memoizeKey][1].main === "undefined") {
+							moduleInitializers[memoizeKey][1].main = loadedModuleInitializers[key][0].main;
+						}
+						if (loadedModuleInitializers[key][0].mappings) {
+							if (!moduleInitializers[memoizeKey][1].mappings) {
+								moduleInitializers[memoizeKey][1].mappings = {};
+							}
+							for (var alias in loadedModuleInitializers[key][0].mappings) {
+								if (typeof moduleInitializers[memoizeKey][1].mappings[alias] === "undefined") {
+									moduleInitializers[memoizeKey][1].mappings[alias] = loadedModuleInitializers[key][0].mappings[alias];
+								}
+							}
+						}
+					} else {
+						moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+					}
+					// Now that we have a [updated] package descriptor, re-initialize it if we have it already in cache.
+					var packageIdentifier = packageIdentifier || key.split("/").shift();
+					if (packages[packageIdentifier]) {
+						packages[packageIdentifier].init();
+					}
+				}
+				// Only add modules that don't already exist!
+				// TODO: Log warning in debug mode if module already exists.
+				if (typeof moduleInitializers[memoizeKey] === "undefined") {
+					moduleInitializers[memoizeKey] = [bundleIdentifier, loadedModuleInitializers[key][0], loadedModuleInitializers[key][1]];
+				}				
+			}
+			loadedBundles.shift();
+
+			pending -= 1;
+			finalize();
+
+			return;
+		}
+
+		var Package = function (packageIdentifier) {
+			if (packages[packageIdentifier]) {
+				return packages[packageIdentifier];
+			}
+
+			var pkg = {
+				id: packageIdentifier,
+				descriptor: {},
+				main: "/main.js",
+				mappings: {},
+				directories: {},
+				libPath: ""
+			};
+
+			var parentModule = lastModule;
+
+			pkg.init = function () {
+				var descriptor = (moduleInitializers[packageIdentifier + "/package.json"] && moduleInitializers[packageIdentifier + "/package.json"][1]) || {};
+				if (descriptor) {
+					pkg.descriptor = descriptor;
+					if (typeof descriptor.main === "string") {
+						pkg.main = descriptor.main;
+					}
+					pkg.mappings = descriptor.mappings || pkg.mappings;
+					pkg.directories = descriptor.directories || pkg.directories;
+					// NOTE: We need `lib` directory support so that the source directory structure can be mapped
+					//       into the bundle structure without modification. If this is not done, a module doing a relative _require_
+					//       for a resource outside of the lib directory will not find the file.
+					pkg.libPath = (typeof pkg.directories.lib !== "undefined" && pkg.directories.lib != "") ? pkg.directories.lib + "/" : pkg.libPath;
+				}
+			}
+			pkg.init();
+
+			function normalizeIdentifier (identifier) {
+				// If we have a period (".") in the basename we want an absolute path from
+				// the root of the package. Otherwise a relative path to the "lib" directory.
+				if (identifier.split("/").pop().indexOf(".") === -1) {
+					// We have a module relative to the "lib" directory of the package.
+					identifier = identifier + ".js";
+				} else
+				if (!/^\//.test(identifier)) {
+					// We want an absolute path for the module from the root of the package.
+					identifier = "/" + identifier;
+				}
+				return identifier;
+			}
+
+			var Module = function (moduleIdentifier, parentModule) {
+
+				var moduleIdentifierSegment = null;
+				if (/^@bundle:/.test(moduleIdentifier)) {
+					moduleIdentifierSegment = moduleIdentifier.replace(packageIdentifier, "").replace(/\/[^\/]*$/, "").split("/");
+				} else {
+					moduleIdentifierSegment = moduleIdentifier.replace(/\/[^\/]*$/, "").split("/");
+				}
+
+				var module = {
+						id: moduleIdentifier,
+						exports: {},
+						parentModule: parentModule,
+						bundle: null,
+						pkg: packageIdentifier
+					};
+
+				function resolveIdentifier (identifier) {
+					if (/\/$/.test(identifier)) {
+						identifier += "index";
+					}
+					lastModule = module;
+					// Check for plugin prefix.
+					var plugin = null;
+					if (/^[^!]*!/.test(identifier)) {
+						var m = identifier.match(/^([^!]*)!(.+)$/);
+						identifier = m[2];
+						plugin = m[1];
+					}
+					function pluginify (id) {
+						if (!plugin) return id;
+						id = new String(id);
+						id.plugin = plugin;
+						return id;
+					}
+					// Check for relative module path to module within same package.
+					if (/^\./.test(identifier)) {
+						var segments = identifier.replace(/^\.\//, "").split("../");
+						identifier = "/" + moduleIdentifierSegment.slice(1, moduleIdentifierSegment.length-segments.length+1).concat(segments[segments.length-1]).join("/");
+						if (identifier === "/.") {
+							return [pkg, pluginify("")];
+						}
+						return [pkg, pluginify(normalizeIdentifier(identifier.replace(/\/\.$/, "/")))];
+					}
+					var splitIdentifier = identifier.split("/");
+					// Check for mapped module path to module within mapped package.
+					if (typeof pkg.mappings[splitIdentifier[0]] !== "undefined") {
+						return [Package(pkg.mappings[splitIdentifier[0]]), pluginify((splitIdentifier.length > 1)?normalizeIdentifier(splitIdentifier.slice(1).join("/")):"")];
+					}
+					/*DEBUG*/ if (!moduleInitializers["/" + normalizeIdentifier(identifier)]) {
+					/*DEBUG*/     throw new Error("Descriptor for package '" + pkg.id + "' in sandbox '" + sandbox.id + "' does not declare 'mappings[\"" + splitIdentifier[0] + "\"]' property nor does sandbox have module memoized at '" + "/" + normalizeIdentifier(identifier) + "' needed to satisfy module path '" + identifier + "' in module '" + moduleIdentifier + "'!");
+					/*DEBUG*/ }
+					return [Package(""), pluginify("/" + normalizeIdentifier(identifier))];
+				}
+
+				// Statically link a module and its dependencies
+				module.require = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require(identifier[1]).exports;
+				};
+
+				module.require.supports = [
+					"ucjs-pinf-0"
+				];
+
+				module.require.id = function (identifier) {
+					identifier = resolveIdentifier(identifier);
+					return identifier[0].require.id(identifier[1]);
+				};
+
+				module.require.async = function (identifier, loadedCallback, errorCallback) {
+					identifier = resolveIdentifier(identifier);					
+					var mi = moduleIdentifier;
+					if (/^\//.test(identifier[0].id)) {
+						mi = "/main.js";
+					}
+					identifier[0].load(identifier[1], module.bundle, function (err, moduleAPI) {
+						if (err) {
+							if (errorCallback) return errorCallback(err);
+							throw err;
+						}
+						loadedCallback(moduleAPI);
+					});
+				};
+
+				module.require.sandbox = normalizeSandboxArguments (function (programIdentifier, options, loadedCallback, errorCallback) {
+					options.load = options.load || sandboxOptions.load;
+					// If the `programIdentifier` is relative it is resolved against the URI of the owning sandbox (not the owning page).
+					if (/^\./.test(programIdentifier))
+					{
+						programIdentifier = sandboxIdentifier + "/" + programIdentifier;
+						// HACK: Temporary hack as zombie (https://github.com/assaf/zombie) does not normalize path before sending to server.
+						programIdentifier = programIdentifier.replace(/\/\.\//g, "/");
+					}
+					return PINF.sandbox(programIdentifier, options, loadedCallback, errorCallback);
+				});
+				module.require.sandbox.id = sandboxIdentifier;
+
+				module.load = function () {
+					module.bundle = moduleInitializers[moduleIdentifier][0];
+					if (typeof moduleInitializers[moduleIdentifier][1] === "function") {
+
+						var moduleInterface = {
+							id: module.id,
+							filename:
+								// The `filename` from the meta info attached to the module.
+								// This is typically where the module was originally found on the filesystem.
+								moduleInitializers[moduleIdentifier][2].filename ||
+								// Fall back to the virtual path of the module in the bundle.
+								// TODO: Insert a delimiter between bundle and module id.
+								(module.bundle.replace(/\.js$/, "") + "/" + module.id).replace(/\/+/g, "/"),
+							exports: {}
+						}
+
+						if (packageIdentifier === "" && pkg.main === moduleIdentifier) {
+							module.require.main = moduleInterface;
+						}
+
+						if (sandboxOptions.onInitModule) {
+							sandboxOptions.onInitModule(moduleInterface, module, pkg, sandbox, {
+								normalizeIdentifier: normalizeIdentifier,
+								resolveIdentifier: resolveIdentifier,
+								finalizeLoad: finalizeLoad,
+								moduleInitializers: moduleInitializers,
+								initializedModules: initializedModules
+							});
+						}
+
+						var exports = moduleInitializers[moduleIdentifier][1].call(exports, module.require, module.exports, moduleInterface);
+						if (
+							typeof moduleInterface.exports !== "undefined" &&
+							(
+								typeof moduleInterface.exports !== "object" ||
+								keys(moduleInterface.exports).length !== 0
+							)
+						) {
+							module.exports = moduleInterface.exports;
+						} else
+						if (typeof exports !== "undefined") {
+							module.exports = exports;
+						}
+					} else
+					if (typeof moduleInitializers[moduleIdentifier][1] === "string") {
+						// TODO: Use more optimal string encoding algorythm to reduce payload size?
+						module.exports = decodeURIComponent(moduleInitializers[moduleIdentifier][1]);
+					} else {
+						module.exports = moduleInitializers[moduleIdentifier][1];
+					}
+				};
+
+				/*DEBUG*/ module.getReport = function () {
+				/*DEBUG*/ 	var exportsCount = 0,
+				/*DEBUG*/ 		key;
+				/*DEBUG*/ 	for (key in module.exports) {
+				/*DEBUG*/ 		exportsCount++;
+				/*DEBUG*/ 	}
+				/*DEBUG*/ 	return {
+				/*DEBUG*/ 		exports: exportsCount
+				/*DEBUG*/ 	};
+				/*DEBUG*/ };
+
+				return module;
+			};
+
+			pkg.load = function (moduleIdentifier, bundleIdentifier, loadedCallback) {
+
+				// If module/bundle to be loaded asynchronously is already memoized we skip the load.
+				if (moduleInitializers[packageIdentifier + (moduleIdentifier || pkg.main)]) {
+					return loadedCallback(null, pkg.require(moduleIdentifier).exports);
+				}
+				var bundleSubPath = bundleIdentifier.substring(sandboxIdentifier.length);
+				load(
+					((!/^\//.test(moduleIdentifier))?"/"+pkg.libPath:"") + moduleIdentifier,
+					packageIdentifier,
+					bundleSubPath.replace(/\.js$/g, ""),
+					function (err) {
+						if (err) return loadedCallback(err);
+						loadedCallback(null, pkg.require(moduleIdentifier).exports);
+					}
+				);
+			}
+
+			pkg.require = function (moduleIdentifier) {
+
+				var plugin = moduleIdentifier.plugin;
+
+				if (moduleIdentifier) {
+					if (!/^\//.test(moduleIdentifier)) {
+						moduleIdentifier = ("/" + ((moduleIdentifier.substring(0, pkg.libPath.length)===pkg.libPath)?"":pkg.libPath)).replace(/\/\.\//, "/") + moduleIdentifier;
+					}
+					moduleIdentifier = packageIdentifier + moduleIdentifier;
+				} else {
+					moduleIdentifier = packageIdentifier + pkg.main;
+				}
+
+				if (
+					!moduleInitializers[moduleIdentifier] &&
+					moduleInitializers[moduleIdentifier.replace(/\.js$/, "/index.js")]
+				) {
+					moduleIdentifier = moduleIdentifier.replace(/\.js$/, "/index.js");
+				}
+
+				// Use a specifically formatted module for requested plugin if available
+				if (
+					plugin &&
+					moduleInitializers[moduleIdentifier + ":" + plugin]
+				) {
+					moduleIdentifier += ":" + plugin;
+				}
+
+				if (!initializedModules[moduleIdentifier]) {
+					/*DEBUG*/ if (!moduleInitializers[moduleIdentifier]) {
+					/*DEBUG*/ 	console.error("[pinf-loader-js]", "moduleInitializers", moduleInitializers);
+					/*DEBUG*/ 	throw new Error("Module '" + moduleIdentifier + "' " + (plugin?"for format '" + plugin + "' ":"") + "not found in sandbox '" + sandbox.id + "'!");
+					/*DEBUG*/ }
+					(initializedModules[moduleIdentifier] = Module(moduleIdentifier, lastModule)).load();
+				}
+
+				var loadingBundlesCallbacks;
+				if (loadingBundles[moduleIdentifier]) {
+					loadingBundlesCallbacks = loadingBundles[moduleIdentifier];
+					delete loadingBundles[moduleIdentifier];
+					for (var i=0 ; i<loadingBundlesCallbacks.length ; i++) {
+						loadingBundlesCallbacks[i](null, sandbox);
+					}
+				}
+
+				// TODO: Do this via plugins registered using sandbox options.
+				// TODO: Cache response so we only process files once.
+
+				var moduleInfo = create(initializedModules[moduleIdentifier]);
+				// RequireJS/AMD international strings plugin using root by default.
+				if (plugin === "i18n") {
+					moduleInfo.exports = moduleInfo.exports.root;
+				}
+
+				return moduleInfo;
+			}
+
+			pkg.require.id = function (moduleIdentifier) {
+				if (!/^\//.test(moduleIdentifier)) {
+					moduleIdentifier = "/" + pkg.libPath + moduleIdentifier;
+				}
+				return (((packageIdentifier !== "")?"/"+packageIdentifier+"/":"") + moduleIdentifier).replace(/\/+/g, "/");
+			}
+
+			/*DEBUG*/ pkg.getReport = function () {
+			/*DEBUG*/ 	return {
+			/*DEBUG*/ 		main: pkg.main,
+			/*DEBUG*/ 		mappings: pkg.mappings,
+			/*DEBUG*/ 		directories: pkg.directories,
+			/*DEBUG*/ 		libPath: pkg.libPath
+			/*DEBUG*/ 	};
+			/*DEBUG*/ }
+
+			if (sandboxOptions.onInitPackage) {
+				sandboxOptions.onInitPackage(pkg, sandbox, {
+					normalizeIdentifier: normalizeIdentifier,
+					finalizeLoad: finalizeLoad,
+					moduleInitializers: moduleInitializers,
+					initializedModules: initializedModules
+				});
+			}
+
+			packages[packageIdentifier] = pkg;
+
+			return pkg;
+		}
+
+		// Get a module and initialize it (statically link its dependencies) if it is not already so
+		sandbox.require = function (moduleIdentifier) {
+			return Package("").require(moduleIdentifier).exports;
+		}
+
+		// Call the 'main' module of the program
+		sandbox.boot = function () {
+			/*DEBUG*/ if (typeof Package("").main !== "string") {
+			/*DEBUG*/ 	throw new Error("No 'main' property declared in '/package.json' in sandbox '" + sandbox.id + "'!");
+			/*DEBUG*/ }
+			return sandbox.require(Package("").main);
+		};
+
+		// Call the 'main' exported function of the main' module of the program
+		sandbox.main = function () {
+			var exports = sandbox.boot();
+			return ((exports.main)?exports.main.apply(null, arguments):exports);
+		};
+
+		/*DEBUG*/ sandbox.getReport = function () {
+		/*DEBUG*/ 	var report = {
+		/*DEBUG*/ 			bundles: {},
+		/*DEBUG*/ 			packages: {},
+		/*DEBUG*/ 			modules: {}
+		/*DEBUG*/ 		},
+		/*DEBUG*/ 		key;
+		/*DEBUG*/ 	for (key in bundleIdentifiers) {
+		/*DEBUG*/ 		report.bundles[key] = bundleIdentifiers[key];
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in packages) {
+		/*DEBUG*/ 		report.packages[key] = packages[key].getReport();
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	for (key in moduleInitializers) {
+		/*DEBUG*/ 		if (initializedModules[key]) {
+		/*DEBUG*/ 			report.modules[key] = initializedModules[key].getReport();
+		/*DEBUG*/ 		} else {
+		/*DEBUG*/ 			report.modules[key] = {};
+		/*DEBUG*/ 		}
+		/*DEBUG*/ 	}
+		/*DEBUG*/ 	return report;
+		/*DEBUG*/ }
+		/*DEBUG*/ sandbox.reset = function () {
+		/*DEBUG*/   moduleInitializers = {};
+		/*DEBUG*/   initializedModules = {};
+		/*DEBUG*/   bundleIdentifiers = {};
+		/*DEBUG*/   packages = {};
+		/*DEBUG*/   loadingBundles = {};
+		/*DEBUG*/ }
+
+		load((sandboxIdentifier.indexOf("?") === -1) ? ".js" : "", "", "", loadedCallback);
+
+		return sandbox;
+	};
+
+	var
+		/*DEBUG*/ bundleIdentifiers = {},
+		sandboxes = {};
+
+	var Require = function (bundle) {
+		var self = this;
+
+		// Address a specific sandbox or currently loading sandbox if initial load.
+		var bundleHandler = function (uid, callback) {
+			/*DEBUG*/ if (uid && bundleIdentifiers[uid]) {
+			/*DEBUG*/ 	throw new Error("You cannot split require.bundle(UID) calls where UID is constant!");
+			/*DEBUG*/ }
+			/*DEBUG*/ bundleIdentifiers[uid] = true;
+			loadedBundles.push([uid, function (sandbox) {
+				var moduleInitializers = {},
+					req = new Require(uid);
+				delete req.bundle;
+				req.sandbox = sandbox;
+				// Store raw module in loading bundle
+				req.memoize = function (moduleIdentifier, moduleInitializer, moduleMeta) {
+					moduleInitializers[
+						moduleIdentifier +
+						// NOTE: This feature may be elevated to a new function argument to 'memoize' if it proves to be prevalent.
+						(
+							(
+								moduleMeta &&
+								moduleMeta.variation
+							) ? ":" + moduleMeta.variation : ""
+						)
+					] = [moduleInitializer, moduleMeta || {}];
+				}
+				callback(req, global || null);
+				return moduleInitializers;
+			}]);
+		}
+		var activeBundleHandler = bundleHandler;
+		this.bundle = function () {
+			return activeBundleHandler.apply(null, arguments);
+		}
+		this.setActiveBundleHandler = function (handler) {
+			var oldHandler = activeBundleHandler;
+			activeBundleHandler = handler;
+			return oldHandler;
+		}
+	}
+
+	var PINF = new Require();
+
+	// TODO: @see URL_TO_SPEC
+	PINF.supports = [
+		"ucjs-pinf-0"
+	];
+
+	// Create a new environment to memoize modules to.
+	// If relative, the `programIdentifier` is resolved against the URI of the owning page (this is only for the global _require_).
+	PINF.sandbox = normalizeSandboxArguments(function (programIdentifier, options, loadedCallback, errorCallback) {
+		if (typeof programIdentifier === "function") {
+			options = options || {};
+			var bundle = programIdentifier;
+			var fallbackLoad = options.load || loadInBrowser;
+			options.load = function (uri, loadedCallback) {
+				if (uri === (programIdentifier + ".js")) {
+					PINF.bundle("", bundle);
+					loadedCallback(null);
+					return;
+				}
+				return fallbackLoad(uri, loadedCallback, options);
+			}
+			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
+		}
+		var sandboxIdentifier = programIdentifier.replace(/\.js$/, "");
+		return sandboxes[sandboxIdentifier] = Sandbox(sandboxIdentifier, options, function (err, sandbox) {
+			if (err) {
+				if (errorCallback) return errorCallback(err);
+				throw err;
+			}
+			loadedCallback(sandbox);
+		});
+	});
+
+	PINF.Loader = Loader;
+
+	/*DEBUG*/ PINF.getReport = function () {
+	/*DEBUG*/ 	var report = {
+	/*DEBUG*/ 			sandboxes: {}
+	/*DEBUG*/ 		};
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		report.sandboxes[key] = sandboxes[key].getReport();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	return report;
+	/*DEBUG*/ }
+	/*DEBUG*/ PINF.reset = function () {
+	/*DEBUG*/ 	for (var key in sandboxes) {
+	/*DEBUG*/ 		sandboxes[key].reset();
+	/*DEBUG*/ 	}
+	/*DEBUG*/ 	sandboxes = {};
+	/*DEBUG*/ 	bundleIdentifiers = {};
+	/*DEBUG*/ 	loadedBundles = [];
+	/*DEBUG*/ }
+
+	return PINF;
+}
+
+if (exports) exports.Loader = Loader;
+
+})(typeof exports !== "undefined" ? exports : null);
+
+},{}]},{},[1])(1)
+});
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/exports.json b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/exports.json
new file mode 100644
index 0000000..cc98805
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/dist/exports.json
@@ -0,0 +1,34 @@
+{
+    "js": {
+        "domplate": {
+            "strict": {
+                "path": "./domplate.js",
+                "summary": "Portable JavaScript source code for strict mode. Can only render pre-compiled templates.",
+                "size": 78116,
+                "hash": "sha1:25e4915af523e8042e3294c54a71f2bdbfe48329"
+            },
+            "eval": {
+                "path": "./domplate-eval.js",
+                "summary": "Portable JavaScript source code using eval. Can compile and render templates from source.",
+                "size": 79038,
+                "hash": "sha1:d0dddd8c8f02f14f1e1903ec93096de058c6f413"
+            }
+        }
+    },
+    "js.browser": {
+        "domplate": {
+            "strict": {
+                "path": "./domplate.browser.js",
+                "summary": "Browser JavaScript source code for strict mode. Can only render pre-compiled templates.",
+                "size": 78724,
+                "hash": "sha1:b94d96afdd377840cebcee5ae0d0926ba386a518"
+            },
+            "eval": {
+                "path": "./domplate-eval.browser.js",
+                "summary": "Browser JavaScript source code using eval. Can compile and render templates from source.",
+                "size": 79638,
+                "hash": "sha1:0fc1eda46932a15be063f8e1554ff11c6af07ad8"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/twisty-closed.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/images/twisty-closed.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/twisty-closed.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/images/twisty-closed.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/twisty-open.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/images/twisty-open.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/twisty-open.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/images/twisty-open.png
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.preview.htm
new file mode 100644
index 0000000..0be642a
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.preview.htm
@@ -0,0 +1,40 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74" __dtid="elements/optiontree" class="dictionary "><div class="member expandable "><span decorator="" class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></div><div class="member expandable "><span decorator="" class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></div></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74" __dtid="elements/optiontree" class="dictionary "><span>filetree(</span><span class="member "><span class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></span><span class="member "><span class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74" __dtid="elements/optiontree" class="dictionary "><span>filetree(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
+
+<br/>
+
+<h2>expandableStub</h2>
+
+
+
+<br/>
+
+<h2>expandedStub</h2>
+
+
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74" __dtid="elements/optiontree" class="more "> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.rep.css
new file mode 100644
index 0000000..c4374b3
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.rep.css
@@ -0,0 +1,51 @@
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > SPAN {
+    color: #9C9C9C;
+}
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > SPAN.collapsed {
+    color: #0000FF;
+    font-weight: normal;
+    padding-left: 5px;
+    padding-right: 5px;
+}
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > SPAN.summary {
+    color: #0000FF;
+    font-weight: normal;
+    padding-left: 5px;
+    padding-right: 5px;
+}
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > SPAN.member {
+    color: #9C9C9C;
+}
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > DIV.member {
+    display: block;
+    padding-left: 20px;
+}
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > DIV.member.expandable {
+    background-image: url(images/twisty-closed.png);
+    background-repeat: no-repeat;
+    background-position: 6px 2px;
+    cursor: pointer;
+}
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > DIV.member.expandable.expanded {
+    background-image: url(images/twisty-open.png);
+}
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > .member > SPAN.name {
+    color: #E59D07;
+    font-weight: normal;
+}
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > .member > SPAN.value {
+    font-weight: normal;
+}
+
+SPAN.dictionary[__dbid="77a4ea3f2f877bdbce48a9c8f3bb26e983097a74"] > .member > SPAN.delimiter, SPAN.dictionary > .member > SPAN.separator, SPAN.dictionary > .member SPAN.more {
+    color: #9C9C9C;
+    padding-left: 2px;
+    padding-right: 2px;
+}        
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.rep.js
new file mode 100644
index 0000000..ebbe4f3
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/optiontree.rep.js
@@ -0,0 +1,355 @@
+PINF.bundle("", function(__require) {
+	__require.memoize("/main.js", function (_require, _exports, _module) {
+var bundle = { require: _require, exports: _exports, module: _module };
+var exports = undefined;
+var module = undefined;
+var define = function (deps, init) {
+_module.exports = init();
+}; define.amd = true;
+       var pmodule = bundle.module;
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+
+
+function impl(domplate) {
+  var T = domplate.tags;
+  return {
+    CONST_Normal: "tag",
+    CONST_Short: "shortTag",
+    CONST_Collapsed: "collapsedTag",
+    tag: T.SPAN({
+      "class": "dictionary"
+    }, T.FOR("member", "$context,$node,$CONST_Normal|dictionaryIterator", T.DIV({
+      "class": "member",
+      "$expandable": "$member.expandable",
+      "_memberObject": "$member",
+      "_contextObject": "$context",
+      "onclick": "$onClick"
+    }, T.SPAN({
+      "class": "name",
+      "decorator": "$member|getMemberNameDecorator"
+    }, "$member.name"), T.SPAN({
+      "class": "delimiter"
+    }, ":"), T.SPAN({
+      "class": "value"
+    }, T.TAG("$member.tag", {
+      "context": "$context",
+      "node": "$member.node",
+      "member": "$member"
+    })), T.IF("$member.more", T.SPAN({
+      "class": "separator"
+    }, ","))))),
+    shortTag: T.SPAN({
+      "class": "dictionary"
+    }, T.SPAN("$node|getLabel("), T.FOR("member", "$context,$node,$CONST_Short|dictionaryIterator", T.SPAN({
+      "class": "member"
+    }, T.SPAN({
+      "class": "name"
+    }, "$member.name"), T.SPAN({
+      "class": "delimiter"
+    }, ":"), T.SPAN({
+      "class": "value"
+    }, T.TAG("$member.tag", {
+      "context": "$context",
+      "node": "$member.node",
+      "member": "$member"
+    })), T.IF("$member.more", T.SPAN({
+      "class": "separator"
+    }, ",")))), T.SPAN(")")),
+    collapsedTag: T.SPAN({
+      "class": "dictionary"
+    }, T.SPAN("$node|getLabel("), T.SPAN({
+      "class": "collapsed"
+    }, "... $node|getMemberCount ..."), T.SPAN(")")),
+    expandableStub: T.TAG("$context,$member,$CONST_Collapsed|getTag", {
+      "context": "$context",
+      "node": "$member.node",
+      "member": "$member"
+    }),
+    expandedStub: T.TAG("$tag", {
+      "context": "$context",
+      "node": "$node",
+      "member": "$member"
+    }),
+    moreTag: T.SPAN({
+      "class": "more"
+    }, " ... "),
+    getLabel: function getLabel(node) {
+      return "filetree";
+    },
+    getMemberNameDecorator: function getMemberNameDecorator(member) {
+      return "";
+    },
+    getMemberCount: function getMemberCount(node) {
+      if (!node.value) return 0;
+      var count = 0;
+
+      for (var name in node.value) {
+        count++;
+      }
+
+      return count;
+    },
+    getTag: function getTag(context, member, type) {
+      if (type === this.CONST_Short) {
+        return context.repForNode(member.node).shortTag;
+      } else if (type === this.CONST_Normal) {
+        if (member.expandable) {
+          return this.expandableStub;
+        } else {
+          return context.repForNode(member.node).tag;
+        }
+      } else if (type === this.CONST_Collapsed) {
+        var rep = context.repForNode(member.node);
+
+        if (typeof rep.collapsedTag === "undefined") {
+          console.error("rep", rep);
+          throw "no 'collapsedTag' property in rep: " + rep.toString();
+        }
+
+        return rep.collapsedTag;
+      }
+    },
+    dictionaryIterator: function dictionaryIterator(context, node, type) {
+      var members = [];
+      if (!node.value || node.value.length == 0) return members;
+
+      for (var name in node.value) {
+        var member = {
+          "name": name,
+          "node": domplate.util.merge(node.value[name], {
+            "wrapped": true
+          }),
+          "more": true,
+          "expandable": this.isExpandable(node.value[name])
+        };
+
+        if (members.length > 1 && type == this.CONST_Short) {
+          member["tag"] = this.moreTag;
+        } else {
+          member["tag"] = this.getTag(context, member, type);
+        }
+
+        members.push(member);
+
+        if (members.length > 2 && type == this.CONST_Short) {
+          break;
+        }
+      }
+
+      if (members.length > 0) {
+        members[members.length - 1]["more"] = false;
+      }
+
+      return members;
+    },
+    isExpandable: function isExpandable(node) {
+      return true;
+      return node.type == "reference" || node.type == "filetree" || node.type == "map" || node.type == "array";
+    },
+    onClick: function onClick(event) {
+      if (!domplate.util.isLeftClick(event)) {
+        return;
+      }
+
+      var row = domplate.util.getAncestorByClass(event.target, "member");
+
+      if (domplate.util.hasClass(row, "expandable")) {
+        if (this.toggleRow(row)) {
+          event.stopPropagation();
+        }
+      }
+    },
+    _isTagExpandable: function _isTagExpandable(tag) {
+      while (true) {
+        if (!tag.parentNode) {
+          return true;
+        }
+
+        if (tag.getAttribute("allowTagExpand") === "false") {
+          return false;
+        }
+
+        tag = tag.parentNode;
+      }
+    },
+    toggleRow: function toggleRow(row) {
+      if (!this._isTagExpandable(row)) {
+        return false;
+      }
+
+      var valueElement = domplate.util.getElementByClass(row, "value");
+
+      if (domplate.util.hasClass(row, "expanded")) {
+        domplate.util.removeClass(row, "expanded");
+        this.expandedStub.replace({
+          "tag": this.expandableStub,
+          "member": row.memberObject,
+          "node": row.memberObject.node,
+          "context": row.contextObject
+        }, valueElement);
+      } else {
+        domplate.util.setClass(row, "expanded");
+        this.expandedStub.replace({
+          "tag": row.contextObject.repForNode(row.memberObject.node).tag,
+          "member": row.memberObject,
+          "node": row.memberObject.node,
+          "context": row.contextObject
+        }, valueElement);
+      }
+
+      return true;
+    }
+  };
+}
+
+exports.main = function (domplate, options) {
+  options = options || {};
+  var rep = impl(domplate);
+  rep.__dom = {
+"tag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0) {  var l0 = 0;  var if_0 = 0;  var e0 = 0;  with (this) {      l0 = __loop__.apply(this, [d0, function(i0,l0,d0,d1,d2,d3,d4,d5) {        node = __path__(root, o,0+l0+0);node.addEventListener("click", __bind__(this, d0), false);node.memberObject = d1;node.contextObject = d2;        node = __path__(root, o,0+l0+0,0+1+1,0);        e0 = __link__(node, d3, d4);      if_0 = __if__.apply(this, [d5, function(if_0) {      }]);        return 0+1;      }]);  }  return 1;})
+}
+,
+"shortTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0) {  var l0 = 0;  var if_0 = 0;  var e0 = 0;  with (this) {      l0 = __loop__.apply(this, [d0, function(i0,l0,d0,d1,d2) {        node = __path__(root, o,0+1+l0+0,0+1+1,0);        e0 = __link__(node, d0, d1);      if_0 = __if__.apply(this, [d2, function(if_0) {      }]);        return 0+1;      }]);  }  return 1;})
+}
+,
+"collapsedTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o) {  with (this) {  }  return 1;})
+}
+,
+"expandableStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0, d1) {  var e0 = 0;  with (this) {        node = __path__(root, o);        e0 = __link__(node, d0, d1);  }  return e0;})
+}
+,
+"expandedStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0, d1) {  var e0 = 0;  with (this) {        node = __path__(root, o);        e0 = __link__(node, d0, d1);  }  return e0;})
+}
+,
+"moreTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o) {  with (this) {  }  return 1;})
+}
+};
+  rep.__markup = {
+"tag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","77a4ea3f2f877bdbce48a9c8f3bb26e983097a74", "\"", " __dtid=\"","elements/optiontree", "\"", " class=\"","dictionary", " ", "\"",">");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Normal), __out__, function(member, __out__) {    __code__.push("","<div", " class=\"","member", " ", (member.expandable ? "expandable" + " " : ""), "\"",">","<span", " decorator=\"",__escape__(getMemberNameDecorator(member)), "\"", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,member,context);__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","</span>");  }}})
+}
+,
+"shortTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","77a4ea3f2f877bdbce48a9c8f3bb26e983097a74", "\"", " __dtid=\"","elements/optiontree", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Short), __out__, function(member, __out__) {    __code__.push("","<span", " class=\"","member", " ", "\"",">","<span", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+}
+,
+"collapsedTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","77a4ea3f2f877bdbce48a9c8f3bb26e983097a74", "\"", " __dtid=\"","elements/optiontree", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getMemberCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
+}
+,
+"expandableStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {__link__(getTag(context,member,CONST_Collapsed), __code__, __out__, {"context":context,"node":member.node,"member":member});  }}})
+}
+,
+"expandedStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {__link__(tag, __code__, __out__, {"context":context,"node":node,"member":member});  }}})
+}
+,
+"moreTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","77a4ea3f2f877bdbce48a9c8f3bb26e983097a74", "\"", " __dtid=\"","elements/optiontree", "\"", " class=\"","more", " ", "\"",">"," ... ","</span>");  }}})
+}
+};
+  rep.__dbid = "77a4ea3f2f877bdbce48a9c8f3bb26e983097a74";
+  rep.__dtid = "elements/optiontree";
+  var res = domplate.domplate(rep);
+  var injectedCss = false;
+
+  rep.__ensureCssInjected = function () {
+    if (injectedCss) return;
+    injectedCss = true;
+    domplate.loadStyle("elements/optiontree.rep.css", options.cssBaseUrl || undefined);
+  };
+
+  Object.keys(rep).forEach(function (tagName) {
+    if (!rep[tagName].tag) return;
+    var replace_orig = res[tagName].replace;
+
+    res[tagName].replace = function () {
+      var res = replace_orig.apply(this, arguments);
+      if (!res) return;
+      setTimeout(function () {
+        rep.__ensureCssInjected();
+      }, 0);
+      return res;
+    };
+  });
+  return res;
+};
+},{}]},{},[1])(1)
+});
+
+	});
+});
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.preview.htm
new file mode 100644
index 0000000..65b2789
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.preview.htm
@@ -0,0 +1,40 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486" __dtid="elements/pathtree" class="dictionary "><div class="member expandable "><span decorator="" class="name ">/demo</span><span class="delimiter "></span><span class="value "></span></div></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486" __dtid="elements/pathtree" class="dictionary "><span>pathtree(</span><span class="member "><span class="name ">/demo</span><span class="delimiter "></span><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486" __dtid="elements/pathtree" class="dictionary "><span>(</span><span class="collapsed ">0</span><span>)</span></span>
+
+<br/>
+
+<h2>expandableStub</h2>
+
+
+
+<br/>
+
+<h2>expandedStub</h2>
+
+
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486" __dtid="elements/pathtree" class="more "> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.rep.css
new file mode 100644
index 0000000..2a2ab06
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.rep.css
@@ -0,0 +1,54 @@
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > SPAN {
+    color: #9C9C9C;
+}
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > SPAN.collapsed {
+    color: #0000ff9c;
+    font-weight: normal;
+    padding-left: 5px;
+    padding-right: 5px;
+}
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > SPAN.summary {
+    color: #0000FF;
+    font-weight: normal;
+    padding-left: 5px;
+    padding-right: 5px;
+}
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > SPAN.member {
+    color: #9C9C9C;
+}
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > DIV.member {
+    display: block;
+    padding-left: 15px;
+    padding-top: 1px;
+    padding-bottom: 1px;
+}
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > DIV.member.expandable {
+    background-image: url(images/twisty-closed.png);
+    background-repeat: no-repeat;
+    background-position: 1px 3px;
+    cursor: pointer;
+}
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > DIV.member.expandable.expanded {
+    background-image: url(images/twisty-open.png);
+}
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > .member > SPAN.name {
+    color: #011b7b;
+    font-weight: normal;
+}
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > .member > SPAN.value {
+    font-weight: normal;
+    padding-top: 3px;
+}
+
+SPAN.dictionary[__dbid="0f0401809dc6d33090f1a3cecfeca8f3955b1486"] > .member > SPAN.delimiter, SPAN.dictionary > .member > SPAN.separator, SPAN.dictionary > .member SPAN.more {
+    color: #9C9C9C;
+    padding-left: 2px;
+    padding-right: 2px;
+}        
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.rep.js
new file mode 100644
index 0000000..897398c
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/elements/pathtree.rep.js
@@ -0,0 +1,426 @@
+PINF.bundle("", function(__require) {
+	__require.memoize("/main.js", function (_require, _exports, _module) {
+var bundle = { require: _require, exports: _exports, module: _module };
+var exports = undefined;
+var module = undefined;
+var define = function (deps, init) {
+_module.exports = init();
+}; define.amd = true;
+       var pmodule = bundle.module;
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+
+
+function impl(domplate) {
+  var T = domplate.tags;
+  return {
+    CONST_Normal: "tag",
+    CONST_Short: "shortTag",
+    CONST_Collapsed: "collapsedTag",
+    tag: T.SPAN({
+      "class": "dictionary"
+    }, T.FOR("member", "$context,$node,$CONST_Normal|dictionaryIterator", T.DIV({
+      "class": "member",
+      "$expandable": "$member.expandable",
+      "_memberObject": "$member",
+      "_contextObject": "$context",
+      "onclick": "$onToggle"
+    }, T.SPAN({
+      "class": "name",
+      "decorator": "$member|getMemberNameDecorator",
+      "onclick": "$onSelect"
+    }, "$member.name"), T.SPAN({
+      "class": "delimiter"
+    }, ""), T.SPAN({
+      "class": "value"
+    }, T.TAG("$member.tag", {
+      "context": "$context",
+      "node": "$member.node",
+      "member": "$member"
+    })), T.IF("$member.more", T.SPAN({
+      "class": "separator"
+    }, ","))))),
+    shortTag: T.SPAN({
+      "class": "dictionary"
+    }, T.SPAN("$node|getLabel("), T.FOR("member", "$context,$node,$CONST_Short|dictionaryIterator", T.SPAN({
+      "class": "member"
+    }, T.SPAN({
+      "class": "name"
+    }, "$member.name"), T.SPAN({
+      "class": "delimiter"
+    }, ""), T.SPAN({
+      "class": "value"
+    }, T.TAG("$member.tag", {
+      "context": "$context",
+      "node": "$member.node",
+      "member": "$member"
+    })), T.IF("$member.more", T.SPAN({
+      "class": "separator"
+    }, ",")))), T.SPAN(")")),
+    collapsedTag: T.SPAN({
+      "class": "dictionary"
+    }, T.SPAN("("), T.SPAN({
+      "class": "collapsed"
+    }, "$node|getMemberCount"), T.SPAN(")")),
+    expandableStub: T.TAG("$context,$member,$CONST_Collapsed|getTag", {
+      "context": "$context",
+      "node": "$member.node",
+      "member": "$member"
+    }),
+    expandedStub: T.TAG("$tag", {
+      "context": "$context",
+      "node": "$node",
+      "member": "$member"
+    }),
+    moreTag: T.SPAN({
+      "class": "more"
+    }, " ... "),
+    getLabel: function getLabel(node) {
+      return "pathtree";
+    },
+    getMemberNameDecorator: function getMemberNameDecorator(member) {
+      return "";
+    },
+    getMemberCount: function getMemberCount(node) {
+      if (!node.value.children) return 0;
+      return node.value.children.length;
+    },
+    getTag: function getTag(context, member, type) {
+      if (type === this.CONST_Short) {
+        return context.repForNode(member.node).shortTag;
+      } else if (type === this.CONST_Normal) {
+        if (member.expandable) {
+          return this.expandableStub;
+        } else {
+          return context.repForNode(member.node).tag;
+        }
+      } else if (type === this.CONST_Collapsed) {
+        var rep = context.repForNode(member.node);
+
+        if (typeof rep.collapsedTag === "undefined") {
+          console.error("rep", rep);
+          throw "no 'collapsedTag' property in rep: " + rep.toString();
+        }
+
+        return rep.collapsedTag;
+      }
+    },
+    _objectListToTree: function _objectListToTree(list) {
+      var map = {},
+          node,
+          roots = [],
+          i,
+          parentIdParts,
+          j,
+          id;
+
+      for (i = 0; i < list.length; i += 1) {
+        id = list[i];
+        list[i] = {
+          id: id,
+          label: id.replace(/^.*(\/[^\/]+)$/, "$1"),
+          parentId: id.replace(/\/[^\/]+$/, "")
+        };
+
+        if (list[i].parentId && !map[list[i].parentId]) {
+          list.push(list[i].parentId);
+          map[list[i].parentId] = list.length - 1;
+        }
+
+        map[list[i].id] = i;
+        list[i].children = [];
+      }
+
+      for (i = 0; i < list.length; i += 1) {
+        node = list[i];
+
+        if (node.parentId !== "") {
+          list[map[node.parentId]].children.push(node);
+        } else {
+          roots.push(node);
+        }
+      }
+
+      return roots;
+    },
+    dictionaryIterator: function dictionaryIterator(context, node, type) {
+      var self = this;
+      var value = node.value;
+      var members = [];
+      if (!value || value.length == 0) return members;
+
+      if (Array.isArray(value)) {
+        value = {
+          children: self._objectListToTree(value)
+        };
+
+        while (value.children.length === 1 && value.children[0].children && value.children[0].children.length === 1) {
+          value.children = value.children[0].children;
+        }
+      }
+
+      if (value.children && value.children.length) {
+        var stop = false;
+        value.children.forEach(function (child, i) {
+          if (stop) {
+            return;
+          }
+
+          var member = {
+            "name": child.label,
+            "node": domplate.util.merge({
+              meta: node.meta,
+              value: child
+            }, {
+              "wrapped": true
+            }),
+            "more": i < value.children - 1,
+            "expandable": child.children && child.children.length
+          };
+
+          if (members.length > 1 && type == self.CONST_Short) {
+            member["tag"] = self.moreTag;
+          } else {
+            member["tag"] = self.getTag(context, member, type);
+          }
+
+          members.push(member);
+
+          if (members.length > 2 && type == self.CONST_Short) {
+            stop = true;
+          }
+        });
+      }
+
+      if (members.length > 0) {
+        members[members.length - 1]["more"] = false;
+      }
+
+      return members;
+    },
+    onToggle: function onToggle(event) {
+      if (!domplate.util.isLeftClick(event)) {
+        return;
+      }
+
+      var memberTag = domplate.util.getAncestorByClass(event.target, "member");
+      var nameTag = domplate.util.getElementByClass(memberTag, "name");
+      var nameTagRect = nameTag.getBoundingClientRect();
+      var pointer = {
+        x: event.clientX,
+        y: event.clientY
+      };
+
+      if (pointer.x < nameTagRect.left || pointer.x > nameTagRect.right || pointer.y < nameTagRect.top || pointer.y > nameTagRect.bottom) {
+        if (domplate.util.hasClass(memberTag, "expandable")) {
+          if (this.toggleRow(memberTag)) {
+            event.stopPropagation();
+          }
+        }
+      }
+    },
+    onSelect: function onSelect(event) {
+      if (!domplate.util.isLeftClick(event)) {
+        return;
+      }
+
+      event.stopPropagation();
+      var memberTag = domplate.util.getAncestorByClass(event.target, "member");
+      memberTag.contextObject.dispatchEvent('click', [event, {
+        "rep": "insight.domplate.reps/elements/pathtree",
+        "node": memberTag.memberObject.node
+      }]);
+    },
+    _isTagExpandable: function _isTagExpandable(tag) {
+      while (true) {
+        if (!tag.parentNode) {
+          return true;
+        }
+
+        if (tag.getAttribute("allowTagExpand") === "false") {
+          return false;
+        }
+
+        tag = tag.parentNode;
+      }
+    },
+    toggleRow: function toggleRow(row) {
+      if (!this._isTagExpandable(row)) {
+        return false;
+      }
+
+      var valueElement = domplate.util.getElementByClass(row, "value");
+
+      if (domplate.util.hasClass(row, "expanded")) {
+        domplate.util.removeClass(row, "expanded");
+        this.expandedStub.replace({
+          "tag": this.expandableStub,
+          "member": row.memberObject,
+          "node": row.memberObject.node,
+          "context": row.contextObject
+        }, valueElement);
+      } else {
+        domplate.util.setClass(row, "expanded");
+        this.expandedStub.replace({
+          "tag": row.contextObject.repForNode(row.memberObject.node).tag,
+          "member": row.memberObject,
+          "node": row.memberObject.node,
+          "context": row.contextObject
+        }, valueElement);
+      }
+
+      return true;
+    }
+  };
+}
+
+exports.main = function (domplate, options) {
+  options = options || {};
+  var rep = impl(domplate);
+  rep.__dom = {
+"tag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0) {  var l0 = 0;  var if_0 = 0;  var e0 = 0;  with (this) {      l0 = __loop__.apply(this, [d0, function(i0,l0,d0,d1,d2,d3,d4,d5,d6) {        node = __path__(root, o,0+l0+0);node.addEventListener("click", __bind__(this, d0), false);node.memberObject = d1;node.contextObject = d2;        node = __path__(root, o,0+l0+0,0);node.addEventListener("click", __bind__(this, d3), false);        node = __path__(root, o,0+l0+0,0+1+1,0);        e0 = __link__(node, d4, d5);      if_0 = __if__.apply(this, [d6, function(if_0) {      }]);        return 0+1;      }]);  }  return 1;})
+}
+,
+"shortTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0) {  var l0 = 0;  var if_0 = 0;  var e0 = 0;  with (this) {      l0 = __loop__.apply(this, [d0, function(i0,l0,d0,d1,d2) {        node = __path__(root, o,0+1+l0+0,0+1+1,0);        e0 = __link__(node, d0, d1);      if_0 = __if__.apply(this, [d2, function(if_0) {      }]);        return 0+1;      }]);  }  return 1;})
+}
+,
+"collapsedTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o) {  with (this) {  }  return 1;})
+}
+,
+"expandableStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0, d1) {  var e0 = 0;  with (this) {        node = __path__(root, o);        e0 = __link__(node, d0, d1);  }  return e0;})
+}
+,
+"expandedStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o, d0, d1) {  var e0 = 0;  with (this) {        node = __path__(root, o);        e0 = __link__(node, d0, d1);  }  return e0;})
+}
+,
+"moreTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __path__ = context.__path__;
+var __bind__ = context.__bind__;
+var __if__ = context.__if__;
+var __link__ = context.__link__;
+var __loop__ = context.__loop__;
+return (function (root, context, o) {  with (this) {  }  return 1;})
+}
+};
+  rep.__markup = {
+"tag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","0f0401809dc6d33090f1a3cecfeca8f3955b1486", "\"", " __dtid=\"","elements/pathtree", "\"", " class=\"","dictionary", " ", "\"",">");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Normal), __out__, function(member, __out__) {    __code__.push("","<div", " class=\"","member", " ", (member.expandable ? "expandable" + " " : ""), "\"",">","<span", " decorator=\"",__escape__(getMemberNameDecorator(member)), "\"", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">","","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onToggle,member,context,onSelect);__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","</span>");  }}})
+}
+,
+"shortTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","0f0401809dc6d33090f1a3cecfeca8f3955b1486", "\"", " __dtid=\"","elements/pathtree", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Short), __out__, function(member, __out__) {    __code__.push("","<span", " class=\"","member", " ", "\"",">","<span", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">","","</span>","<span", " class=\"","value", " ", "\"",">");__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+}
+,
+"collapsedTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","0f0401809dc6d33090f1a3cecfeca8f3955b1486", "\"", " __dtid=\"","elements/pathtree", "\"", " class=\"","dictionary", " ", "\"",">","<span",">","(","</span>","<span", " class=\"","collapsed", " ", "\"",">",__escape__(getMemberCount(node)),"</span>","<span",">",")","</span>","</span>");  }}})
+}
+,
+"expandableStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {__link__(getTag(context,member,CONST_Collapsed), __code__, __out__, {"context":context,"node":member.node,"member":member});  }}})
+}
+,
+"expandedStub":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {__link__(tag, __code__, __out__, {"context":context,"node":node,"member":member});  }}})
+}
+,
+"moreTag":function (context) {
+var DomplateDebug = context.DomplateDebug;
+var __escape__ = context.__escape__;
+var __if__ = context.__if__;
+var __loop__ = context.__loop__;
+var __link__ = context.__link__;
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","0f0401809dc6d33090f1a3cecfeca8f3955b1486", "\"", " __dtid=\"","elements/pathtree", "\"", " class=\"","more", " ", "\"",">"," ... ","</span>");  }}})
+}
+};
+  rep.__dbid = "0f0401809dc6d33090f1a3cecfeca8f3955b1486";
+  rep.__dtid = "elements/pathtree";
+  var res = domplate.domplate(rep);
+  var injectedCss = false;
+
+  rep.__ensureCssInjected = function () {
+    if (injectedCss) return;
+    injectedCss = true;
+    domplate.loadStyle("elements/pathtree.rep.css", options.cssBaseUrl || undefined);
+  };
+
+  Object.keys(rep).forEach(function (tagName) {
+    if (!rep[tagName].tag) return;
+    var replace_orig = res[tagName].replace;
+
+    res[tagName].replace = function () {
+      var res = replace_orig.apply(this, arguments);
+      if (!res) return;
+      setTimeout(function () {
+        rep.__ensureCssInjected();
+      }, 0);
+      return res;
+    };
+  });
+  return res;
+};
+},{}]},{},[1])(1)
+});
+
+	});
+});
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.preview.htm
new file mode 100644
index 0000000..517a354
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.preview.htm
@@ -0,0 +1,28 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="php/array-associative" class="map "><span>map(</span><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></div><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></div><span>)</span></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="php/array-associative" class="map "><span>map(</span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="php/array-associative" class="map "><span>map(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="php/array-associative"> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.rep.js
similarity index 82%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.rep.js
index 6f638b2..6d9de36 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-associative.rep.js
@@ -230,7 +230,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","insight.domplate.reps/php/array-associative", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Normal), __out__, function(pair, __out__) {    __code__.push("","<div", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","php/array-associative", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Normal), __out__, function(pair, __out__) {    __code__.push("","<div", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -239,7 +239,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","insight.domplate.reps/php/array-associative", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Short), __out__, function(pair, __out__) {    __code__.push("","<span", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","php/array-associative", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");__out__.push(node);    __loop__.apply(this, [mapIterator(context,node,CONST_Short), __out__, function(pair, __out__) {    __code__.push("","<span", " class=\"","pair", " ", "\"",">");__link__(pair.key.tag, __code__, __out__, {"node":pair.key.node,"context":context});    __code__.push("","<span", " class=\"","delimiter", " ", "\"",">","=>","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,pair.value.node,context,pair.value.expandable);__link__(pair.value.tag, __code__, __out__, {"node":pair.value.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [pair.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -248,7 +248,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","insight.domplate.reps/php/array-associative", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getItemCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","php/array-associative", "\"", " class=\"","map", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getItemCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "moreTag":function (context) {
@@ -257,11 +257,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","insight.domplate.reps/php/array-associative", "\"",">"," ... ","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a95f694649c73f58a7f6356cfd1437e0be74823", "\"", " __dtid=\"","php/array-associative", "\"",">"," ... ","</span>");  }}})
 }
 };
   rep.__dbid = "4a95f694649c73f58a7f6356cfd1437e0be74823";
-  rep.__dtid = "insight.domplate.reps/php/array-associative";
+  rep.__dtid = "php/array-associative";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.preview.htm
new file mode 100644
index 0000000..13ddcc9
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.preview.htm
@@ -0,0 +1,40 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="php/array-indexed" class="array "><span>array(</span><div class="element "><span class="value "></span><span class="separator ">,</span></div><div class="element "><span class="value "></span></div><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="php/array-indexed" class="array "><span>array(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="php/array-indexed" class="array "><span>array(</span><span class="element "><span class="value "></span><span class="separator ">,</span></span><span class="element "><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>expandableStub</h2>
+
+
+
+<br/>
+
+<h2>expandedStub</h2>
+
+
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="php/array-indexed"> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.rep.js
similarity index 87%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.rep.js
index 1325414..fe4f12d 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/array-indexed.rep.js
@@ -227,7 +227,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","insight.domplate.reps/php/array-indexed", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Normal), __out__, function(element, __out__) {    __code__.push("","<div", " class=\"","element", " ", (element.expandable ? "expandable" + " " : ""), "\"",">","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,context,element);__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","php/array-indexed", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Normal), __out__, function(element, __out__) {    __code__.push("","<div", " class=\"","element", " ", (element.expandable ? "expandable" + " " : ""), "\"",">","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,context,element);__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -236,7 +236,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","insight.domplate.reps/php/array-indexed", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getElementCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","php/array-indexed", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getElementCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -245,7 +245,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","insight.domplate.reps/php/array-indexed", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Short), __out__, function(element, __out__) {    __code__.push("","<span", " class=\"","element", " ", "\"",">","<span", " class=\"","value", " ", "\"",">");__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","php/array-indexed", "\"", " class=\"","array", " ", "\"",">","<span",">",__escape__(VAR_label),"(","</span>");    __loop__.apply(this, [elementIterator(context,node,CONST_Short), __out__, function(element, __out__) {    __code__.push("","<span", " class=\"","element", " ", "\"",">","<span", " class=\"","value", " ", "\"",">");__link__(element.tag, __code__, __out__, {"element":element,"node":element.node,"context":context});    __code__.push("","</span>");__if__.apply(this, [element.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "expandableStub":function (context) {
@@ -272,11 +272,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","insight.domplate.reps/php/array-indexed", "\"",">"," ... ","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e7edf46c5cf053a32cad8a256bef09bba0a1db61", "\"", " __dtid=\"","php/array-indexed", "\"",">"," ... ","</span>");  }}})
 }
 };
   rep.__dbid = "e7edf46c5cf053a32cad8a256bef09bba0a1db61";
-  rep.__dtid = "insight.domplate.reps/php/array-indexed";
+  rep.__dtid = "php/array-indexed";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.preview.htm
similarity index 61%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.preview.htm
index 25fa5f6..4d36513 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="07daf9a04792322a3256378f92d6af6c8f0f23b8" __dtid="insight.domplate.reps/php/boolean" class="boolean ">TRUE</span>
+<span __dbid="07daf9a04792322a3256378f92d6af6c8f0f23b8" __dtid="php/boolean" class="boolean ">TRUE</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="07daf9a04792322a3256378f92d6af6c8f0f23b8" __dtid="insight.domplate.reps/php/boolean" class="boolean ">TRUE</span>
+<span __dbid="07daf9a04792322a3256378f92d6af6c8f0f23b8" __dtid="php/boolean" class="boolean ">TRUE</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.rep.js
similarity index 92%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.rep.js
index 6dcde6a..d71201a 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/boolean.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/boolean.rep.js
@@ -57,7 +57,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","07daf9a04792322a3256378f92d6af6c8f0f23b8", "\"", " __dtid=\"","insight.domplate.reps/php/boolean", "\"", " class=\"","boolean", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","07daf9a04792322a3256378f92d6af6c8f0f23b8", "\"", " __dtid=\"","php/boolean", "\"", " class=\"","boolean", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -66,11 +66,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","07daf9a04792322a3256378f92d6af6c8f0f23b8", "\"", " __dtid=\"","insight.domplate.reps/php/boolean", "\"", " class=\"","boolean", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","07daf9a04792322a3256378f92d6af6c8f0f23b8", "\"", " __dtid=\"","php/boolean", "\"", " class=\"","boolean", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 };
   rep.__dbid = "07daf9a04792322a3256378f92d6af6c8f0f23b8";
-  rep.__dtid = "insight.domplate.reps/php/boolean";
+  rep.__dtid = "php/boolean";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.preview.htm
new file mode 100644
index 0000000..9b1334f
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.preview.htm
@@ -0,0 +1,16 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<div __dbid="900280dccb9220e71264efb50a24debff867f7f3" __dtid="php/exception" class="structures-trace "><table cellpadding="3" cellspacing="0"><tbody><tr><th class="header-file ">File</th><th class="header-line ">Line</th><th class="header-inst ">Instruction</th></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">10</td><td class="cell-inst "><div>Class(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">5</td><td class="cell-inst "><div>Function(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr></tbody></table></div>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="900280dccb9220e71264efb50a24debff867f7f3" __dtid="php/exception" class="structures-trace "></span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.rep.js
similarity index 86%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.rep.js
index e0a2859..3c8edc2 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/exception.rep.js
@@ -257,7 +257,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","900280dccb9220e71264efb50a24debff867f7f3", "\"", " __dtid=\"","insight.domplate.reps/php/exception", "\"", " class=\"","structures-trace", " ", "\"",">","<table", " cellpadding=\"","3", "\"", " cellspacing=\"","0", "\"",">","<tbody",">","<tr",">","<th", " class=\"","header-file", " ", "\"",">","File","</th>","<th", " class=\"","header-line", " ", "\"",">","Line","</th>","<th", " class=\"","header-inst", " ", "\"",">","Instruction","</th>","</tr>");__out__.push(node);    __loop__.apply(this, [getCallList(node), __out__, function(frame, __out__) {    __code__.push("","<tr",">","<td", " class=\"","cell-file", " ", "\"",">",__escape__(frame.file),"</td>","<td", " class=\"","cell-line", " ", "\"",">",__escape__(frame.line),"</td>","<td", " class=\"","cell-inst", " ", "\"",">","<div",">",__escape__(getFrameLabel(frame)),"(");__out__.push(frame.node,onFileClick,onFileClick);    __loop__.apply(this, [argIterator(context,frame), __out__, function(arg, __out__) {    __code__.push("","<div", " class=\"","arg", " ", "\"",">");__out__.push(onArgClick,arg.node);__link__(arg.tag, __code__, __out__, {"node":arg.node,"context":context});__if__.apply(this, [arg.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("",")","</div>","</td>","</tr>");    }]);    __code__.push("","</tbody>","</table>","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","900280dccb9220e71264efb50a24debff867f7f3", "\"", " __dtid=\"","php/exception", "\"", " class=\"","structures-trace", " ", "\"",">","<table", " cellpadding=\"","3", "\"", " cellspacing=\"","0", "\"",">","<tbody",">","<tr",">","<th", " class=\"","header-file", " ", "\"",">","File","</th>","<th", " class=\"","header-line", " ", "\"",">","Line","</th>","<th", " class=\"","header-inst", " ", "\"",">","Instruction","</th>","</tr>");__out__.push(node);    __loop__.apply(this, [getCallList(node), __out__, function(frame, __out__) {    __code__.push("","<tr",">","<td", " class=\"","cell-file", " ", "\"",">",__escape__(frame.file),"</td>","<td", " class=\"","cell-line", " ", "\"",">",__escape__(frame.line),"</td>","<td", " class=\"","cell-inst", " ", "\"",">","<div",">",__escape__(getFrameLabel(frame)),"(");__out__.push(frame.node,onFileClick,onFileClick);    __loop__.apply(this, [argIterator(context,frame), __out__, function(arg, __out__) {    __code__.push("","<div", " class=\"","arg", " ", "\"",">");__out__.push(onArgClick,arg.node);__link__(arg.tag, __code__, __out__, {"node":arg.node,"context":context});__if__.apply(this, [arg.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("",")","</div>","</td>","</tr>");    }]);    __code__.push("","</tbody>","</table>","</div>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -266,11 +266,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","900280dccb9220e71264efb50a24debff867f7f3", "\"", " __dtid=\"","insight.domplate.reps/php/exception", "\"", " class=\"","structures-trace", " ", "\"",">");__out__.push(node);__link__(getCaptionTag(context,node), __code__, __out__, {"node":getCaptionNode(node)});    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","900280dccb9220e71264efb50a24debff867f7f3", "\"", " __dtid=\"","php/exception", "\"", " class=\"","structures-trace", " ", "\"",">");__out__.push(node);__link__(getCaptionTag(context,node), __code__, __out__, {"node":getCaptionNode(node)});    __code__.push("","</span>");  }}})
 }
 };
   rep.__dbid = "900280dccb9220e71264efb50a24debff867f7f3";
-  rep.__dtid = "insight.domplate.reps/php/exception";
+  rep.__dtid = "php/exception";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.preview.htm
similarity index 63%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.preview.htm
index 318ddeb..83329ff 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="f0137b4e5003190e0ca62ea1c5350bfa1f3eb570" __dtid="insight.domplate.reps/php/float" class="float ">1.1</span>
+<span __dbid="f0137b4e5003190e0ca62ea1c5350bfa1f3eb570" __dtid="php/float" class="float ">1.1</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="f0137b4e5003190e0ca62ea1c5350bfa1f3eb570" __dtid="insight.domplate.reps/php/float" class="float ">1.1</span>
+<span __dbid="f0137b4e5003190e0ca62ea1c5350bfa1f3eb570" __dtid="php/float" class="float ">1.1</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.rep.js
similarity index 93%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.rep.js
index ebab717..70e4c4a 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/float.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/float.rep.js
@@ -70,7 +70,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","f0137b4e5003190e0ca62ea1c5350bfa1f3eb570", "\"", " __dtid=\"","insight.domplate.reps/php/float", "\"", " class=\"","float", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","f0137b4e5003190e0ca62ea1c5350bfa1f3eb570", "\"", " __dtid=\"","php/float", "\"", " class=\"","float", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -79,11 +79,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","f0137b4e5003190e0ca62ea1c5350bfa1f3eb570", "\"", " __dtid=\"","insight.domplate.reps/php/float", "\"", " class=\"","float", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","f0137b4e5003190e0ca62ea1c5350bfa1f3eb570", "\"", " __dtid=\"","php/float", "\"", " class=\"","float", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 };
   rep.__dbid = "f0137b4e5003190e0ca62ea1c5350bfa1f3eb570";
-  rep.__dtid = "insight.domplate.reps/php/float";
+  rep.__dtid = "php/float";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/edit-rule.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/edit-rule.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/edit-rule.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/edit-rule.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/object-member-visibility-sprite.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/object-member-visibility-sprite.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/images/object-member-visibility-sprite.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/object-member-visibility-sprite.png
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/twisty-closed.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/twisty-closed.png
new file mode 100644
index 0000000..f80319b
Binary files /dev/null and b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/twisty-closed.png differ
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/twisty-open.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/twisty-open.png
new file mode 100644
index 0000000..8680124
Binary files /dev/null and b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/images/twisty-open.png differ
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.preview.htm
similarity index 61%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.preview.htm
index 473da89..b1d7cb9 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="5ed341c51e0e5ac9d3dc49339a6e69b8d999d256" __dtid="insight.domplate.reps/php/integer" class="integer ">1000</span>
+<span __dbid="5ed341c51e0e5ac9d3dc49339a6e69b8d999d256" __dtid="php/integer" class="integer ">1000</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="5ed341c51e0e5ac9d3dc49339a6e69b8d999d256" __dtid="insight.domplate.reps/php/integer" class="integer ">1000</span>
+<span __dbid="5ed341c51e0e5ac9d3dc49339a6e69b8d999d256" __dtid="php/integer" class="integer ">1000</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.rep.js
similarity index 92%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.rep.js
index 718e93f..f0712d9 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/integer.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/integer.rep.js
@@ -70,7 +70,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ed341c51e0e5ac9d3dc49339a6e69b8d999d256", "\"", " __dtid=\"","insight.domplate.reps/php/integer", "\"", " class=\"","integer", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ed341c51e0e5ac9d3dc49339a6e69b8d999d256", "\"", " __dtid=\"","php/integer", "\"", " class=\"","integer", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -79,11 +79,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ed341c51e0e5ac9d3dc49339a6e69b8d999d256", "\"", " __dtid=\"","insight.domplate.reps/php/integer", "\"", " class=\"","integer", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","5ed341c51e0e5ac9d3dc49339a6e69b8d999d256", "\"", " __dtid=\"","php/integer", "\"", " class=\"","integer", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 };
   rep.__dbid = "5ed341c51e0e5ac9d3dc49339a6e69b8d999d256";
-  rep.__dtid = "insight.domplate.reps/php/integer";
+  rep.__dtid = "php/integer";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.preview.htm
similarity index 63%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.preview.htm
index 6ed3344..2bec42f 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="a255b796936893a5df535a7e12b22d5a662ea2a2" __dtid="insight.domplate.reps/php/null" class="null ">NULL</span>
+<span __dbid="a255b796936893a5df535a7e12b22d5a662ea2a2" __dtid="php/null" class="null ">NULL</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="a255b796936893a5df535a7e12b22d5a662ea2a2" __dtid="insight.domplate.reps/php/null" class="null ">NULL</span>
+<span __dbid="a255b796936893a5df535a7e12b22d5a662ea2a2" __dtid="php/null" class="null ">NULL</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.rep.js
similarity index 92%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.rep.js
index 47e3f4c..1768b5a 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/null.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/null.rep.js
@@ -57,7 +57,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","a255b796936893a5df535a7e12b22d5a662ea2a2", "\"", " __dtid=\"","insight.domplate.reps/php/null", "\"", " class=\"","null", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","a255b796936893a5df535a7e12b22d5a662ea2a2", "\"", " __dtid=\"","php/null", "\"", " class=\"","null", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -66,11 +66,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","a255b796936893a5df535a7e12b22d5a662ea2a2", "\"", " __dtid=\"","insight.domplate.reps/php/null", "\"", " class=\"","null", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","a255b796936893a5df535a7e12b22d5a662ea2a2", "\"", " __dtid=\"","php/null", "\"", " class=\"","null", " ", "\"",">",__escape__(getValue(node)),"</span>");  }}})
 }
 };
   rep.__dbid = "a255b796936893a5df535a7e12b22d5a662ea2a2";
-  rep.__dtid = "insight.domplate.reps/php/null";
+  rep.__dtid = "php/null";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.preview.htm
new file mode 100644
index 0000000..13a615f
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.preview.htm
@@ -0,0 +1,40 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="php/object" class="dictionary "><span>Foo(</span><div class="member "><span decorator="" class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></div><div class="member "><span decorator="" class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></div><span>)</span></span>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="php/object" class="dictionary "><span>Foo(</span><span class="member "><span class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></span><span class="member "><span class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></span><span>)</span></span>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="php/object" class="dictionary "><span>Foo(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
+
+<br/>
+
+<h2>expandableStub</h2>
+
+
+
+<br/>
+
+<h2>expandedStub</h2>
+
+
+
+<br/>
+
+<h2>moreTag</h2>
+
+<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="php/object" class="more "> ... </span>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.rep.js
similarity index 87%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.rep.js
index fe43358..d11c449 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/object.rep.js
@@ -306,7 +306,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","insight.domplate.reps/php/object", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Normal), __out__, function(member, __out__) {    __code__.push("","<div", " class=\"","member", " ", (member.expandable ? "expandable" + " " : ""), "\"",">","<span", " decorator=\"",__escape__(getMemberNameDecorator(member)), "\"", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,member,context);__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","php/object", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Normal), __out__, function(member, __out__) {    __code__.push("","<div", " class=\"","member", " ", (member.expandable ? "expandable" + " " : ""), "\"",">","<span", " decorator=\"",__escape__(getMemberNameDecorator(member)), "\"", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__out__.push(onClick,member,context);__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</div>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -315,7 +315,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","insight.domplate.reps/php/object", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Short), __out__, function(member, __out__) {    __code__.push("","<span", " class=\"","member", " ", "\"",">","<span", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","php/object", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>");    __loop__.apply(this, [dictionaryIterator(context,node,CONST_Short), __out__, function(member, __out__) {    __code__.push("","<span", " class=\"","member", " ", "\"",">","<span", " class=\"","name", " ", "\"",">",__escape__(member.name),"</span>","<span", " class=\"","delimiter", " ", "\"",">",":","</span>","<span", " class=\"","value", " ", "\"",">");__link__(member.tag, __code__, __out__, {"context":context,"node":member.node,"member":member});    __code__.push("","</span>");__if__.apply(this, [member.more, __out__, function(__out__) {    __code__.push("","<span", " class=\"","separator", " ", "\"",">",",","</span>");}]);    __code__.push("","</span>");    }]);    __code__.push("","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -324,7 +324,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","insight.domplate.reps/php/object", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getMemberCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","php/object", "\"", " class=\"","dictionary", " ", "\"",">","<span",">",__escape__(getLabel(node)),"(","</span>","<span", " class=\"","collapsed", " ", "\"",">","... ",__escape__(getMemberCount(node))," ...","</span>","<span",">",")","</span>","</span>");  }}})
 }
 ,
 "expandableStub":function (context) {
@@ -351,11 +351,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","insight.domplate.reps/php/object", "\"", " class=\"","more", " ", "\"",">"," ... ","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","fc2f04314c99af4367ff734e927c773cb9b1251b", "\"", " __dtid=\"","php/object", "\"", " class=\"","more", " ", "\"",">"," ... ","</span>");  }}})
 }
 };
   rep.__dbid = "fc2f04314c99af4367ff734e927c773cb9b1251b";
-  rep.__dtid = "insight.domplate.reps/php/object";
+  rep.__dtid = "php/object";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.preview.htm
similarity index 58%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.preview.htm
index 341b49f..92a3de8 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.preview.htm
@@ -3,13 +3,13 @@
 
 <h2>tag</h2>
 
-<span __dbid="4a4960b1fa287b63ab13aa5373caa1f7076ff7e5" __dtid="insight.domplate.reps/php/resource" class="resource ">[RESOURCE]</span>
+<span __dbid="4a4960b1fa287b63ab13aa5373caa1f7076ff7e5" __dtid="php/resource" class="resource ">[RESOURCE]</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span __dbid="4a4960b1fa287b63ab13aa5373caa1f7076ff7e5" __dtid="insight.domplate.reps/php/resource" class="resource ">[RESOURCE]</span>
+<span __dbid="4a4960b1fa287b63ab13aa5373caa1f7076ff7e5" __dtid="php/resource" class="resource ">[RESOURCE]</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.rep.js
similarity index 91%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.rep.js
index cbd2b0f..6c2f85f 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/resource.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/resource.rep.js
@@ -57,7 +57,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a4960b1fa287b63ab13aa5373caa1f7076ff7e5", "\"", " __dtid=\"","insight.domplate.reps/php/resource", "\"", " class=\"","resource", " ", "\"",">","[",__escape__(getValue(node)),"]","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a4960b1fa287b63ab13aa5373caa1f7076ff7e5", "\"", " __dtid=\"","php/resource", "\"", " class=\"","resource", " ", "\"",">","[",__escape__(getValue(node)),"]","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -66,11 +66,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a4960b1fa287b63ab13aa5373caa1f7076ff7e5", "\"", " __dtid=\"","insight.domplate.reps/php/resource", "\"", " class=\"","resource", " ", "\"",">","[",__escape__(getValue(node)),"]","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","4a4960b1fa287b63ab13aa5373caa1f7076ff7e5", "\"", " __dtid=\"","php/resource", "\"", " class=\"","resource", " ", "\"",">","[",__escape__(getValue(node)),"]","</span>");  }}})
 }
 };
   rep.__dbid = "4a4960b1fa287b63ab13aa5373caa1f7076ff7e5";
-  rep.__dtid = "insight.domplate.reps/php/resource";
+  rep.__dtid = "php/resource";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.preview.htm
similarity index 54%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.preview.htm
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.preview.htm
index 75fa9dc..3b06ea2 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.preview.htm
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.preview.htm
@@ -3,19 +3,19 @@
 
 <h2>tag</h2>
 
-<span wrapped="undefined" __dbid="e8cfecc4a8618496bf811fa7fbd33dfc2338df59" __dtid="insight.domplate.reps/php/string" class="string ">Hello World</span>
+<span wrapped="undefined" __dbid="e8cfecc4a8618496bf811fa7fbd33dfc2338df59" __dtid="php/string" class="string ">Hello World</span>
 
 <br/>
 
 <h2>shortTag</h2>
 
-<span wrapped="undefined" __dbid="e8cfecc4a8618496bf811fa7fbd33dfc2338df59" __dtid="insight.domplate.reps/php/string" class="string ">Hello World</span>
+<span wrapped="undefined" __dbid="e8cfecc4a8618496bf811fa7fbd33dfc2338df59" __dtid="php/string" class="string ">Hello World</span>
 
 <br/>
 
 <h2>trimmedNoticeTag</h2>
 
-<span __dbid="e8cfecc4a8618496bf811fa7fbd33dfc2338df59" __dtid="insight.domplate.reps/php/string" class="trimmed ">undefined</span>
+<span __dbid="e8cfecc4a8618496bf811fa7fbd33dfc2338df59" __dtid="php/string" class="trimmed ">undefined</span>
 
 
 </body>
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.rep.js
similarity index 83%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.rep.js
index cfd0235..a459178 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/string.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/string.rep.js
@@ -195,7 +195,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " wrapped=\"",__escape__(node.wrapped), "\"", " __dbid=\"","e8cfecc4a8618496bf811fa7fbd33dfc2338df59", "\"", " __dtid=\"","insight.domplate.reps/php/string", "\"", " class=\"","string", " ", "\"",">");__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __loop__.apply(this, [getValue(node), __out__, function(line, __out__) {    __code__.push("",__escape__(line.value));__if__.apply(this, [line.special, __out__, function(__out__) {    __code__.push("","<span", " class=\"","special", " ", "\"",">",__escape__(line.specialvalue),"</span>");}]);__if__.apply(this, [line.more, __out__, function(__out__) {    __code__.push("","<br","/>");}]);__if__.apply(this, [line.trimmed, __out__, function(__out__) {__link__(getTrimmedTag(node), __code__, __out__, {"node":node});}]);    }]);__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " wrapped=\"",__escape__(node.wrapped), "\"", " __dbid=\"","e8cfecc4a8618496bf811fa7fbd33dfc2338df59", "\"", " __dtid=\"","php/string", "\"", " class=\"","string", " ", "\"",">");__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __loop__.apply(this, [getValue(node), __out__, function(line, __out__) {    __code__.push("",__escape__(line.value));__if__.apply(this, [line.special, __out__, function(__out__) {    __code__.push("","<span", " class=\"","special", " ", "\"",">",__escape__(line.specialvalue),"</span>");}]);__if__.apply(this, [line.more, __out__, function(__out__) {    __code__.push("","<br","/>");}]);__if__.apply(this, [line.trimmed, __out__, function(__out__) {__link__(getTrimmedTag(node), __code__, __out__, {"node":node});}]);    }]);__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __code__.push("","</span>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -204,7 +204,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " wrapped=\"",__escape__(node.wrapped), "\"", " __dbid=\"","e8cfecc4a8618496bf811fa7fbd33dfc2338df59", "\"", " __dtid=\"","insight.domplate.reps/php/string", "\"", " class=\"","string", " ", "\"",">");__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __loop__.apply(this, [getShortValue(node), __out__, function(line, __out__) {    __code__.push("",__escape__(line.value));__if__.apply(this, [line.special, __out__, function(__out__) {    __code__.push("","<span", " class=\"","special", " ", "\"",">",__escape__(line.specialvalue),"</span>");}]);__if__.apply(this, [line.more, __out__, function(__out__) {    __code__.push("","<br","/>");}]);__if__.apply(this, [line.trimmed, __out__, function(__out__) {__link__(getTrimmedTag(node), __code__, __out__, {"node":node});}]);    }]);__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __code__.push("","</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " wrapped=\"",__escape__(node.wrapped), "\"", " __dbid=\"","e8cfecc4a8618496bf811fa7fbd33dfc2338df59", "\"", " __dtid=\"","php/string", "\"", " class=\"","string", " ", "\"",">");__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __loop__.apply(this, [getShortValue(node), __out__, function(line, __out__) {    __code__.push("",__escape__(line.value));__if__.apply(this, [line.special, __out__, function(__out__) {    __code__.push("","<span", " class=\"","special", " ", "\"",">",__escape__(line.specialvalue),"</span>");}]);__if__.apply(this, [line.more, __out__, function(__out__) {    __code__.push("","<br","/>");}]);__if__.apply(this, [line.trimmed, __out__, function(__out__) {__link__(getTrimmedTag(node), __code__, __out__, {"node":node});}]);    }]);__if__.apply(this, [node.wrapped, __out__, function(__out__) {    __code__.push("","'");}]);    __code__.push("","</span>");  }}})
 }
 ,
 "trimmedNoticeTag":function (context) {
@@ -213,11 +213,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e8cfecc4a8618496bf811fa7fbd33dfc2338df59", "\"", " __dtid=\"","insight.domplate.reps/php/string", "\"", " class=\"","trimmed", " ", "\"",">",__escape__(getNotice(node)),"</span>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<span", " __dbid=\"","e8cfecc4a8618496bf811fa7fbd33dfc2338df59", "\"", " __dtid=\"","php/string", "\"", " class=\"","trimmed", " ", "\"",">",__escape__(getNotice(node)),"</span>");  }}})
 }
 };
   rep.__dbid = "e8cfecc4a8618496bf811fa7fbd33dfc2338df59";
-  rep.__dtid = "insight.domplate.reps/php/string";
+  rep.__dtid = "php/string";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.preview.htm
new file mode 100644
index 0000000..c00b5df
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.preview.htm
@@ -0,0 +1,22 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<div __dbid="859944ec3afd0136c208c76b33fd54c9f08cb1a5" __dtid="php/unknown" class="unknown ">UNKNOWN EXPANDED</div>
+
+<br/>
+
+<h2>collapsedTag</h2>
+
+<div __dbid="859944ec3afd0136c208c76b33fd54c9f08cb1a5" __dtid="php/unknown" class="unknown ">UNKNOWN COLLAPSED</div>
+
+<br/>
+
+<h2>shortTag</h2>
+
+<div __dbid="859944ec3afd0136c208c76b33fd54c9f08cb1a5" __dtid="php/unknown" class="unknown ">UNKNOWN SHORT</div>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.rep.js
similarity index 91%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.rep.js
index d0cb237..1d58a65 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/php/unknown.rep.js
@@ -67,7 +67,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","859944ec3afd0136c208c76b33fd54c9f08cb1a5", "\"", " __dtid=\"","insight.domplate.reps/php/unknown", "\"", " class=\"","unknown", " ", "\"",">","UNKNOWN EXPANDED","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","859944ec3afd0136c208c76b33fd54c9f08cb1a5", "\"", " __dtid=\"","php/unknown", "\"", " class=\"","unknown", " ", "\"",">","UNKNOWN EXPANDED","</div>");  }}})
 }
 ,
 "collapsedTag":function (context) {
@@ -76,7 +76,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","859944ec3afd0136c208c76b33fd54c9f08cb1a5", "\"", " __dtid=\"","insight.domplate.reps/php/unknown", "\"", " class=\"","unknown", " ", "\"",">","UNKNOWN COLLAPSED","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","859944ec3afd0136c208c76b33fd54c9f08cb1a5", "\"", " __dtid=\"","php/unknown", "\"", " class=\"","unknown", " ", "\"",">","UNKNOWN COLLAPSED","</div>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -85,11 +85,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","859944ec3afd0136c208c76b33fd54c9f08cb1a5", "\"", " __dtid=\"","insight.domplate.reps/php/unknown", "\"", " class=\"","unknown", " ", "\"",">","UNKNOWN SHORT","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","859944ec3afd0136c208c76b33fd54c9f08cb1a5", "\"", " __dtid=\"","php/unknown", "\"", " class=\"","unknown", " ", "\"",">","UNKNOWN SHORT","</div>");  }}})
 }
 };
   rep.__dbid = "859944ec3afd0136c208c76b33fd54c9f08cb1a5";
-  rep.__dtid = "insight.domplate.reps/php/unknown";
+  rep.__dtid = "php/unknown";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/reps.json b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/reps.json
new file mode 100644
index 0000000..13df6c6
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/reps.json
@@ -0,0 +1,28 @@
+[
+    "default/array",
+    "default/constant",
+    "default/dictionary",
+    "default/map",
+    "default/reference",
+    "default/string",
+    "default/table",
+    "default/trace",
+    "default/trimmed",
+    "default/unknown",
+    "elements/optiontree",
+    "elements/pathtree",
+    "php/array-associative",
+    "php/array-indexed",
+    "php/boolean",
+    "php/exception",
+    "php/float",
+    "php/integer",
+    "php/null",
+    "php/object",
+    "php/resource",
+    "php/string",
+    "php/unknown",
+    "wrappers/console",
+    "wrappers/request",
+    "wrappers/viewer"
+]
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.preview.htm
new file mode 100644
index 0000000..2b996f0
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.preview.htm
@@ -0,0 +1,22 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<div expandable="true" expanded="false" __dbid="f19222d1afdcb813baf69909d376ee63829cda3c" __dtid="wrappers/console" class="console-message "><div hideonexpand="true" class="header "><div class="expander "></div><div class="actions "><div class="inspect  "></div><div class="file hidden "></div></div><span allowtagexpand="false" class="summary "><span class="label "></span></span><span class="fileline "><div></div></span></div><div class="body "></div></div>
+
+<br/>
+
+<h2>shortTag</h2>
+
+
+
+<br/>
+
+<h2>groupNoMessagesTag</h2>
+
+<div __dbid="f19222d1afdcb813baf69909d376ee63829cda3c" __dtid="wrappers/console" class="group-no-messages ">No Messages</div>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.rep.css
new file mode 100644
index 0000000..a6f8f13
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.rep.css
@@ -0,0 +1,181 @@
+
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] {
+    position: relative;
+    margin: 0;
+    border-bottom: 1px solid #D7D7D7;
+    padding: 0px;
+    background-color: #FFFFFF;
+}
+DIV.console-message.selected[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] {
+    background-color: #35FC03 !important;
+}
+DIV.console-message-group[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expanded=true] {
+    background-color: #77CDD9;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header {
+    position: relative;
+    padding-left: 34px;
+    padding-right: 10px;
+    padding-top: 3px;
+    padding-bottom: 4px;
+    cursor: pointer;
+    min-height: 16px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expanded=true] > DIV.header > SPAN.summary {
+    opacity: 0.4;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expanded=false] > DIV.header:hover {
+    background-color: #ffc73d;
+}
+DIV.console-message-group[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header {
+    background: url(images/document_page_next.png) no-repeat;
+    background-position: 2px 3px;
+    font-weight: bold;
+    background-color: #77CDD9;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header-priority-info {
+    background: url(images/information.png) no-repeat;
+    background-position: 2px 3px;
+    background-color: #c6eeff;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header-priority-warn {
+    background: url(images/exclamation-diamond.png) no-repeat;
+    background-position: 2px 3px;
+    background-color: #ffe68d;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header-priority-error {
+    background: url(images/exclamation-red.png) no-repeat;
+    background-position: 2px 3px;
+    background-color: #ffa7a7;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > DIV.expander {
+    background-color: black;
+    width: 18px;
+    height: 18px;
+    display: inline-block;
+    float: left;
+    position: relative;
+    top: -1px;
+    margin-left: -14px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > DIV.expander:hover {
+    cursor: pointer;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expanded=false] > DIV.header > DIV.expander {
+    background: url(images/plus-small-white.png) no-repeat;
+    background-position: 0px 1px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expanded=true] > DIV.header > DIV.expander {
+    background: url(images/minus-small-white.png) no-repeat;
+    background-position: 0px 1px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > SPAN.summary > SPAN.label > SPAN, DIV.console-message > DIV.header > SPAN.fileline > DIV > DIV.label {
+    margin-right: 5px;
+    background-color: rgba(69,68,60,1);
+    padding-left: 5px;
+    padding-right: 5px;
+    color: white;
+    vertical-align: top;
+    margin-top: 1px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > SPAN.fileline > DIV > DIV.label {
+    float: left;
+    margin-top: 0px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > SPAN.summary > SPAN > SPAN.count {
+    color: #525252;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > SPAN.fileline {
+    color: #525252;
+    word-wrap: break-word;
+    float: right;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expandedSummary=true] > DIV.header > SPAN.summary {
+    display: none;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][keeptitle=true] > DIV.header, DIV.console-message-group > DIV.header {
+    text-align: left !important;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][keeptitle=true] > DIV.header > SPAN.fileline, DIV.console-message-group > DIV.header > SPAN.fileline {
+    display: none !important;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][keeptitle=true] > DIV.header > SPAN.summary, DIV.console-message-group > DIV.header > SPAN.summary {
+    display: inline !important;
+}
+DIV.console-message-group[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > DIV.actions {
+    display: none !important;
+}
+DIV.console-message-group[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > SPAN.summary > SPAN > SPAN.count {
+    color: #ffffff !important;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expanded=false] > DIV.header > SPAN.fileline {
+    display: none;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > DIV.actions {
+    display: inline-block;
+    position: relative;
+    top: 0px;
+    left: 10px;
+    float: right;
+    margin-left: 0px;
+    margin-right: 5px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > DIV.actions DIV.inspect {
+    display: inline-block;
+    background: url(images/node-magnifier.png) no-repeat;
+    width: 16px;
+    height: 16px;
+    margin-right: 4px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > DIV.actions > DIV.file {
+    display: inline-block;
+    background: url(images/document-binary.png) no-repeat;
+    width: 16px;
+    height: 16px;
+    margin-right: 4px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.header > DIV.actions > DIV.inspect:hover, DIV.console-message > DIV.header > DIV.actions > DIV.file:hover {
+    cursor: pointer;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.body {
+    padding: 6px;
+    margin: 3px;
+    margin-top: 0px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"][expanded=false] > DIV.body {
+    display: none;
+}
+DIV.console-message-group[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.body {
+    padding: 0px;
+    margin: 0px;
+    margin-left: 20px;
+    border-top: 1px solid #000000;
+    border-left: 1px solid #000000;
+    margin-bottom: -1px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.body-priority-info {
+    border: 3px solid #c6eeff;
+    margin: 0px;
+    border-top: 0px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.body-priority-warn {
+    border: 3px solid #ffe68d;
+    margin: 0px;
+    border-top: 0px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.body-priority-error {
+    border: 3px solid #ffa7a7;
+    margin: 0px;
+    border-top: 0px;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] > DIV.body > DIV.group-no-messages {
+    background-color: white;
+    padding-left: 4px;
+    padding-right: 4px;
+    padding-top: 3px;
+    padding-bottom: 3px;
+    color: gray;
+}
+DIV.console-message[__dbid="f19222d1afdcb813baf69909d376ee63829cda3c"] .hidden {
+    display: none !important;
+}
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.rep.js
similarity index 89%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.rep.js
index 0442486..ec14413 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/console.rep.js
@@ -136,7 +136,7 @@ function impl(domplate) {
     },
     _getFileActionClass: function _getFileActionClass(message) {
       if (message.meta["file"] && message.meta.console && typeof message.meta.console["enableFileInspect"] !== "undefined") {
-        return message.meta.console["enableInspect"] ? "" : "hidden";
+        return message.meta.console["enableFileInspect"] ? "" : "hidden";
       }
 
       return "hidden";
@@ -362,7 +362,7 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " expandable=\"",__escape__(_isExpandable(node)), "\"", " expanded=\"","false", "\"", " __dbid=\"","3a7bebf32f91801fb7aab559c5946d27a0b717b9", "\"", " __dtid=\"","insight.domplate.reps/wrappers/console", "\"", " class=\"",__escape__(_getMessageClass(node)), " ", "\"",">","<div", " hideOnExpand=\"",__escape__(_getHideShortTagOnExpand(context,node)), "\"", " class=\"",__escape__(_getHeaderClass(node)), " ", "\"",">","<div", " class=\"","expander", " ", "\"",">","</div>","<div", " class=\"","actions", " ", "\"",">","<div", " class=\"","inspect ",__escape__(_getInspectActionClass(context,node)), " ", "\"",">","</div>","<div", " class=\"","file ",__escape__(_getFileActionClass(node)), " ", "\"",">","</div>","</div>","<span", " allowTagExpand=\"","false", "\"", " class=\"","summary", " ", "\"",">","<span", " class=\"","label", " ", "\"",">");__out__.push(onMouseOver,onMouseMove,onMouseOut,onClick,node,context,node,onClick,onClick);__if__.apply(this, [_hasLabel(node), __out__, function(__out__) {    __code__.push("","<span",">",__escape__(_getLabel(node)),"</span>");}]);    __code__.push("","</span>");__link__(_getTag(context,node,CONST_Short), __code__, __out__, {"node":_getValue(context,node),"context":context});    __code__.push("","</span>","<span", " class=\"","fileline", " ", "\"",">","<div",">",__escape__(_getFileLine(node)),"</div>","</span>","</div>","<div", " class=\"",__escape__(_getBodyClass(node)), " ", "\"",">","</div>","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " expandable=\"",__escape__(_isExpandable(node)), "\"", " expanded=\"","false", "\"", " __dbid=\"","f19222d1afdcb813baf69909d376ee63829cda3c", "\"", " __dtid=\"","wrappers/console", "\"", " class=\"",__escape__(_getMessageClass(node)), " ", "\"",">","<div", " hideOnExpand=\"",__escape__(_getHideShortTagOnExpand(context,node)), "\"", " class=\"",__escape__(_getHeaderClass(node)), " ", "\"",">","<div", " class=\"","expander", " ", "\"",">","</div>","<div", " class=\"","actions", " ", "\"",">","<div", " class=\"","inspect ",__escape__(_getInspectActionClass(context,node)), " ", "\"",">","</div>","<div", " class=\"","file ",__escape__(_getFileActionClass(node)), " ", "\"",">","</div>","</div>","<span", " allowTagExpand=\"","false", "\"", " class=\"","summary", " ", "\"",">","<span", " class=\"","label", " ", "\"",">");__out__.push(onMouseOver,onMouseMove,onMouseOut,onClick,node,context,node,onClick,onClick);__if__.apply(this, [_hasLabel(node), __out__, function(__out__) {    __code__.push("","<span",">",__escape__(_getLabel(node)),"</span>");}]);    __code__.push("","</span>");__link__(_getTag(context,node,CONST_Short), __code__, __out__, {"node":_getValue(context,node),"context":context});    __code__.push("","</span>","<span", " class=\"","fileline", " ", "\"",">","<div",">",__escape__(_getFileLine(node)),"</div>","</span>","</div>","<div", " class=\"",__escape__(_getBodyClass(node)), " ", "\"",">","</div>","</div>");  }}})
 }
 ,
 "shortTag":function (context) {
@@ -380,11 +380,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","3a7bebf32f91801fb7aab559c5946d27a0b717b9", "\"", " __dtid=\"","insight.domplate.reps/wrappers/console", "\"", " class=\"","group-no-messages", " ", "\"",">","No Messages","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","f19222d1afdcb813baf69909d376ee63829cda3c", "\"", " __dtid=\"","wrappers/console", "\"", " class=\"","group-no-messages", " ", "\"",">","No Messages","</div>");  }}})
 }
 };
-  rep.__dbid = "3a7bebf32f91801fb7aab559c5946d27a0b717b9";
-  rep.__dtid = "insight.domplate.reps/wrappers/console";
+  rep.__dbid = "f19222d1afdcb813baf69909d376ee63829cda3c";
+  rep.__dtid = "wrappers/console";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/document-binary.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/document-binary.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/document-binary.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/document-binary.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/document_page_next.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/document_page_next.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/document_page_next.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/document_page_next.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/exclamation-diamond.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/exclamation-diamond.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/exclamation-diamond.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/exclamation-diamond.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/exclamation-red.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/exclamation-red.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/exclamation-red.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/exclamation-red.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/information.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/information.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/information.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/information.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/minus-small-white.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/minus-small-white.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/minus-small-white.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/minus-small-white.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/node-magnifier.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/node-magnifier.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/node-magnifier.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/node-magnifier.png
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/plus-small-white.png b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/plus-small-white.png
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/images/plus-small-white.png
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/images/plus-small-white.png
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.preview.htm
new file mode 100644
index 0000000..9005d90
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.preview.htm
@@ -0,0 +1,10 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<div expanded="true" __dbid="42e45d15f38354db25f807078ed4de450a918dea" __dtid="wrappers/request" class="console-request "><div class="header "><div class="expander "></div><div class="labels ">AJAX</div><span class="summary "></span></div><div class="body "></div></div>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.rep.js
similarity index 89%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.rep.js
index a72ad9e..ee37ea8 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/request.rep.js
@@ -131,11 +131,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " expanded=\"","true", "\"", " __dbid=\"","42e45d15f38354db25f807078ed4de450a918dea", "\"", " __dtid=\"","insight.domplate.reps/wrappers/request", "\"", " class=\"",__escape__(_getMessageClass(node)), " ", "\"",">","<div", " class=\"",__escape__(_getHeaderClass(node)), " ", "\"",">","<div", " class=\"","expander", " ", "\"",">","</div>","<div", " class=\"","labels", " ", "\"",">",__escape__(_getTypeLabelValueString(context,node)),"</div>","<span", " class=\"","summary", " ", "\"",">");__out__.push(onClick,node,context,node);__link__(_getTitleTag(context,node,CONST_Short), __code__, __out__, {"node":_getTitleValue(context,node),"context":context});    __code__.push("","</span>","</div>","<div", " class=\"","body", " ", "\"",">","</div>","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " expanded=\"","true", "\"", " __dbid=\"","42e45d15f38354db25f807078ed4de450a918dea", "\"", " __dtid=\"","wrappers/request", "\"", " class=\"",__escape__(_getMessageClass(node)), " ", "\"",">","<div", " class=\"",__escape__(_getHeaderClass(node)), " ", "\"",">","<div", " class=\"","expander", " ", "\"",">","</div>","<div", " class=\"","labels", " ", "\"",">",__escape__(_getTypeLabelValueString(context,node)),"</div>","<span", " class=\"","summary", " ", "\"",">");__out__.push(onClick,node,context,node);__link__(_getTitleTag(context,node,CONST_Short), __code__, __out__, {"node":_getTitleValue(context,node),"context":context});    __code__.push("","</span>","</div>","<div", " class=\"","body", " ", "\"",">","</div>","</div>");  }}})
 }
 };
   rep.__dbid = "42e45d15f38354db25f807078ed4de450a918dea";
-  rep.__dtid = "insight.domplate.reps/wrappers/request";
+  rep.__dtid = "wrappers/request";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.preview.htm b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.preview.htm
new file mode 100644
index 0000000..7842ca4
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.preview.htm
@@ -0,0 +1,10 @@
+<html>
+<body>
+
+<h2>tag</h2>
+
+<div __dbid="81dd6cc349cc607697b22148aa1809050c0fcda4" __dtid="wrappers/viewer" class="viewer-harness "></div>
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.rep.css b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.rep.css
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.rep.css
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.rep.js
similarity index 92%
rename from dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.rep.js
index 49a4b90..cce1b52 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.domplate.reps/dist/reps/wrappers/viewer.rep.js
@@ -53,11 +53,11 @@ var __escape__ = context.__escape__;
 var __if__ = context.__if__;
 var __loop__ = context.__loop__;
 var __link__ = context.__link__;
-return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","81dd6cc349cc607697b22148aa1809050c0fcda4", "\"", " __dtid=\"","insight.domplate.reps/wrappers/viewer", "\"", " class=\"","viewer-harness", " ", "\"",">");__link__(_getTag(context,node), __code__, __out__, {"node":node,"context":context});    __code__.push("","</div>");  }}})
+return (function (__code__, __context__, __in__, __out__) {  with (this) {  with (__in__) {    __code__.push("","<div", " __dbid=\"","81dd6cc349cc607697b22148aa1809050c0fcda4", "\"", " __dtid=\"","wrappers/viewer", "\"", " class=\"","viewer-harness", " ", "\"",">");__link__(_getTag(context,node), __code__, __out__, {"node":node,"context":context});    __code__.push("","</div>");  }}})
 }
 };
   rep.__dbid = "81dd6cc349cc607697b22148aa1809050c0fcda4";
-  rep.__dtid = "insight.domplate.reps/wrappers/viewer";
+  rep.__dtid = "wrappers/viewer";
   var res = domplate.domplate(rep);
   var injectedCss = false;
 
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.rep.js
similarity index 95%
rename from dist/firephp.build/scripts/devtools/dist/insight.rep.js
rename to dist/firephp.build/scripts/devtools/dist/dist/reps/insight.rep.js
index 278226f..49ac8dd 100644
--- a/dist/firephp.build/scripts/devtools/dist/insight.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/insight.rep.js
@@ -1697,7 +1697,7 @@ exports.PINF = (function (global) {
 **/
 
 (function (exports) {
- 
+
 // The global `require` for the 'external' (to the loader) environment.
 var Loader = function (global) {
 
@@ -1744,7 +1744,7 @@ var Loader = function (global) {
 	}
 
 	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
-	function loadInBrowser (uri, loadedCallback) {
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
 		try {
 			// See if we are in a web worker.
 			if (typeof importScripts !== "undefined") {
@@ -1772,7 +1772,9 @@ var Loader = function (global) {
 				if (ev.type === "load" || readyStates[this.readyState]) {
 					this.onload = this.onreadystatechange = this.onerror = null;
 					loadedCallback(null, function () {
-						element.parentNode.removeChild(element);
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
 					});
 				}
 			}
@@ -1885,7 +1887,8 @@ var Loader = function (global) {
 										cleanupCallback();
 									}
 								});
-							}
+							},
+							sandboxOptions
 						);
 					}
 				}
@@ -1937,7 +1940,8 @@ var Loader = function (global) {
 									function () {
 										pending -= 1;
 										finalize();
-									}
+									},
+									sandboxOptions
 								);
 							}
 						}
@@ -2426,7 +2430,7 @@ var Loader = function (global) {
 					loadedCallback(null);
 					return;
 				}
-				return fallbackLoad(uri, loadedCallback);
+				return fallbackLoad(uri, loadedCallback, options);
 			}
 			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
 		}
@@ -4463,7 +4467,7 @@ function Renderer (options) {
                     !rootNode.instances[node.value]
                 ) {
                     console.error("node", node);
-                    throw new Error("Object instance for reference '" + node.value + "' not found 'instances'!");
+                    throw new Error("Object instance for reference '" + node.value + "' not found in 'instances'!");
                 }
                 return rootNode.instances[node.value];
             }
@@ -4474,74 +4478,84 @@ function Renderer (options) {
     var context = new InsightDomplateContext();
 
     function ensureRepsForNodeLoaded (node) {
+        try {
+            // TODO: Optionally pre-fill with already loaded reps.
+            // TODO: Move node traversal into helper module.
+            var loadTypes = {
+    //            "default/unknown": true
+            };
+            function traverse (node) {
 
-        // TODO: Optionally pre-fill with already loaded reps.
-        // TODO: Move node traversal into helper module.
-        var loadTypes = {
-//            "default/unknown": true
-        };
-        function traverse (node) {
-
-            if (node.type) {
-                loadTypes["default/" + node.type] = true;
-            }
+                if (node.type) {
+                    loadTypes["default/" + node.type] = true;
+                }
 
-            if (node.meta) {
-                if (node.meta["encoder.trimmed"]) {
-                    loadTypes["default/trimmed"] = true;
-                } else
-                // DEPRECATED
-                if (node.meta.renderer === "structures/table") {
-                    loadTypes["default/table"] = true;
-                    loadTypes["default/string"] = true;
-                    node.type = "table";
-                } else
-                // DEPRECATED
-                if (node.meta.renderer === "structures/trace") {
-                    loadTypes["default/trace"] = true;
-                    loadTypes["default/string"] = true;
-                    node.type = "trace";
-                } else
-                if (
-                    node.meta["lang"] &&
-                    node.meta["lang.type"]
-                ) {
-                    // TODO: Lookup 'node.meta["lang"]/node.meta["lang.type"]' and see
-                    // what it extends to determine what sub-structures to parse.
-                    if (node.meta["lang"] === "php") {
-                        if (node.meta["lang.type"] === "array") {
-                            if (node.value[0] && Array.isArray(node.value[0])) {
+                if (node.meta) {
+                    if (node.meta["encoder.trimmed"]) {
+                        loadTypes["default/trimmed"] = true;
+                    } else
+                    // DEPRECATED
+                    if (node.meta.renderer === "structures/table") {
+                        loadTypes["default/table"] = true;
+                        loadTypes["default/string"] = true;
+                        node.type = "table";
+                    } else
+                    // DEPRECATED
+                    if (node.meta.renderer === "structures/trace") {
+                        loadTypes["default/trace"] = true;
+                        loadTypes["default/string"] = true;
+                        node.type = "trace";
+                    } else
+                    if (
+                        node.meta["lang"] &&
+                        node.meta["lang.type"]
+                    ) {
+                        // TODO: Lookup 'node.meta["lang"]/node.meta["lang.type"]' and see
+                        // what it extends to determine what sub-structures to parse.
+                        if (node.meta["lang"] === "php") {
+                            if (node.meta["lang.type"] === "array") {
+                                if (node.value[0] && Array.isArray(node.value[0])) {
+                                    loadTypes["php/array-associative"] = true;
+                                    node.value.forEach(function (pair) {
+                                        traverse(pair[0]);
+                                        traverse(pair[1]);
+                                    });
+                                } else {
+                                    loadTypes["php/array-indexed"] = true;
+                                    node.value.forEach(function (node) {
+                                        traverse(node);
+                                    });
+                                }
+                            } else
+                            if (node.meta["lang.type"] === "map") {
                                 loadTypes["php/array-associative"] = true;
                                 node.value.forEach(function (pair) {
                                     traverse(pair[0]);
                                     traverse(pair[1]);
                                 });
-                            } else {
-                                loadTypes["php/array-indexed"] = true;
-                                node.value.forEach(function (node) {
-                                    traverse(node);
-                                });
-                            }
-                        } else
-                        if (node.meta["lang.type"] === "map") {
-                            loadTypes["php/array-associative"] = true;
-                            node.value.forEach(function (pair) {
-                                traverse(pair[0]);
-                                traverse(pair[1]);
-                            });
-                        } else
-                        if (node.meta["lang.type"] === "exception") {
-                            loadTypes["php/exception"] = true;
-                            loadTypes["default/string"] = true;
-                            if (node.value.title) {
-                                traverse(node.value.title);
-                            }
-                            if (node.value.stack) {
-                                node.value.stack.forEach(function (frame) {
-                                    frame.args.forEach(function (arg) {
-                                        traverse(arg);
+                            } else
+                            if (node.meta["lang.type"] === "exception") {
+                                loadTypes["php/exception"] = true;
+                                loadTypes["default/string"] = true;
+                                if (node.value.title) {
+                                    traverse(node.value.title);
+                                }
+                                if (node.value.stack) {
+                                    node.value.stack.forEach(function (frame) {
+                                        frame.args.forEach(function (arg) {
+                                            traverse(arg);
+                                        });
                                     });
-                                });
+                                }
+                            } else {
+                                loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
+
+                                if (node.meta["lang.type"] === "table") {
+                                    loadTypes["default/string"] = true;
+                                } else
+                                if (node.meta["lang.type"] === "trace") {
+                                    loadTypes["default/string"] = true;
+                                }    
                             }
                         } else {
                             loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
@@ -4551,116 +4565,119 @@ function Renderer (options) {
                             } else
                             if (node.meta["lang.type"] === "trace") {
                                 loadTypes["default/string"] = true;
-                            }    
+                            } else
+                            if (node.meta["lang.type"] === "pathtree") {
+                                loadTypes["default/string"] = true;
+                            } else
+                            if (node.meta["lang.type"] === "optiontree") {
+                                loadTypes["default/string"] = true;
+                            }
                         }
-                    } else {
-                        loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
-
-                        if (node.meta["lang.type"] === "table") {
-                            loadTypes["default/string"] = true;
-                        } else
-                        if (node.meta["lang.type"] === "trace") {
+    /*                    
+                    } else
+                    if (node.meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers/packages/php/master") {
+                        if (node.meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/table") {
+                            loadTypes["default/table"] = true;
                             loadTypes["default/string"] = true;
+                            node.type = "table";
                         }
+    */
                     }
-/*                    
-                } else
-                if (node.meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers/packages/php/master") {
-                    if (node.meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/table") {
-                        loadTypes["default/table"] = true;
-                        loadTypes["default/string"] = true;
-                        node.type = "table";
-                    }
-*/
-                }
-                if (node.meta.wrapper) {
-                    loadTypes[node.meta.wrapper] = true;
+                    if (node.meta.wrapper) {
+                        loadTypes[node.meta.wrapper] = true;
 
-                    if (node.meta.wrapper === "wrappers/request") {
-                        if (node.value.title) {
-                            traverse(node.value.title);
+                        if (node.meta.wrapper === "wrappers/request") {
+                            if (node.value.title) {
+                                traverse(node.value.title);
+                            }
                         }
                     }
                 }
-            }
 
-            if (typeof node.value !== 'undefined') {
+                if (
+                    node.value !== null &&
+                    typeof node.value !== 'undefined'
+                ) {
 
-                let type = node.type || node.meta["lang.type"];
+                    let type = node.type || node.meta["lang.type"];
 
-                if (type === "array") {
-                    node.value.forEach(function (node) {
-                        traverse(node);
-                    });
-                } else
-                if (type === "dictionary") {
-                    Object.keys(node.value).forEach(function (key) {
-                        traverse(node.value[key]);
-                    });
-                } else
-                if (type === "map") {
-                    node.value.forEach(function (pair) {
-                        traverse(pair[0]);
-                        traverse(pair[1]);
-                    });
-                } else
-                if (type === "reference") {
-                    if (node.value.instance) {
-                        traverse(node.value.instance);
+                    if (type === "array") {
+                        node.value.forEach(function (node) {
+                            traverse(node);
+                        });
                     } else
-                    if (
-                        node.instances &&
-                        typeof node.value === "number"
-                    ) {
-                        traverse(node.instances[node.value]);
+                    if (type === "dictionary") {
+                        Object.keys(node.value).forEach(function (key) {
+                            traverse(node.value[key]);
+                        });
                     } else
-                    if (typeof node.getInstance === 'function') {
-                        traverse(node.getInstance());
-                    }
-                } else
-                if (type === "table") {
-                    if (node.value.title) {
-                        traverse(node.value.title);
-                    }
-                    if (node.value.header) {
-                        node.value.header.forEach(function (node) {
-                            traverse(node);
+                    if (type === "map") {
+                        node.value.forEach(function (pair) {
+                            traverse(pair[0]);
+                            traverse(pair[1]);
                         });
-                    }
-                    if (node.value.body) {
-                        node.value.body.forEach(function (row) {
-                            row.forEach(function (cell) {
-                                traverse(cell);
+                    } else
+                    if (type === "reference") {
+                        if (node.value.instance) {
+                            traverse(node.value.instance);
+                        } else
+                        if (
+                            node.instances &&
+                            typeof node.value === "number"
+                        ) {
+                            traverse(node.instances[node.value]);
+                        } else
+                        if (typeof node.getInstance === 'function') {
+                            traverse(node.getInstance());
+                        }
+                    } else
+                    if (type === "table") {
+                        if (node.value.title) {
+                            traverse(node.value.title);
+                        }
+                        if (node.value.header) {
+                            node.value.header.forEach(function (node) {
+                                traverse(node);
                             });
-                        });
-                    }
-                } else
-                if (type === "trace") {
-                    if (node.value.title) {
-                        traverse(node.value.title);
-                    }
-                    if (node.value.stack) {
-                        node.value.stack.forEach(function (frame) {
-                            frame.args.forEach(function (arg) {
-                                traverse(arg);
+                        }
+                        if (node.value.body) {
+                            node.value.body.forEach(function (row) {
+                                row.forEach(function (cell) {
+                                    traverse(cell);
+                                });
                             });
-                        });
+                        }
+                    } else
+                    if (type === "trace") {
+                        if (node.value.title) {
+                            traverse(node.value.title);
+                        }
+                        if (node.value.stack) {
+                            node.value.stack.forEach(function (frame) {
+                                frame.args.forEach(function (arg) {
+                                    traverse(arg);
+                                });
+                            });
+                        }
                     }
                 }
             }
-        }
-        traverse(node);
+            traverse(node);
 
-        return Promise.all(Object.keys(loadTypes).map(function (type) {
+            return Promise.all(Object.keys(loadTypes).map(function (type) {
 
-            type = type.split("/");
+                type = type.split("/");
 
-            var repUri = loader.repUriForType(type[0], type[1]);
+                var repUri = loader.repUriForType(type[0], type[1]);
 
-            return loader.ensureRepForUri(repUri).then(function () {
-                return null;
-            });
-        }));
+                return loader.ensureRepForUri(repUri).then(function () {
+                    return null;
+                });
+            }));
+        } catch (err) {
+            console.error('Error checking node:', node);
+            throw err;
+        }
     }
 
     self.renderNodeInto = function (node, selectorOrElement, options) {
@@ -4852,13 +4869,16 @@ var DECODER = require("insight-for-js/lib/decoder/default");
 var repsBaseUrl = "/reps";
 
 if (typeof bundle !== "undefined") {
-  repsBaseUrl = bundle.module.filename.replace(/\/[^\/]+\/[^\/]+$/, '/insight.domplate.reps');
+  repsBaseUrl = bundle.module.filename.replace(/\/[^\/]+\/[^\/]+$/, '/insight.domplate.reps/dist/reps');
 }
 
 var repLoader = new REPS.Loader({
   repsBaseUrl: repsBaseUrl
 });
 var encoder = ENCODER.Encoder();
+encoder.setOption("maxObjectDepth", 1000);
+encoder.setOption("maxArrayDepth", 1000);
+encoder.setOption("maxOverallDepth", 1000);
 
 exports.main = function (JSONREP, node) {
   var og = DECODER.generateFromMessage({
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/reps/io.shields.img.rep.js b/dist/firephp.build/scripts/devtools/dist/dist/reps/io.shields.img.rep.js
new file mode 100644
index 0000000..1bc2774
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/reps/io.shields.img.rep.js
@@ -0,0 +1,10 @@
+
+PINF.bundle("", function(require) {
+    require.memoize("/main.js", function(require, exports, module) {
+
+        exports.main = function (JSONREP, node) {
+
+            return '<img src="https://img.shields.io/badge/' + node.subject + '-' + node.status + '-' + node.color + '.svg">';
+        }
+	});
+});
diff --git a/dist/firephp.build/scripts/devtools/dist/riot.csp.js b/dist/firephp.build/scripts/devtools/dist/dist/riot.csp.js
similarity index 100%
rename from dist/firephp.build/scripts/devtools/dist/riot.csp.js
rename to dist/firephp.build/scripts/devtools/dist/dist/riot.csp.js
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/riot.js b/dist/firephp.build/scripts/devtools/dist/dist/riot.js
new file mode 100644
index 0000000..a8ae3fd
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/riot.js
@@ -0,0 +1,3108 @@
+/* Riot v3.13.2, @license MIT */
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+  typeof define === 'function' && define.amd ? define(['exports'], factory) :
+  (factory((global.riot = {})));
+}(this, (function (exports) { 'use strict';
+
+  /**
+   * Shorter and fast way to select a single node in the DOM
+   * @param   { String } selector - unique dom selector
+   * @param   { Object } ctx - DOM node where the target of our search will is located
+   * @returns { Object } dom node found
+   */
+  function $(selector, ctx) {
+    return (ctx || document).querySelector(selector)
+  }
+
+  var
+    // be aware, internal usage
+    // ATTENTION: prefix the global dynamic variables with `__`
+    // tags instances cache
+    __TAGS_CACHE = [],
+    // tags implementation cache
+    __TAG_IMPL = {},
+    YIELD_TAG = 'yield',
+
+    /**
+     * Const
+     */
+    GLOBAL_MIXIN = '__global_mixin',
+
+    // riot specific prefixes or attributes
+    ATTRS_PREFIX = 'riot-',
+
+    // Riot Directives
+    REF_DIRECTIVES = ['ref', 'data-ref'],
+    IS_DIRECTIVE = 'data-is',
+    CONDITIONAL_DIRECTIVE = 'if',
+    LOOP_DIRECTIVE = 'each',
+    LOOP_NO_REORDER_DIRECTIVE = 'no-reorder',
+    SHOW_DIRECTIVE = 'show',
+    HIDE_DIRECTIVE = 'hide',
+    KEY_DIRECTIVE = 'key',
+    RIOT_EVENTS_KEY = '__riot-events__',
+
+    // for typeof == '' comparisons
+    T_STRING = 'string',
+    T_OBJECT = 'object',
+    T_UNDEF  = 'undefined',
+    T_FUNCTION = 'function',
+
+    XLINK_NS = 'http://www.w3.org/1999/xlink',
+    SVG_NS = 'http://www.w3.org/2000/svg',
+    XLINK_REGEX = /^xlink:(\w+)/,
+
+    WIN = typeof window === T_UNDEF ? /* istanbul ignore next */ undefined : window,
+
+    // special native tags that cannot be treated like the others
+    RE_SPECIAL_TAGS = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?|opt(?:ion|group))$/,
+    RE_SPECIAL_TAGS_NO_OPTION = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/,
+    RE_EVENTS_PREFIX = /^on/,
+    RE_HTML_ATTRS = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g,
+    // some DOM attributes must be normalized
+    CASE_SENSITIVE_ATTRIBUTES = {
+      'viewbox': 'viewBox',
+      'preserveaspectratio': 'preserveAspectRatio'
+    },
+    /**
+     * Matches boolean HTML attributes in the riot tag definition.
+     * With a long list like this, a regex is faster than `[].indexOf` in most browsers.
+     * @const {RegExp}
+     * @see [attributes.md](https://github.com/riot/compiler/blob/dev/doc/attributes.md)
+     */
+    RE_BOOL_ATTRS = /^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|selected|sortable|truespeed|typemustmatch)$/,
+    // version# for IE 8-11, 0 for others
+    IE_VERSION = (WIN && WIN.document || /* istanbul ignore next */ {}).documentMode | 0;
+
+  /**
+   * Create a generic DOM node
+   * @param   { String } name - name of the DOM node we want to create
+   * @returns { Object } DOM node just created
+   */
+  function makeElement(name) {
+    return name === 'svg' ? document.createElementNS(SVG_NS, name) : document.createElement(name)
+  }
+
+  /**
+   * Set any DOM attribute
+   * @param { Object } dom - DOM node we want to update
+   * @param { String } name - name of the property we want to set
+   * @param { String } val - value of the property we want to set
+   */
+  function setAttribute(dom, name, val) {
+    var xlink = XLINK_REGEX.exec(name);
+    if (xlink && xlink[1])
+      { dom.setAttributeNS(XLINK_NS, xlink[1], val); }
+    else
+      { dom.setAttribute(name, val); }
+  }
+
+  var styleNode;
+  // Create cache and shortcut to the correct property
+  var cssTextProp;
+  var byName = {};
+  var needsInject = false;
+
+  // skip the following code on the server
+  if (WIN) {
+    styleNode = ((function () {
+      // create a new style element with the correct type
+      var newNode = makeElement('style');
+      // replace any user node or insert the new one into the head
+      var userNode = $('style[type=riot]');
+
+      setAttribute(newNode, 'type', 'text/css');
+      /* istanbul ignore next */
+      if (userNode) {
+        if (userNode.id) { newNode.id = userNode.id; }
+        userNode.parentNode.replaceChild(newNode, userNode);
+      } else { document.head.appendChild(newNode); }
+
+      return newNode
+    }))();
+    cssTextProp = styleNode.styleSheet;
+  }
+
+  /**
+   * Object that will be used to inject and manage the css of every tag instance
+   */
+  var styleManager = {
+    styleNode: styleNode,
+    /**
+     * Save a tag style to be later injected into DOM
+     * @param { String } css - css string
+     * @param { String } name - if it's passed we will map the css to a tagname
+     */
+    add: function add(css, name) {
+      byName[name] = css;
+      needsInject = true;
+    },
+    /**
+     * Inject all previously saved tag styles into DOM
+     * innerHTML seems slow: http://jsperf.com/riot-insert-style
+     */
+    inject: function inject() {
+      if (!WIN || !needsInject) { return }
+      needsInject = false;
+      var style = Object.keys(byName)
+        .map(function (k) { return byName[k]; })
+        .join('\n');
+      /* istanbul ignore next */
+      if (cssTextProp) { cssTextProp.cssText = style; }
+      else { styleNode.innerHTML = style; }
+    },
+
+    /**
+     * Remove a tag style of injected DOM later.
+     * @param {String} name a registered tagname
+     */
+    remove: function remove(name) {
+      delete byName[name];
+      needsInject = true;
+    }
+  };
+
+  /**
+   * The riot template engine
+   * @version v3.0.8
+   */
+
+  /* istanbul ignore next */
+  var skipRegex = (function () { //eslint-disable-line no-unused-vars
+
+    var beforeReChars = '[{(,;:?=|&!^~>%*/';
+
+    var beforeReWords = [
+      'case',
+      'default',
+      'do',
+      'else',
+      'in',
+      'instanceof',
+      'prefix',
+      'return',
+      'typeof',
+      'void',
+      'yield'
+    ];
+
+    var wordsLastChar = beforeReWords.reduce(function (s, w) {
+      return s + w.slice(-1)
+    }, '');
+
+    var RE_REGEX = /^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/;
+    var RE_VN_CHAR = /[$\w]/;
+
+    function prev (code, pos) {
+      while (--pos >= 0 && /\s/.test(code[pos])){ }
+      return pos
+    }
+
+    function _skipRegex (code, start) {
+
+      var re = /.*/g;
+      var pos = re.lastIndex = start++;
+      var match = re.exec(code)[0].match(RE_REGEX);
+
+      if (match) {
+        var next = pos + match[0].length;
+
+        pos = prev(code, pos);
+        var c = code[pos];
+
+        if (pos < 0 || ~beforeReChars.indexOf(c)) {
+          return next
+        }
+
+        if (c === '.') {
+
+          if (code[pos - 1] === '.') {
+            start = next;
+          }
+
+        } else if (c === '+' || c === '-') {
+
+          if (code[--pos] !== c ||
+              (pos = prev(code, pos)) < 0 ||
+              !RE_VN_CHAR.test(code[pos])) {
+            start = next;
+          }
+
+        } else if (~wordsLastChar.indexOf(c)) {
+
+          var end = pos + 1;
+
+          while (--pos >= 0 && RE_VN_CHAR.test(code[pos])){ }
+          if (~beforeReWords.indexOf(code.slice(pos + 1, end))) {
+            start = next;
+          }
+        }
+      }
+
+      return start
+    }
+
+    return _skipRegex
+
+  })();
+
+  /**
+   * riot.util.brackets
+   *
+   * - `brackets    ` - Returns a string or regex based on its parameter
+   * - `brackets.set` - Change the current riot brackets
+   *
+   * @module
+   */
+
+  /* global riot */
+
+  /* istanbul ignore next */
+  var brackets = (function (UNDEF) {
+
+    var
+      REGLOB = 'g',
+
+      R_MLCOMMS = /\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g,
+
+      R_STRINGS = /"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|`[^`\\]*(?:\\[\S\s][^`\\]*)*`/g,
+
+      S_QBLOCKS = R_STRINGS.source + '|' +
+        /(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source + '|' +
+        /\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?([^<]\/)[gim]*/.source,
+
+      UNSUPPORTED = RegExp('[\\' + 'x00-\\x1F<>a-zA-Z0-9\'",;\\\\]'),
+
+      NEED_ESCAPE = /(?=[[\]()*+?.^$|])/g,
+
+      S_QBLOCK2 = R_STRINGS.source + '|' + /(\/)(?![*\/])/.source,
+
+      FINDBRACES = {
+        '(': RegExp('([()])|'   + S_QBLOCK2, REGLOB),
+        '[': RegExp('([[\\]])|' + S_QBLOCK2, REGLOB),
+        '{': RegExp('([{}])|'   + S_QBLOCK2, REGLOB)
+      },
+
+      DEFAULT = '{ }';
+
+    var _pairs = [
+      '{', '}',
+      '{', '}',
+      /{[^}]*}/,
+      /\\([{}])/g,
+      /\\({)|{/g,
+      RegExp('\\\\(})|([[({])|(})|' + S_QBLOCK2, REGLOB),
+      DEFAULT,
+      /^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/,
+      /(^|[^\\]){=[\S\s]*?}/
+    ];
+
+    var
+      cachedBrackets = UNDEF,
+      _regex,
+      _cache = [],
+      _settings;
+
+    function _loopback (re) { return re }
+
+    function _rewrite (re, bp) {
+      if (!bp) { bp = _cache; }
+      return new RegExp(
+        re.source.replace(/{/g, bp[2]).replace(/}/g, bp[3]), re.global ? REGLOB : ''
+      )
+    }
+
+    function _create (pair) {
+      if (pair === DEFAULT) { return _pairs }
+
+      var arr = pair.split(' ');
+
+      if (arr.length !== 2 || UNSUPPORTED.test(pair)) {
+        throw new Error('Unsupported brackets "' + pair + '"')
+      }
+      arr = arr.concat(pair.replace(NEED_ESCAPE, '\\').split(' '));
+
+      arr[4] = _rewrite(arr[1].length > 1 ? /{[\S\s]*?}/ : _pairs[4], arr);
+      arr[5] = _rewrite(pair.length > 3 ? /\\({|})/g : _pairs[5], arr);
+      arr[6] = _rewrite(_pairs[6], arr);
+      arr[7] = RegExp('\\\\(' + arr[3] + ')|([[({])|(' + arr[3] + ')|' + S_QBLOCK2, REGLOB);
+      arr[8] = pair;
+      return arr
+    }
+
+    function _brackets (reOrIdx) {
+      return reOrIdx instanceof RegExp ? _regex(reOrIdx) : _cache[reOrIdx]
+    }
+
+    _brackets.split = function split (str, tmpl, _bp) {
+      // istanbul ignore next: _bp is for the compiler
+      if (!_bp) { _bp = _cache; }
+
+      var
+        parts = [],
+        match,
+        isexpr,
+        start,
+        pos,
+        re = _bp[6];
+
+      var qblocks = [];
+      var prevStr = '';
+      var mark, lastIndex;
+
+      isexpr = start = re.lastIndex = 0;
+
+      while ((match = re.exec(str))) {
+
+        lastIndex = re.lastIndex;
+        pos = match.index;
+
+        if (isexpr) {
+
+          if (match[2]) {
+
+            var ch = match[2];
+            var rech = FINDBRACES[ch];
+            var ix = 1;
+
+            rech.lastIndex = lastIndex;
+            while ((match = rech.exec(str))) {
+              if (match[1]) {
+                if (match[1] === ch) { ++ix; }
+                else if (!--ix) { break }
+              } else {
+                rech.lastIndex = pushQBlock(match.index, rech.lastIndex, match[2]);
+              }
+            }
+            re.lastIndex = ix ? str.length : rech.lastIndex;
+            continue
+          }
+
+          if (!match[3]) {
+            re.lastIndex = pushQBlock(pos, lastIndex, match[4]);
+            continue
+          }
+        }
+
+        if (!match[1]) {
+          unescapeStr(str.slice(start, pos));
+          start = re.lastIndex;
+          re = _bp[6 + (isexpr ^= 1)];
+          re.lastIndex = start;
+        }
+      }
+
+      if (str && start < str.length) {
+        unescapeStr(str.slice(start));
+      }
+
+      parts.qblocks = qblocks;
+
+      return parts
+
+      function unescapeStr (s) {
+        if (prevStr) {
+          s = prevStr + s;
+          prevStr = '';
+        }
+        if (tmpl || isexpr) {
+          parts.push(s && s.replace(_bp[5], '$1'));
+        } else {
+          parts.push(s);
+        }
+      }
+
+      function pushQBlock(_pos, _lastIndex, slash) { //eslint-disable-line
+        if (slash) {
+          _lastIndex = skipRegex(str, _pos);
+        }
+
+        if (tmpl && _lastIndex > _pos + 2) {
+          mark = '\u2057' + qblocks.length + '~';
+          qblocks.push(str.slice(_pos, _lastIndex));
+          prevStr += str.slice(start, _pos) + mark;
+          start = _lastIndex;
+        }
+        return _lastIndex
+      }
+    };
+
+    _brackets.hasExpr = function hasExpr (str) {
+      return _cache[4].test(str)
+    };
+
+    _brackets.loopKeys = function loopKeys (expr) {
+      var m = expr.match(_cache[9]);
+
+      return m
+        ? { key: m[1], pos: m[2], val: _cache[0] + m[3].trim() + _cache[1] }
+        : { val: expr.trim() }
+    };
+
+    _brackets.array = function array (pair) {
+      return pair ? _create(pair) : _cache
+    };
+
+    function _reset (pair) {
+      if ((pair || (pair = DEFAULT)) !== _cache[8]) {
+        _cache = _create(pair);
+        _regex = pair === DEFAULT ? _loopback : _rewrite;
+        _cache[9] = _regex(_pairs[9]);
+      }
+      cachedBrackets = pair;
+    }
+
+    function _setSettings (o) {
+      var b;
+
+      o = o || {};
+      b = o.brackets;
+      Object.defineProperty(o, 'brackets', {
+        set: _reset,
+        get: function () { return cachedBrackets },
+        enumerable: true
+      });
+      _settings = o;
+      _reset(b);
+    }
+
+    Object.defineProperty(_brackets, 'settings', {
+      set: _setSettings,
+      get: function () { return _settings }
+    });
+
+    /* istanbul ignore next: in the browser riot is always in the scope */
+    _brackets.settings = typeof riot !== 'undefined' && riot.settings || {};
+    _brackets.set = _reset;
+    _brackets.skipRegex = skipRegex;
+
+    _brackets.R_STRINGS = R_STRINGS;
+    _brackets.R_MLCOMMS = R_MLCOMMS;
+    _brackets.S_QBLOCKS = S_QBLOCKS;
+    _brackets.S_QBLOCK2 = S_QBLOCK2;
+
+    return _brackets
+
+  })();
+
+  /**
+   * @module tmpl
+   *
+   * tmpl          - Root function, returns the template value, render with data
+   * tmpl.hasExpr  - Test the existence of a expression inside a string
+   * tmpl.loopKeys - Get the keys for an 'each' loop (used by `_each`)
+   */
+
+  /* istanbul ignore next */
+  var tmpl = (function () {
+
+    var _cache = {};
+
+    function _tmpl (str, data) {
+      if (!str) { return str }
+
+      return (_cache[str] || (_cache[str] = _create(str))).call(
+        data, _logErr.bind({
+          data: data,
+          tmpl: str
+        })
+      )
+    }
+
+    _tmpl.hasExpr = brackets.hasExpr;
+
+    _tmpl.loopKeys = brackets.loopKeys;
+
+    // istanbul ignore next
+    _tmpl.clearCache = function () { _cache = {}; };
+
+    _tmpl.errorHandler = null;
+
+    function _logErr (err, ctx) {
+
+      err.riotData = {
+        tagName: ctx && ctx.__ && ctx.__.tagName,
+        _riot_id: ctx && ctx._riot_id  //eslint-disable-line camelcase
+      };
+
+      if (_tmpl.errorHandler) { _tmpl.errorHandler(err); }
+      else if (
+        typeof console !== 'undefined' &&
+        typeof console.error === 'function'
+      ) {
+        console.error(err.message);
+        console.log('<%s> %s', err.riotData.tagName || 'Unknown tag', this.tmpl); // eslint-disable-line
+        console.log(this.data); // eslint-disable-line
+      }
+    }
+
+    function _create (str) {
+      var expr = _getTmpl(str);
+
+      if (expr.slice(0, 11) !== 'try{return ') { expr = 'return ' + expr; }
+
+      return new Function('E', expr + ';')    // eslint-disable-line no-new-func
+    }
+
+    var RE_DQUOTE = /\u2057/g;
+    var RE_QBMARK = /\u2057(\d+)~/g;
+
+    function _getTmpl (str) {
+      var parts = brackets.split(str.replace(RE_DQUOTE, '"'), 1);
+      var qstr = parts.qblocks;
+      var expr;
+
+      if (parts.length > 2 || parts[0]) {
+        var i, j, list = [];
+
+        for (i = j = 0; i < parts.length; ++i) {
+
+          expr = parts[i];
+
+          if (expr && (expr = i & 1
+
+              ? _parseExpr(expr, 1, qstr)
+
+              : '"' + expr
+                  .replace(/\\/g, '\\\\')
+                  .replace(/\r\n?|\n/g, '\\n')
+                  .replace(/"/g, '\\"') +
+                '"'
+
+            )) { list[j++] = expr; }
+
+        }
+
+        expr = j < 2 ? list[0]
+             : '[' + list.join(',') + '].join("")';
+
+      } else {
+
+        expr = _parseExpr(parts[1], 0, qstr);
+      }
+
+      if (qstr.length) {
+        expr = expr.replace(RE_QBMARK, function (_, pos) {
+          return qstr[pos]
+            .replace(/\r/g, '\\r')
+            .replace(/\n/g, '\\n')
+        });
+      }
+      return expr
+    }
+
+    var RE_CSNAME = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/;
+    var
+      RE_BREND = {
+        '(': /[()]/g,
+        '[': /[[\]]/g,
+        '{': /[{}]/g
+      };
+
+    function _parseExpr (expr, asText, qstr) {
+
+      expr = expr
+        .replace(/\s+/g, ' ').trim()
+        .replace(/\ ?([[\({},?\.:])\ ?/g, '$1');
+
+      if (expr) {
+        var
+          list = [],
+          cnt = 0,
+          match;
+
+        while (expr &&
+              (match = expr.match(RE_CSNAME)) &&
+              !match.index
+          ) {
+          var
+            key,
+            jsb,
+            re = /,|([[{(])|$/g;
+
+          expr = RegExp.rightContext;
+          key  = match[2] ? qstr[match[2]].slice(1, -1).trim().replace(/\s+/g, ' ') : match[1];
+
+          while (jsb = (match = re.exec(expr))[1]) { skipBraces(jsb, re); }
+
+          jsb  = expr.slice(0, match.index);
+          expr = RegExp.rightContext;
+
+          list[cnt++] = _wrapExpr(jsb, 1, key);
+        }
+
+        expr = !cnt ? _wrapExpr(expr, asText)
+             : cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0];
+      }
+      return expr
+
+      function skipBraces (ch, re) {
+        var
+          mm,
+          lv = 1,
+          ir = RE_BREND[ch];
+
+        ir.lastIndex = re.lastIndex;
+        while (mm = ir.exec(expr)) {
+          if (mm[0] === ch) { ++lv; }
+          else if (!--lv) { break }
+        }
+        re.lastIndex = lv ? expr.length : ir.lastIndex;
+      }
+    }
+
+    // istanbul ignore next: not both
+    var // eslint-disable-next-line max-len
+      JS_CONTEXT = '"in this?this:' + (typeof window !== 'object' ? 'global' : 'window') + ').',
+      JS_VARNAME = /[,{][\$\w]+(?=:)|(^ *|[^$\w\.{])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,
+      JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/;
+
+    function _wrapExpr (expr, asText, key) {
+      var tb;
+
+      expr = expr.replace(JS_VARNAME, function (match, p, mvar, pos, s) {
+        if (mvar) {
+          pos = tb ? 0 : pos + match.length;
+
+          if (mvar !== 'this' && mvar !== 'global' && mvar !== 'window') {
+            match = p + '("' + mvar + JS_CONTEXT + mvar;
+            if (pos) { tb = (s = s[pos]) === '.' || s === '(' || s === '['; }
+          } else if (pos) {
+            tb = !JS_NOPROPS.test(s.slice(pos));
+          }
+        }
+        return match
+      });
+
+      if (tb) {
+        expr = 'try{return ' + expr + '}catch(e){E(e,this)}';
+      }
+
+      if (key) {
+
+        expr = (tb
+            ? 'function(){' + expr + '}.call(this)' : '(' + expr + ')'
+          ) + '?"' + key + '":""';
+
+      } else if (asText) {
+
+        expr = 'function(v){' + (tb
+            ? expr.replace('return ', 'v=') : 'v=(' + expr + ')'
+          ) + ';return v||v===0?v:""}.call(this)';
+      }
+
+      return expr
+    }
+
+    _tmpl.version = brackets.version = 'v3.0.8';
+
+    return _tmpl
+
+  })();
+
+  /* istanbul ignore next */
+  var observable = function(el) {
+
+    /**
+     * Extend the original object or create a new empty one
+     * @type { Object }
+     */
+
+    el = el || {};
+
+    /**
+     * Private variables
+     */
+    var callbacks = {},
+      slice = Array.prototype.slice;
+
+    /**
+     * Public Api
+     */
+
+    // extend the el object adding the observable methods
+    Object.defineProperties(el, {
+      /**
+       * Listen to the given `event` ands
+       * execute the `callback` each time an event is triggered.
+       * @param  { String } event - event id
+       * @param  { Function } fn - callback function
+       * @returns { Object } el
+       */
+      on: {
+        value: function(event, fn) {
+          if (typeof fn == 'function')
+            { (callbacks[event] = callbacks[event] || []).push(fn); }
+          return el
+        },
+        enumerable: false,
+        writable: false,
+        configurable: false
+      },
+
+      /**
+       * Removes the given `event` listeners
+       * @param   { String } event - event id
+       * @param   { Function } fn - callback function
+       * @returns { Object } el
+       */
+      off: {
+        value: function(event, fn) {
+          if (event == '*' && !fn) { callbacks = {}; }
+          else {
+            if (fn) {
+              var arr = callbacks[event];
+              for (var i = 0, cb; cb = arr && arr[i]; ++i) {
+                if (cb == fn) { arr.splice(i--, 1); }
+              }
+            } else { delete callbacks[event]; }
+          }
+          return el
+        },
+        enumerable: false,
+        writable: false,
+        configurable: false
+      },
+
+      /**
+       * Listen to the given `event` and
+       * execute the `callback` at most once
+       * @param   { String } event - event id
+       * @param   { Function } fn - callback function
+       * @returns { Object } el
+       */
+      one: {
+        value: function(event, fn) {
+          function on() {
+            el.off(event, on);
+            fn.apply(el, arguments);
+          }
+          return el.on(event, on)
+        },
+        enumerable: false,
+        writable: false,
+        configurable: false
+      },
+
+      /**
+       * Execute all callback functions that listen to
+       * the given `event`
+       * @param   { String } event - event id
+       * @returns { Object } el
+       */
+      trigger: {
+        value: function(event) {
+          var arguments$1 = arguments;
+
+
+          // getting the arguments
+          var arglen = arguments.length - 1,
+            args = new Array(arglen),
+            fns,
+            fn,
+            i;
+
+          for (i = 0; i < arglen; i++) {
+            args[i] = arguments$1[i + 1]; // skip first argument
+          }
+
+          fns = slice.call(callbacks[event] || [], 0);
+
+          for (i = 0; fn = fns[i]; ++i) {
+            fn.apply(el, args);
+          }
+
+          if (callbacks['*'] && event != '*')
+            { el.trigger.apply(el, ['*', event].concat(args)); }
+
+          return el
+        },
+        enumerable: false,
+        writable: false,
+        configurable: false
+      }
+    });
+
+    return el
+
+  };
+
+  /**
+   * Short alias for Object.getOwnPropertyDescriptor
+   */
+  function getPropDescriptor (o, k) {
+    return Object.getOwnPropertyDescriptor(o, k)
+  }
+
+  /**
+   * Check if passed argument is undefined
+   * @param   { * } value -
+   * @returns { Boolean } -
+   */
+  function isUndefined(value) {
+    return typeof value === T_UNDEF
+  }
+
+  /**
+   * Check whether object's property could be overridden
+   * @param   { Object }  obj - source object
+   * @param   { String }  key - object property
+   * @returns { Boolean } true if writable
+   */
+  function isWritable(obj, key) {
+    var descriptor = getPropDescriptor(obj, key);
+    return isUndefined(obj[key]) || descriptor && descriptor.writable
+  }
+
+  /**
+   * Extend any object with other properties
+   * @param   { Object } src - source object
+   * @returns { Object } the resulting extended object
+   *
+   * var obj = { foo: 'baz' }
+   * extend(obj, {bar: 'bar', foo: 'bar'})
+   * console.log(obj) => {bar: 'bar', foo: 'bar'}
+   *
+   */
+  function extend(src) {
+    var obj;
+    var i = 1;
+    var args = arguments;
+    var l = args.length;
+
+    for (; i < l; i++) {
+      if (obj = args[i]) {
+        for (var key in obj) {
+          // check if this property of the source object could be overridden
+          if (isWritable(src, key))
+            { src[key] = obj[key]; }
+        }
+      }
+    }
+    return src
+  }
+
+  /**
+   * Alias for Object.create
+   */
+  function create(src) {
+    return Object.create(src)
+  }
+
+  var settings = extend(create(brackets.settings), {
+    skipAnonymousTags: true,
+    // the "value" attributes will be preserved
+    keepValueAttributes: false,
+    // handle the auto updates on any DOM event
+    autoUpdate: true
+  });
+
+  /**
+   * Shorter and fast way to select multiple nodes in the DOM
+   * @param   { String } selector - DOM selector
+   * @param   { Object } ctx - DOM node where the targets of our search will is located
+   * @returns { Object } dom nodes found
+   */
+  function $$(selector, ctx) {
+    return [].slice.call((ctx || document).querySelectorAll(selector))
+  }
+
+  /**
+   * Create a document text node
+   * @returns { Object } create a text node to use as placeholder
+   */
+  function createDOMPlaceholder() {
+    return document.createTextNode('')
+  }
+
+  /**
+   * Toggle the visibility of any DOM node
+   * @param   { Object }  dom - DOM node we want to hide
+   * @param   { Boolean } show - do we want to show it?
+   */
+
+  function toggleVisibility(dom, show) {
+    dom.style.display = show ? '' : 'none';
+    dom.hidden = show ? false : true;
+  }
+
+  /**
+   * Get the value of any DOM attribute on a node
+   * @param   { Object } dom - DOM node we want to parse
+   * @param   { String } name - name of the attribute we want to get
+   * @returns { String | undefined } name of the node attribute whether it exists
+   */
+  function getAttribute(dom, name) {
+    return dom.getAttribute(name)
+  }
+
+  /**
+   * Remove any DOM attribute from a node
+   * @param   { Object } dom - DOM node we want to update
+   * @param   { String } name - name of the property we want to remove
+   */
+  function removeAttribute(dom, name) {
+    dom.removeAttribute(name);
+  }
+
+  /**
+   * Set the inner html of any DOM node SVGs included
+   * @param { Object } container - DOM node where we'll inject new html
+   * @param { String } html - html to inject
+   * @param { Boolean } isSvg - svg tags should be treated a bit differently
+   */
+  /* istanbul ignore next */
+  function setInnerHTML(container, html, isSvg) {
+    // innerHTML is not supported on svg tags so we neet to treat them differently
+    if (isSvg) {
+      var node = container.ownerDocument.importNode(
+        new DOMParser()
+          .parseFromString(("<svg xmlns=\"" + SVG_NS + "\">" + html + "</svg>"), 'application/xml')
+          .documentElement,
+        true
+      );
+
+      container.appendChild(node);
+    } else {
+      container.innerHTML = html;
+    }
+  }
+
+  /**
+   * Minimize risk: only zero or one _space_ between attr & value
+   * @param   { String }   html - html string we want to parse
+   * @param   { Function } fn - callback function to apply on any attribute found
+   */
+  function walkAttributes(html, fn) {
+    if (!html) { return }
+    var m;
+    while (m = RE_HTML_ATTRS.exec(html))
+      { fn(m[1].toLowerCase(), m[2] || m[3] || m[4]); }
+  }
+
+  /**
+   * Create a document fragment
+   * @returns { Object } document fragment
+   */
+  function createFragment() {
+    return document.createDocumentFragment()
+  }
+
+  /**
+   * Insert safely a tag to fix #1962 #1649
+   * @param   { HTMLElement } root - children container
+   * @param   { HTMLElement } curr - node to insert
+   * @param   { HTMLElement } next - node that should preceed the current node inserted
+   */
+  function safeInsert(root, curr, next) {
+    root.insertBefore(curr, next.parentNode && next);
+  }
+
+  /**
+   * Convert a style object to a string
+   * @param   { Object } style - style object we need to parse
+   * @returns { String } resulting css string
+   * @example
+   * styleObjectToString({ color: 'red', height: '10px'}) // => 'color: red; height: 10px'
+   */
+  function styleObjectToString(style) {
+    return Object.keys(style).reduce(function (acc, prop) {
+      return (acc + " " + prop + ": " + (style[prop]) + ";")
+    }, '')
+  }
+
+  /**
+   * Walk down recursively all the children tags starting dom node
+   * @param   { Object }   dom - starting node where we will start the recursion
+   * @param   { Function } fn - callback to transform the child node just found
+   * @param   { Object }   context - fn can optionally return an object, which is passed to children
+   */
+  function walkNodes(dom, fn, context) {
+    if (dom) {
+      var res = fn(dom, context);
+      var next;
+      // stop the recursion
+      if (res === false) { return }
+
+      dom = dom.firstChild;
+
+      while (dom) {
+        next = dom.nextSibling;
+        walkNodes(dom, fn, res);
+        dom = next;
+      }
+    }
+  }
+
+
+
+  var dom = /*#__PURE__*/Object.freeze({
+    $$: $$,
+    $: $,
+    createDOMPlaceholder: createDOMPlaceholder,
+    mkEl: makeElement,
+    setAttr: setAttribute,
+    toggleVisibility: toggleVisibility,
+    getAttr: getAttribute,
+    remAttr: removeAttribute,
+    setInnerHTML: setInnerHTML,
+    walkAttrs: walkAttributes,
+    createFrag: createFragment,
+    safeInsert: safeInsert,
+    styleObjectToString: styleObjectToString,
+    walkNodes: walkNodes
+  });
+
+  /**
+   * Check against the null and undefined values
+   * @param   { * }  value -
+   * @returns {Boolean} -
+   */
+  function isNil(value) {
+    return isUndefined(value) || value === null
+  }
+
+  /**
+   * Check if passed argument is empty. Different from falsy, because we dont consider 0 or false to be blank
+   * @param { * } value -
+   * @returns { Boolean } -
+   */
+  function isBlank(value) {
+    return isNil(value) || value === ''
+  }
+
+  /**
+   * Check if passed argument is a function
+   * @param   { * } value -
+   * @returns { Boolean } -
+   */
+  function isFunction(value) {
+    return typeof value === T_FUNCTION
+  }
+
+  /**
+   * Check if passed argument is an object, exclude null
+   * NOTE: use isObject(x) && !isArray(x) to excludes arrays.
+   * @param   { * } value -
+   * @returns { Boolean } -
+   */
+  function isObject(value) {
+    return value && typeof value === T_OBJECT // typeof null is 'object'
+  }
+
+  /**
+   * Check if a DOM node is an svg tag or part of an svg
+   * @param   { HTMLElement }  el - node we want to test
+   * @returns {Boolean} true if it's an svg node
+   */
+  function isSvg(el) {
+    var owner = el.ownerSVGElement;
+    return !!owner || owner === null
+  }
+
+  /**
+   * Check if passed argument is a kind of array
+   * @param   { * } value -
+   * @returns { Boolean } -
+   */
+  function isArray(value) {
+    return Array.isArray(value) || value instanceof Array
+  }
+
+  /**
+   * Check if the passed argument is a boolean attribute
+   * @param   { String } value -
+   * @returns { Boolean } -
+   */
+  function isBoolAttr(value) {
+    return RE_BOOL_ATTRS.test(value)
+  }
+
+  /**
+   * Check if passed argument is a string
+   * @param   { * } value -
+   * @returns { Boolean } -
+   */
+  function isString(value) {
+    return typeof value === T_STRING
+  }
+
+
+
+  var check = /*#__PURE__*/Object.freeze({
+    isBlank: isBlank,
+    isFunction: isFunction,
+    isObject: isObject,
+    isSvg: isSvg,
+    isWritable: isWritable,
+    isArray: isArray,
+    isBoolAttr: isBoolAttr,
+    isNil: isNil,
+    isString: isString,
+    isUndefined: isUndefined
+  });
+
+  /**
+   * Check whether an array contains an item
+   * @param   { Array } array - target array
+   * @param   { * } item - item to test
+   * @returns { Boolean } -
+   */
+  function contains(array, item) {
+    return array.indexOf(item) !== -1
+  }
+
+  /**
+   * Specialized function for looping an array-like collection with `each={}`
+   * @param   { Array } list - collection of items
+   * @param   {Function} fn - callback function
+   * @returns { Array } the array looped
+   */
+  function each(list, fn) {
+    var len = list ? list.length : 0;
+    var i = 0;
+    for (; i < len; i++) { fn(list[i], i); }
+    return list
+  }
+
+  /**
+   * Faster String startsWith alternative
+   * @param   { String } str - source string
+   * @param   { String } value - test string
+   * @returns { Boolean } -
+   */
+  function startsWith(str, value) {
+    return str.slice(0, value.length) === value
+  }
+
+  /**
+   * Function returning always a unique identifier
+   * @returns { Number } - number from 0...n
+   */
+  var uid = (function uid() {
+    var i = -1;
+    return function () { return ++i; }
+  })();
+
+  /**
+   * Helper function to set an immutable property
+   * @param   { Object } el - object where the new property will be set
+   * @param   { String } key - object key where the new property will be stored
+   * @param   { * } value - value of the new property
+   * @param   { Object } options - set the propery overriding the default options
+   * @returns { Object } - the initial object
+   */
+  function define(el, key, value, options) {
+    Object.defineProperty(el, key, extend({
+      value: value,
+      enumerable: false,
+      writable: false,
+      configurable: true
+    }, options));
+    return el
+  }
+
+  /**
+   * Convert a string containing dashes to camel case
+   * @param   { String } str - input string
+   * @returns { String } my-string -> myString
+   */
+  function toCamel(str) {
+    return str.replace(/-(\w)/g, function (_, c) { return c.toUpperCase(); })
+  }
+
+  /**
+   * Warn a message via console
+   * @param   {String} message - warning message
+   */
+  function warn(message) {
+    if (console && console.warn) { console.warn(message); }
+  }
+
+
+
+  var misc = /*#__PURE__*/Object.freeze({
+    contains: contains,
+    each: each,
+    getPropDescriptor: getPropDescriptor,
+    startsWith: startsWith,
+    uid: uid,
+    defineProperty: define,
+    objectCreate: create,
+    extend: extend,
+    toCamel: toCamel,
+    warn: warn
+  });
+
+  /**
+   * Set the property of an object for a given key. If something already
+   * exists there, then it becomes an array containing both the old and new value.
+   * @param { Object } obj - object on which to set the property
+   * @param { String } key - property name
+   * @param { Object } value - the value of the property to be set
+   * @param { Boolean } ensureArray - ensure that the property remains an array
+   * @param { Number } index - add the new item in a certain array position
+   */
+  function arrayishAdd(obj, key, value, ensureArray, index) {
+    var dest = obj[key];
+    var isArr = isArray(dest);
+    var hasIndex = !isUndefined(index);
+
+    if (dest && dest === value) { return }
+
+    // if the key was never set, set it once
+    if (!dest && ensureArray) { obj[key] = [value]; }
+    else if (!dest) { obj[key] = value; }
+    // if it was an array and not yet set
+    else {
+      if (isArr) {
+        var oldIndex = dest.indexOf(value);
+        // this item never changed its position
+        if (oldIndex === index) { return }
+        // remove the item from its old position
+        if (oldIndex !== -1) { dest.splice(oldIndex, 1); }
+        // move or add the item
+        if (hasIndex) {
+          dest.splice(index, 0, value);
+        } else {
+          dest.push(value);
+        }
+      } else { obj[key] = [dest, value]; }
+    }
+  }
+
+  /**
+   * Detect the tag implementation by a DOM node
+   * @param   { Object } dom - DOM node we need to parse to get its tag implementation
+   * @returns { Object } it returns an object containing the implementation of a custom tag (template and boot function)
+   */
+  function get(dom) {
+    return dom.tagName && __TAG_IMPL[getAttribute(dom, IS_DIRECTIVE) ||
+      getAttribute(dom, IS_DIRECTIVE) || dom.tagName.toLowerCase()]
+  }
+
+  /**
+   * Get the tag name of any DOM node
+   * @param   { Object } dom - DOM node we want to parse
+   * @param   { Boolean } skipDataIs - hack to ignore the data-is attribute when attaching to parent
+   * @returns { String } name to identify this dom node in riot
+   */
+  function getName(dom, skipDataIs) {
+    var child = get(dom);
+    var namedTag = !skipDataIs && getAttribute(dom, IS_DIRECTIVE);
+    return namedTag && !tmpl.hasExpr(namedTag) ?
+      namedTag : child ? child.name : dom.tagName.toLowerCase()
+  }
+
+  /**
+   * Return a temporary context containing also the parent properties
+   * @this Tag
+   * @param { Tag } - temporary tag context containing all the parent properties
+   */
+  function inheritParentProps() {
+    if (this.parent) { return extend(create(this), this.parent) }
+    return this
+  }
+
+  /*
+    Includes hacks needed for the Internet Explorer version 9 and below
+    See: http://kangax.github.io/compat-table/es5/#ie8
+         http://codeplanet.io/dropping-ie8/
+  */
+
+  var
+    reHasYield  = /<yield\b/i,
+    reYieldAll  = /<yield\s*(?:\/>|>([\S\s]*?)<\/yield\s*>|>)/ig,
+    reYieldSrc  = /<yield\s+to=['"]([^'">]*)['"]\s*>([\S\s]*?)<\/yield\s*>/ig,
+    reYieldDest = /<yield\s+from=['"]?([-\w]+)['"]?\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/ig,
+    rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' },
+    tblTags = IE_VERSION && IE_VERSION < 10 ? RE_SPECIAL_TAGS : RE_SPECIAL_TAGS_NO_OPTION,
+    GENERIC = 'div',
+    SVG = 'svg';
+
+
+  /*
+    Creates the root element for table or select child elements:
+    tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup
+  */
+  function specialTags(el, tmpl, tagName) {
+
+    var
+      select = tagName[0] === 'o',
+      parent = select ? 'select>' : 'table>';
+
+    // trim() is important here, this ensures we don't have artifacts,
+    // so we can check if we have only one element inside the parent
+    el.innerHTML = '<' + parent + tmpl.trim() + '</' + parent;
+    parent = el.firstChild;
+
+    // returns the immediate parent if tr/th/td/col is the only element, if not
+    // returns the whole tree, as this can include additional elements
+    /* istanbul ignore next */
+    if (select) {
+      parent.selectedIndex = -1;  // for IE9, compatible w/current riot behavior
+    } else {
+      // avoids insertion of cointainer inside container (ex: tbody inside tbody)
+      var tname = rootEls[tagName];
+      if (tname && parent.childElementCount === 1) { parent = $(tname, parent); }
+    }
+    return parent
+  }
+
+  /*
+    Replace the yield tag from any tag template with the innerHTML of the
+    original tag in the page
+  */
+  function replaceYield(tmpl, html) {
+    // do nothing if no yield
+    if (!reHasYield.test(tmpl)) { return tmpl }
+
+    // be careful with #1343 - string on the source having `$1`
+    var src = {};
+
+    html = html && html.replace(reYieldSrc, function (_, ref, text) {
+      src[ref] = src[ref] || text;   // preserve first definition
+      return ''
+    }).trim();
+
+    return tmpl
+      .replace(reYieldDest, function (_, ref, def) {  // yield with from - to attrs
+        return src[ref] || def || ''
+      })
+      .replace(reYieldAll, function (_, def) {        // yield without any "from"
+        return html || def || ''
+      })
+  }
+
+  /**
+   * Creates a DOM element to wrap the given content. Normally an `DIV`, but can be
+   * also a `TABLE`, `SELECT`, `TBODY`, `TR`, or `COLGROUP` element.
+   *
+   * @param   { String } tmpl  - The template coming from the custom tag definition
+   * @param   { String } html - HTML content that comes from the DOM element where you
+   *           will mount the tag, mostly the original tag in the page
+   * @param   { Boolean } isSvg - true if the root node is an svg
+   * @returns { HTMLElement } DOM element with _tmpl_ merged through `YIELD` with the _html_.
+   */
+  function mkdom(tmpl, html, isSvg) {
+    var match   = tmpl && tmpl.match(/^\s*<([-\w]+)/);
+    var  tagName = match && match[1].toLowerCase();
+    var el = makeElement(isSvg ? SVG : GENERIC);
+
+    // replace all the yield tags with the tag inner html
+    tmpl = replaceYield(tmpl, html);
+
+    /* istanbul ignore next */
+    if (tblTags.test(tagName))
+      { el = specialTags(el, tmpl, tagName); }
+    else
+      { setInnerHTML(el, tmpl, isSvg); }
+
+    return el
+  }
+
+  var EVENT_ATTR_RE = /^on/;
+
+  /**
+   * True if the event attribute starts with 'on'
+   * @param   { String } attribute - event attribute
+   * @returns { Boolean }
+   */
+  function isEventAttribute(attribute) {
+    return EVENT_ATTR_RE.test(attribute)
+  }
+
+  /**
+   * Loop backward all the parents tree to detect the first custom parent tag
+   * @param   { Object } tag - a Tag instance
+   * @returns { Object } the instance of the first custom parent tag found
+   */
+  function getImmediateCustomParent(tag) {
+    var ptag = tag;
+    while (ptag.__.isAnonymous) {
+      if (!ptag.parent) { break }
+      ptag = ptag.parent;
+    }
+    return ptag
+  }
+
+  /**
+   * Trigger DOM events
+   * @param   { HTMLElement } dom - dom element target of the event
+   * @param   { Function } handler - user function
+   * @param   { Object } e - event object
+   */
+  function handleEvent(dom, handler, e) {
+    var ptag = this.__.parent;
+    var item = this.__.item;
+
+    if (!item)
+      { while (ptag && !item) {
+        item = ptag.__.item;
+        ptag = ptag.__.parent;
+      } }
+
+    // override the event properties
+    /* istanbul ignore next */
+    if (isWritable(e, 'currentTarget')) { e.currentTarget = dom; }
+    /* istanbul ignore next */
+    if (isWritable(e, 'target')) { e.target = e.srcElement; }
+    /* istanbul ignore next */
+    if (isWritable(e, 'which')) { e.which = e.charCode || e.keyCode; }
+
+    e.item = item;
+
+    handler.call(this, e);
+
+    // avoid auto updates
+    if (!settings.autoUpdate) { return }
+
+    if (!e.preventUpdate) {
+      var p = getImmediateCustomParent(this);
+      // fixes #2083
+      if (p.isMounted) { p.update(); }
+    }
+  }
+
+  /**
+   * Attach an event to a DOM node
+   * @param { String } name - event name
+   * @param { Function } handler - event callback
+   * @param { Object } dom - dom node
+   * @param { Tag } tag - tag instance
+   */
+  function setEventHandler(name, handler, dom, tag) {
+    var eventName;
+    var cb = handleEvent.bind(tag, dom, handler);
+
+    // avoid to bind twice the same event
+    // possible fix for #2332
+    dom[name] = null;
+
+    // normalize event name
+    eventName = name.replace(RE_EVENTS_PREFIX, '');
+
+    // cache the listener into the listeners array
+    if (!contains(tag.__.listeners, dom)) { tag.__.listeners.push(dom); }
+    if (!dom[RIOT_EVENTS_KEY]) { dom[RIOT_EVENTS_KEY] = {}; }
+    if (dom[RIOT_EVENTS_KEY][name]) { dom.removeEventListener(eventName, dom[RIOT_EVENTS_KEY][name]); }
+
+    dom[RIOT_EVENTS_KEY][name] = cb;
+    dom.addEventListener(eventName, cb, false);
+  }
+
+  /**
+   * Create a new child tag including it correctly into its parent
+   * @param   { Object } child - child tag implementation
+   * @param   { Object } opts - tag options containing the DOM node where the tag will be mounted
+   * @param   { String } innerHTML - inner html of the child node
+   * @param   { Object } parent - instance of the parent tag including the child custom tag
+   * @returns { Object } instance of the new child tag just created
+   */
+  function initChild(child, opts, innerHTML, parent) {
+    var tag = createTag(child, opts, innerHTML);
+    var tagName = opts.tagName || getName(opts.root, true);
+    var ptag = getImmediateCustomParent(parent);
+    // fix for the parent attribute in the looped elements
+    define(tag, 'parent', ptag);
+    // store the real parent tag
+    // in some cases this could be different from the custom parent tag
+    // for example in nested loops
+    tag.__.parent = parent;
+
+    // add this tag to the custom parent tag
+    arrayishAdd(ptag.tags, tagName, tag);
+
+    // and also to the real parent tag
+    if (ptag !== parent)
+      { arrayishAdd(parent.tags, tagName, tag); }
+
+    return tag
+  }
+
+  /**
+   * Removes an item from an object at a given key. If the key points to an array,
+   * then the item is just removed from the array.
+   * @param { Object } obj - object on which to remove the property
+   * @param { String } key - property name
+   * @param { Object } value - the value of the property to be removed
+   * @param { Boolean } ensureArray - ensure that the property remains an array
+  */
+  function arrayishRemove(obj, key, value, ensureArray) {
+    if (isArray(obj[key])) {
+      var index = obj[key].indexOf(value);
+      if (index !== -1) { obj[key].splice(index, 1); }
+      if (!obj[key].length) { delete obj[key]; }
+      else if (obj[key].length === 1 && !ensureArray) { obj[key] = obj[key][0]; }
+    } else if (obj[key] === value)
+      { delete obj[key]; } // otherwise just delete the key
+  }
+
+  /**
+   * Adds the elements for a virtual tag
+   * @this Tag
+   * @param { Node } src - the node that will do the inserting or appending
+   * @param { Tag } target - only if inserting, insert before this tag's first child
+   */
+  function makeVirtual(src, target) {
+    var this$1 = this;
+
+    var head = createDOMPlaceholder();
+    var tail = createDOMPlaceholder();
+    var frag = createFragment();
+    var sib;
+    var el;
+
+    this.root.insertBefore(head, this.root.firstChild);
+    this.root.appendChild(tail);
+
+    this.__.head = el = head;
+    this.__.tail = tail;
+
+    while (el) {
+      sib = el.nextSibling;
+      frag.appendChild(el);
+      this$1.__.virts.push(el); // hold for unmounting
+      el = sib;
+    }
+
+    if (target)
+      { src.insertBefore(frag, target.__.head); }
+    else
+      { src.appendChild(frag); }
+  }
+
+  /**
+   * makes a tag virtual and replaces a reference in the dom
+   * @this Tag
+   * @param { tag } the tag to make virtual
+   * @param { ref } the dom reference location
+   */
+  function makeReplaceVirtual(tag, ref) {
+    if (!ref.parentNode) { return }
+    var frag = createFragment();
+    makeVirtual.call(tag, frag);
+    ref.parentNode.replaceChild(frag, ref);
+  }
+
+  /**
+   * Update dynamically created data-is tags with changing expressions
+   * @param { Object } expr - expression tag and expression info
+   * @param { Tag }    parent - parent for tag creation
+   * @param { String } tagName - tag implementation we want to use
+   */
+  function updateDataIs(expr, parent, tagName) {
+    var tag = expr.tag || expr.dom._tag;
+    var ref;
+
+    var ref$1 = tag ? tag.__ : {};
+    var head = ref$1.head;
+    var isVirtual = expr.dom.tagName === 'VIRTUAL';
+
+    if (tag && expr.tagName === tagName) {
+      tag.update();
+      return
+    }
+
+    // sync _parent to accommodate changing tagnames
+    if (tag) {
+      // need placeholder before unmount
+      if(isVirtual) {
+        ref = createDOMPlaceholder();
+        head.parentNode.insertBefore(ref, head);
+      }
+
+      tag.unmount(true);
+    }
+
+    // unable to get the tag name
+    if (!isString(tagName)) { return }
+
+    expr.impl = __TAG_IMPL[tagName];
+
+    // unknown implementation
+    if (!expr.impl) { return }
+
+    expr.tag = tag = initChild(
+      expr.impl, {
+        root: expr.dom,
+        parent: parent,
+        tagName: tagName
+      },
+      expr.dom.innerHTML,
+      parent
+    );
+
+    each(expr.attrs, function (a) { return setAttribute(tag.root, a.name, a.value); });
+    expr.tagName = tagName;
+    tag.mount();
+
+    // root exist first time, after use placeholder
+    if (isVirtual) { makeReplaceVirtual(tag, ref || tag.root); }
+
+    // parent is the placeholder tag, not the dynamic tag so clean up
+    parent.__.onUnmount = function () {
+      var delName = tag.opts.dataIs;
+      arrayishRemove(tag.parent.tags, delName, tag);
+      arrayishRemove(tag.__.parent.tags, delName, tag);
+      tag.unmount();
+    };
+  }
+
+  /**
+   * Nomalize any attribute removing the "riot-" prefix
+   * @param   { String } attrName - original attribute name
+   * @returns { String } valid html attribute name
+   */
+  function normalizeAttrName(attrName) {
+    if (!attrName) { return null }
+    attrName = attrName.replace(ATTRS_PREFIX, '');
+    if (CASE_SENSITIVE_ATTRIBUTES[attrName]) { attrName = CASE_SENSITIVE_ATTRIBUTES[attrName]; }
+    return attrName
+  }
+
+  /**
+   * Update on single tag expression
+   * @this Tag
+   * @param { Object } expr - expression logic
+   * @returns { undefined }
+   */
+  function updateExpression(expr) {
+    if (this.root && getAttribute(this.root,'virtualized')) { return }
+
+    var dom = expr.dom;
+    // remove the riot- prefix
+    var attrName = normalizeAttrName(expr.attr);
+    var isToggle = contains([SHOW_DIRECTIVE, HIDE_DIRECTIVE], attrName);
+    var isVirtual = expr.root && expr.root.tagName === 'VIRTUAL';
+    var ref = this.__;
+    var isAnonymous = ref.isAnonymous;
+    var parent = dom && (expr.parent || dom.parentNode);
+    var keepValueAttributes = settings.keepValueAttributes;
+    // detect the style attributes
+    var isStyleAttr = attrName === 'style';
+    var isClassAttr = attrName === 'class';
+    var isValueAttr = attrName === 'value';
+
+    var value;
+
+    // if it's a tag we could totally skip the rest
+    if (expr._riot_id) {
+      if (expr.__.wasCreated) {
+        expr.update();
+      // if it hasn't been mounted yet, do that now.
+      } else {
+        expr.mount();
+        if (isVirtual) {
+          makeReplaceVirtual(expr, expr.root);
+        }
+      }
+      return
+    }
+
+    // if this expression has the update method it means it can handle the DOM changes by itself
+    if (expr.update) { return expr.update() }
+
+    var context = isToggle && !isAnonymous ? inheritParentProps.call(this) : this;
+
+    // ...it seems to be a simple expression so we try to calculate its value
+    value = tmpl(expr.expr, context);
+
+    var hasValue = !isBlank(value);
+    var isObj = isObject(value);
+
+    // convert the style/class objects to strings
+    if (isObj) {
+      if (isClassAttr) {
+        value = tmpl(JSON.stringify(value), this);
+      } else if (isStyleAttr) {
+        value = styleObjectToString(value);
+      }
+    }
+
+    // remove original attribute
+    if (expr.attr &&
+        (
+          // the original attribute can be removed only if we are parsing the original expression
+          !expr.wasParsedOnce ||
+          // or its value is false
+          value === false ||
+          // or if its value is currently falsy...
+          // We will keep the "value" attributes if the "keepValueAttributes"
+          // is enabled though
+          (!hasValue && (!isValueAttr || isValueAttr && !keepValueAttributes))
+        )
+    ) {
+      // remove either riot-* attributes or just the attribute name
+      removeAttribute(dom, getAttribute(dom, expr.attr) ? expr.attr : attrName);
+    }
+
+    // for the boolean attributes we don't need the value
+    // we can convert it to checked=true to checked=checked
+    if (expr.bool) { value = value ? attrName : false; }
+    if (expr.isRtag) { return updateDataIs(expr, this, value) }
+    if (expr.wasParsedOnce && expr.value === value) { return }
+
+    // update the expression value
+    expr.value = value;
+    expr.wasParsedOnce = true;
+
+    // if the value is an object (and it's not a style or class attribute) we can not do much more with it
+    if (isObj && !isClassAttr && !isStyleAttr && !isToggle) { return }
+    // avoid to render undefined/null values
+    if (!hasValue) { value = ''; }
+
+    // textarea and text nodes have no attribute name
+    if (!attrName) {
+      // about #815 w/o replace: the browser converts the value to a string,
+      // the comparison by "==" does too, but not in the server
+      value += '';
+      // test for parent avoids error with invalid assignment to nodeValue
+      if (parent) {
+        // cache the parent node because somehow it will become null on IE
+        // on the next iteration
+        expr.parent = parent;
+        if (parent.tagName === 'TEXTAREA') {
+          parent.value = value;                    // #1113
+          if (!IE_VERSION) { dom.nodeValue = value; }  // #1625 IE throws here, nodeValue
+        }                                         // will be available on 'updated'
+        else { dom.nodeValue = value; }
+      }
+      return
+    }
+
+    switch (true) {
+    // handle events binding
+    case isFunction(value):
+      if (isEventAttribute(attrName)) {
+        setEventHandler(attrName, value, dom, this);
+      }
+      break
+    // show / hide
+    case isToggle:
+      toggleVisibility(dom, attrName === HIDE_DIRECTIVE ? !value : value);
+      break
+    // handle attributes
+    default:
+      if (expr.bool) {
+        dom[attrName] = value;
+      }
+
+      if (isValueAttr && dom.value !== value) {
+        dom.value = value;
+      } else if (hasValue && value !== false) {
+        setAttribute(dom, attrName, value);
+      }
+
+      // make sure that in case of style changes
+      // the element stays hidden
+      if (isStyleAttr && dom.hidden) { toggleVisibility(dom, false); }
+    }
+  }
+
+  /**
+   * Update all the expressions in a Tag instance
+   * @this Tag
+   * @param { Array } expressions - expression that must be re evaluated
+   */
+  function update(expressions) {
+    each(expressions, updateExpression.bind(this));
+  }
+
+  /**
+   * We need to update opts for this tag. That requires updating the expressions
+   * in any attributes on the tag, and then copying the result onto opts.
+   * @this Tag
+   * @param   {Boolean} isLoop - is it a loop tag?
+   * @param   { Tag }  parent - parent tag node
+   * @param   { Boolean }  isAnonymous - is it a tag without any impl? (a tag not registered)
+   * @param   { Object }  opts - tag options
+   * @param   { Array }  instAttrs - tag attributes array
+   */
+  function updateOpts(isLoop, parent, isAnonymous, opts, instAttrs) {
+    // isAnonymous `each` tags treat `dom` and `root` differently. In this case
+    // (and only this case) we don't need to do updateOpts, because the regular parse
+    // will update those attrs. Plus, isAnonymous tags don't need opts anyway
+    if (isLoop && isAnonymous) { return }
+    var ctx = isLoop ? inheritParentProps.call(this) : parent || this;
+
+    each(instAttrs, function (attr) {
+      if (attr.expr) { updateExpression.call(ctx, attr.expr); }
+      // normalize the attribute names
+      opts[toCamel(attr.name).replace(ATTRS_PREFIX, '')] = attr.expr ? attr.expr.value : attr.value;
+    });
+  }
+
+  /**
+   * Update the tag expressions and options
+   * @param { Tag } tag - tag object
+   * @param { * } data - data we want to use to extend the tag properties
+   * @param { Array } expressions - component expressions array
+   * @returns { Tag } the current tag instance
+   */
+  function componentUpdate(tag, data, expressions) {
+    var __ = tag.__;
+    var nextOpts = {};
+    var canTrigger = tag.isMounted && !__.skipAnonymous;
+
+    // inherit properties from the parent tag
+    if (__.isAnonymous && __.parent) { extend(tag, __.parent); }
+    extend(tag, data);
+
+    updateOpts.apply(tag, [__.isLoop, __.parent, __.isAnonymous, nextOpts, __.instAttrs]);
+
+    if (
+      canTrigger &&
+      tag.isMounted &&
+      isFunction(tag.shouldUpdate) && !tag.shouldUpdate(data, nextOpts)
+    ) {
+      return tag
+    }
+
+    extend(tag.opts, nextOpts);
+
+    if (canTrigger) { tag.trigger('update', data); }
+    update.call(tag, expressions);
+    if (canTrigger) { tag.trigger('updated'); }
+
+    return tag
+  }
+
+  /**
+   * Get selectors for tags
+   * @param   { Array } tags - tag names to select
+   * @returns { String } selector
+   */
+  function query(tags) {
+    // select all tags
+    if (!tags) {
+      var keys = Object.keys(__TAG_IMPL);
+      return keys + query(keys)
+    }
+
+    return tags
+      .filter(function (t) { return !/[^-\w]/.test(t); })
+      .reduce(function (list, t) {
+        var name = t.trim().toLowerCase();
+        return list + ",[" + IS_DIRECTIVE + "=\"" + name + "\"]"
+      }, '')
+  }
+
+  /**
+   * Another way to create a riot tag a bit more es6 friendly
+   * @param { HTMLElement } el - tag DOM selector or DOM node/s
+   * @param { Object } opts - tag logic
+   * @returns { Tag } new riot tag instance
+   */
+  function Tag(el, opts) {
+    // get the tag properties from the class constructor
+    var ref = this;
+    var name = ref.name;
+    var tmpl = ref.tmpl;
+    var css = ref.css;
+    var attrs = ref.attrs;
+    var onCreate = ref.onCreate;
+    // register a new tag and cache the class prototype
+    if (!__TAG_IMPL[name]) {
+      tag(name, tmpl, css, attrs, onCreate);
+      // cache the class constructor
+      __TAG_IMPL[name].class = this.constructor;
+    }
+
+    // mount the tag using the class instance
+    mount$1(el, name, opts, this);
+    // inject the component css
+    if (css) { styleManager.inject(); }
+
+    return this
+  }
+
+  /**
+   * Create a new riot tag implementation
+   * @param   { String }   name - name/id of the new riot tag
+   * @param   { String }   tmpl - tag template
+   * @param   { String }   css - custom tag css
+   * @param   { String }   attrs - root tag attributes
+   * @param   { Function } fn - user function
+   * @returns { String } name/id of the tag just created
+   */
+  function tag(name, tmpl, css, attrs, fn) {
+    if (isFunction(attrs)) {
+      fn = attrs;
+
+      if (/^[\w-]+\s?=/.test(css)) {
+        attrs = css;
+        css = '';
+      } else
+        { attrs = ''; }
+    }
+
+    if (css) {
+      if (isFunction(css))
+        { fn = css; }
+      else
+        { styleManager.add(css, name); }
+    }
+
+    name = name.toLowerCase();
+    __TAG_IMPL[name] = { name: name, tmpl: tmpl, attrs: attrs, fn: fn };
+
+    return name
+  }
+
+  /**
+   * Create a new riot tag implementation (for use by the compiler)
+   * @param   { String }   name - name/id of the new riot tag
+   * @param   { String }   tmpl - tag template
+   * @param   { String }   css - custom tag css
+   * @param   { String }   attrs - root tag attributes
+   * @param   { Function } fn - user function
+   * @returns { String } name/id of the tag just created
+   */
+  function tag2(name, tmpl, css, attrs, fn) {
+    if (css) { styleManager.add(css, name); }
+
+    __TAG_IMPL[name] = { name: name, tmpl: tmpl, attrs: attrs, fn: fn };
+
+    return name
+  }
+
+  /**
+   * Mount a tag using a specific tag implementation
+   * @param   { * } selector - tag DOM selector or DOM node/s
+   * @param   { String } tagName - tag implementation name
+   * @param   { Object } opts - tag logic
+   * @returns { Array } new tags instances
+   */
+  function mount(selector, tagName, opts) {
+    var tags = [];
+    var elem, allTags;
+
+    function pushTagsTo(root) {
+      if (root.tagName) {
+        var riotTag = getAttribute(root, IS_DIRECTIVE), tag;
+
+        // have tagName? force riot-tag to be the same
+        if (tagName && riotTag !== tagName) {
+          riotTag = tagName;
+          setAttribute(root, IS_DIRECTIVE, tagName);
+        }
+
+        tag = mount$1(
+          root,
+          riotTag || root.tagName.toLowerCase(),
+          isFunction(opts) ? opts() : opts
+        );
+
+        if (tag)
+          { tags.push(tag); }
+      } else if (root.length)
+        { each(root, pushTagsTo); } // assume nodeList
+    }
+
+    // inject styles into DOM
+    styleManager.inject();
+
+    if (isObject(tagName) || isFunction(tagName)) {
+      opts = tagName;
+      tagName = 0;
+    }
+
+    // crawl the DOM to find the tag
+    if (isString(selector)) {
+      selector = selector === '*' ?
+        // select all registered tags
+        // & tags found with the riot-tag attribute set
+        allTags = query() :
+        // or just the ones named like the selector
+        selector + query(selector.split(/, */));
+
+      // make sure to pass always a selector
+      // to the querySelectorAll function
+      elem = selector ? $$(selector) : [];
+    }
+    else
+      // probably you have passed already a tag or a NodeList
+      { elem = selector; }
+
+    // select all the registered and mount them inside their root elements
+    if (tagName === '*') {
+      // get all custom tags
+      tagName = allTags || query();
+      // if the root els it's just a single tag
+      if (elem.tagName)
+        { elem = $$(tagName, elem); }
+      else {
+        // select all the children for all the different root elements
+        var nodeList = [];
+
+        each(elem, function (_el) { return nodeList.push($$(tagName, _el)); });
+
+        elem = nodeList;
+      }
+      // get rid of the tagName
+      tagName = 0;
+    }
+
+    pushTagsTo(elem);
+
+    return tags
+  }
+
+  // Create a mixin that could be globally shared across all the tags
+  var mixins = {};
+  var globals = mixins[GLOBAL_MIXIN] = {};
+  var mixins_id = 0;
+
+  /**
+   * Create/Return a mixin by its name
+   * @param   { String }  name - mixin name (global mixin if object)
+   * @param   { Object }  mix - mixin logic
+   * @param   { Boolean } g - is global?
+   * @returns { Object }  the mixin logic
+   */
+  function mixin(name, mix, g) {
+    // Unnamed global
+    if (isObject(name)) {
+      mixin(("__" + (mixins_id++) + "__"), name, true);
+      return
+    }
+
+    var store = g ? globals : mixins;
+
+    // Getter
+    if (!mix) {
+      if (isUndefined(store[name]))
+        { throw new Error(("Unregistered mixin: " + name)) }
+
+      return store[name]
+    }
+
+    // Setter
+    store[name] = isFunction(mix) ?
+      extend(mix.prototype, store[name] || {}) && mix :
+      extend(store[name] || {}, mix);
+  }
+
+  /**
+   * Update all the tags instances created
+   * @returns { Array } all the tags instances
+   */
+  function update$1() {
+    return each(__TAGS_CACHE, function (tag) { return tag.update(); })
+  }
+
+  function unregister(name) {
+    styleManager.remove(name);
+    return delete __TAG_IMPL[name]
+  }
+
+  var version = 'v3.13.2';
+
+  var core = /*#__PURE__*/Object.freeze({
+    Tag: Tag,
+    tag: tag,
+    tag2: tag2,
+    mount: mount,
+    mixin: mixin,
+    update: update$1,
+    unregister: unregister,
+    version: version
+  });
+
+  /**
+   * Add a mixin to this tag
+   * @returns { Tag } the current tag instance
+   */
+  function componentMixin(tag$$1) {
+    var mixins = [], len = arguments.length - 1;
+    while ( len-- > 0 ) mixins[ len ] = arguments[ len + 1 ];
+
+    each(mixins, function (mix) {
+      var instance;
+      var obj;
+      var props = [];
+
+      // properties blacklisted and will not be bound to the tag instance
+      var propsBlacklist = ['init', '__proto__'];
+
+      mix = isString(mix) ? mixin(mix) : mix;
+
+      // check if the mixin is a function
+      if (isFunction(mix)) {
+        // create the new mixin instance
+        instance = new mix();
+      } else { instance = mix; }
+
+      var proto = Object.getPrototypeOf(instance);
+
+      // build multilevel prototype inheritance chain property list
+      do { props = props.concat(Object.getOwnPropertyNames(obj || instance)); }
+      while (obj = Object.getPrototypeOf(obj || instance))
+
+      // loop the keys in the function prototype or the all object keys
+      each(props, function (key) {
+        // bind methods to tag
+        // allow mixins to override other properties/parent mixins
+        if (!contains(propsBlacklist, key)) {
+          // check for getters/setters
+          var descriptor = getPropDescriptor(instance, key) || getPropDescriptor(proto, key);
+          var hasGetterSetter = descriptor && (descriptor.get || descriptor.set);
+
+          // apply method only if it does not already exist on the instance
+          if (!tag$$1.hasOwnProperty(key) && hasGetterSetter) {
+            Object.defineProperty(tag$$1, key, descriptor);
+          } else {
+            tag$$1[key] = isFunction(instance[key]) ?
+              instance[key].bind(tag$$1) :
+              instance[key];
+          }
+        }
+      });
+
+      // init method will be called automatically
+      if (instance.init)
+        { instance.init.bind(tag$$1)(tag$$1.opts); }
+    });
+
+    return tag$$1
+  }
+
+  /**
+   * Move the position of a custom tag in its parent tag
+   * @this Tag
+   * @param   { String } tagName - key where the tag was stored
+   * @param   { Number } newPos - index where the new tag will be stored
+   */
+  function moveChild(tagName, newPos) {
+    var parent = this.parent;
+    var tags;
+    // no parent no move
+    if (!parent) { return }
+
+    tags = parent.tags[tagName];
+
+    if (isArray(tags))
+      { tags.splice(newPos, 0, tags.splice(tags.indexOf(this), 1)[0]); }
+    else { arrayishAdd(parent.tags, tagName, this); }
+  }
+
+  /**
+   * Move virtual tag and all child nodes
+   * @this Tag
+   * @param { Node } src  - the node that will do the inserting
+   * @param { Tag } target - insert before this tag's first child
+   */
+  function moveVirtual(src, target) {
+    var this$1 = this;
+
+    var el = this.__.head;
+    var sib;
+    var frag = createFragment();
+
+    while (el) {
+      sib = el.nextSibling;
+      frag.appendChild(el);
+      el = sib;
+      if (el === this$1.__.tail) {
+        frag.appendChild(el);
+        src.insertBefore(frag, target.__.head);
+        break
+      }
+    }
+  }
+
+  /**
+   * Convert the item looped into an object used to extend the child tag properties
+   * @param   { Object } expr - object containing the keys used to extend the children tags
+   * @param   { * } key - value to assign to the new object returned
+   * @param   { * } val - value containing the position of the item in the array
+   * @returns { Object } - new object containing the values of the original item
+   *
+   * The variables 'key' and 'val' are arbitrary.
+   * They depend on the collection type looped (Array, Object)
+   * and on the expression used on the each tag
+   *
+   */
+  function mkitem(expr, key, val) {
+    var item = {};
+    item[expr.key] = key;
+    if (expr.pos) { item[expr.pos] = val; }
+    return item
+  }
+
+  /**
+   * Unmount the redundant tags
+   * @param   { Array } items - array containing the current items to loop
+   * @param   { Array } tags - array containing all the children tags
+   */
+  function unmountRedundant(items, tags, filteredItemsCount) {
+    var i = tags.length;
+    var j = items.length - filteredItemsCount;
+
+    while (i > j) {
+      i--;
+      remove.apply(tags[i], [tags, i]);
+    }
+  }
+
+
+  /**
+   * Remove a child tag
+   * @this Tag
+   * @param   { Array } tags - tags collection
+   * @param   { Number } i - index of the tag to remove
+   */
+  function remove(tags, i) {
+    tags.splice(i, 1);
+    this.unmount();
+    arrayishRemove(this.parent, this, this.__.tagName, true);
+  }
+
+  /**
+   * Move the nested custom tags in non custom loop tags
+   * @this Tag
+   * @param   { Number } i - current position of the loop tag
+   */
+  function moveNestedTags(i) {
+    var this$1 = this;
+
+    each(Object.keys(this.tags), function (tagName) {
+      moveChild.apply(this$1.tags[tagName], [tagName, i]);
+    });
+  }
+
+  /**
+   * Move a child tag
+   * @this Tag
+   * @param   { HTMLElement } root - dom node containing all the loop children
+   * @param   { Tag } nextTag - instance of the next tag preceding the one we want to move
+   * @param   { Boolean } isVirtual - is it a virtual tag?
+   */
+  function move(root, nextTag, isVirtual) {
+    if (isVirtual)
+      { moveVirtual.apply(this, [root, nextTag]); }
+    else
+      { safeInsert(root, this.root, nextTag.root); }
+  }
+
+  /**
+   * Insert and mount a child tag
+   * @this Tag
+   * @param   { HTMLElement } root - dom node containing all the loop children
+   * @param   { Tag } nextTag - instance of the next tag preceding the one we want to insert
+   * @param   { Boolean } isVirtual - is it a virtual tag?
+   */
+  function insert(root, nextTag, isVirtual) {
+    if (isVirtual)
+      { makeVirtual.apply(this, [root, nextTag]); }
+    else
+      { safeInsert(root, this.root, nextTag.root); }
+  }
+
+  /**
+   * Append a new tag into the DOM
+   * @this Tag
+   * @param   { HTMLElement } root - dom node containing all the loop children
+   * @param   { Boolean } isVirtual - is it a virtual tag?
+   */
+  function append(root, isVirtual) {
+    if (isVirtual)
+      { makeVirtual.call(this, root); }
+    else
+      { root.appendChild(this.root); }
+  }
+
+  /**
+   * Return the value we want to use to lookup the postion of our items in the collection
+   * @param   { String }  keyAttr         - lookup string or expression
+   * @param   { * }       originalItem    - original item from the collection
+   * @param   { Object }  keyedItem       - object created by riot via { item, i in collection }
+   * @param   { Boolean } hasKeyAttrExpr  - flag to check whether the key is an expression
+   * @returns { * } value that we will use to figure out the item position via collection.indexOf
+   */
+  function getItemId(keyAttr, originalItem, keyedItem, hasKeyAttrExpr) {
+    if (keyAttr) {
+      return hasKeyAttrExpr ?  tmpl(keyAttr, keyedItem) :  originalItem[keyAttr]
+    }
+
+    return originalItem
+  }
+
+  /**
+   * Manage tags having the 'each'
+   * @param   { HTMLElement } dom - DOM node we need to loop
+   * @param   { Tag } parent - parent tag instance where the dom node is contained
+   * @param   { String } expr - string contained in the 'each' attribute
+   * @returns { Object } expression object for this each loop
+   */
+  function _each(dom, parent, expr) {
+    var mustReorder = typeof getAttribute(dom, LOOP_NO_REORDER_DIRECTIVE) !== T_STRING || removeAttribute(dom, LOOP_NO_REORDER_DIRECTIVE);
+    var keyAttr = getAttribute(dom, KEY_DIRECTIVE);
+    var hasKeyAttrExpr = keyAttr ? tmpl.hasExpr(keyAttr) : false;
+    var tagName = getName(dom);
+    var impl = __TAG_IMPL[tagName];
+    var parentNode = dom.parentNode;
+    var placeholder = createDOMPlaceholder();
+    var child = get(dom);
+    var ifExpr = getAttribute(dom, CONDITIONAL_DIRECTIVE);
+    var tags = [];
+    var isLoop = true;
+    var innerHTML = dom.innerHTML;
+    var isAnonymous = !__TAG_IMPL[tagName];
+    var isVirtual = dom.tagName === 'VIRTUAL';
+    var oldItems = [];
+
+    // remove the each property from the original tag
+    removeAttribute(dom, LOOP_DIRECTIVE);
+    removeAttribute(dom, KEY_DIRECTIVE);
+
+    // parse the each expression
+    expr = tmpl.loopKeys(expr);
+    expr.isLoop = true;
+
+    if (ifExpr) { removeAttribute(dom, CONDITIONAL_DIRECTIVE); }
+
+    // insert a marked where the loop tags will be injected
+    parentNode.insertBefore(placeholder, dom);
+    parentNode.removeChild(dom);
+
+    expr.update = function updateEach() {
+      // get the new items collection
+      expr.value = tmpl(expr.val, parent);
+
+      var items = expr.value;
+      var frag = createFragment();
+      var isObject = !isArray(items) && !isString(items);
+      var root = placeholder.parentNode;
+      var tmpItems = [];
+      var hasKeys = isObject && !!items;
+
+      // if this DOM was removed the update here is useless
+      // this condition fixes also a weird async issue on IE in our unit test
+      if (!root) { return }
+
+      // object loop. any changes cause full redraw
+      if (isObject) {
+        items = items ? Object.keys(items).map(function (key) { return mkitem(expr, items[key], key); }) : [];
+      }
+
+      // store the amount of filtered items
+      var filteredItemsCount = 0;
+
+      // loop all the new items
+      each(items, function (_item, index) {
+        var i = index - filteredItemsCount;
+        var item = !hasKeys && expr.key ? mkitem(expr, _item, index) : _item;
+
+        // skip this item because it must be filtered
+        if (ifExpr && !tmpl(ifExpr, extend(create(parent), item))) {
+          filteredItemsCount ++;
+          return
+        }
+
+        var itemId = getItemId(keyAttr, _item, item, hasKeyAttrExpr);
+        // reorder only if the items are not objects
+        // or a key attribute has been provided
+        var doReorder = !isObject && mustReorder && typeof _item === T_OBJECT || keyAttr;
+        var oldPos = oldItems.indexOf(itemId);
+        var isNew = oldPos === -1;
+        var pos = !isNew && doReorder ? oldPos : i;
+        // does a tag exist in this position?
+        var tag = tags[pos];
+        var mustAppend = i >= oldItems.length;
+        var mustCreate = doReorder && isNew || !doReorder && !tag || !tags[i];
+
+        // new tag
+        if (mustCreate) {
+          tag = createTag(impl, {
+            parent: parent,
+            isLoop: isLoop,
+            isAnonymous: isAnonymous,
+            tagName: tagName,
+            root: dom.cloneNode(isAnonymous),
+            item: item,
+            index: i,
+          }, innerHTML);
+
+          // mount the tag
+          tag.mount();
+
+          if (mustAppend)
+            { append.apply(tag, [frag || root, isVirtual]); }
+          else
+            { insert.apply(tag, [root, tags[i], isVirtual]); }
+
+          if (!mustAppend) { oldItems.splice(i, 0, item); }
+          tags.splice(i, 0, tag);
+          if (child) { arrayishAdd(parent.tags, tagName, tag, true); }
+        } else if (pos !== i && doReorder) {
+          // move
+          if (keyAttr || contains(items, oldItems[pos])) {
+            move.apply(tag, [root, tags[i], isVirtual]);
+            // move the old tag instance
+            tags.splice(i, 0, tags.splice(pos, 1)[0]);
+            // move the old item
+            oldItems.splice(i, 0, oldItems.splice(pos, 1)[0]);
+          }
+
+          // update the position attribute if it exists
+          if (expr.pos) { tag[expr.pos] = i; }
+
+          // if the loop tags are not custom
+          // we need to move all their custom tags into the right position
+          if (!child && tag.tags) { moveNestedTags.call(tag, i); }
+        }
+
+        // cache the original item to use it in the events bound to this node
+        // and its children
+        extend(tag.__, {
+          item: item,
+          index: i,
+          parent: parent
+        });
+
+        tmpItems[i] = itemId;
+
+        if (!mustCreate) { tag.update(item); }
+      });
+
+      // remove the redundant tags
+      unmountRedundant(items, tags, filteredItemsCount);
+
+      // clone the items array
+      oldItems = tmpItems.slice();
+
+      root.insertBefore(frag, placeholder);
+    };
+
+    expr.unmount = function () {
+      each(tags, function (t) { t.unmount(); });
+    };
+
+    return expr
+  }
+
+  var RefExpr = {
+    init: function init(dom, parent, attrName, attrValue) {
+      this.dom = dom;
+      this.attr = attrName;
+      this.rawValue = attrValue;
+      this.parent = parent;
+      this.hasExp = tmpl.hasExpr(attrValue);
+      return this
+    },
+    update: function update() {
+      var old = this.value;
+      var customParent = this.parent && getImmediateCustomParent(this.parent);
+      // if the referenced element is a custom tag, then we set the tag itself, rather than DOM
+      var tagOrDom = this.dom.__ref || this.tag || this.dom;
+
+      this.value = this.hasExp ? tmpl(this.rawValue, this.parent) : this.rawValue;
+
+      // the name changed, so we need to remove it from the old key (if present)
+      if (!isBlank(old) && customParent) { arrayishRemove(customParent.refs, old, tagOrDom); }
+      if (!isBlank(this.value) && isString(this.value)) {
+        // add it to the refs of parent tag (this behavior was changed >=3.0)
+        if (customParent) { arrayishAdd(
+          customParent.refs,
+          this.value,
+          tagOrDom,
+          // use an array if it's a looped node and the ref is not an expression
+          null,
+          this.parent.__.index
+        ); }
+
+        if (this.value !== old) {
+          setAttribute(this.dom, this.attr, this.value);
+        }
+      } else {
+        removeAttribute(this.dom, this.attr);
+      }
+
+      // cache the ref bound to this dom node
+      // to reuse it in future (see also #2329)
+      if (!this.dom.__ref) { this.dom.__ref = tagOrDom; }
+    },
+    unmount: function unmount() {
+      var tagOrDom = this.tag || this.dom;
+      var customParent = this.parent && getImmediateCustomParent(this.parent);
+      if (!isBlank(this.value) && customParent)
+        { arrayishRemove(customParent.refs, this.value, tagOrDom); }
+    }
+  };
+
+  /**
+   * Create a new ref directive
+   * @param   { HTMLElement } dom - dom node having the ref attribute
+   * @param   { Tag } context - tag instance where the DOM node is located
+   * @param   { String } attrName - either 'ref' or 'data-ref'
+   * @param   { String } attrValue - value of the ref attribute
+   * @returns { RefExpr } a new RefExpr object
+   */
+  function createRefDirective(dom, tag, attrName, attrValue) {
+    return create(RefExpr).init(dom, tag, attrName, attrValue)
+  }
+
+  /**
+   * Trigger the unmount method on all the expressions
+   * @param   { Array } expressions - DOM expressions
+   */
+  function unmountAll(expressions) {
+    each(expressions, function (expr) {
+      if (expr.unmount) { expr.unmount(true); }
+      else if (expr.tagName) { expr.tag.unmount(true); }
+      else if (expr.unmount) { expr.unmount(); }
+    });
+  }
+
+  var IfExpr = {
+    init: function init(dom, tag, expr) {
+      removeAttribute(dom, CONDITIONAL_DIRECTIVE);
+      extend(this, { tag: tag, expr: expr, stub: createDOMPlaceholder(), pristine: dom });
+      var p = dom.parentNode;
+      p.insertBefore(this.stub, dom);
+      p.removeChild(dom);
+
+      return this
+    },
+    update: function update$$1() {
+      this.value = tmpl(this.expr, this.tag);
+
+      if (!this.stub.parentNode) { return }
+
+      if (this.value && !this.current) { // insert
+        this.current = this.pristine.cloneNode(true);
+        this.stub.parentNode.insertBefore(this.current, this.stub);
+        this.expressions = parseExpressions.apply(this.tag, [this.current, true]);
+      } else if (!this.value && this.current) { // remove
+        this.unmount();
+        this.current = null;
+        this.expressions = [];
+      }
+
+      if (this.value) { update.call(this.tag, this.expressions); }
+    },
+    unmount: function unmount() {
+      if (this.current) {
+        if (this.current._tag) {
+          this.current._tag.unmount();
+        } else if (this.current.parentNode) {
+          this.current.parentNode.removeChild(this.current);
+        }
+      }
+
+      unmountAll(this.expressions || []);
+    }
+  };
+
+  /**
+   * Create a new if directive
+   * @param   { HTMLElement } dom - if root dom node
+   * @param   { Tag } context - tag instance where the DOM node is located
+   * @param   { String } attr - if expression
+   * @returns { IFExpr } a new IfExpr object
+   */
+  function createIfDirective(dom, tag, attr) {
+    return create(IfExpr).init(dom, tag, attr)
+  }
+
+  /**
+   * Walk the tag DOM to detect the expressions to evaluate
+   * @this Tag
+   * @param   { HTMLElement } root - root tag where we will start digging the expressions
+   * @param   { Boolean } mustIncludeRoot - flag to decide whether the root must be parsed as well
+   * @returns { Array } all the expressions found
+   */
+  function parseExpressions(root, mustIncludeRoot) {
+    var this$1 = this;
+
+    var expressions = [];
+
+    walkNodes(root, function (dom) {
+      var type = dom.nodeType;
+      var attr;
+      var tagImpl;
+
+      if (!mustIncludeRoot && dom === root) { return }
+
+      // text node
+      if (type === 3 && dom.parentNode.tagName !== 'STYLE' && tmpl.hasExpr(dom.nodeValue))
+        { expressions.push({dom: dom, expr: dom.nodeValue}); }
+
+      if (type !== 1) { return }
+
+      var isVirtual = dom.tagName === 'VIRTUAL';
+
+      // loop. each does it's own thing (for now)
+      if (attr = getAttribute(dom, LOOP_DIRECTIVE)) {
+        if(isVirtual) { setAttribute(dom, 'loopVirtual', true); } // ignore here, handled in _each
+        expressions.push(_each(dom, this$1, attr));
+        return false
+      }
+
+      // if-attrs become the new parent. Any following expressions (either on the current
+      // element, or below it) become children of this expression.
+      if (attr = getAttribute(dom, CONDITIONAL_DIRECTIVE)) {
+        expressions.push(createIfDirective(dom, this$1, attr));
+        return false
+      }
+
+      if (attr = getAttribute(dom, IS_DIRECTIVE)) {
+        if (tmpl.hasExpr(attr)) {
+          expressions.push({
+            isRtag: true,
+            expr: attr,
+            dom: dom,
+            attrs: [].slice.call(dom.attributes)
+          });
+
+          return false
+        }
+      }
+
+      // if this is a tag, stop traversing here.
+      // we ignore the root, since parseExpressions is called while we're mounting that root
+      tagImpl = get(dom);
+
+      if(isVirtual) {
+        if(getAttribute(dom, 'virtualized')) {dom.parentElement.removeChild(dom); } // tag created, remove from dom
+        if(!tagImpl && !getAttribute(dom, 'virtualized') && !getAttribute(dom, 'loopVirtual'))  // ok to create virtual tag
+          { tagImpl = { tmpl: dom.outerHTML }; }
+      }
+
+      if (tagImpl && (dom !== root || mustIncludeRoot)) {
+        var hasIsDirective = getAttribute(dom, IS_DIRECTIVE);
+        if(isVirtual && !hasIsDirective) { // handled in update
+          // can not remove attribute like directives
+          // so flag for removal after creation to prevent maximum stack error
+          setAttribute(dom, 'virtualized', true);
+          var tag = createTag(
+            {tmpl: dom.outerHTML},
+            {root: dom, parent: this$1},
+            dom.innerHTML
+          );
+
+          expressions.push(tag); // no return, anonymous tag, keep parsing
+        } else {
+          if (hasIsDirective && isVirtual)
+            { warn(("Virtual tags shouldn't be used together with the \"" + IS_DIRECTIVE + "\" attribute - https://github.com/riot/riot/issues/2511")); }
+
+          expressions.push(
+            initChild(
+              tagImpl,
+              {
+                root: dom,
+                parent: this$1
+              },
+              dom.innerHTML,
+              this$1
+            )
+          );
+          return false
+        }
+      }
+
+      // attribute expressions
+      parseAttributes.apply(this$1, [dom, dom.attributes, function (attr, expr) {
+        if (!expr) { return }
+        expressions.push(expr);
+      }]);
+    });
+
+    return expressions
+  }
+
+  /**
+   * Calls `fn` for every attribute on an element. If that attr has an expression,
+   * it is also passed to fn.
+   * @this Tag
+   * @param   { HTMLElement } dom - dom node to parse
+   * @param   { Array } attrs - array of attributes
+   * @param   { Function } fn - callback to exec on any iteration
+   */
+  function parseAttributes(dom, attrs, fn) {
+    var this$1 = this;
+
+    each(attrs, function (attr) {
+      if (!attr) { return false }
+
+      var name = attr.name;
+      var bool = isBoolAttr(name);
+      var expr;
+
+      if (contains(REF_DIRECTIVES, name) && dom.tagName.toLowerCase() !== YIELD_TAG) {
+        expr =  createRefDirective(dom, this$1, name, attr.value);
+      } else if (tmpl.hasExpr(attr.value)) {
+        expr = {dom: dom, expr: attr.value, attr: name, bool: bool};
+      }
+
+      fn(attr, expr);
+    });
+  }
+
+  /**
+   * Manage the mount state of a tag triggering also the observable events
+   * @this Tag
+   * @param { Boolean } value - ..of the isMounted flag
+   */
+  function setMountState(value) {
+    var ref = this.__;
+    var isAnonymous = ref.isAnonymous;
+    var skipAnonymous = ref.skipAnonymous;
+
+    define(this, 'isMounted', value);
+
+    if (!isAnonymous || !skipAnonymous) {
+      if (value) { this.trigger('mount'); }
+      else {
+        this.trigger('unmount');
+        this.off('*');
+        this.__.wasCreated = false;
+      }
+    }
+  }
+
+  /**
+   * Mount the current tag instance
+   * @returns { Tag } the current tag instance
+   */
+  function componentMount(tag$$1, dom, expressions, opts) {
+    var __ = tag$$1.__;
+    var root = __.root;
+    root._tag = tag$$1; // keep a reference to the tag just created
+
+    // Read all the attrs on this instance. This give us the info we need for updateOpts
+    parseAttributes.apply(__.parent, [root, root.attributes, function (attr, expr) {
+      if (!__.isAnonymous && RefExpr.isPrototypeOf(expr)) { expr.tag = tag$$1; }
+      attr.expr = expr;
+      __.instAttrs.push(attr);
+    }]);
+
+    // update the root adding custom attributes coming from the compiler
+    walkAttributes(__.impl.attrs, function (k, v) { __.implAttrs.push({name: k, value: v}); });
+    parseAttributes.apply(tag$$1, [root, __.implAttrs, function (attr, expr) {
+      if (expr) { expressions.push(expr); }
+      else { setAttribute(root, attr.name, attr.value); }
+    }]);
+
+    // initialiation
+    updateOpts.apply(tag$$1, [__.isLoop, __.parent, __.isAnonymous, opts, __.instAttrs]);
+
+    // add global mixins
+    var globalMixin = mixin(GLOBAL_MIXIN);
+
+    if (globalMixin && !__.skipAnonymous) {
+      for (var i in globalMixin) {
+        if (globalMixin.hasOwnProperty(i)) {
+          tag$$1.mixin(globalMixin[i]);
+        }
+      }
+    }
+
+    if (__.impl.fn) { __.impl.fn.call(tag$$1, opts); }
+
+    if (!__.skipAnonymous) { tag$$1.trigger('before-mount'); }
+
+    // parse layout after init. fn may calculate args for nested custom tags
+    each(parseExpressions.apply(tag$$1, [dom, __.isAnonymous]), function (e) { return expressions.push(e); });
+
+    tag$$1.update(__.item);
+
+    if (!__.isAnonymous && !__.isInline) {
+      while (dom.firstChild) { root.appendChild(dom.firstChild); }
+    }
+
+    define(tag$$1, 'root', root);
+
+    // if we need to wait that the parent "mount" or "updated" event gets triggered
+    if (!__.skipAnonymous && tag$$1.parent) {
+      var p = getImmediateCustomParent(tag$$1.parent);
+      p.one(!p.isMounted ? 'mount' : 'updated', function () {
+        setMountState.call(tag$$1, true);
+      });
+    } else {
+      // otherwise it's not a child tag we can trigger its mount event
+      setMountState.call(tag$$1, true);
+    }
+
+    tag$$1.__.wasCreated = true;
+
+    return tag$$1
+  }
+
+  /**
+   * Unmount the tag instance
+   * @param { Boolean } mustKeepRoot - if it's true the root node will not be removed
+   * @returns { Tag } the current tag instance
+   */
+  function tagUnmount(tag, mustKeepRoot, expressions) {
+    var __ = tag.__;
+    var root = __.root;
+    var tagIndex = __TAGS_CACHE.indexOf(tag);
+    var p = root.parentNode;
+
+    if (!__.skipAnonymous) { tag.trigger('before-unmount'); }
+
+    // clear all attributes coming from the mounted tag
+    walkAttributes(__.impl.attrs, function (name) {
+      if (startsWith(name, ATTRS_PREFIX))
+        { name = name.slice(ATTRS_PREFIX.length); }
+
+      removeAttribute(root, name);
+    });
+
+    // remove all the event listeners
+    tag.__.listeners.forEach(function (dom) {
+      Object.keys(dom[RIOT_EVENTS_KEY]).forEach(function (eventName) {
+        dom.removeEventListener(eventName, dom[RIOT_EVENTS_KEY][eventName]);
+      });
+    });
+
+    // remove tag instance from the global tags cache collection
+    if (tagIndex !== -1) { __TAGS_CACHE.splice(tagIndex, 1); }
+
+    // clean up the parent tags object
+    if (__.parent && !__.isAnonymous) {
+      var ptag = getImmediateCustomParent(__.parent);
+
+      if (__.isVirtual) {
+        Object
+          .keys(tag.tags)
+          .forEach(function (tagName) { return arrayishRemove(ptag.tags, tagName, tag.tags[tagName]); });
+      } else {
+        arrayishRemove(ptag.tags, __.tagName, tag);
+      }
+    }
+
+    // unmount all the virtual directives
+    if (tag.__.virts) {
+      each(tag.__.virts, function (v) {
+        if (v.parentNode) { v.parentNode.removeChild(v); }
+      });
+    }
+
+    // allow expressions to unmount themselves
+    unmountAll(expressions);
+    each(__.instAttrs, function (a) { return a.expr && a.expr.unmount && a.expr.unmount(); });
+
+    // clear the tag html if it's necessary
+    if (mustKeepRoot) { setInnerHTML(root, ''); }
+    // otherwise detach the root tag from the DOM
+    else if (p) { p.removeChild(root); }
+
+    // custom internal unmount function to avoid relying on the observable
+    if (__.onUnmount) { __.onUnmount(); }
+
+    // weird fix for a weird edge case #2409 and #2436
+    // some users might use your software not as you've expected
+    // so I need to add these dirty hacks to mitigate unexpected issues
+    if (!tag.isMounted) { setMountState.call(tag, true); }
+
+    setMountState.call(tag, false);
+
+    delete root._tag;
+
+    return tag
+  }
+
+  /**
+   * Tag creation factory function
+   * @constructor
+   * @param { Object } impl - it contains the tag template, and logic
+   * @param { Object } conf - tag options
+   * @param { String } innerHTML - html that eventually we need to inject in the tag
+   */
+  function createTag(impl, conf, innerHTML) {
+    if ( impl === void 0 ) impl = {};
+    if ( conf === void 0 ) conf = {};
+
+    var tag = conf.context || {};
+    var opts = conf.opts || {};
+    var parent = conf.parent;
+    var isLoop = conf.isLoop;
+    var isAnonymous = !!conf.isAnonymous;
+    var skipAnonymous = settings.skipAnonymousTags && isAnonymous;
+    var item = conf.item;
+    // available only for the looped nodes
+    var index = conf.index;
+    // All attributes on the Tag when it's first parsed
+    var instAttrs = [];
+    // expressions on this type of Tag
+    var implAttrs = [];
+    var tmpl = impl.tmpl;
+    var expressions = [];
+    var root = conf.root;
+    var tagName = conf.tagName || getName(root);
+    var isVirtual = tagName === 'virtual';
+    var isInline = !isVirtual && !tmpl;
+    var dom;
+
+    if (isInline || isLoop && isAnonymous) {
+      dom = root;
+    } else {
+      if (!isVirtual) { root.innerHTML = ''; }
+      dom = mkdom(tmpl, innerHTML, isSvg(root));
+    }
+
+    // make this tag observable
+    if (!skipAnonymous) { observable(tag); }
+
+    // only call unmount if we have a valid __TAG_IMPL (has name property)
+    if (impl.name && root._tag) { root._tag.unmount(true); }
+
+    define(tag, '__', {
+      impl: impl,
+      root: root,
+      skipAnonymous: skipAnonymous,
+      implAttrs: implAttrs,
+      isAnonymous: isAnonymous,
+      instAttrs: instAttrs,
+      innerHTML: innerHTML,
+      tagName: tagName,
+      index: index,
+      isLoop: isLoop,
+      isInline: isInline,
+      item: item,
+      parent: parent,
+      // tags having event listeners
+      // it would be better to use weak maps here but we can not introduce breaking changes now
+      listeners: [],
+      // these vars will be needed only for the virtual tags
+      virts: [],
+      wasCreated: false,
+      tail: null,
+      head: null
+    });
+
+    // tag protected properties
+    return [
+      ['isMounted', false],
+      // create a unique id to this tag
+      // it could be handy to use it also to improve the virtual dom rendering speed
+      ['_riot_id', uid()],
+      ['root', root],
+      ['opts', opts, { writable: true, enumerable: true }],
+      ['parent', parent || null],
+      // protect the "tags" and "refs" property from being overridden
+      ['tags', {}],
+      ['refs', {}],
+      ['update', function (data) { return componentUpdate(tag, data, expressions); }],
+      ['mixin', function () {
+        var mixins = [], len = arguments.length;
+        while ( len-- ) mixins[ len ] = arguments[ len ];
+
+        return componentMixin.apply(void 0, [ tag ].concat( mixins ));
+    }],
+      ['mount', function () { return componentMount(tag, dom, expressions, opts); }],
+      ['unmount', function (mustKeepRoot) { return tagUnmount(tag, mustKeepRoot, expressions); }]
+    ].reduce(function (acc, ref) {
+      var key = ref[0];
+      var value = ref[1];
+      var opts = ref[2];
+
+      define(tag, key, value, opts);
+      return acc
+    }, extend(tag, item))
+  }
+
+  /**
+   * Mount a tag creating new Tag instance
+   * @param   { Object } root - dom node where the tag will be mounted
+   * @param   { String } tagName - name of the riot tag we want to mount
+   * @param   { Object } opts - options to pass to the Tag instance
+   * @param   { Object } ctx - optional context that will be used to extend an existing class ( used in riot.Tag )
+   * @returns { Tag } a new Tag instance
+   */
+  function mount$1(root, tagName, opts, ctx) {
+    var impl = __TAG_IMPL[tagName];
+    var implClass = __TAG_IMPL[tagName].class;
+    var context = ctx || (implClass ? create(implClass.prototype) : {});
+    // cache the inner HTML to fix #855
+    var innerHTML = root._innerHTML = root._innerHTML || root.innerHTML;
+    var conf = extend({ root: root, opts: opts, context: context }, { parent: opts ? opts.parent : null });
+    var tag;
+
+    if (impl && root) { tag = createTag(impl, conf, innerHTML); }
+
+    if (tag && tag.mount) {
+      tag.mount(true);
+      // add this tag to the virtualDom variable
+      if (!contains(__TAGS_CACHE, tag)) { __TAGS_CACHE.push(tag); }
+    }
+
+    return tag
+  }
+
+
+
+  var tags = /*#__PURE__*/Object.freeze({
+    arrayishAdd: arrayishAdd,
+    getTagName: getName,
+    inheritParentProps: inheritParentProps,
+    mountTo: mount$1,
+    selectTags: query,
+    arrayishRemove: arrayishRemove,
+    getTag: get,
+    initChildTag: initChild,
+    moveChildTag: moveChild,
+    makeReplaceVirtual: makeReplaceVirtual,
+    getImmediateCustomParentTag: getImmediateCustomParent,
+    makeVirtual: makeVirtual,
+    moveVirtual: moveVirtual,
+    unmountAll: unmountAll,
+    createIfDirective: createIfDirective,
+    createRefDirective: createRefDirective
+  });
+
+  /**
+   * Riot public api
+   */
+  var settings$1 = settings;
+  var util = {
+    tmpl: tmpl,
+    brackets: brackets,
+    styleManager: styleManager,
+    vdom: __TAGS_CACHE,
+    styleNode: styleManager.styleNode,
+    // export the riot internal utils as well
+    dom: dom,
+    check: check,
+    misc: misc,
+    tags: tags
+  };
+
+  // export the core props/methods
+  var Tag$1 = Tag;
+  var tag$1 = tag;
+  var tag2$1 = tag2;
+  var mount$2 = mount;
+  var mixin$1 = mixin;
+  var update$2 = update$1;
+  var unregister$1 = unregister;
+  var version$1 = version;
+  var observable$1 = observable;
+
+  var riot$1 = extend({}, core, {
+    observable: observable,
+    settings: settings$1,
+    util: util,
+  });
+
+  exports.settings = settings$1;
+  exports.util = util;
+  exports.Tag = Tag$1;
+  exports.tag = tag$1;
+  exports.tag2 = tag2$1;
+  exports.mount = mount$2;
+  exports.mixin = mixin$1;
+  exports.update = update$2;
+  exports.unregister = unregister$1;
+  exports.version = version$1;
+  exports.observable = observable$1;
+  exports.default = riot$1;
+
+  Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/dist/firephp.build/scripts/devtools/dist/dist/riot.min.js b/dist/firephp.build/scripts/devtools/dist/dist/riot.min.js
new file mode 100644
index 0000000..60a1513
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/dist/riot.min.js
@@ -0,0 +1,2 @@
+/* Riot v3.13.2, @license MIT */
+var e,t;e=this,t=function(e){"use strict";function a(e,t){return(t||document).querySelector(e)}var t,n,y=[],$={},s="yield",_="__global_mixin",N="riot-",u=["ref","data-ref"],p="data-is",f="if",d="each",r="no-reorder",C="show",O="hide",i="key",x="__riot-events__",o="string",H="object",l="undefined",c="function",h="http://www.w3.org/1999/xlink",m="http://www.w3.org/2000/svg",g=/^xlink:(\w+)/,v=typeof window===l?void 0:window,b=/^on/,w=/([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g,k={viewbox:"viewBox",preserveaspectratio:"preserveAspectRatio"},A=/^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|selected|sortable|truespeed|typemustmatch)$/,E=0|(v&&v.document||{}).documentMode;function L(e){return"svg"===e?document.createElementNS(m,e):document.createElement(e)}function T(e,t,n){var r=g.exec(t);r&&r[1]?e.setAttributeNS(h,r[1],n):e.setAttribute(t,n)}var j,M,S={},I=!1;v&&(j=L("style"),M=a("style[type=riot]"),T(j,"type","text/css"),M?(M.id&&(j.id=M.id),M.parentNode.replaceChild(j,M)):document.head.appendChild(j),n=(t=j).styleSheet);var R={styleNode:t,add:function(e,t){S[t]=e,I=!0},inject:function(){if(v&&I){I=!1;var e=Object.keys(S).map(function(e){return S[e]}).join("\n");n?n.cssText=e:t.innerHTML=e}},remove:function(e){delete S[e],I=!0}},P=function(){var u=["case","default","do","else","in","instanceof","prefix","return","typeof","void","yield"],l=u.reduce(function(e,t){return e+t.slice(-1)},""),c=/^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/,p=/[$\w]/;function f(e,t){for(;0<=--t&&/\s/.test(e[t]););return t}return function(e,t){var n=/.*/g,r=n.lastIndex=t++,i=n.exec(e)[0].match(c);if(i){var o=r+i[0].length,a=e[r=f(e,r)];if(r<0||~"[{(,;:?=|&!^~>%*/".indexOf(a))return o;if("."===a)"."===e[r-1]&&(t=o);else if("+"===a||"-"===a)(e[--r]!==a||(r=f(e,r))<0||!p.test(e[r]))&&(t=o);else if(~l.indexOf(a)){for(var s=r+1;0<=--r&&p.test(e[r]););~u.indexOf(e.slice(r+1,s))&&(t=o)}}return t}}(),V=function(e){var t,n,r="g",i=/"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|`[^`\\]*(?:\\[\S\s][^`\\]*)*`/g,o=i.source+"|"+/(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source+"|"+/\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?([^<]\/)[gim]*/.source,a=RegExp("[\\x00-\\x1F<>a-zA-Z0-9'\",;\\\\]"),s=/(?=[[\]()*+?.^$|])/g,u=i.source+"|"+/(\/)(?![*\/])/.source,y={"(":RegExp("([()])|"+u,r),"[":RegExp("([[\\]])|"+u,r),"{":RegExp("([{}])|"+u,r)},l="{ }",c=["{","}","{","}",/{[^}]*}/,/\\([{}])/g,/\\({)|{/g,RegExp("\\\\(})|([[({])|(})|"+u,r),l,/^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/,/(^|[^\\]){=[\S\s]*?}/],p=void 0,_=[];function f(e){return e}function d(e,t){return t||(t=_),new RegExp(e.source.replace(/{/g,t[2]).replace(/}/g,t[3]),e.global?r:"")}function h(e){if(e===l)return c;var t=e.split(" ");if(2!==t.length||a.test(e))throw new Error('Unsupported brackets "'+e+'"');return(t=t.concat(e.replace(s,"\\").split(" ")))[4]=d(1<t[1].length?/{[\S\s]*?}/:c[4],t),t[5]=d(3<e.length?/\\({|})/g:c[5],t),t[6]=d(c[6],t),t[7]=RegExp("\\\\("+t[3]+")|([[({])|("+t[3]+")|"+u,r),t[8]=e,t}function m(e){return e instanceof RegExp?t(e):_[e]}function g(e){(e||(e=l))!==_[8]&&(_=h(e),t=e===l?f:d,_[9]=t(c[9])),p=e}return m.split=function(r,i,t){t||(t=_);var e,n,o,a,s,u,l=[],c=t[6],p=[],f="";for(n=o=c.lastIndex=0;e=c.exec(r);){if(u=c.lastIndex,a=e.index,n){if(e[2]){var d=e[2],h=y[d],m=1;for(h.lastIndex=u;e=h.exec(r);)if(e[1]){if(e[1]===d)++m;else if(!--m)break}else h.lastIndex=v(e.index,h.lastIndex,e[2]);c.lastIndex=m?r.length:h.lastIndex;continue}if(!e[3]){c.lastIndex=v(a,u,e[4]);continue}}e[1]||(g(r.slice(o,a)),o=c.lastIndex,(c=t[6+(n^=1)]).lastIndex=o)}return r&&o<r.length&&g(r.slice(o)),l.qblocks=p,l;function g(e){f&&(e=f+e,f=""),i||n?l.push(e&&e.replace(t[5],"$1")):l.push(e)}function v(e,t,n){return n&&(t=P(r,e)),i&&e+2<t&&(s="⁗"+p.length+"~",p.push(r.slice(e,t)),f+=r.slice(o,e)+s,o=t),t}},m.hasExpr=function(e){return _[4].test(e)},m.loopKeys=function(e){var t=e.match(_[9]);return t?{key:t[1],pos:t[2],val:_[0]+t[3].trim()+_[1]}:{val:e.trim()}},m.array=function(e){return e?h(e):_},Object.defineProperty(m,"settings",{set:function(e){var t;t=(e=e||{}).brackets,Object.defineProperty(e,"brackets",{set:g,get:function(){return p},enumerable:!0}),n=e,g(t)},get:function(){return n}}),m.settings="undefined"!=typeof riot&&riot.settings||{},m.set=g,m.skipRegex=P,m.R_STRINGS=i,m.R_MLCOMMS=/\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g,m.S_QBLOCKS=o,m.S_QBLOCK2=u,m}(),U=function(){var n={};function r(e,t){return e?(n[e]||(n[e]=function(e){var t=function(e){var t,n=V.split(e.replace(s,'"'),1),r=n.qblocks;if(2<n.length||n[0]){var i,o,a=[];for(i=o=0;i<n.length;++i)(t=n[i])&&(t=1&i?l(t,1,r):'"'+t.replace(/\\/g,"\\\\").replace(/\r\n?|\n/g,"\\n").replace(/"/g,'\\"')+'"')&&(a[o++]=t);t=o<2?a[0]:"["+a.join(",")+'].join("")'}else t=l(n[1],0,r);r.length&&(t=t.replace(u,function(e,t){return r[t].replace(/\r/g,"\\r").replace(/\n/g,"\\n")}));return t}(e);"try{return "!==t.slice(0,11)&&(t="return "+t);return new Function("E",t+";")}(e))).call(t,function(e,t){e.riotData={tagName:t&&t.__&&t.__.tagName,_riot_id:t&&t._riot_id},r.errorHandler?r.errorHandler(e):"undefined"!=typeof console&&"function"==typeof console.error&&(console.error(e.message),console.log("<%s> %s",e.riotData.tagName||"Unknown tag",this.tmpl),console.log(this.data))}.bind({data:t,tmpl:e})):e}r.hasExpr=V.hasExpr,r.loopKeys=V.loopKeys,r.clearCache=function(){n={}},r.errorHandler=null;var s=/\u2057/g,u=/\u2057(\d+)~/g;var c=/^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/,p={"(":/[()]/g,"[":/[[\]]/g,"{":/[{}]/g};function l(o,e,t){if(o=o.replace(/\s+/g," ").trim().replace(/\ ?([[\({},?\.:])\ ?/g,"$1")){for(var n,r=[],i=0;o&&(n=o.match(c))&&!n.index;){var a,s,u=/,|([[{(])|$/g;for(o=RegExp.rightContext,a=n[2]?t[n[2]].slice(1,-1).trim().replace(/\s+/g," "):n[1];s=(n=u.exec(o))[1];)l(s,u);s=o.slice(0,n.index),o=RegExp.rightContext,r[i++]=d(s,1,a)}o=i?1<i?"["+r.join(",")+'].join(" ").trim()':r[0]:d(o,e)}return o;function l(e,t){var n,r=1,i=p[e];for(i.lastIndex=t.lastIndex;n=i.exec(o);)if(n[0]===e)++r;else if(!--r)break;t.lastIndex=r?o.length:i.lastIndex}}var a='"in this?this:'+("object"!=typeof window?"global":"window")+").",i=/[,{][\$\w]+(?=:)|(^ *|[^$\w\.{])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,f=/^(?=(\.[$\w]+))\1(?:[^.[(]|$)/;function d(e,t,n){var o;return e=e.replace(i,function(e,t,n,r,i){return n&&(r=o?0:r+e.length,"this"!==n&&"global"!==n&&"window"!==n?(e=t+'("'+n+a+n,r&&(o="."===(i=i[r])||"("===i||"["===i)):r&&(o=!f.test(i.slice(r)))),e}),o&&(e="try{return "+e+"}catch(e){E(e,this)}"),n?e=(o?"function(){"+e+"}.call(this)":"("+e+")")+'?"'+n+'":""':t&&(e="function(v){"+(o?e.replace("return ","v="):"v=("+e+")")+';return v||v===0?v:""}.call(this)'),e}return r.version=V.version="v3.0.8",r}(),B=function(s){s=s||{};var u={},l=Array.prototype.slice;return Object.defineProperties(s,{on:{value:function(e,t){return"function"==typeof t&&(u[e]=u[e]||[]).push(t),s},enumerable:!1,writable:!1,configurable:!1},off:{value:function(e,t){if("*"!=e||t)if(t)for(var n,r=u[e],i=0;n=r&&r[i];++i)n==t&&r.splice(i--,1);else delete u[e];else u={};return s},enumerable:!1,writable:!1,configurable:!1},one:{value:function(t,n){return s.on(t,function e(){s.off(t,e),n.apply(s,arguments)})},enumerable:!1,writable:!1,configurable:!1},trigger:{value:function(e){var t,n,r,i=arguments,o=arguments.length-1,a=new Array(o);for(r=0;r<o;r++)a[r]=i[r+1];for(t=l.call(u[e]||[],0),r=0;n=t[r];++r)n.apply(s,a);return u["*"]&&"*"!=e&&s.trigger.apply(s,["*",e].concat(a)),s},enumerable:!1,writable:!1,configurable:!1}}),s};function z(e,t){return Object.getOwnPropertyDescriptor(e,t)}function D(e){return typeof e===l}function F(e,t){var n=z(e,t);return D(e[t])||n&&n.writable}function K(e){for(var t,n=1,r=arguments,i=r.length;n<i;n++)if(t=r[n])for(var o in t)F(e,o)&&(e[o]=t[o]);return e}function q(e){return Object.create(e)}var W=K(q(V.settings),{skipAnonymousTags:!0,keepValueAttributes:!1,autoUpdate:!0});function Z(e,t){return[].slice.call((t||document).querySelectorAll(e))}function G(){return document.createTextNode("")}function Q(e,t){e.style.display=t?"":"none",e.hidden=!t}function J(e,t){return e.getAttribute(t)}function X(e,t){e.removeAttribute(t)}function Y(e,t,n){if(n){var r=e.ownerDocument.importNode((new DOMParser).parseFromString('<svg xmlns="'+m+'">'+t+"</svg>","application/xml").documentElement,!0);e.appendChild(r)}else e.innerHTML=t}function ee(e,t){if(e)for(var n;n=w.exec(e);)t(n[1].toLowerCase(),n[2]||n[3]||n[4])}function te(){return document.createDocumentFragment()}function ne(e,t,n){e.insertBefore(t,n.parentNode&&n)}function re(n){return Object.keys(n).reduce(function(e,t){return e+" "+t+": "+n[t]+";"},"")}function ie(e,t,n){if(e){var r,i=t(e,n);if(!1===i)return;for(e=e.firstChild;e;)r=e.nextSibling,ie(e,t,i),e=r}}var oe=Object.freeze({$$:Z,$:a,createDOMPlaceholder:G,mkEl:L,setAttr:T,toggleVisibility:Q,getAttr:J,remAttr:X,setInnerHTML:Y,walkAttrs:ee,createFrag:te,safeInsert:ne,styleObjectToString:re,walkNodes:ie});function ae(e){return D(e)||null===e}function se(e){return ae(e)||""===e}function ue(e){return typeof e===c}function le(e){return e&&typeof e===H}function ce(e){var t=e.ownerSVGElement;return!!t||null===t}function pe(e){return Array.isArray(e)||e instanceof Array}function fe(e){return A.test(e)}function de(e){return typeof e===o}var he=Object.freeze({isBlank:se,isFunction:ue,isObject:le,isSvg:ce,isWritable:F,isArray:pe,isBoolAttr:fe,isNil:ae,isString:de,isUndefined:D});function me(e,t){return-1!==e.indexOf(t)}function ge(e,t){for(var n=e?e.length:0,r=0;r<n;r++)t(e[r],r);return e}function ve(e,t){return e.slice(0,t.length)===t}var ye,_e=(ye=-1,function(){return++ye});function xe(e,t,n,r){return Object.defineProperty(e,t,K({value:n,enumerable:!1,writable:!1,configurable:!0},r)),e}function be(e){return e.replace(/-(\w)/g,function(e,t){return t.toUpperCase()})}function we(e){console&&console.warn&&console.warn(e)}var Ae=Object.freeze({contains:me,each:ge,getPropDescriptor:z,startsWith:ve,uid:_e,defineProperty:xe,objectCreate:q,extend:K,toCamel:be,warn:we});function Ne(e,t,n,r,i){var o=e[t],a=pe(o),s=!D(i);if(!o||o!==n)if(!o&&r)e[t]=[n];else if(o)if(a){var u=o.indexOf(n);if(u===i)return;-1!==u&&o.splice(u,1),s?o.splice(i,0,n):o.push(n)}else e[t]=[o,n];else e[t]=n}function Ce(e){return e.tagName&&$[J(e,p)||J(e,p)||e.tagName.toLowerCase()]}function Oe(e,t){var n=Ce(e),r=!t&&J(e,p);return r&&!U.hasExpr(r)?r:n?n.name:e.tagName.toLowerCase()}function ke(){return this.parent?K(q(this),this.parent):this}var Ee=/<yield\b/i,Le=/<yield\s*(?:\/>|>([\S\s]*?)<\/yield\s*>|>)/gi,Te=/<yield\s+to=['"]([^'">]*)['"]\s*>([\S\s]*?)<\/yield\s*>/gi,je=/<yield\s+from=['"]?([-\w]+)['"]?\s*(?:\/>|>([\S\s]*?)<\/yield\s*>)/gi,Me={tr:"tbody",th:"tr",td:"tr",col:"colgroup"},Se=E&&E<10?/^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?|opt(?:ion|group))$/:/^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/,Ie="div",Re="svg";function Pe(e,t,n){var r=e&&e.match(/^\s*<([-\w]+)/),i=r&&r[1].toLowerCase(),o=L(n?Re:Ie);return e=function(e,n){if(!Ee.test(e))return e;var r={};return n=n&&n.replace(Te,function(e,t,n){return r[t]=r[t]||n,""}).trim(),e.replace(je,function(e,t,n){return r[t]||n||""}).replace(Le,function(e,t){return n||t||""})}(e,t),Se.test(i)?o=function(e,t,n){var r="o"===n[0],i=r?"select>":"table>";if(e.innerHTML="<"+i+t.trim()+"</"+i,i=e.firstChild,r)i.selectedIndex=-1;else{var o=Me[n];o&&1===i.childElementCount&&(i=a(o,i))}return i}(o,e,i):Y(o,e,n),o}var Ve=/^on/;function $e(e){for(var t=e;t.__.isAnonymous&&t.parent;)t=t.parent;return t}function He(e,t,n,r){var i,o=function(e,t,n){var r=this.__.parent,i=this.__.item;if(!i)for(;r&&!i;)i=r.__.item,r=r.__.parent;if(F(n,"currentTarget")&&(n.currentTarget=e),F(n,"target")&&(n.target=n.srcElement),F(n,"which")&&(n.which=n.charCode||n.keyCode),n.item=i,t.call(this,n),W.autoUpdate&&!n.preventUpdate){var o=$e(this);o.isMounted&&o.update()}}.bind(r,n,t);n[e]=null,i=e.replace(b,""),me(r.__.listeners,n)||r.__.listeners.push(n),n[x]||(n[x]={}),n[x][e]&&n.removeEventListener(i,n[x][e]),n[x][e]=o,n.addEventListener(i,o,!1)}function Ue(e,t,n,r){var i=yt(e,t,n),o=t.tagName||Oe(t.root,!0),a=$e(r);return xe(i,"parent",a),i.__.parent=r,Ne(a.tags,o,i),a!==r&&Ne(r.tags,o,i),i}function Be(e,t,n,r){if(pe(e[t])){var i=e[t].indexOf(n);-1!==i&&e[t].splice(i,1),e[t].length?1!==e[t].length||r||(e[t]=e[t][0]):delete e[t]}else e[t]===n&&delete e[t]}function ze(e,t){var n,r,i=G(),o=G(),a=te();for(this.root.insertBefore(i,this.root.firstChild),this.root.appendChild(o),this.__.head=r=i,this.__.tail=o;r;)n=r.nextSibling,a.appendChild(r),this.__.virts.push(r),r=n;t?e.insertBefore(a,t.__.head):e.appendChild(a)}function De(e,t){if(t.parentNode){var n=te();ze.call(e,n),t.parentNode.replaceChild(n,t)}}function Fe(e){if(!this.root||!J(this.root,"virtualized")){var t,n,r=e.dom,i=(t=e.attr)?(t=t.replace(N,""),k[t]&&(t=k[t]),t):null,o=me([C,O],i),a=e.root&&"VIRTUAL"===e.root.tagName,s=this.__.isAnonymous,u=r&&(e.parent||r.parentNode),l=W.keepValueAttributes,c="style"===i,p="class"===i,f="value"===i;if(e._riot_id)e.__.wasCreated?e.update():(e.mount(),a&&De(e,e.root));else{if(e.update)return e.update();var d,h,m,g,v,y,_,x=o&&!s?ke.call(this):this,b=!se(n=U(e.expr,x)),w=le(n);if(w&&(p?n=U(JSON.stringify(n),this):c&&(n=re(n))),!e.attr||e.wasParsedOnce&&!1!==n&&(b||f&&(!f||l))||X(r,J(r,e.attr)?e.attr:i),e.bool&&(n=!!n&&i),e.isRtag)return h=this,m=n,v=(d=e).tag||d.dom._tag,y=(v?v.__:{}).head,_="VIRTUAL"===d.dom.tagName,void(v&&d.tagName===m?v.update():(v&&(_&&(g=G(),y.parentNode.insertBefore(g,y)),v.unmount(!0)),de(m)&&(d.impl=$[m],d.impl&&(d.tag=v=Ue(d.impl,{root:d.dom,parent:h,tagName:m},d.dom.innerHTML,h),ge(d.attrs,function(e){return T(v.root,e.name,e.value)}),d.tagName=m,v.mount(),_&&De(v,g||v.root),h.__.onUnmount=function(){var e=v.opts.dataIs;Be(v.parent.tags,e,v),Be(v.__.parent.tags,e,v),v.unmount()}))));if((!e.wasParsedOnce||e.value!==n)&&(e.value=n,e.wasParsedOnce=!0,!w||p||c||o)){if(b||(n=""),!i)return n+="",void(u&&("TEXTAREA"===(e.parent=u).tagName?(u.value=n,E||(r.nodeValue=n)):r.nodeValue=n));switch(!0){case ue(n):A=i,Ve.test(A)&&He(i,n,r,this);break;case o:Q(r,i===O?!n:n);break;default:e.bool&&(r[i]=n),f&&r.value!==n?r.value=n:b&&!1!==n&&T(r,i,n),c&&r.hidden&&Q(r,!1)}var A}}}}function Ke(e){ge(e,Fe.bind(this))}function qe(e,t,n,r,i){if(!e||!n){var o=e?ke.call(this):t||this;ge(i,function(e){e.expr&&Fe.call(o,e.expr),r[be(e.name).replace(N,"")]=e.expr?e.expr.value:e.value})}}function We(e){if(e)return e.filter(function(e){return!/[^-\w]/.test(e)}).reduce(function(e,t){var n=t.trim().toLowerCase();return e+",["+p+'="'+n+'"]'},"");var t=Object.keys($);return t+We(t)}function Ze(e,t){var n=this,r=n.name,i=n.tmpl,o=n.css,a=n.attrs,s=n.onCreate;return $[r]||(Ge(r,i,o,a,s),$[r].class=this.constructor),_t(e,r,t,this),o&&R.inject(),this}function Ge(e,t,n,r,i){return ue(r)&&(i=r,/^[\w-]+\s?=/.test(n)?(r=n,n=""):r=""),n&&(ue(n)?i=n:R.add(n,e)),e=e.toLowerCase(),$[e]={name:e,tmpl:t,attrs:r,fn:i},e}function Qe(e,t,n,r,i){return n&&R.add(n,e),$[e]={name:e,tmpl:t,attrs:r,fn:i},e}function Je(e,i,o){var t,n,a=[];if(R.inject(),(le(i)||ue(i))&&(o=i,i=0),t=de(e)?(e="*"===e?n=We():e+We(e.split(/, */)))?Z(e):[]:e,"*"===i){if(i=n||We(),t.tagName)t=Z(i,t);else{var r=[];ge(t,function(e){return r.push(Z(i,e))}),t=r}i=0}return function e(t){if(t.tagName){var n,r=J(t,p);i&&r!==i&&T(t,p,r=i),(n=_t(t,r||t.tagName.toLowerCase(),ue(o)?o():o))&&a.push(n)}else t.length&&ge(t,e)}(t),a}var Xe={},Ye=Xe[_]={},et=0;function tt(e,t,n){if(le(e))tt("__"+et+++"__",e,!0);else{var r=n?Ye:Xe;if(!t){if(D(r[e]))throw new Error("Unregistered mixin: "+e);return r[e]}r[e]=ue(t)?K(t.prototype,r[e]||{})&&t:K(r[e]||{},t)}}function nt(){return ge(y,function(e){return e.update()})}function rt(e){return R.remove(e),delete $[e]}var it=Object.freeze({Tag:Ze,tag:Ge,tag2:Qe,mount:Je,mixin:tt,update:nt,unregister:rt,version:"v3.13.2"});function ot(e,t){var n,r=this.parent;r&&(pe(n=r.tags[e])?n.splice(t,0,n.splice(n.indexOf(this),1)[0]):Ne(r.tags,e,this))}function at(e,t){for(var n,r=this.__.head,i=te();r;)if(n=r.nextSibling,i.appendChild(r),(r=n)===this.__.tail){i.appendChild(r),e.insertBefore(i,t.__.head);break}}function st(e,t,n){var r={};return r[e.key]=t,e.pos&&(r[e.pos]=n),r}function ut(e,t){e.splice(t,1),this.unmount(),Be(this.parent,this,this.__.tagName,!0)}function lt(A,N,C){var O=typeof J(A,r)!==o||X(A,r),k=J(A,i),E=!!k&&U.hasExpr(k),L=Oe(A),T=$[L],e=A.parentNode,t=G(),j=Ce(A),M=J(A,f),S=[],I=A.innerHTML,R=!$[L],P="VIRTUAL"===A.tagName,V=[];return X(A,d),X(A,i),(C=U.loopKeys(C)).isLoop=!0,M&&X(A,f),e.insertBefore(t,A),e.removeChild(A),C.update=function(){C.value=U(C.val,N);var g=C.value,v=te(),y=!pe(g)&&!de(g),_=t.parentNode,x=[],b=y&&!!g;if(_){y&&(g=g?Object.keys(g).map(function(e){return st(C,g[e],e)}):[]);var w=0;ge(g,function(e,t){var n=t-w,r=!b&&C.key?st(C,e,t):e;if(!M||U(M,K(q(N),r))){var i,o,a,s,u=(o=e,a=r,s=E,(i=k)?s?U(i,a):o[i]:o),l=!y&&O&&typeof e===H||k,c=V.indexOf(u),p=-1===c,f=!p&&l?c:n,d=S[f],h=n>=V.length,m=l&&p||!l&&!d||!S[n];m?((d=yt(T,{parent:N,isLoop:!0,isAnonymous:R,tagName:L,root:A.cloneNode(R),item:r,index:n},I)).mount(),h?function(e,t){t?ze.call(this,e):e.appendChild(this.root)}.apply(d,[v||_,P]):function(e,t,n){n?ze.apply(this,[e,t]):ne(e,this.root,t.root)}.apply(d,[_,S[n],P]),h||V.splice(n,0,r),S.splice(n,0,d),j&&Ne(N.tags,L,d,!0)):f!==n&&l&&((k||me(g,V[f]))&&(function(e,t,n){n?at.apply(this,[e,t]):ne(e,this.root,t.root)}.apply(d,[_,S[n],P]),S.splice(n,0,S.splice(f,1)[0]),V.splice(n,0,V.splice(f,1)[0])),C.pos&&(d[C.pos]=n),!j&&d.tags&&function(t){var n=this;ge(Object.keys(this.tags),function(e){ot.apply(n.tags[e],[e,t])})}.call(d,n)),K(d.__,{item:r,index:n,parent:N}),x[n]=u,m||d.update(r)}else w++}),function(e,t,n){for(var r=t.length,i=e.length-n;i<r;)ut.apply(t[--r],[t,r])}(g,S,w),V=x.slice(),_.insertBefore(v,t)}},C.unmount=function(){ge(S,function(e){e.unmount()})},C}var ct={init:function(e,t,n,r){return this.dom=e,this.attr=n,this.rawValue=r,this.parent=t,this.hasExp=U.hasExpr(r),this},update:function(){var e=this.value,t=this.parent&&$e(this.parent),n=this.dom.__ref||this.tag||this.dom;this.value=this.hasExp?U(this.rawValue,this.parent):this.rawValue,!se(e)&&t&&Be(t.refs,e,n),!se(this.value)&&de(this.value)?(t&&Ne(t.refs,this.value,n,null,this.parent.__.index),this.value!==e&&T(this.dom,this.attr,this.value)):X(this.dom,this.attr),this.dom.__ref||(this.dom.__ref=n)},unmount:function(){var e=this.tag||this.dom,t=this.parent&&$e(this.parent);!se(this.value)&&t&&Be(t.refs,this.value,e)}};function pt(e,t,n,r){return q(ct).init(e,t,n,r)}function ft(e){ge(e,function(e){e.unmount?e.unmount(!0):e.tagName?e.tag.unmount(!0):e.unmount&&e.unmount()})}var dt={init:function(e,t,n){X(e,f),K(this,{tag:t,expr:n,stub:G(),pristine:e});var r=e.parentNode;return r.insertBefore(this.stub,e),r.removeChild(e),this},update:function(){this.value=U(this.expr,this.tag),this.stub.parentNode&&(this.value&&!this.current?(this.current=this.pristine.cloneNode(!0),this.stub.parentNode.insertBefore(this.current,this.stub),this.expressions=mt.apply(this.tag,[this.current,!0])):!this.value&&this.current&&(this.unmount(),this.current=null,this.expressions=[]),this.value&&Ke.call(this.tag,this.expressions))},unmount:function(){this.current&&(this.current._tag?this.current._tag.unmount():this.current.parentNode&&this.current.parentNode.removeChild(this.current)),ft(this.expressions||[])}};function ht(e,t,n){return q(dt).init(e,t,n)}function mt(s,u){var l=this,c=[];return ie(s,function(e){var t,n,r=e.nodeType;if((u||e!==s)&&(3===r&&"STYLE"!==e.parentNode.tagName&&U.hasExpr(e.nodeValue)&&c.push({dom:e,expr:e.nodeValue}),1===r)){var i="VIRTUAL"===e.tagName;if(t=J(e,d))return i&&T(e,"loopVirtual",!0),c.push(lt(e,l,t)),!1;if(t=J(e,f))return c.push(ht(e,l,t)),!1;if((t=J(e,p))&&U.hasExpr(t))return c.push({isRtag:!0,expr:t,dom:e,attrs:[].slice.call(e.attributes)}),!1;if(n=Ce(e),i&&(J(e,"virtualized")&&e.parentElement.removeChild(e),n||J(e,"virtualized")||J(e,"loopVirtual")||(n={tmpl:e.outerHTML})),n&&(e!==s||u)){var o=J(e,p);if(!i||o)return o&&i&&we("Virtual tags shouldn't be used together with the \""+p+'" attribute - https://github.com/riot/riot/issues/2511'),c.push(Ue(n,{root:e,parent:l},e.innerHTML,l)),!1;T(e,"virtualized",!0);var a=yt({tmpl:e.outerHTML},{root:e,parent:l},e.innerHTML);c.push(a)}gt.apply(l,[e,e.attributes,function(e,t){t&&c.push(t)}])}}),c}function gt(i,e,o){var a=this;ge(e,function(e){if(!e)return!1;var t,n=e.name,r=fe(n);me(u,n)&&i.tagName.toLowerCase()!==s?t=pt(i,a,n,e.value):U.hasExpr(e.value)&&(t={dom:i,expr:e.value,attr:n,bool:r}),o(e,t)})}function vt(e){var t=this.__,n=t.isAnonymous,r=t.skipAnonymous;xe(this,"isMounted",e),n&&r||(e?this.trigger("mount"):(this.trigger("unmount"),this.off("*"),this.__.wasCreated=!1))}function yt(e,t,n){void 0===e&&(e={}),void 0===t&&(t={});var r,s=t.context||{},i=t.opts||{},o=t.parent,a=t.isLoop,u=!!t.isAnonymous,l=W.skipAnonymousTags&&u,c=t.item,p=t.index,f=e.tmpl,d=[],h=t.root,m=t.tagName||Oe(h),g="virtual"===m,v=!g&&!f;return r=v||a&&u?h:(g||(h.innerHTML=""),Pe(f,n,ce(h))),l||B(s),e.name&&h._tag&&h._tag.unmount(!0),xe(s,"__",{impl:e,root:h,skipAnonymous:l,implAttrs:[],isAnonymous:u,instAttrs:[],innerHTML:n,tagName:m,index:p,isLoop:a,isInline:v,item:c,parent:o,listeners:[],virts:[],wasCreated:!1,tail:null,head:null}),[["isMounted",!1],["_riot_id",_e()],["root",h],["opts",i,{writable:!0,enumerable:!0}],["parent",o||null],["tags",{}],["refs",{}],["update",function(e){return n=e,r=d,i=(t=s).__,o={},a=t.isMounted&&!i.skipAnonymous,i.isAnonymous&&i.parent&&K(t,i.parent),K(t,n),qe.apply(t,[i.isLoop,i.parent,i.isAnonymous,o,i.instAttrs]),a&&t.isMounted&&ue(t.shouldUpdate)&&!t.shouldUpdate(n,o)||(K(t.opts,o),a&&t.trigger("update",n),Ke.call(t,r),a&&t.trigger("updated")),t;var t,n,r,i,o,a}],["mixin",function(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];return function(a){for(var e=[],t=arguments.length-1;0<t--;)e[t]=arguments[t+1];return ge(e,function(e){var r,t,n=[],i=["init","__proto__"];e=de(e)?tt(e):e,r=ue(e)?new e:e;for(var o=Object.getPrototypeOf(r);n=n.concat(Object.getOwnPropertyNames(t||r)),t=Object.getPrototypeOf(t||r););ge(n,function(e){if(!me(i,e)){var t=z(r,e)||z(o,e),n=t&&(t.get||t.set);!a.hasOwnProperty(e)&&n?Object.defineProperty(a,e,t):a[e]=ue(r[e])?r[e].bind(a):r[e]}}),r.init&&r.init.bind(a)(a.opts)}),a}.apply(void 0,[s].concat(e))}],["mount",function(){return function(n,e,r,t){var i=n.__,o=i.root;o._tag=n,gt.apply(i.parent,[o,o.attributes,function(e,t){!i.isAnonymous&&ct.isPrototypeOf(t)&&(t.tag=n),e.expr=t,i.instAttrs.push(e)}]),ee(i.impl.attrs,function(e,t){i.implAttrs.push({name:e,value:t})}),gt.apply(n,[o,i.implAttrs,function(e,t){t?r.push(t):T(o,e.name,e.value)}]),qe.apply(n,[i.isLoop,i.parent,i.isAnonymous,t,i.instAttrs]);var a=tt(_);if(a&&!i.skipAnonymous)for(var s in a)a.hasOwnProperty(s)&&n.mixin(a[s]);if(i.impl.fn&&i.impl.fn.call(n,t),i.skipAnonymous||n.trigger("before-mount"),ge(mt.apply(n,[e,i.isAnonymous]),function(e){return r.push(e)}),n.update(i.item),!i.isAnonymous&&!i.isInline)for(;e.firstChild;)o.appendChild(e.firstChild);if(xe(n,"root",o),!i.skipAnonymous&&n.parent){var u=$e(n.parent);u.one(u.isMounted?"updated":"mount",function(){vt.call(n,!0)})}else vt.call(n,!0);return n.__.wasCreated=!0,n}(s,r,d,i)}],["unmount",function(e){return function(t,e,n){var r=t.__,i=r.root,o=y.indexOf(t),a=i.parentNode;if(r.skipAnonymous||t.trigger("before-unmount"),ee(r.impl.attrs,function(e){ve(e,N)&&(e=e.slice(N.length)),X(i,e)}),t.__.listeners.forEach(function(t){Object.keys(t[x]).forEach(function(e){t.removeEventListener(e,t[x][e])})}),-1!==o&&y.splice(o,1),r.parent&&!r.isAnonymous){var s=$e(r.parent);r.isVirtual?Object.keys(t.tags).forEach(function(e){return Be(s.tags,e,t.tags[e])}):Be(s.tags,r.tagName,t)}return t.__.virts&&ge(t.__.virts,function(e){e.parentNode&&e.parentNode.removeChild(e)}),ft(n),ge(r.instAttrs,function(e){return e.expr&&e.expr.unmount&&e.expr.unmount()}),e?Y(i,""):a&&a.removeChild(i),r.onUnmount&&r.onUnmount(),t.isMounted||vt.call(t,!0),vt.call(t,!1),delete i._tag,t}(s,e,d)}]].reduce(function(e,t){var n=t[0],r=t[1],i=t[2];return xe(s,n,r,i),e},K(s,c))}function _t(e,t,n,r){var i,o=$[t],a=$[t].class,s=r||(a?q(a.prototype):{}),u=e._innerHTML=e._innerHTML||e.innerHTML,l=K({root:e,opts:n,context:s},{parent:n?n.parent:null});return o&&e&&(i=yt(o,l,u)),i&&i.mount&&(i.mount(!0),me(y,i)||y.push(i)),i}var xt=Object.freeze({arrayishAdd:Ne,getTagName:Oe,inheritParentProps:ke,mountTo:_t,selectTags:We,arrayishRemove:Be,getTag:Ce,initChildTag:Ue,moveChildTag:ot,makeReplaceVirtual:De,getImmediateCustomParentTag:$e,makeVirtual:ze,moveVirtual:at,unmountAll:ft,createIfDirective:ht,createRefDirective:pt}),bt=W,wt={tmpl:U,brackets:V,styleManager:R,vdom:y,styleNode:R.styleNode,dom:oe,check:he,misc:Ae,tags:xt},At=Ze,Nt=Ge,Ct=Qe,Ot=Je,kt=tt,Et=nt,Lt=rt,Tt=B,jt=K({},it,{observable:B,settings:bt,util:wt});e.settings=bt,e.util=wt,e.Tag=At,e.tag=Nt,e.tag2=Ct,e.mount=Ot,e.mixin=kt,e.update=Et,e.unregister=Lt,e.version="v3.13.2",e.observable=Tt,e.default=jt,Object.defineProperty(e,"__esModule",{value:!0})},"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.riot={});
diff --git a/dist/firephp.build/scripts/devtools/dist/editor.rep.css b/dist/firephp.build/scripts/devtools/dist/editor.rep.css
new file mode 100644
index 0000000..bddfc72
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/editor.rep.css
@@ -0,0 +1,38 @@
+
+
+    DIV[data-is="tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153"] TABLE {
+        height: 100vh;
+        width: 100vw;
+    }
+
+    DIV[data-is="tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153"] TD.menu {
+        background-color: #efefef;
+        border-bottom: 1px solid #bcbcbc;
+        padding: 3px;
+        padding-left: 10px;
+        padding-right: 10px;
+    }
+
+    DIV[data-is="tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153"] TD.menu-right {
+        text-align: right;
+    }
+
+    DIV[data-is="tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153"] TD.filetree {
+        border-right: 1px solid #bcbcbc;
+        padding: 10px;
+        vertical-align: top;
+        height: 100%;
+        min-width: 20%;
+    }
+
+    DIV[data-is="tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153"] TD.editor {
+        min-width: 80%;
+        vertical-align: top;
+        position: relative;
+        box-sizing: border-box;
+    }
+
+    DIV[data-is="tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153"] DIV.CodeMirror {
+        position: absolute;
+    }
+
diff --git a/dist/firephp.build/scripts/devtools/dist/editor.rep.js b/dist/firephp.build/scripts/devtools/dist/editor.rep.js
new file mode 100644
index 0000000..cb397a5
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/editor.rep.js
@@ -0,0 +1,1793 @@
+PINF.bundle("", function(__require) {
+	__require.memoize("/main.js", function (_require, _exports, _module) {
+var bundle = { require: _require, exports: _exports, module: _module };
+var exports = undefined;
+var module = undefined;
+var define = function (deps, init) {
+_module.exports = init();
+}; define.amd = true;
+       var pmodule = bundle.module;
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mainModule = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+var root = require('./_root');
+
+/** Built-in value references. */
+var Symbol = root.Symbol;
+
+module.exports = Symbol;
+
+},{"./_root":6}],2:[function(require,module,exports){
+var Symbol = require('./_Symbol'),
+    getRawTag = require('./_getRawTag'),
+    objectToString = require('./_objectToString');
+
+/** `Object#toString` result references. */
+var nullTag = '[object Null]',
+    undefinedTag = '[object Undefined]';
+
+/** Built-in value references. */
+var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+
+/**
+ * The base implementation of `getTag` without fallbacks for buggy environments.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+function baseGetTag(value) {
+  if (value == null) {
+    return value === undefined ? undefinedTag : nullTag;
+  }
+  return (symToStringTag && symToStringTag in Object(value))
+    ? getRawTag(value)
+    : objectToString(value);
+}
+
+module.exports = baseGetTag;
+
+},{"./_Symbol":1,"./_getRawTag":4,"./_objectToString":5}],3:[function(require,module,exports){
+(function (global){
+/** Detect free variable `global` from Node.js. */
+var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+
+module.exports = freeGlobal;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],4:[function(require,module,exports){
+var Symbol = require('./_Symbol');
+
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var nativeObjectToString = objectProto.toString;
+
+/** Built-in value references. */
+var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+
+/**
+ * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the raw `toStringTag`.
+ */
+function getRawTag(value) {
+  var isOwn = hasOwnProperty.call(value, symToStringTag),
+      tag = value[symToStringTag];
+
+  try {
+    value[symToStringTag] = undefined;
+    var unmasked = true;
+  } catch (e) {}
+
+  var result = nativeObjectToString.call(value);
+  if (unmasked) {
+    if (isOwn) {
+      value[symToStringTag] = tag;
+    } else {
+      delete value[symToStringTag];
+    }
+  }
+  return result;
+}
+
+module.exports = getRawTag;
+
+},{"./_Symbol":1}],5:[function(require,module,exports){
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var nativeObjectToString = objectProto.toString;
+
+/**
+ * Converts `value` to a string using `Object.prototype.toString`.
+ *
+ * @private
+ * @param {*} value The value to convert.
+ * @returns {string} Returns the converted string.
+ */
+function objectToString(value) {
+  return nativeObjectToString.call(value);
+}
+
+module.exports = objectToString;
+
+},{}],6:[function(require,module,exports){
+var freeGlobal = require('./_freeGlobal');
+
+/** Detect free variable `self`. */
+var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+/** Used as a reference to the global object. */
+var root = freeGlobal || freeSelf || Function('return this')();
+
+module.exports = root;
+
+},{"./_freeGlobal":3}],7:[function(require,module,exports){
+var isObject = require('./isObject'),
+    now = require('./now'),
+    toNumber = require('./toNumber');
+
+/** Error message constants. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/* Built-in method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max,
+    nativeMin = Math.min;
+
+/**
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked. The debounced function comes with a `cancel` method to cancel
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
+ * Provide `options` to indicate whether `func` should be invoked on the
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+ * with the last arguments provided to the debounced function. Subsequent
+ * calls to the debounced function return the result of the last `func`
+ * invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the debounced function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.debounce` and `_.throttle`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} [wait=0] The number of milliseconds to delay.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=false]
+ *  Specify invoking on the leading edge of the timeout.
+ * @param {number} [options.maxWait]
+ *  The maximum time `func` is allowed to be delayed before it's invoked.
+ * @param {boolean} [options.trailing=true]
+ *  Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // Avoid costly calculations while the window size is in flux.
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
+ *   'leading': true,
+ *   'trailing': false
+ * }));
+ *
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', debounced);
+ *
+ * // Cancel the trailing debounced invocation.
+ * jQuery(window).on('popstate', debounced.cancel);
+ */
+function debounce(func, wait, options) {
+  var lastArgs,
+      lastThis,
+      maxWait,
+      result,
+      timerId,
+      lastCallTime,
+      lastInvokeTime = 0,
+      leading = false,
+      maxing = false,
+      trailing = true;
+
+  if (typeof func != 'function') {
+    throw new TypeError(FUNC_ERROR_TEXT);
+  }
+  wait = toNumber(wait) || 0;
+  if (isObject(options)) {
+    leading = !!options.leading;
+    maxing = 'maxWait' in options;
+    maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
+    trailing = 'trailing' in options ? !!options.trailing : trailing;
+  }
+
+  function invokeFunc(time) {
+    var args = lastArgs,
+        thisArg = lastThis;
+
+    lastArgs = lastThis = undefined;
+    lastInvokeTime = time;
+    result = func.apply(thisArg, args);
+    return result;
+  }
+
+  function leadingEdge(time) {
+    // Reset any `maxWait` timer.
+    lastInvokeTime = time;
+    // Start the timer for the trailing edge.
+    timerId = setTimeout(timerExpired, wait);
+    // Invoke the leading edge.
+    return leading ? invokeFunc(time) : result;
+  }
+
+  function remainingWait(time) {
+    var timeSinceLastCall = time - lastCallTime,
+        timeSinceLastInvoke = time - lastInvokeTime,
+        timeWaiting = wait - timeSinceLastCall;
+
+    return maxing
+      ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
+      : timeWaiting;
+  }
+
+  function shouldInvoke(time) {
+    var timeSinceLastCall = time - lastCallTime,
+        timeSinceLastInvoke = time - lastInvokeTime;
+
+    // Either this is the first call, activity has stopped and we're at the
+    // trailing edge, the system time has gone backwards and we're treating
+    // it as the trailing edge, or we've hit the `maxWait` limit.
+    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
+      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
+  }
+
+  function timerExpired() {
+    var time = now();
+    if (shouldInvoke(time)) {
+      return trailingEdge(time);
+    }
+    // Restart the timer.
+    timerId = setTimeout(timerExpired, remainingWait(time));
+  }
+
+  function trailingEdge(time) {
+    timerId = undefined;
+
+    // Only invoke if we have `lastArgs` which means `func` has been
+    // debounced at least once.
+    if (trailing && lastArgs) {
+      return invokeFunc(time);
+    }
+    lastArgs = lastThis = undefined;
+    return result;
+  }
+
+  function cancel() {
+    if (timerId !== undefined) {
+      clearTimeout(timerId);
+    }
+    lastInvokeTime = 0;
+    lastArgs = lastCallTime = lastThis = timerId = undefined;
+  }
+
+  function flush() {
+    return timerId === undefined ? result : trailingEdge(now());
+  }
+
+  function debounced() {
+    var time = now(),
+        isInvoking = shouldInvoke(time);
+
+    lastArgs = arguments;
+    lastThis = this;
+    lastCallTime = time;
+
+    if (isInvoking) {
+      if (timerId === undefined) {
+        return leadingEdge(lastCallTime);
+      }
+      if (maxing) {
+        // Handle invocations in a tight loop.
+        clearTimeout(timerId);
+        timerId = setTimeout(timerExpired, wait);
+        return invokeFunc(lastCallTime);
+      }
+    }
+    if (timerId === undefined) {
+      timerId = setTimeout(timerExpired, wait);
+    }
+    return result;
+  }
+  debounced.cancel = cancel;
+  debounced.flush = flush;
+  return debounced;
+}
+
+module.exports = debounce;
+
+},{"./isObject":8,"./now":11,"./toNumber":12}],8:[function(require,module,exports){
+/**
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+function isObject(value) {
+  var type = typeof value;
+  return value != null && (type == 'object' || type == 'function');
+}
+
+module.exports = isObject;
+
+},{}],9:[function(require,module,exports){
+/**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+function isObjectLike(value) {
+  return value != null && typeof value == 'object';
+}
+
+module.exports = isObjectLike;
+
+},{}],10:[function(require,module,exports){
+var baseGetTag = require('./_baseGetTag'),
+    isObjectLike = require('./isObjectLike');
+
+/** `Object#toString` result references. */
+var symbolTag = '[object Symbol]';
+
+/**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+function isSymbol(value) {
+  return typeof value == 'symbol' ||
+    (isObjectLike(value) && baseGetTag(value) == symbolTag);
+}
+
+module.exports = isSymbol;
+
+},{"./_baseGetTag":2,"./isObjectLike":9}],11:[function(require,module,exports){
+var root = require('./_root');
+
+/**
+ * Gets the timestamp of the number of milliseconds that have elapsed since
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Date
+ * @returns {number} Returns the timestamp.
+ * @example
+ *
+ * _.defer(function(stamp) {
+ *   console.log(_.now() - stamp);
+ * }, _.now());
+ * // => Logs the number of milliseconds it took for the deferred invocation.
+ */
+var now = function() {
+  return root.Date.now();
+};
+
+module.exports = now;
+
+},{"./_root":6}],12:[function(require,module,exports){
+var isObject = require('./isObject'),
+    isSymbol = require('./isSymbol');
+
+/** Used as references for various `Number` constants. */
+var NAN = 0 / 0;
+
+/** Used to match leading and trailing whitespace. */
+var reTrim = /^\s+|\s+$/g;
+
+/** Used to detect bad signed hexadecimal string values. */
+var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+
+/** Used to detect binary string values. */
+var reIsBinary = /^0b[01]+$/i;
+
+/** Used to detect octal string values. */
+var reIsOctal = /^0o[0-7]+$/i;
+
+/** Built-in method references without a dependency on `root`. */
+var freeParseInt = parseInt;
+
+/**
+ * Converts `value` to a number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
+ * @example
+ *
+ * _.toNumber(3.2);
+ * // => 3.2
+ *
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3.2');
+ * // => 3.2
+ */
+function toNumber(value) {
+  if (typeof value == 'number') {
+    return value;
+  }
+  if (isSymbol(value)) {
+    return NAN;
+  }
+  if (isObject(value)) {
+    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+    value = isObject(other) ? (other + '') : other;
+  }
+  if (typeof value != 'string') {
+    return value === 0 ? value : +value;
+  }
+  value = value.replace(reTrim, '');
+  var isBinary = reIsBinary.test(value);
+  return (isBinary || reIsOctal.test(value))
+    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+    : (reIsBadHex.test(value) ? NAN : +value);
+}
+
+module.exports = toNumber;
+
+},{"./isObject":8,"./isSymbol":10}],13:[function(require,module,exports){
+"use strict";
+
+exports.main = function (JSONREP, node, options) {
+  return JSONREP.makeRep2({
+    "config": {
+      "node": node
+    },
+    "code": function (context, options) {
+      var JSONREP = this;
+      return {
+        html: "<div></div>",
+        "on": {
+          "mount": function (el) {
+            if ("1" === "1") {
+              riot.util.styleManager.inject = function () {
+                if (!options) return;
+                JSONREP.loadStyle(options.renderer.uri + '.css');
+              };
+            } else {
+              riot.util.styleManager.add = function (cssText, name) {
+                if (!cssText) {
+                  return;
+                }
+
+                if (window.document.createStyleSheet) {
+                  var sheet = window.document.createStyleSheet();
+                  sheet.cssText = cssText;
+                } else {
+                  var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
+                  style.appendChild(window.document.createTextNode(cssText));
+                  var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+                  head.appendChild(style);
+                }
+              };
+            }
+
+            riot.tag('raw', '<div></div>', function (opts) {
+              this.set = function () {
+                this.root.childNodes[0].innerHTML = opts.html;
+              };
+
+              this.on('update', this.set);
+              this.on('mount', this.set);
+            });
+            riot.tag2('tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153', '<table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0"> <tr> <td class="menu"> </td> <td class="menu menu-right"> <button onclick="{triggerClose}">Close</button> </td> </tr> <tr> <td class="filetree" width="20%"> <div class="filetree"> </div> </td> <td class="editor" width="80%"> <textarea class="editor"></textarea> </td> </tr> </table>', '', '', function (opts) {
+              var COMPONENT = require("./component");
+
+              var tag = this;
+              var editor = null;
+              var comp = COMPONENT.for({
+                browser: window.crossbrowser,
+                handlers: {
+                  "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/request/0": function (message) {
+                    console.log("MESSAGE in EDITOR ::::", message);
+                    var filetreeTag = tag.root.querySelector('DIV.filetree');
+                    WINDOW.FC.fireconsole.repRenderer.renderNodeInto({
+                      meta: {
+                        "lang": "elements",
+                        "lang.type": "pathtree"
+                      },
+                      value: message.data
+                    }, filetreeTag);
+                    window.FC.on("click", function (args) {
+                      console.log("EDITOR FC CLICK", args);
+
+                      if (args.rep === "insight.domplate.reps/elements/pathtree") {
+                        comp.loadFile(args.node.value.id);
+                      }
+                    });
+
+                    if (editor) {
+                      editor.getDoc().setValue(JSON.stringify({
+                        "foo": "bar"
+                      }, null, 4));
+                    }
+                  }
+                }
+              });
+              comp.on("changed.context", function (context) {
+                comp.contextChangeAcknowledged();
+              });
+              comp.on("message", function (message) {
+                if (message.action === "show-file") {
+                  console.log("EDITOR show-file::", message);
+                  editor.getDoc().setValue(message.args.content);
+
+                  if (typeof message.args.line !== "undefined") {
+                    console.log("set cursor to", message.args.line || 0);
+                    setTimeout(function () {
+                      editor.getDoc().setCursor(message.args.line - 1 || 0, 0);
+                      editor.scrollIntoView(null, 100);
+                    }, 10);
+                  }
+                } else if (message.event === "editor") {
+                  var forceEditor = false;
+
+                  if (typeof message.value !== 'undefined') {
+                    forceEditor = message.value;
+                  } else {
+                    forceEditor = true;
+                  }
+
+                  if (forceEditor) {
+                    setTimeout(function () {
+                      syncSize();
+                    }, 100);
+                  }
+
+                  console.log("SHOW EDITOR::", message);
+
+                  if (message.args && message.args.show && message.args.show.file) {
+                    comp.loadFile(message.args.show.file, message.args.show.line || 0);
+                  }
+                }
+              });
+
+              function syncSize() {
+                if (!editor) {
+                  return;
+                }
+
+                var editorTableTag = tag.root.querySelector("TD.editor");
+                editor.setSize(editorTableTag.getBoundingClientRect().width, editorTableTag.getBoundingClientRect().height);
+                editor.refresh();
+              }
+
+              tag.on("mount", function () {
+                editor = CodeMirror.fromTextArea(tag.root.querySelector("TEXTAREA.editor"), {
+                  value: "",
+                  lineNumbers: true,
+                  mode: "application/x-httpd-php",
+                  autofocus: true,
+                  autoRefresh: true,
+                  styleActiveLine: true,
+                  styleActiveSelected: true
+                });
+                window.addEventListener("resize", function () {
+                  syncSize();
+                }, false);
+                syncSize();
+              });
+
+              tag.triggerClose = function (event) {
+                comp.hideView("editor");
+              };
+            });
+            riot.mount(el, 'tag_e7ce2eb402dcaff08a8df09ee619bef75dd33153', context);
+          }
+        }
+      };
+    }
+  }, options);
+};
+},{"./component":14}],14:[function(require,module,exports){
+(function (setImmediate){
+"use strict";
+
+var EVENTS = require("events");
+
+var DEBOUNCE = require('lodash/debounce');
+
+exports.for = function (ctx) {
+  var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
+  events.currentContext = null;
+  setImmediate(function () {
+    onContextMessage(null);
+    broadcastCurrentContext();
+  });
+  var contextChangeAcknowledged = false;
+
+  events.contextChangeAcknowledged = function () {
+    contextChangeAcknowledged = true;
+  };
+
+  function onContextMessage(context) {
+    if (context !== events.currentContext && (!events.currentContext || !context || context.pageUid !== events.currentContext.pageUid)) {
+      events.currentContext = context;
+      contextChangeAcknowledged = false;
+    }
+
+    if (!contextChangeAcknowledged) {
+      events.emit("changed.context", events.currentContext);
+    }
+  }
+
+  events.handleBroadcastMessage = function (message) {
+    try {
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
+
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
+        }
+      }
+    } catch (err) {
+      console.error(err);
+    }
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
+  ctx.browser.storage.onChanged.addListener(function (changes, area) {
+    try {
+      if (!events.currentContext) {
+        return;
+      }
+
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
+
+      for (var item of Object.keys(changes)) {
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
+
+        if (item.substring(0, prefix.length) === prefix) {
+          var name = item.substring(prefix.length);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
+        }
+      }
+    } catch (err) {
+      console.error(err);
+    }
+  });
+  var broadcastCurrentContext = DEBOUNCE(function () {
+    ctx.browser.runtime.sendMessage({
+      to: "broadcast",
+      event: "currentContext"
+    });
+  }, 250);
+
+  events.getSetting = async function (name) {
+    if (!events.currentContext) {
+      return Promise.resolve(null);
+    }
+
+    return events._getSettingForHostname(events.currentContext.hostname, name);
+  };
+
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (typeof ctx.browser === "undefined") {
+      return Promise.resolve(null);
+    }
+
+    var key = "hostname[" + hostname + "]." + name;
+    return ctx.browser.storage.local.get(key).then(function (value) {
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
+    });
+  };
+
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
+    if (!events.currentContext) {
+      throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
+    }
+
+    return events._setSettingForHostname(events.currentContext.hostname, name, value);
+  };
+
+  events._setSettingForHostname = async function (hostname, name, value) {
+    if (typeof ctx.browser === "undefined") {
+      return Promise.resolve(null);
+    }
+
+    return events._getSettingForHostname(hostname, name).then(function (existingValue) {
+      if (value === existingValue) {
+        return;
+      }
+
+      var obj = {};
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
+    });
+  };
+
+  events.getGlobalSetting = async function (name) {
+    if (typeof ctx.browser === "undefined") {
+      return null;
+    }
+
+    var defaultValue;
+
+    if (name === "reloadOnEnable") {
+      defaultValue = true;
+    }
+
+    return ctx.browser.storage.local.get(name).then(function (value) {
+      if (typeof value[name] === "undefined") {
+        if (typeof defaultValue !== "undefined") {
+          return defaultValue;
+        }
+
+        return null;
+      }
+
+      return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
+    });
+  };
+
+  events.setGlobalSetting = async function (name, value) {
+    if (typeof ctx.browser === "undefined") {
+      return null;
+    }
+
+    return events.getGlobalSetting(name).then(function (existingValue) {
+      if (value === existingValue) {
+        return;
+      }
+
+      var obj = {};
+      obj[name] = value;
+      globalSettings[name] = value;
+      return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
+    }).catch(function (err) {
+      console.error(err);
+      throw err;
+    });
+  };
+
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
+  };
+
+  events.reloadBrowser = function () {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "reload",
+      context: {
+        tabId: ctx.browser.devtools.inspectedWindow.tabId
+      }
+    });
+  };
+
+  events.clearConsole = function () {
+    ctx.browser.runtime.sendMessage({
+      to: "broadcast",
+      event: "clear"
+    });
+  };
+
+  events.showView = function (name, args) {
+    if (name === "manage") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "manage"
+      });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
+    }
+  };
+
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
+    }
+  };
+
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
+  return events;
+};
+}).call(this,require("timers").setImmediate)
+},{"events":15,"lodash/debounce":7,"timers":17}],15:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var objectCreate = Object.create || objectCreatePolyfill
+var objectKeys = Object.keys || objectKeysPolyfill
+var bind = Function.prototype.bind || functionBindPolyfill
+
+function EventEmitter() {
+  if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
+    this._events = objectCreate(null);
+    this._eventsCount = 0;
+  }
+
+  this._maxListeners = this._maxListeners || undefined;
+}
+module.exports = EventEmitter;
+
+// Backwards-compat with node 0.10.x
+EventEmitter.EventEmitter = EventEmitter;
+
+EventEmitter.prototype._events = undefined;
+EventEmitter.prototype._maxListeners = undefined;
+
+// By default EventEmitters will print a warning if more than 10 listeners are
+// added to it. This is a useful default which helps finding memory leaks.
+var defaultMaxListeners = 10;
+
+var hasDefineProperty;
+try {
+  var o = {};
+  if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
+  hasDefineProperty = o.x === 0;
+} catch (err) { hasDefineProperty = false }
+if (hasDefineProperty) {
+  Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
+    enumerable: true,
+    get: function() {
+      return defaultMaxListeners;
+    },
+    set: function(arg) {
+      // check whether the input is a positive number (whose value is zero or
+      // greater and not a NaN).
+      if (typeof arg !== 'number' || arg < 0 || arg !== arg)
+        throw new TypeError('"defaultMaxListeners" must be a positive number');
+      defaultMaxListeners = arg;
+    }
+  });
+} else {
+  EventEmitter.defaultMaxListeners = defaultMaxListeners;
+}
+
+// Obviously not all Emitters should be limited to 10. This function allows
+// that to be increased. Set to zero for unlimited.
+EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
+  if (typeof n !== 'number' || n < 0 || isNaN(n))
+    throw new TypeError('"n" argument must be a positive number');
+  this._maxListeners = n;
+  return this;
+};
+
+function $getMaxListeners(that) {
+  if (that._maxListeners === undefined)
+    return EventEmitter.defaultMaxListeners;
+  return that._maxListeners;
+}
+
+EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
+  return $getMaxListeners(this);
+};
+
+// These standalone emit* functions are used to optimize calling of event
+// handlers for fast cases because emit() itself often has a variable number of
+// arguments and can be deoptimized because of that. These functions always have
+// the same number of arguments and thus do not get deoptimized, so the code
+// inside them can execute faster.
+function emitNone(handler, isFn, self) {
+  if (isFn)
+    handler.call(self);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self);
+  }
+}
+function emitOne(handler, isFn, self, arg1) {
+  if (isFn)
+    handler.call(self, arg1);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self, arg1);
+  }
+}
+function emitTwo(handler, isFn, self, arg1, arg2) {
+  if (isFn)
+    handler.call(self, arg1, arg2);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self, arg1, arg2);
+  }
+}
+function emitThree(handler, isFn, self, arg1, arg2, arg3) {
+  if (isFn)
+    handler.call(self, arg1, arg2, arg3);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].call(self, arg1, arg2, arg3);
+  }
+}
+
+function emitMany(handler, isFn, self, args) {
+  if (isFn)
+    handler.apply(self, args);
+  else {
+    var len = handler.length;
+    var listeners = arrayClone(handler, len);
+    for (var i = 0; i < len; ++i)
+      listeners[i].apply(self, args);
+  }
+}
+
+EventEmitter.prototype.emit = function emit(type) {
+  var er, handler, len, args, i, events;
+  var doError = (type === 'error');
+
+  events = this._events;
+  if (events)
+    doError = (doError && events.error == null);
+  else if (!doError)
+    return false;
+
+  // If there is no 'error' event listener then throw.
+  if (doError) {
+    if (arguments.length > 1)
+      er = arguments[1];
+    if (er instanceof Error) {
+      throw er; // Unhandled 'error' event
+    } else {
+      // At least give some kind of context to the user
+      var err = new Error('Unhandled "error" event. (' + er + ')');
+      err.context = er;
+      throw err;
+    }
+    return false;
+  }
+
+  handler = events[type];
+
+  if (!handler)
+    return false;
+
+  var isFn = typeof handler === 'function';
+  len = arguments.length;
+  switch (len) {
+      // fast cases
+    case 1:
+      emitNone(handler, isFn, this);
+      break;
+    case 2:
+      emitOne(handler, isFn, this, arguments[1]);
+      break;
+    case 3:
+      emitTwo(handler, isFn, this, arguments[1], arguments[2]);
+      break;
+    case 4:
+      emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
+      break;
+      // slower
+    default:
+      args = new Array(len - 1);
+      for (i = 1; i < len; i++)
+        args[i - 1] = arguments[i];
+      emitMany(handler, isFn, this, args);
+  }
+
+  return true;
+};
+
+function _addListener(target, type, listener, prepend) {
+  var m;
+  var events;
+  var existing;
+
+  if (typeof listener !== 'function')
+    throw new TypeError('"listener" argument must be a function');
+
+  events = target._events;
+  if (!events) {
+    events = target._events = objectCreate(null);
+    target._eventsCount = 0;
+  } else {
+    // To avoid recursion in the case that type === "newListener"! Before
+    // adding it to the listeners, first emit "newListener".
+    if (events.newListener) {
+      target.emit('newListener', type,
+          listener.listener ? listener.listener : listener);
+
+      // Re-assign `events` because a newListener handler could have caused the
+      // this._events to be assigned to a new object
+      events = target._events;
+    }
+    existing = events[type];
+  }
+
+  if (!existing) {
+    // Optimize the case of one listener. Don't need the extra array object.
+    existing = events[type] = listener;
+    ++target._eventsCount;
+  } else {
+    if (typeof existing === 'function') {
+      // Adding the second element, need to change to array.
+      existing = events[type] =
+          prepend ? [listener, existing] : [existing, listener];
+    } else {
+      // If we've already got an array, just append.
+      if (prepend) {
+        existing.unshift(listener);
+      } else {
+        existing.push(listener);
+      }
+    }
+
+    // Check for listener leak
+    if (!existing.warned) {
+      m = $getMaxListeners(target);
+      if (m && m > 0 && existing.length > m) {
+        existing.warned = true;
+        var w = new Error('Possible EventEmitter memory leak detected. ' +
+            existing.length + ' "' + String(type) + '" listeners ' +
+            'added. Use emitter.setMaxListeners() to ' +
+            'increase limit.');
+        w.name = 'MaxListenersExceededWarning';
+        w.emitter = target;
+        w.type = type;
+        w.count = existing.length;
+        if (typeof console === 'object' && console.warn) {
+          console.warn('%s: %s', w.name, w.message);
+        }
+      }
+    }
+  }
+
+  return target;
+}
+
+EventEmitter.prototype.addListener = function addListener(type, listener) {
+  return _addListener(this, type, listener, false);
+};
+
+EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+
+EventEmitter.prototype.prependListener =
+    function prependListener(type, listener) {
+      return _addListener(this, type, listener, true);
+    };
+
+function onceWrapper() {
+  if (!this.fired) {
+    this.target.removeListener(this.type, this.wrapFn);
+    this.fired = true;
+    switch (arguments.length) {
+      case 0:
+        return this.listener.call(this.target);
+      case 1:
+        return this.listener.call(this.target, arguments[0]);
+      case 2:
+        return this.listener.call(this.target, arguments[0], arguments[1]);
+      case 3:
+        return this.listener.call(this.target, arguments[0], arguments[1],
+            arguments[2]);
+      default:
+        var args = new Array(arguments.length);
+        for (var i = 0; i < args.length; ++i)
+          args[i] = arguments[i];
+        this.listener.apply(this.target, args);
+    }
+  }
+}
+
+function _onceWrap(target, type, listener) {
+  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
+  var wrapped = bind.call(onceWrapper, state);
+  wrapped.listener = listener;
+  state.wrapFn = wrapped;
+  return wrapped;
+}
+
+EventEmitter.prototype.once = function once(type, listener) {
+  if (typeof listener !== 'function')
+    throw new TypeError('"listener" argument must be a function');
+  this.on(type, _onceWrap(this, type, listener));
+  return this;
+};
+
+EventEmitter.prototype.prependOnceListener =
+    function prependOnceListener(type, listener) {
+      if (typeof listener !== 'function')
+        throw new TypeError('"listener" argument must be a function');
+      this.prependListener(type, _onceWrap(this, type, listener));
+      return this;
+    };
+
+// Emits a 'removeListener' event if and only if the listener was removed.
+EventEmitter.prototype.removeListener =
+    function removeListener(type, listener) {
+      var list, events, position, i, originalListener;
+
+      if (typeof listener !== 'function')
+        throw new TypeError('"listener" argument must be a function');
+
+      events = this._events;
+      if (!events)
+        return this;
+
+      list = events[type];
+      if (!list)
+        return this;
+
+      if (list === listener || list.listener === listener) {
+        if (--this._eventsCount === 0)
+          this._events = objectCreate(null);
+        else {
+          delete events[type];
+          if (events.removeListener)
+            this.emit('removeListener', type, list.listener || listener);
+        }
+      } else if (typeof list !== 'function') {
+        position = -1;
+
+        for (i = list.length - 1; i >= 0; i--) {
+          if (list[i] === listener || list[i].listener === listener) {
+            originalListener = list[i].listener;
+            position = i;
+            break;
+          }
+        }
+
+        if (position < 0)
+          return this;
+
+        if (position === 0)
+          list.shift();
+        else
+          spliceOne(list, position);
+
+        if (list.length === 1)
+          events[type] = list[0];
+
+        if (events.removeListener)
+          this.emit('removeListener', type, originalListener || listener);
+      }
+
+      return this;
+    };
+
+EventEmitter.prototype.removeAllListeners =
+    function removeAllListeners(type) {
+      var listeners, events, i;
+
+      events = this._events;
+      if (!events)
+        return this;
+
+      // not listening for removeListener, no need to emit
+      if (!events.removeListener) {
+        if (arguments.length === 0) {
+          this._events = objectCreate(null);
+          this._eventsCount = 0;
+        } else if (events[type]) {
+          if (--this._eventsCount === 0)
+            this._events = objectCreate(null);
+          else
+            delete events[type];
+        }
+        return this;
+      }
+
+      // emit removeListener for all listeners on all events
+      if (arguments.length === 0) {
+        var keys = objectKeys(events);
+        var key;
+        for (i = 0; i < keys.length; ++i) {
+          key = keys[i];
+          if (key === 'removeListener') continue;
+          this.removeAllListeners(key);
+        }
+        this.removeAllListeners('removeListener');
+        this._events = objectCreate(null);
+        this._eventsCount = 0;
+        return this;
+      }
+
+      listeners = events[type];
+
+      if (typeof listeners === 'function') {
+        this.removeListener(type, listeners);
+      } else if (listeners) {
+        // LIFO order
+        for (i = listeners.length - 1; i >= 0; i--) {
+          this.removeListener(type, listeners[i]);
+        }
+      }
+
+      return this;
+    };
+
+function _listeners(target, type, unwrap) {
+  var events = target._events;
+
+  if (!events)
+    return [];
+
+  var evlistener = events[type];
+  if (!evlistener)
+    return [];
+
+  if (typeof evlistener === 'function')
+    return unwrap ? [evlistener.listener || evlistener] : [evlistener];
+
+  return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
+}
+
+EventEmitter.prototype.listeners = function listeners(type) {
+  return _listeners(this, type, true);
+};
+
+EventEmitter.prototype.rawListeners = function rawListeners(type) {
+  return _listeners(this, type, false);
+};
+
+EventEmitter.listenerCount = function(emitter, type) {
+  if (typeof emitter.listenerCount === 'function') {
+    return emitter.listenerCount(type);
+  } else {
+    return listenerCount.call(emitter, type);
+  }
+};
+
+EventEmitter.prototype.listenerCount = listenerCount;
+function listenerCount(type) {
+  var events = this._events;
+
+  if (events) {
+    var evlistener = events[type];
+
+    if (typeof evlistener === 'function') {
+      return 1;
+    } else if (evlistener) {
+      return evlistener.length;
+    }
+  }
+
+  return 0;
+}
+
+EventEmitter.prototype.eventNames = function eventNames() {
+  return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
+};
+
+// About 1.5x faster than the two-arg version of Array#splice().
+function spliceOne(list, index) {
+  for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
+    list[i] = list[k];
+  list.pop();
+}
+
+function arrayClone(arr, n) {
+  var copy = new Array(n);
+  for (var i = 0; i < n; ++i)
+    copy[i] = arr[i];
+  return copy;
+}
+
+function unwrapListeners(arr) {
+  var ret = new Array(arr.length);
+  for (var i = 0; i < ret.length; ++i) {
+    ret[i] = arr[i].listener || arr[i];
+  }
+  return ret;
+}
+
+function objectCreatePolyfill(proto) {
+  var F = function() {};
+  F.prototype = proto;
+  return new F;
+}
+function objectKeysPolyfill(obj) {
+  var keys = [];
+  for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
+    keys.push(k);
+  }
+  return k;
+}
+function functionBindPolyfill(context) {
+  var fn = this;
+  return function () {
+    return fn.apply(context, arguments);
+  };
+}
+
+},{}],16:[function(require,module,exports){
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things.  But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals.  It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+    throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+    throw new Error('clearTimeout has not been defined');
+}
+(function () {
+    try {
+        if (typeof setTimeout === 'function') {
+            cachedSetTimeout = setTimeout;
+        } else {
+            cachedSetTimeout = defaultSetTimout;
+        }
+    } catch (e) {
+        cachedSetTimeout = defaultSetTimout;
+    }
+    try {
+        if (typeof clearTimeout === 'function') {
+            cachedClearTimeout = clearTimeout;
+        } else {
+            cachedClearTimeout = defaultClearTimeout;
+        }
+    } catch (e) {
+        cachedClearTimeout = defaultClearTimeout;
+    }
+} ())
+function runTimeout(fun) {
+    if (cachedSetTimeout === setTimeout) {
+        //normal enviroments in sane situations
+        return setTimeout(fun, 0);
+    }
+    // if setTimeout wasn't available but was latter defined
+    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+        cachedSetTimeout = setTimeout;
+        return setTimeout(fun, 0);
+    }
+    try {
+        // when when somebody has screwed with setTimeout but no I.E. maddness
+        return cachedSetTimeout(fun, 0);
+    } catch(e){
+        try {
+            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+            return cachedSetTimeout.call(null, fun, 0);
+        } catch(e){
+            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+            return cachedSetTimeout.call(this, fun, 0);
+        }
+    }
+
+
+}
+function runClearTimeout(marker) {
+    if (cachedClearTimeout === clearTimeout) {
+        //normal enviroments in sane situations
+        return clearTimeout(marker);
+    }
+    // if clearTimeout wasn't available but was latter defined
+    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+        cachedClearTimeout = clearTimeout;
+        return clearTimeout(marker);
+    }
+    try {
+        // when when somebody has screwed with setTimeout but no I.E. maddness
+        return cachedClearTimeout(marker);
+    } catch (e){
+        try {
+            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
+            return cachedClearTimeout.call(null, marker);
+        } catch (e){
+            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+            return cachedClearTimeout.call(this, marker);
+        }
+    }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+    if (!draining || !currentQueue) {
+        return;
+    }
+    draining = false;
+    if (currentQueue.length) {
+        queue = currentQueue.concat(queue);
+    } else {
+        queueIndex = -1;
+    }
+    if (queue.length) {
+        drainQueue();
+    }
+}
+
+function drainQueue() {
+    if (draining) {
+        return;
+    }
+    var timeout = runTimeout(cleanUpNextTick);
+    draining = true;
+
+    var len = queue.length;
+    while(len) {
+        currentQueue = queue;
+        queue = [];
+        while (++queueIndex < len) {
+            if (currentQueue) {
+                currentQueue[queueIndex].run();
+            }
+        }
+        queueIndex = -1;
+        len = queue.length;
+    }
+    currentQueue = null;
+    draining = false;
+    runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+    var args = new Array(arguments.length - 1);
+    if (arguments.length > 1) {
+        for (var i = 1; i < arguments.length; i++) {
+            args[i - 1] = arguments[i];
+        }
+    }
+    queue.push(new Item(fun, args));
+    if (queue.length === 1 && !draining) {
+        runTimeout(drainQueue);
+    }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+    this.fun = fun;
+    this.array = array;
+}
+Item.prototype.run = function () {
+    this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+    throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+    throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],17:[function(require,module,exports){
+(function (setImmediate,clearImmediate){
+var nextTick = require('process/browser.js').nextTick;
+var apply = Function.prototype.apply;
+var slice = Array.prototype.slice;
+var immediateIds = {};
+var nextImmediateId = 0;
+
+// DOM APIs, for completeness
+
+exports.setTimeout = function() {
+  return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
+};
+exports.setInterval = function() {
+  return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
+};
+exports.clearTimeout =
+exports.clearInterval = function(timeout) { timeout.close(); };
+
+function Timeout(id, clearFn) {
+  this._id = id;
+  this._clearFn = clearFn;
+}
+Timeout.prototype.unref = Timeout.prototype.ref = function() {};
+Timeout.prototype.close = function() {
+  this._clearFn.call(window, this._id);
+};
+
+// Does not start the time, just sets up the members needed.
+exports.enroll = function(item, msecs) {
+  clearTimeout(item._idleTimeoutId);
+  item._idleTimeout = msecs;
+};
+
+exports.unenroll = function(item) {
+  clearTimeout(item._idleTimeoutId);
+  item._idleTimeout = -1;
+};
+
+exports._unrefActive = exports.active = function(item) {
+  clearTimeout(item._idleTimeoutId);
+
+  var msecs = item._idleTimeout;
+  if (msecs >= 0) {
+    item._idleTimeoutId = setTimeout(function onTimeout() {
+      if (item._onTimeout)
+        item._onTimeout();
+    }, msecs);
+  }
+};
+
+// That's not how node.js implements it but the exposed api is the same.
+exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) {
+  var id = nextImmediateId++;
+  var args = arguments.length < 2 ? false : slice.call(arguments, 1);
+
+  immediateIds[id] = true;
+
+  nextTick(function onNextTick() {
+    if (immediateIds[id]) {
+      // fn.call() is faster so we optimize for the common use-case
+      // @see http://jsperf.com/call-apply-segu
+      if (args) {
+        fn.apply(null, args);
+      } else {
+        fn.call(null);
+      }
+      // Prevent ids from leaking
+      exports.clearImmediate(id);
+    }
+  });
+
+  return id;
+};
+
+exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
+  delete immediateIds[id];
+};
+}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
+},{"process/browser.js":16,"timers":17}]},{},[13])(13)
+});
+
+	});
+});
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/enabler.rep.css b/dist/firephp.build/scripts/devtools/dist/enabler.rep.css
similarity index 76%
rename from dist/firephp.build/scripts/devtools/enabler.rep.css
rename to dist/firephp.build/scripts/devtools/dist/enabler.rep.css
index 07ddd85..08f4725 100644
--- a/dist/firephp.build/scripts/devtools/enabler.rep.css
+++ b/dist/firephp.build/scripts/devtools/dist/enabler.rep.css
@@ -1,6 +1,6 @@
 
 
-    DIV[data-is="tag_d0ea6bf319102fa8d3ab8032fb6c98d7c86ce04b"] BUTTON {
+    DIV[data-is="tag_2d81e5cf2d8935869345101da946d98aae96dbc9"] BUTTON {
         width: 100%;
         height: 20px;
         font-weight: bold;
@@ -10,7 +10,7 @@
         height: auto;
     }
 
-    DIV[data-is="tag_d0ea6bf319102fa8d3ab8032fb6c98d7c86ce04b"] BUTTON.enable {
+    DIV[data-is="tag_2d81e5cf2d8935869345101da946d98aae96dbc9"] BUTTON.enable {
         color: #000000;
         border-top: 2px solid #ff5151;
         border-bottom: 2px solid #ff5151;
@@ -18,7 +18,7 @@
         border-right: 10px solid #ff5151;
     }
 
-    DIV[data-is="tag_d0ea6bf319102fa8d3ab8032fb6c98d7c86ce04b"] BUTTON.disable {
+    DIV[data-is="tag_2d81e5cf2d8935869345101da946d98aae96dbc9"] BUTTON.disable {
         color: #000000;
         border-top: 2px solid #05c600;
         border-bottom: 2px solid #05c600;
diff --git a/dist/firephp.build/scripts/devtools/enabler.rep.js b/dist/firephp.build/scripts/devtools/dist/enabler.rep.js
similarity index 85%
rename from dist/firephp.build/scripts/devtools/enabler.rep.js
rename to dist/firephp.build/scripts/devtools/dist/enabler.rep.js
index 0f0f225..2f437c1 100644
--- a/dist/firephp.build/scripts/devtools/enabler.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/enabler.rep.js
@@ -313,6 +313,7 @@ function debounce(func, wait, options) {
       }
       if (maxing) {
         // Handle invocations in a tight loop.
+        clearTimeout(timerId);
         timerId = setTimeout(timerExpired, wait);
         return invokeFunc(lastCallTime);
       }
@@ -548,26 +549,28 @@ exports.main = function (JSONREP, node, options) {
         html: "<div></div>",
         "on": {
           "mount": function (el) {
-            riot.util.styleManager.add = function (cssText, name) {
-              if (!cssText) {
-                return;
-              }
-
-              if (window.document.createStyleSheet) {
-                var sheet = window.document.createStyleSheet();
-                sheet.cssText = cssText;
-              } else {
-                var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
-                style.appendChild(window.document.createTextNode(cssText));
-                var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
-                head.appendChild(style);
-              }
-            };
-
-            riot.util.styleManager.inject = function () {
-              if (!options) return;
-              JSONREP.loadStyle(options.renderer.uri + '.css');
-            };
+            if ("1" === "1") {
+              riot.util.styleManager.inject = function () {
+                if (!options) return;
+                JSONREP.loadStyle(options.renderer.uri + '.css');
+              };
+            } else {
+              riot.util.styleManager.add = function (cssText, name) {
+                if (!cssText) {
+                  return;
+                }
+
+                if (window.document.createStyleSheet) {
+                  var sheet = window.document.createStyleSheet();
+                  sheet.cssText = cssText;
+                } else {
+                  var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
+                  style.appendChild(window.document.createTextNode(cssText));
+                  var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+                  head.appendChild(style);
+                }
+              };
+            }
 
             riot.tag('raw', '<div></div>', function (opts) {
               this.set = function () {
@@ -577,7 +580,7 @@ exports.main = function (JSONREP, node, options) {
               this.on('update', this.set);
               this.on('mount', this.set);
             });
-            riot.tag2('tag_d0ea6bf319102fa8d3ab8032fb6c98d7c86ce04b', '<div> <button if="{enabled === false}" onclick="{triggerEnable}" class="enable">Enable</button> <button if="{enabled === true}" onclick="{triggerDisable}" class="disable">Disable</button> </div>', '', '', function (opts) {
+            riot.tag2('tag_2d81e5cf2d8935869345101da946d98aae96dbc9', '<div> <button if="{enabled === false}" onclick="{triggerEnable}" class="enable">Click to Enable</button> <button if="{enabled === true}" onclick="{triggerDisable}" class="disable">Click to Disable</button> </div>', '', '', function (opts) {
               var COMPONENT = require("./component");
 
               var tag = this;
@@ -626,7 +629,7 @@ exports.main = function (JSONREP, node, options) {
                 });
               };
             });
-            riot.mount(el, 'tag_d0ea6bf319102fa8d3ab8032fb6c98d7c86ce04b', context);
+            riot.mount(el, 'tag_2d81e5cf2d8935869345101da946d98aae96dbc9', context);
           }
         }
       };
@@ -643,6 +646,7 @@ var DEBOUNCE = require('lodash/debounce');
 
 exports.for = function (ctx) {
   var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
   events.currentContext = null;
   setImmediate(function () {
     onContextMessage(null);
@@ -665,38 +669,52 @@ exports.for = function (ctx) {
     }
   }
 
-  ctx.browser.runtime.onMessage.addListener(function (message) {
+  events.handleBroadcastMessage = function (message) {
     try {
-      if (!ctx.browser || !ctx.browser.devtools || !ctx.browser.devtools.inspectedWindow || !message.context || message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId) {
-        return;
-      }
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
 
-      if (message.to === "message-listener") {
-        if (message.event === "currentContext" && typeof message.context !== "undefined") {
-          onContextMessage(message.context);
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
         }
-
-        events.emit("message", message);
       }
     } catch (err) {
       console.error(err);
     }
-  });
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
   ctx.browser.storage.onChanged.addListener(function (changes, area) {
     try {
       if (!events.currentContext) {
         return;
       }
 
-      var prefix = "domain[".concat(events.currentContext.hostname, "].");
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
 
       for (var item of Object.keys(changes)) {
-        if (!/^domain\[.+\]\..+$/.test(item)) continue;
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
         if (item.substring(0, prefix.length) === prefix) {
           var name = item.substring(prefix.length);
-          events.emit("setting." + name, changes[item].newValue);
-          events.emit("changed.setting", name, changes[item].newValue);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
         }
       }
     } catch (err) {
@@ -710,7 +728,7 @@ exports.for = function (ctx) {
     });
   }, 250);
 
-  events.getSetting = function (name) {
+  events.getSetting = async function (name) {
     if (!events.currentContext) {
       return Promise.resolve(null);
     }
@@ -718,21 +736,47 @@ exports.for = function (ctx) {
     return events._getSettingForHostname(events.currentContext.hostname, name);
   };
 
-  events._getSettingForHostname = function (hostname, name) {
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
 
-    var key = "domain[" + hostname + "]." + name;
+    var key = "hostname[" + hostname + "]." + name;
     return ctx.browser.storage.local.get(key).then(function (value) {
-      return value[key] || false;
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setSetting = function (name, value) {
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
     if (!events.currentContext) {
       throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
     }
@@ -740,7 +784,7 @@ exports.for = function (ctx) {
     return events._setSettingForHostname(events.currentContext.hostname, name, value);
   };
 
-  events._setSettingForHostname = function (hostname, name, value) {
+  events._setSettingForHostname = async function (hostname, name, value) {
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
@@ -751,7 +795,9 @@ exports.for = function (ctx) {
       }
 
       var obj = {};
-      obj["domain[" + hostname + "]." + name] = value;
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -759,9 +805,9 @@ exports.for = function (ctx) {
     });
   };
 
-  events.getGlobalSetting = function (name) {
+  events.getGlobalSetting = async function (name) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     var defaultValue;
@@ -780,15 +826,18 @@ exports.for = function (ctx) {
       }
 
       return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setGlobalSetting = function (name, value) {
+  events.setGlobalSetting = async function (name, value) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     return events.getGlobalSetting(name).then(function (existingValue) {
@@ -798,6 +847,7 @@ exports.for = function (ctx) {
 
       var obj = {};
       obj[name] = value;
+      globalSettings[name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -805,14 +855,54 @@ exports.for = function (ctx) {
     });
   };
 
-  events.isConfigured = function () {
-    return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return enableUserAgentHeader || enableFirePHPHeader || enableChromeLoggerData;
-        });
-      });
-    });
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
   };
 
   events.reloadBrowser = function () {
@@ -832,15 +922,41 @@ exports.for = function (ctx) {
     });
   };
 
-  events.showView = function (name) {
+  events.showView = function (name, args) {
     if (name === "manage") {
       ctx.browser.runtime.sendMessage({
         to: "broadcast",
         event: "manage"
       });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
+    }
+  };
+
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
     }
   };
 
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
   return events;
 };
 }).call(this,require("timers").setImmediate)
diff --git a/dist/firephp.build/scripts/devtools/fireconsole.rep.css b/dist/firephp.build/scripts/devtools/dist/fireconsole.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/fireconsole.rep.css
rename to dist/firephp.build/scripts/devtools/dist/fireconsole.rep.css
diff --git a/dist/firephp.build/scripts/devtools/fireconsole.rep.js b/dist/firephp.build/scripts/devtools/dist/fireconsole.rep.js
similarity index 93%
rename from dist/firephp.build/scripts/devtools/fireconsole.rep.js
rename to dist/firephp.build/scripts/devtools/dist/fireconsole.rep.js
index 7e6e40f..c5d09b8 100644
--- a/dist/firephp.build/scripts/devtools/fireconsole.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/fireconsole.rep.js
@@ -1697,7 +1697,7 @@ exports.PINF = (function (global) {
 **/
 
 (function (exports) {
- 
+
 // The global `require` for the 'external' (to the loader) environment.
 var Loader = function (global) {
 
@@ -1744,7 +1744,7 @@ var Loader = function (global) {
 	}
 
 	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
-	function loadInBrowser (uri, loadedCallback) {
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
 		try {
 			// See if we are in a web worker.
 			if (typeof importScripts !== "undefined") {
@@ -1772,7 +1772,9 @@ var Loader = function (global) {
 				if (ev.type === "load" || readyStates[this.readyState]) {
 					this.onload = this.onreadystatechange = this.onerror = null;
 					loadedCallback(null, function () {
-						element.parentNode.removeChild(element);
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
 					});
 				}
 			}
@@ -1885,7 +1887,8 @@ var Loader = function (global) {
 										cleanupCallback();
 									}
 								});
-							}
+							},
+							sandboxOptions
 						);
 					}
 				}
@@ -1937,7 +1940,8 @@ var Loader = function (global) {
 									function () {
 										pending -= 1;
 										finalize();
-									}
+									},
+									sandboxOptions
 								);
 							}
 						}
@@ -2426,7 +2430,7 @@ var Loader = function (global) {
 					loadedCallback(null);
 					return;
 				}
-				return fallbackLoad(uri, loadedCallback);
+				return fallbackLoad(uri, loadedCallback, options);
 			}
 			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
 		}
@@ -3251,7 +3255,7 @@ if (exports) exports.Loader = Loader;
 }();
 
 }).call(this,require('_process'))
-},{"_process":130}],3:[function(require,module,exports){
+},{"_process":132}],3:[function(require,module,exports){
 var hashClear = require('./_hashClear'),
     hashDelete = require('./_hashDelete'),
     hashGet = require('./_hashGet'),
@@ -5860,14 +5864,6 @@ module.exports = toPlainObject;
 },{"./_copyObject":33,"./keysIn":95}],99:[function(require,module,exports){
 "use strict";
 
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
-
-function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
-
-function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
-
 var WINDOW = window;
 
 var LODASH_MERGE = require("lodash/merge");
@@ -5879,7 +5875,7 @@ var REPS = require("insight.domplate.reps");
 var repsBaseUrl = "/reps";
 
 if (typeof bundle !== "undefined") {
-  repsBaseUrl = bundle.module.filename.replace(/(^|\/)[^\/]+\/[^\/]+$/, '$1dist/insight.domplate.reps');
+  repsBaseUrl = bundle.module.filename.replace(/(^|\/)[^\/]+\/[^\/]+$/, '$1dist/reps/insight.domplate.reps/dist/reps');
 }
 
 var repLoader = new REPS.Loader({
@@ -5892,19 +5888,22 @@ var BROWSER_API_ENCODER = require("./encoders/BrowserApi-0.1");
 
 var FIREBUG_CONSOLE_DECODER = require("./decoders/FirebugConsole-0.1");
 
+var INSIGHT_DECODER = require("./decoders/Insight-0.1");
+
 var encoder = new BROWSER_API_ENCODER.Encoder();
 var decoder = new FIREBUG_CONSOLE_DECODER.Decoder();
+var insightDecoder = new INSIGHT_DECODER.Decoder();
 var receiver = WILDFIRE.Receiver();
 receiver.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
 receiver.addListener({
-  onMessageReceived: function onMessageReceived(request, message) {
+  onMessageReceived: function (request, message) {
     exports.fireconsole.appendMessage(decoder.formatMessage(message));
   }
 });
 var receiverDump = WILDFIRE.Receiver();
 receiverDump.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1");
 receiverDump.addListener({
-  onMessageReceived: function onMessageReceived(request, message) {
+  onMessageReceived: function (request, message) {
     exports.fireconsole.appendMessage(decoder.formatMessage(message));
   }
 });
@@ -5980,7 +5979,7 @@ function FireConsole() {
         var node = repLoader.domplate.util.getElementByClass(domNode, "body");
         supervisor.groupStack.push(node);
 
-        if (_typeof(meta["group.expand"]) && meta["group.expand"] === meta["group"] && node.parentNode) {
+        if (typeof meta["group.expand"] && meta["group.expand"] === meta["group"] && node.parentNode) {
           node.parentNode.setAttribute("expanded", "true");
         }
       }
@@ -6074,15 +6073,17 @@ function FireConsole() {
   var renderSupervisor = new Supervisor();
   var repRenderer = self.repRenderer = new REPS.Renderer({
     loader: repLoader,
-    onEvent: function onEvent(name, args) {
+    onEvent: function (name, args) {
       console.log('repRenderer.onEvent()', name, args);
 
-      if (name === "click") {} else if (name === "expand") {} else if (name === "contract") {} else if (name === "inspectMessage") {
+      if (name === "click") {
+        self.emit("click", args[1]);
+      } else if (name === "expand") {} else if (name === "contract") {} else if (name === "inspectMessage") {
         self.emit(name, {
           node: args[1].args.node
         });
       } else if (name === "inspectFile") {
-        console.log("INSPECT FILE", args);
+        self.emit(name, args[1].args);
       } else if (name === "inspectNode") {
         args[1].args.node['#'] = "InsightTree";
         self.emit(name, {
@@ -6117,7 +6118,7 @@ function FireConsole() {
       }
 
       var msg = {
-        render: function render(el, view, messageObject) {
+        render: function (el, view, messageObject) {
           throw new Error("Render!!");
 
           if (typeof meta["group.start"] !== "undefined" && meta["group.start"]) {
@@ -6205,99 +6206,85 @@ function FireConsole() {
 
 FireConsole.prototype = Object.create(EVENT_EMITTER.prototype);
 
-var PublicAPI = function () {
-  function PublicAPI(fireconsole, options) {
-    _classCallCheck(this, PublicAPI);
-
+class PublicAPI {
+  constructor(fireconsole, options) {
     this.fireconsole = fireconsole;
     this.options = options || {};
     this.FireConsole = FireConsole;
   }
 
-  _createClass(PublicAPI, [{
-    key: "clear",
-    value: function clear() {
-      return this.fireconsole.clear.apply(this.fireconsole, arguments);
-    }
-  }, {
-    key: "on",
-    value: function on() {
-      return this.fireconsole.on.apply(this.fireconsole, arguments);
-    }
-  }, {
-    key: "off",
-    value: function off() {
-      return this.fireconsole.off.apply(this.fireconsole, arguments);
-    }
-  }, {
-    key: "_logObjectWithPriority",
-    value: function _logObjectWithPriority(priority, message) {
-      this.fireconsole.appendMessage(message, LODASH_MERGE({}, this.options, {
-        priority: priority
-      }));
-    }
-  }, {
-    key: "renderMessageInto",
-    value: function renderMessageInto(panelEl, message) {
-      this.fireconsole.appendMessage(message, {
-        panelEl: panelEl,
-        clear: true,
-        view: "detail"
-      });
-    }
-  }, {
-    key: "label",
-    value: function label(_label) {
-      return new PublicAPI(this.fireconsole, {
-        label: _label
-      });
-    }
-  }, {
-    key: "log",
-    value: function log(message) {
-      this._logObjectWithPriority("log", message);
-    }
-  }, {
-    key: "info",
-    value: function info(message) {
-      this._logObjectWithPriority("info", message);
-    }
-  }, {
-    key: "warn",
-    value: function warn(message) {
-      this._logObjectWithPriority("warn", message);
-    }
-  }, {
-    key: "error",
-    value: function error(message) {
-      this._logObjectWithPriority("error", message);
-    }
-  }, {
-    key: "send",
-    value: function send(message) {
-      if (message.sender && message.receiver) {
-        if (message.receiver === "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1") {
-          this.fireconsole.appendMessage(decoder.formatMessage(message));
-        } else if (message.receiver === "http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1") {
-          this.fireconsole.appendMessage(decoder.formatMessage(message));
-        } else if (message.receiver === "https://gi0.FireConsole.org/rep.js/InsightTree/0.1") {
-          message.data["#"] = "InsightTree";
-          this.fireconsole.appendMessage(message.data);
-        } else {
-          throw new Error("Receiver for ID '".concat(message.receiver, "' not implemented!"));
-        }
-      } else {
-        if (!Array.isArray(message)) {
-          message = [message];
-        }
+  clear() {
+    return this.fireconsole.clear.apply(this.fireconsole, arguments);
+  }
 
-        this.fireconsole.parseReceivedPostMessage(message);
+  on() {
+    return this.fireconsole.on.apply(this.fireconsole, arguments);
+  }
+
+  off() {
+    return this.fireconsole.off.apply(this.fireconsole, arguments);
+  }
+
+  _logObjectWithPriority(priority, message) {
+    this.fireconsole.appendMessage(message, LODASH_MERGE({}, this.options, {
+      priority: priority
+    }));
+  }
+
+  renderMessageInto(panelEl, message) {
+    this.fireconsole.appendMessage(message, {
+      panelEl: panelEl,
+      clear: true,
+      view: "detail"
+    });
+  }
+
+  label(label) {
+    return new PublicAPI(this.fireconsole, {
+      label: label
+    });
+  }
+
+  log(message) {
+    this._logObjectWithPriority("log", message);
+  }
+
+  info(message) {
+    this._logObjectWithPriority("info", message);
+  }
+
+  warn(message) {
+    this._logObjectWithPriority("warn", message);
+  }
+
+  error(message) {
+    this._logObjectWithPriority("error", message);
+  }
+
+  send(message) {
+    if (message.sender && message.receiver) {
+      if (message.receiver === "http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1") {
+        this.fireconsole.appendMessage(decoder.formatMessage(message));
+      } else if (message.receiver === "http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1") {
+        this.fireconsole.appendMessage(decoder.formatMessage(message));
+      } else if (message.receiver === "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/firephp/0" || message.receiver === "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/page/0") {
+        this.fireconsole.appendMessage(insightDecoder.formatMessage(message));
+      } else if (message.receiver === "https://gi0.FireConsole.org/rep.js/InsightTree/0.1") {
+        message.data["#"] = "InsightTree";
+        this.fireconsole.appendMessage(message.data);
+      } else {
+        throw new Error("Receiver for ID '".concat(message.receiver, "' not implemented!"));
+      }
+    } else {
+      if (!Array.isArray(message)) {
+        message = [message];
       }
+
+      this.fireconsole.parseReceivedPostMessage(message);
     }
-  }]);
+  }
 
-  return PublicAPI;
-}();
+}
 
 ;
 exports.fireconsole = new FireConsole();
@@ -6332,30 +6319,22 @@ exports.main = function (JSONREP, node) {
     return JSONREP.makeRep('<div></div>', {
       css: {
         ".@": "github.com~0ink~codeblock/codeblock:Codeblock",
-        "_code": "{\"_cssid\":\"71e851a61fa880aafcc2b86479d6153e22f7af9c\",\"repUri\":\"fireconsole\"}",
+        "_code": "{\"_cssid\":\"e89c1c9331aa7abb16042f9573f69f4fd9ca5d7c\",\"repUri\":\"fireconsole\"}",
         "_format": "json",
         "_args": [],
         "_compiled": false
       },
       on: {
-        mount: function mount(el) {
+        mount: function (el) {
           exports.fireconsole.setPanelElement(el);
         }
       }
     });
   });
 };
-},{"./decoders/FirebugConsole-0.1":100,"./encoders/BrowserApi-0.1":101,"eventemitter2":2,"insight.domplate.reps":126,"lodash/merge":96,"wildfire-for-js/lib/wildfire":113}],100:[function(require,module,exports){
+},{"./decoders/FirebugConsole-0.1":100,"./decoders/Insight-0.1":101,"./encoders/BrowserApi-0.1":102,"eventemitter2":2,"insight.domplate.reps":128,"lodash/merge":96,"wildfire-for-js/lib/wildfire":114}],100:[function(require,module,exports){
 "use strict";
 
-function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
-
-function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
-
 var VERBOSE = false;
 
 var ENCODER = require("insight-for-js/lib/encoder/default");
@@ -6365,55 +6344,76 @@ encoder.setOption("maxObjectDepth", 1000);
 encoder.setOption("maxArrayDepth", 1000);
 encoder.setOption("maxOverallDepth", 1000);
 
-var Decoder = function () {
-  function Decoder() {
-    _classCallCheck(this, Decoder);
-  }
-
-  _createClass(Decoder, [{
-    key: "formatMessage",
-    value: function formatMessage(message) {
-      var node = {
-        "#": "InsightTree",
-        "meta": {}
-      };
+class Decoder {
+  formatMessage(message) {
+    var node = {
+      "#": "InsightTree",
+      "meta": {}
+    };
 
-      try {
-        if (VERBOSE) console.log("Decoder.formatMessage():", message);
-        var dataNode = null;
-        var meta = JSON.parse(message.meta);
-        var data = JSON.parse(message.data);
-        if (VERBOSE) console.log("Decoder.formatMessage() meta:", JSON.stringify(meta, null, 4));
-        if (VERBOSE) console.log("Decoder.formatMessage() data:", JSON.stringify(data, null, 4));
-
-        if (meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers/packages/php/master" && meta["msg.preprocessor"] === "FirePHPCoreCompatibility") {
-          if (meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/table") {
+    try {
+      if (VERBOSE) console.log("Decoder.formatMessage():", message);
+      var dataNode = null;
+      var meta = typeof message.meta === "string" ? JSON.parse(message.meta) : message.meta;
+      var data = JSON.parse(message.data);
+      if (VERBOSE) console.log("Decoder.formatMessage() meta:", JSON.stringify(meta, null, 4));
+      if (VERBOSE) console.log("Decoder.formatMessage() data:", JSON.stringify(data, null, 4));
+
+      if (meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers/packages/php/master" && meta["msg.preprocessor"] === "FirePHPCoreCompatibility") {
+        if (meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/table") {
+          node.meta = {
+            "lang": "default",
+            "lang.type": "table"
+          };
+          node.value = {
+            title: {
+              type: "string",
+              value: data.title
+            },
+            header: data.header.map(function (value) {
+              return {
+                type: "string",
+                value: value
+              };
+            })
+          };
+          dataNode = encoder.encode(data.data, {
+            "lang": "php"
+          }, {
+            "jsonEncode": false
+          });
+          node.value.body = dataNode.origin.value.map(function (row) {
+            return row.value;
+          });
+          node.instances = dataNode.instances;
+        } else if (meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/trace") {
             node.meta = {
               "lang": "default",
-              "lang.type": "table"
+              "lang.type": "trace"
             };
             node.value = {
               title: {
                 type: "string",
                 value: data.title
-              },
-              header: data.header.map(function (value) {
-                return {
-                  type: "string",
-                  value: value
-                };
-              })
+              }
             };
-            dataNode = encoder.encode(data.data, {
-              "lang": "php"
-            }, {
-              "jsonEncode": false
-            });
-            node.value.body = dataNode.origin.value.map(function (row) {
-              return row.value;
+            node.value.stack = data.trace.map(function (frame) {
+              dataNode = encoder.encode(frame.args || [], {
+                "lang": "php"
+              }, {
+                "jsonEncode": false
+              });
+              return {
+                file: frame.file,
+                line: frame.line,
+                class: frame.class,
+                function: frame.function,
+                args: dataNode.origin.value.map(function (arg) {
+                  return arg;
+                })
+              };
             });
-            node.instances = dataNode.instances;
-          } else if (meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/trace") {
+          } else if (data && data.__isException === true) {
               node.meta = {
                 "lang": "default",
                 "lang.type": "trace"
@@ -6421,10 +6421,10 @@ var Decoder = function () {
               node.value = {
                 title: {
                   type: "string",
-                  value: data.title
+                  value: data['protected:message']
                 }
               };
-              node.value.stack = data.trace.map(function (frame) {
+              node.value.stack = data["private:trace"].map(function (frame) {
                 dataNode = encoder.encode(frame.args || [], {
                   "lang": "php"
                 }, {
@@ -6440,86 +6440,95 @@ var Decoder = function () {
                   })
                 };
               });
-            } else if (data && data.__isException === true) {
-                node.meta = {
-                  "lang": "default",
-                  "lang.type": "trace"
-                };
-                node.value = {
-                  title: {
-                    type: "string",
-                    value: data['protected:message']
-                  }
-                };
-                node.value.stack = data["private:trace"].map(function (frame) {
-                  dataNode = encoder.encode(frame.args || [], {
-                    "lang": "php"
-                  }, {
-                    "jsonEncode": false
-                  });
-                  return {
-                    file: frame.file,
-                    line: frame.line,
-                    class: frame.class,
-                    function: frame.function,
-                    args: dataNode.origin.value.map(function (arg) {
-                      return arg;
-                    })
-                  };
+            } else if (data && typeof data === "object" && typeof data.__className === "string") {
+                dataNode = encoder.encode(data, {
+                  "lang": "php"
+                }, {
+                  "jsonEncode": false
                 });
-              } else if (data && _typeof(data) === "object" && typeof data.__className === "string") {
-                  dataNode = encoder.encode(data, {
-                    "lang": "php"
-                  }, {
-                    "jsonEncode": false
-                  });
-                  node.meta = dataNode.origin.meta;
-                  node.type = dataNode.origin.type;
-                  node.value = dataNode.origin.value;
-                  node.instances = dataNode.instances;
-                } else {
-                  dataNode = encoder.encode(data, {
-                    "lang": "php"
-                  }, {
-                    "jsonEncode": false
-                  });
-                  node.meta = dataNode.origin.meta;
-                  node.value = dataNode.origin.value;
-                  node.instances = dataNode.instances;
-                }
-        }
-
-        ['priority', 'label', 'file', 'line', 'target', 'group', 'group.start', 'group.end', 'group.title', 'group.expand'].forEach(function (name) {
-          if (typeof meta[name] !== 'undefined') node.meta[name] = meta[name];
-        });
+                node.meta = dataNode.origin.meta;
+                node.type = dataNode.origin.type;
+                node.value = dataNode.origin.value;
+                node.instances = dataNode.instances;
+              } else {
+                dataNode = encoder.encode(data, {
+                  "lang": "php"
+                }, {
+                  "jsonEncode": false
+                });
+                node.meta = dataNode.origin.meta;
+                node.value = dataNode.origin.value;
+                node.instances = dataNode.instances;
+              }
+      }
 
-        if (typeof node.meta["group.start"] != "undefined") {
-          node.value = node.meta["group.title"] || '?';
-          node.type = 'string';
-        }
+      ['priority', 'label', 'file', 'line', 'target', 'group', 'group.start', 'group.end', 'group.title', 'group.expand', 'console'].forEach(function (name) {
+        if (typeof meta[name] !== 'undefined') node.meta[name] = meta[name];
+      });
 
-        if (VERBOSE) console.log("Decoder.formatMessage() RETURN:", node);
-        return node;
-      } catch (err) {
-        console.error("message", message);
-        console.error("node", node);
-        throw err;
+      if (typeof node.meta["group.start"] != "undefined") {
+        node.value = node.meta["group.title"] || '?';
+        node.type = 'string';
       }
+
+      if (VERBOSE) console.log("Decoder.formatMessage() RETURN:", node);
+      return node;
+    } catch (err) {
+      console.error("message", message);
+      console.error("node", node);
+      throw err;
     }
-  }]);
+  }
 
-  return Decoder;
-}();
+}
 
 exports.Decoder = Decoder;
-},{"insight-for-js/lib/encoder/default":123}],101:[function(require,module,exports){
+},{"insight-for-js/lib/encoder/default":125}],101:[function(require,module,exports){
 "use strict";
 
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+var DECODER = require("insight-for-js/lib/decoder/default");
+
+class Decoder {
+  formatMessage(message) {
+    var meta = typeof message.meta === "string" ? JSON.parse(message.meta) : message.meta;
+
+    if (meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers\/packages\/php\/master") {
+      meta["lang"] = "php";
+      delete meta["lang.id"];
+    } else {
+      throw new Error("'lang.id of '" + meta["lang.id"] + "' not supported!");
+    }
+
+    var data = JSON.parse(message.data);
+
+    if (!data.origin) {
+      throw new Error("No 'data.origin' found in message data");
+    }
+
+    Object.keys(data.origin).forEach(function (name) {
+      meta[name] = data.origin[name];
+    });
+    var node = {
+      "#": "InsightTree",
+      meta: meta,
+      type: meta.type,
+      value: meta.value
+    };
+    delete meta.type;
+    delete meta.value;
+
+    if (data.instances) {
+      node.instances = data.instances;
+    }
 
-function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+    return node;
+  }
 
-function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
+}
+
+exports.Decoder = Decoder;
+},{"insight-for-js/lib/decoder/default":124}],102:[function(require,module,exports){
+"use strict";
 
 var VERBOSE = false;
 
@@ -6530,51 +6539,43 @@ encoder.setOption("maxObjectDepth", 1000);
 encoder.setOption("maxArrayDepth", 1000);
 encoder.setOption("maxOverallDepth", 1000);
 
-var Encoder = function () {
-  function Encoder() {
-    _classCallCheck(this, Encoder);
-  }
-
-  _createClass(Encoder, [{
-    key: "formatMessage",
-    value: function formatMessage(message, options) {
-      var node = {
-        "#": "InsightTree",
-        "meta": {}
-      };
-
-      try {
-        if (VERBOSE) console.log("Encoder.formatMessage():", message);
-        var dataNode = encoder.encode(message, {}, {
-          "jsonEncode": false
-        });
-        node.meta = dataNode.origin.meta;
-        node.type = dataNode.origin.type || undefined;
-        node.value = dataNode.origin.value;
+class Encoder {
+  formatMessage(message, options) {
+    var node = {
+      "#": "InsightTree",
+      "meta": {}
+    };
 
-        if (options.priority) {
-          node.meta.priority = options.priority;
-        }
+    try {
+      if (VERBOSE) console.log("Encoder.formatMessage():", message);
+      var dataNode = encoder.encode(message, {}, {
+        "jsonEncode": false
+      });
+      node.meta = dataNode.origin.meta;
+      node.type = dataNode.origin.type || undefined;
+      node.value = dataNode.origin.value;
 
-        if (options.label) {
-          node.meta.label = options.label;
-        }
+      if (options.priority) {
+        node.meta.priority = options.priority;
+      }
 
-        if (VERBOSE) console.log("Encoder.formatMessage() RETURN:", node);
-        return node;
-      } catch (err) {
-        console.error("message", message);
-        console.error("node", node);
-        throw err;
+      if (options.label) {
+        node.meta.label = options.label;
       }
+
+      if (VERBOSE) console.log("Encoder.formatMessage() RETURN:", node);
+      return node;
+    } catch (err) {
+      console.error("message", message);
+      console.error("node", node);
+      throw err;
     }
-  }]);
+  }
 
-  return Encoder;
-}();
+}
 
 exports.Encoder = Encoder;
-},{"insight-for-js/lib/encoder/default":123}],102:[function(require,module,exports){
+},{"insight-for-js/lib/encoder/default":125}],103:[function(require,module,exports){
 
 
 var CHANNEL = require("./channel");
@@ -6689,7 +6690,7 @@ HttpHeaderChannel.prototype.getMozillaRequestObserverListener = function(globals
     return this.mozillaRequestObserverListener;
 }
 
-},{"./channel":105}],103:[function(require,module,exports){
+},{"./channel":106}],104:[function(require,module,exports){
 
 var CHANNEL = require("./channel"),
     UTIL = require("fp-modules-for-nodejs/lib/util");
@@ -6753,7 +6754,7 @@ PostMessageChannel.prototype.parseReceivedPostMessage = function(msg)
     });
 }
 
-},{"./channel":105,"fp-modules-for-nodejs/lib/util":122}],104:[function(require,module,exports){
+},{"./channel":106,"fp-modules-for-nodejs/lib/util":123}],105:[function(require,module,exports){
 
 var CHANNEL = require("./channel");
 
@@ -6770,7 +6771,7 @@ var ShellCommandChannel = exports.ShellCommandChannel = function () {
 
 ShellCommandChannel.prototype = CHANNEL.Channel();
 
-},{"./channel":105}],105:[function(require,module,exports){
+},{"./channel":106}],106:[function(require,module,exports){
 
 var UTIL = require("fp-modules-for-nodejs/lib/util");
 var PROTOCOL = require("./protocol");
@@ -7300,7 +7301,7 @@ Channel.prototype.setTransport = function(transport) {
 }
 
 
-},{"./protocol":109,"./transport":112,"fp-modules-for-nodejs/lib/util":122}],106:[function(require,module,exports){
+},{"./protocol":110,"./transport":113,"fp-modules-for-nodejs/lib/util":123}],107:[function(require,module,exports){
 
 var CHANNEL = require("../channel"),
     UTIL = require("fp-modules-for-nodejs/lib/util"),
@@ -7397,7 +7398,7 @@ HttpClientChannel.prototype.flush = function(applicator, bypassTransport)
     return self._flush(applicator);
 }
 
-},{"../channel":105,"fp-modules-for-nodejs/lib/http-client":115,"fp-modules-for-nodejs/lib/json":116,"fp-modules-for-nodejs/lib/util":122}],107:[function(require,module,exports){
+},{"../channel":106,"fp-modules-for-nodejs/lib/http-client":116,"fp-modules-for-nodejs/lib/json":117,"fp-modules-for-nodejs/lib/util":123}],108:[function(require,module,exports){
 
 var Dispatcher = exports.Dispatcher = function () {
     if (!(this instanceof exports.Dispatcher))
@@ -7436,7 +7437,7 @@ Dispatcher.prototype._dispatch = function(message, bypassReceivers) {
     this.channel.enqueueOutgoing(message, bypassReceivers);
 }
 
-},{}],108:[function(require,module,exports){
+},{}],109:[function(require,module,exports){
 
 var Message = exports.Message = function (dispatcher) {
     if (!(this instanceof exports.Message))
@@ -7494,7 +7495,7 @@ Message.prototype.getData = function() {
     return this.data;
 }
 
-},{}],109:[function(require,module,exports){
+},{}],110:[function(require,module,exports){
 
 var MESSAGE = require("./message");
 var JSON = require("fp-modules-for-nodejs/lib/json");
@@ -8334,7 +8335,7 @@ function chunk_split(value, length) {
     return parts;
 }
 
-},{"./message":108,"fp-modules-for-nodejs/lib/json":116,"fp-modules-for-nodejs/lib/util":122}],110:[function(require,module,exports){
+},{"./message":109,"fp-modules-for-nodejs/lib/json":117,"fp-modules-for-nodejs/lib/util":123}],111:[function(require,module,exports){
 
 var Receiver = exports.Receiver = function () {
     if (!(this instanceof exports.Receiver))
@@ -8425,7 +8426,7 @@ Receiver.prototype._dispatch = function(event, args) {
     return returnOptions;
 }
 
-},{}],111:[function(require,module,exports){
+},{}],112:[function(require,module,exports){
 
 var WILDFIRE = require("../wildfire"),
     JSON = require("fp-modules-for-nodejs/lib/json");
@@ -8555,7 +8556,7 @@ CallbackStream.prototype.receive = function(handler)
     this.receiveHandler = handler;
 }
 
-},{"../wildfire":113,"fp-modules-for-nodejs/lib/json":116}],112:[function(require,module,exports){
+},{"../wildfire":114,"fp-modules-for-nodejs/lib/json":117}],113:[function(require,module,exports){
 
 
 const RECEIVER_ID = "http://registry.pinf.org/cadorn.org/wildfire/@meta/receiver/transport/0";
@@ -8671,7 +8672,7 @@ throw new Error("OOPS!!!");
 }
 
 
-},{"./message":108,"./receiver":110,"./wildfire":113,"fp-modules-for-nodejs/lib/json":116,"fp-modules-for-nodejs/lib/md5":117,"fp-modules-for-nodejs/lib/struct":120}],113:[function(require,module,exports){
+},{"./message":109,"./receiver":111,"./wildfire":114,"fp-modules-for-nodejs/lib/json":117,"fp-modules-for-nodejs/lib/md5":118,"fp-modules-for-nodejs/lib/struct":121}],114:[function(require,module,exports){
 
 exports.Receiver = function() {
     return require("./receiver").Receiver();
@@ -8705,7 +8706,7 @@ exports.CallbackStream = function() {
     return require("./stream/callback").CallbackStream();
 }
 
-},{"./channel-httpheader":102,"./channel-postmessage":103,"./channel-shellcommand":104,"./channel/http-client":106,"./dispatcher":107,"./message":108,"./receiver":110,"./stream/callback":111}],114:[function(require,module,exports){
+},{"./channel-httpheader":103,"./channel-postmessage":104,"./channel-shellcommand":105,"./channel/http-client":107,"./dispatcher":108,"./message":109,"./receiver":111,"./stream/callback":112}],115:[function(require,module,exports){
 
 /* Binary */
 // -- tlrobinson Tom Robinson
@@ -9469,7 +9470,7 @@ ByteArray.prototype.toSource = function() {
 };
 
 
-},{"./platform/node/binary":119,"./util":122}],115:[function(require,module,exports){
+},{"./platform/node/binary":120,"./util":123}],116:[function(require,module,exports){
 
 //var ENGINE = require("./platform/{platform}/http-client");
 var ENGINE = require("./platform/browser/http-client");
@@ -9518,12 +9519,12 @@ exports.request = function(options, successCallback, errorCallback)
     return ENGINE.request(options, successCallback, errorCallback);
 }
 
-},{"./platform/browser/http-client":118,"./uri":121}],116:[function(require,module,exports){
+},{"./platform/browser/http-client":119,"./uri":122}],117:[function(require,module,exports){
 
 exports.encode = JSON.stringify;
 exports.decode = JSON.parse;
 
-},{}],117:[function(require,module,exports){
+},{}],118:[function(require,module,exports){
 
 /*!
     A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
@@ -9696,7 +9697,7 @@ var core_hmac_md5 = function (key, data, _characterSize) {
 };
 
 
-},{"./struct":120,"./util":122}],118:[function(require,module,exports){
+},{"./struct":121,"./util":123}],119:[function(require,module,exports){
 
 exports.request = function(options, successCallback, errorCallback)
 {
@@ -9744,7 +9745,7 @@ exports.request = function(options, successCallback, errorCallback)
     }
 }
 
-},{}],119:[function(require,module,exports){
+},{}],120:[function(require,module,exports){
 (function (Buffer){
 
 //var Buffer = require("../../buffer").Buffer;
@@ -9797,7 +9798,7 @@ exports.B_TRANSCODE = function(bytes, offset, length, sourceCharset, targetChars
 };
 
 }).call(this,require("buffer").Buffer)
-},{"buffer":128}],120:[function(require,module,exports){
+},{"buffer":130}],121:[function(require,module,exports){
 
 // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
 
@@ -10063,7 +10064,7 @@ exports.bin2hex = function (bin) {
     return str;
 }
 
-},{"./binary":114,"./util":122}],121:[function(require,module,exports){
+},{"./binary":115,"./util":123}],122:[function(require,module,exports){
 
 // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
 // gmosx, George Moschovitis
@@ -10486,7 +10487,7 @@ exports.pathToUri = function (path) {
 };
 */
 
-},{}],122:[function(require,module,exports){
+},{}],123:[function(require,module,exports){
 
 // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
 // -- isaacs Isaac Schlueter
@@ -11669,7 +11670,359 @@ exports.title = function (value, delimiter) {
 };
 
 
-},{}],123:[function(require,module,exports){
+},{}],124:[function(require,module,exports){
+
+var UTIL = require("fp-modules-for-nodejs/lib/util"),
+    JSON = require("fp-modules-for-nodejs/lib/json"),
+    ENCODER = require("../encoder/default");
+
+exports.EXTENDED = "EXTENDED";
+exports.SIMPLE = "SIMPLE";
+
+
+exports.generateFromMessage = function(message, format)
+{
+    format = format || exports.EXTENDED;
+
+    var og = new ObjectGraph();
+
+    var meta = {},
+        data;
+
+    if (typeof message.getMeta == "function")
+    {
+        meta = JSON.decode(message.getMeta() || "{}");
+    }
+    else
+    if (typeof message.meta == "string")
+    {
+        meta = JSON.decode(message.meta);
+    }
+    else
+    if (typeof message.meta == "object")
+    {
+        meta = message.meta;
+    }
+
+    if (typeof message.getData == "function")
+    {
+        data = message.getData();
+    }
+    else
+    if (typeof message.data != "undefined")
+    {
+        data = message.data;
+    }
+    else
+        throw new Error("NYI");
+
+    if(meta["msg.preprocessor"] && meta["msg.preprocessor"]=="FirePHPCoreCompatibility") {
+        var parts = convertFirePHPCoreData(meta, data);
+        if (typeof message.setMeta == "function")
+            message.setMeta(JSON.encode(parts[0]));
+        else
+            message.meta = JSON.encode(parts[0]);
+        data = parts[1];
+
+    } else
+    if(typeof data !== "undefined" && data != "") {
+        try {
+
+            data = JSON.decode(data);
+
+        } catch(e) {
+            console.error("Error decoding JSON data: " + data);
+            throw e;
+        }
+    } else {
+        data = {};
+    }
+
+    // assign group title to value if applicable
+    if(typeof meta["group.title"] != "undefined") {
+        data = {
+            "origin": {
+                "type": "string",
+                "string": meta["group.title"]
+            }
+        };
+    }
+
+    if(data.instances) {
+        for( var i=0 ; i<data.instances.length ; i++ ) {
+            data.instances[i] = generateNodesFromData(og, data.instances[i]);
+        }
+        og.setInstances(data.instances);
+    }
+
+    if(meta["lang.id"]) {
+        og.setLanguageId(meta["lang.id"]);
+    }
+
+    og.setMeta(meta);
+
+    if(UTIL.has(data, "origin")) {
+        if(format==exports.EXTENDED) {
+            og.setOrigin(generateNodesFromData(og, data.origin));
+        } else
+        if(format==exports.SIMPLE) {
+            og.setOrigin(generateObjectsFromData(og, data.origin));
+        } else {
+            throw new Error("unsupported format: " + format);
+        }
+    }
+
+    return og;
+}
+
+function generateObjectsFromData(objectGraph, data) {
+
+    var node;
+
+    if(data.type=="array") {
+        node = [];
+        for( var i=0 ; i<data[data.type].length ; i++ ) {
+            node.push(generateObjectsFromData(objectGraph, data[data.type][i]));
+        }
+    } else
+    if(data.type=="map") {
+        node = [];
+        for( var i=0 ; i<data[data.type].length ; i++ ) {
+            node.push([
+                generateObjectsFromData(objectGraph, data[data.type][i][0]),
+                generateObjectsFromData(objectGraph, data[data.type][i][1])
+            ]);
+        }
+    } else
+    if(data.type=="dictionary") {
+        node = {};
+        for( var name in data[data.type] ) {
+            node[name] = generateObjectsFromData(objectGraph, data[data.type][name]);
+        }
+    } else {
+        node = data[data.type];
+    }
+
+    return node;
+}
+
+
+function generateNodesFromData(objectGraph, data, parentNode) {
+    
+    parentNode = parentNode || null;
+    
+    var node = new Node(objectGraph, data, parentNode);
+    
+    if(node.value!==null && typeof node.value != "undefined") {
+        // some types need nested nodes decoded
+        if(node.type=="array") {
+            for( var i=0 ; i<node.value.length ; i++ ) {
+                node.value[i] = generateNodesFromData(objectGraph, node.value[i], node);
+            }
+        } else
+        if(node.type=="map") {
+            for( var i=0 ; i<node.value.length ; i++ ) {
+                node.value[i][0] = generateNodesFromData(objectGraph, node.value[i][0], node);
+                node.value[i][1] = generateNodesFromData(objectGraph, node.value[i][1], node);
+            }
+        } else
+        if(node.type=="dictionary") {
+            for( var name in node.value ) {
+                node.value[name] = generateNodesFromData(objectGraph, node.value[name], node);
+            }
+        }
+    } else {
+        node.value = null;
+    }
+
+    return node;
+}
+
+
+
+var Node = function(objectGraph, data, parentNode) {
+    var self = this;
+//    self.parentNode = parentNode || null;
+    self.type = data.type;
+    self.value = (typeof data.value !== "undefined" && data.value) || date[data.type];
+    self.meta = objectGraph.meta || {};
+    UTIL.every(data, function(item) {
+        if(item[0]!="type" && item[0]!=self.type) {
+            self.meta[item[0]] = item[1];
+        }
+    });
+    if(self.type=="reference") {
+        self.getInstance = function() {
+            return objectGraph.getInstance(self.value);
+        }
+    }
+//    self.getObjectGraph = function() {
+//        return objectGraph;
+//    }
+}
+
+Node.prototype.getTemplateId = function() {
+    if(UTIL.has(this.meta, "tpl.id")) {
+        return this.meta["tpl.id"];
+    }
+    return false;
+}
+
+Node.prototype.compact = function() {
+    if(!this.compacted) {
+        if(this.type=="map") {
+            this.compacted = {};
+            for( var i=0 ; i<this.value.length ; i++ ) {
+                this.compacted[this.value[i][0].value] = this.value[i][1];
+            }
+        }
+    }
+    return this.compacted;
+}
+/*
+Node.prototype.getPath = function(locateChild) {
+    var path = [];
+    if (this.parentNode)
+        path = path.concat(this.parentNode.getPath(this));
+    else
+        path = path.concat(this.getObjectGraph().getPath(this));
+    if (locateChild)
+    {
+        if(this.type=="map") {
+            for( var i=0 ; i<this.value.length ; i++ ) {
+                if (this.value[i][1] === locateChild)
+                {
+                    path.push("value[" + i + "][1]");
+                    break;
+                }
+            }
+        } else
+        if(this.type=="dictionary") {
+            for (var key in this.value)
+            {
+                if (this.value[key] === locateChild)
+                {
+                    path.push("value['" + key + "']");
+                    break;
+                }
+            }
+        } else
+        if(this.type=="array") {
+            for( var i=0 ; i<this.value.length ; i++ ) {
+                if (this.value[i] === locateChild)
+                {
+                    path.push("value[" + i + "]");
+                    break;
+                }
+            }
+        } else {
+console.error("NYI - getPath() for this.type = '" + this.type + "'", this);            
+        }
+    }
+    return path;
+}
+*/
+Node.prototype.forPath = function(path) {
+    if (!path || path.length === 0)
+        return this;
+    if(this.type=="map") {
+        var m = path[0].match(/^value\[(\d*)\]\[1\]$/);
+        return this.value[parseInt(m[1])][1].forPath(path.slice(1));
+    } else
+    if(this.type=="dictionary") {
+        var m = path[0].match(/^value\['(.*?)'\]$/);
+        return this.value[m[1]].forPath(path.slice(1));
+    } else
+    if(this.type=="array") {
+        var m = path[0].match(/^value\[(\d*)\]$/);
+        return this.value[parseInt(m[1])].forPath(path.slice(1));
+    } else {
+//console.error("NYI - forPath('" + path + "') for this.type = '" + this.type + "'", this);            
+    }
+    return null;
+}
+
+//Node.prototype.renderIntoViewer = function(viewerDocument, options) {
+//    throw new Error("NYI - Node.prototype.renderIntoViewer in " + module.id);
+//    return RENDERER.renderIntoViewer(this, viewerDocument, options);
+//}
+
+
+var ObjectGraph = function() {
+//    this.message = message;
+}
+//ObjectGraph.prototype = Object.create(new Node());
+
+ObjectGraph.prototype.setOrigin = function(node) {
+    this.origin = node;
+}
+
+ObjectGraph.prototype.getOrigin = function() {
+    return this.origin;
+}
+
+ObjectGraph.prototype.setInstances = function(instances) {
+    this.instances = instances;
+}
+
+ObjectGraph.prototype.getInstance = function(index) {
+    return this.instances[index];
+}
+
+ObjectGraph.prototype.setLanguageId = function(id) {
+    this.languageId = id;
+}
+
+ObjectGraph.prototype.getLanguageId = function() {
+    return this.languageId;
+}
+
+ObjectGraph.prototype.setMeta = function(meta) {
+    this.meta = meta;
+}
+
+ObjectGraph.prototype.getMeta = function() {
+    return this.meta;
+}
+/*
+ObjectGraph.prototype.getPath = function(locateChild) {
+    if (this.origin === locateChild)
+    {
+        return ["origin"];
+    }
+    for( var i=0 ; i<this.instances.length ; i++ ) {
+        if (this.instances[i] === locateChild)
+        {
+            return ["instances[" + i + "]"];
+        }
+    }
+    throw new Error("Child node not found. We should never reach this!");
+}
+*/
+ObjectGraph.prototype.nodeForPath = function(path) {
+    var m = path[0].match(/^instances\[(\d*)\]$/);
+    if (m) {
+        return this.instances[parseInt(m[1])].forPath(path.slice(1));
+    } else {
+        // assume path[0] == 'origin'
+        return this.origin.forPath(path.slice(1));
+    }
+    return node;
+}
+
+
+var encoder = ENCODER.Encoder();
+encoder.setOption("maxObjectDepth", 1000);
+encoder.setOption("maxArrayDepth", 1000);
+encoder.setOption("maxOverallDepth", 1000);
+function convertFirePHPCoreData(meta, data) {
+    data = encoder.encode(JSON.decode(data), null, {
+        "jsonEncode": false
+    });
+    return [meta, data]; 
+}
+
+},{"../encoder/default":125,"fp-modules-for-nodejs/lib/json":126,"fp-modules-for-nodejs/lib/util":127}],125:[function(require,module,exports){
 
 var UTIL = require("fp-modules-for-nodejs/lib/util");
 var JSON = require("fp-modules-for-nodejs/lib/json");
@@ -12080,11 +12433,11 @@ Encoder.prototype.encodeObject = function(meta, object, objectDepth, arrayDepth,
 
     return ret;
 }
-},{"fp-modules-for-nodejs/lib/json":124,"fp-modules-for-nodejs/lib/util":125}],124:[function(require,module,exports){
-arguments[4][116][0].apply(exports,arguments)
-},{"dup":116}],125:[function(require,module,exports){
-arguments[4][122][0].apply(exports,arguments)
-},{"dup":122}],126:[function(require,module,exports){
+},{"fp-modules-for-nodejs/lib/json":126,"fp-modules-for-nodejs/lib/util":127}],126:[function(require,module,exports){
+arguments[4][117][0].apply(exports,arguments)
+},{"dup":117}],127:[function(require,module,exports){
+arguments[4][123][0].apply(exports,arguments)
+},{"dup":123}],128:[function(require,module,exports){
 
 const WINDOW = window;
 
@@ -12125,7 +12478,7 @@ function Renderer (options) {
                     !rootNode.instances[node.value]
                 ) {
                     console.error("node", node);
-                    throw new Error("Object instance for reference '" + node.value + "' not found 'instances'!");
+                    throw new Error("Object instance for reference '" + node.value + "' not found in 'instances'!");
                 }
                 return rootNode.instances[node.value];
             }
@@ -12136,74 +12489,84 @@ function Renderer (options) {
     var context = new InsightDomplateContext();
 
     function ensureRepsForNodeLoaded (node) {
+        try {
+            // TODO: Optionally pre-fill with already loaded reps.
+            // TODO: Move node traversal into helper module.
+            var loadTypes = {
+    //            "default/unknown": true
+            };
+            function traverse (node) {
 
-        // TODO: Optionally pre-fill with already loaded reps.
-        // TODO: Move node traversal into helper module.
-        var loadTypes = {
-//            "default/unknown": true
-        };
-        function traverse (node) {
-
-            if (node.type) {
-                loadTypes["default/" + node.type] = true;
-            }
+                if (node.type) {
+                    loadTypes["default/" + node.type] = true;
+                }
 
-            if (node.meta) {
-                if (node.meta["encoder.trimmed"]) {
-                    loadTypes["default/trimmed"] = true;
-                } else
-                // DEPRECATED
-                if (node.meta.renderer === "structures/table") {
-                    loadTypes["default/table"] = true;
-                    loadTypes["default/string"] = true;
-                    node.type = "table";
-                } else
-                // DEPRECATED
-                if (node.meta.renderer === "structures/trace") {
-                    loadTypes["default/trace"] = true;
-                    loadTypes["default/string"] = true;
-                    node.type = "trace";
-                } else
-                if (
-                    node.meta["lang"] &&
-                    node.meta["lang.type"]
-                ) {
-                    // TODO: Lookup 'node.meta["lang"]/node.meta["lang.type"]' and see
-                    // what it extends to determine what sub-structures to parse.
-                    if (node.meta["lang"] === "php") {
-                        if (node.meta["lang.type"] === "array") {
-                            if (node.value[0] && Array.isArray(node.value[0])) {
+                if (node.meta) {
+                    if (node.meta["encoder.trimmed"]) {
+                        loadTypes["default/trimmed"] = true;
+                    } else
+                    // DEPRECATED
+                    if (node.meta.renderer === "structures/table") {
+                        loadTypes["default/table"] = true;
+                        loadTypes["default/string"] = true;
+                        node.type = "table";
+                    } else
+                    // DEPRECATED
+                    if (node.meta.renderer === "structures/trace") {
+                        loadTypes["default/trace"] = true;
+                        loadTypes["default/string"] = true;
+                        node.type = "trace";
+                    } else
+                    if (
+                        node.meta["lang"] &&
+                        node.meta["lang.type"]
+                    ) {
+                        // TODO: Lookup 'node.meta["lang"]/node.meta["lang.type"]' and see
+                        // what it extends to determine what sub-structures to parse.
+                        if (node.meta["lang"] === "php") {
+                            if (node.meta["lang.type"] === "array") {
+                                if (node.value[0] && Array.isArray(node.value[0])) {
+                                    loadTypes["php/array-associative"] = true;
+                                    node.value.forEach(function (pair) {
+                                        traverse(pair[0]);
+                                        traverse(pair[1]);
+                                    });
+                                } else {
+                                    loadTypes["php/array-indexed"] = true;
+                                    node.value.forEach(function (node) {
+                                        traverse(node);
+                                    });
+                                }
+                            } else
+                            if (node.meta["lang.type"] === "map") {
                                 loadTypes["php/array-associative"] = true;
                                 node.value.forEach(function (pair) {
                                     traverse(pair[0]);
                                     traverse(pair[1]);
                                 });
-                            } else {
-                                loadTypes["php/array-indexed"] = true;
-                                node.value.forEach(function (node) {
-                                    traverse(node);
-                                });
-                            }
-                        } else
-                        if (node.meta["lang.type"] === "map") {
-                            loadTypes["php/array-associative"] = true;
-                            node.value.forEach(function (pair) {
-                                traverse(pair[0]);
-                                traverse(pair[1]);
-                            });
-                        } else
-                        if (node.meta["lang.type"] === "exception") {
-                            loadTypes["php/exception"] = true;
-                            loadTypes["default/string"] = true;
-                            if (node.value.title) {
-                                traverse(node.value.title);
-                            }
-                            if (node.value.stack) {
-                                node.value.stack.forEach(function (frame) {
-                                    frame.args.forEach(function (arg) {
-                                        traverse(arg);
+                            } else
+                            if (node.meta["lang.type"] === "exception") {
+                                loadTypes["php/exception"] = true;
+                                loadTypes["default/string"] = true;
+                                if (node.value.title) {
+                                    traverse(node.value.title);
+                                }
+                                if (node.value.stack) {
+                                    node.value.stack.forEach(function (frame) {
+                                        frame.args.forEach(function (arg) {
+                                            traverse(arg);
+                                        });
                                     });
-                                });
+                                }
+                            } else {
+                                loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
+
+                                if (node.meta["lang.type"] === "table") {
+                                    loadTypes["default/string"] = true;
+                                } else
+                                if (node.meta["lang.type"] === "trace") {
+                                    loadTypes["default/string"] = true;
+                                }    
                             }
                         } else {
                             loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
@@ -12213,116 +12576,119 @@ function Renderer (options) {
                             } else
                             if (node.meta["lang.type"] === "trace") {
                                 loadTypes["default/string"] = true;
-                            }    
+                            } else
+                            if (node.meta["lang.type"] === "pathtree") {
+                                loadTypes["default/string"] = true;
+                            } else
+                            if (node.meta["lang.type"] === "optiontree") {
+                                loadTypes["default/string"] = true;
+                            }
                         }
-                    } else {
-                        loadTypes[node.meta["lang"] + "/" + node.meta["lang.type"]] = true;
-
-                        if (node.meta["lang.type"] === "table") {
-                            loadTypes["default/string"] = true;
-                        } else
-                        if (node.meta["lang.type"] === "trace") {
+    /*                    
+                    } else
+                    if (node.meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers/packages/php/master") {
+                        if (node.meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/table") {
+                            loadTypes["default/table"] = true;
                             loadTypes["default/string"] = true;
+                            node.type = "table";
                         }
+    */
                     }
-/*                    
-                } else
-                if (node.meta["lang.id"] === "registry.pinf.org/cadorn.org/github/renderers/packages/php/master") {
-                    if (node.meta["renderer"] === "http://registry.pinf.org/cadorn.org/renderers/packages/insight/0:structures/table") {
-                        loadTypes["default/table"] = true;
-                        loadTypes["default/string"] = true;
-                        node.type = "table";
-                    }
-*/
-                }
-                if (node.meta.wrapper) {
-                    loadTypes[node.meta.wrapper] = true;
+                    if (node.meta.wrapper) {
+                        loadTypes[node.meta.wrapper] = true;
 
-                    if (node.meta.wrapper === "wrappers/request") {
-                        if (node.value.title) {
-                            traverse(node.value.title);
+                        if (node.meta.wrapper === "wrappers/request") {
+                            if (node.value.title) {
+                                traverse(node.value.title);
+                            }
                         }
                     }
                 }
-            }
 
-            if (typeof node.value !== 'undefined') {
+                if (
+                    node.value !== null &&
+                    typeof node.value !== 'undefined'
+                ) {
 
-                let type = node.type || node.meta["lang.type"];
+                    let type = node.type || node.meta["lang.type"];
 
-                if (type === "array") {
-                    node.value.forEach(function (node) {
-                        traverse(node);
-                    });
-                } else
-                if (type === "dictionary") {
-                    Object.keys(node.value).forEach(function (key) {
-                        traverse(node.value[key]);
-                    });
-                } else
-                if (type === "map") {
-                    node.value.forEach(function (pair) {
-                        traverse(pair[0]);
-                        traverse(pair[1]);
-                    });
-                } else
-                if (type === "reference") {
-                    if (node.value.instance) {
-                        traverse(node.value.instance);
+                    if (type === "array") {
+                        node.value.forEach(function (node) {
+                            traverse(node);
+                        });
                     } else
-                    if (
-                        node.instances &&
-                        typeof node.value === "number"
-                    ) {
-                        traverse(node.instances[node.value]);
+                    if (type === "dictionary") {
+                        Object.keys(node.value).forEach(function (key) {
+                            traverse(node.value[key]);
+                        });
                     } else
-                    if (typeof node.getInstance === 'function') {
-                        traverse(node.getInstance());
-                    }
-                } else
-                if (type === "table") {
-                    if (node.value.title) {
-                        traverse(node.value.title);
-                    }
-                    if (node.value.header) {
-                        node.value.header.forEach(function (node) {
-                            traverse(node);
+                    if (type === "map") {
+                        node.value.forEach(function (pair) {
+                            traverse(pair[0]);
+                            traverse(pair[1]);
                         });
-                    }
-                    if (node.value.body) {
-                        node.value.body.forEach(function (row) {
-                            row.forEach(function (cell) {
-                                traverse(cell);
+                    } else
+                    if (type === "reference") {
+                        if (node.value.instance) {
+                            traverse(node.value.instance);
+                        } else
+                        if (
+                            node.instances &&
+                            typeof node.value === "number"
+                        ) {
+                            traverse(node.instances[node.value]);
+                        } else
+                        if (typeof node.getInstance === 'function') {
+                            traverse(node.getInstance());
+                        }
+                    } else
+                    if (type === "table") {
+                        if (node.value.title) {
+                            traverse(node.value.title);
+                        }
+                        if (node.value.header) {
+                            node.value.header.forEach(function (node) {
+                                traverse(node);
                             });
-                        });
-                    }
-                } else
-                if (type === "trace") {
-                    if (node.value.title) {
-                        traverse(node.value.title);
-                    }
-                    if (node.value.stack) {
-                        node.value.stack.forEach(function (frame) {
-                            frame.args.forEach(function (arg) {
-                                traverse(arg);
+                        }
+                        if (node.value.body) {
+                            node.value.body.forEach(function (row) {
+                                row.forEach(function (cell) {
+                                    traverse(cell);
+                                });
                             });
-                        });
+                        }
+                    } else
+                    if (type === "trace") {
+                        if (node.value.title) {
+                            traverse(node.value.title);
+                        }
+                        if (node.value.stack) {
+                            node.value.stack.forEach(function (frame) {
+                                frame.args.forEach(function (arg) {
+                                    traverse(arg);
+                                });
+                            });
+                        }
                     }
                 }
             }
-        }
-        traverse(node);
+            traverse(node);
 
-        return Promise.all(Object.keys(loadTypes).map(function (type) {
+            return Promise.all(Object.keys(loadTypes).map(function (type) {
 
-            type = type.split("/");
+                type = type.split("/");
 
-            var repUri = loader.repUriForType(type[0], type[1]);
+                var repUri = loader.repUriForType(type[0], type[1]);
 
-            return loader.ensureRepForUri(repUri).then(function () {
-                return null;
-            });
-        }));
+                return loader.ensureRepForUri(repUri).then(function () {
+                    return null;
+                });
+            }));
+        } catch (err) {
+            console.error('Error checking node:', node);
+            throw err;
+        }
     }
 
     self.renderNodeInto = function (node, selectorOrElement, options) {
@@ -12502,7 +12868,7 @@ function Loader (options) {
 
 exports.Loader = Loader;
 
-},{"domplate/dist/domplate.js":1}],127:[function(require,module,exports){
+},{"domplate/dist/domplate.js":1}],129:[function(require,module,exports){
 'use strict'
 
 exports.byteLength = byteLength
@@ -12655,7 +13021,7 @@ function fromByteArray (uint8) {
   return parts.join('')
 }
 
-},{}],128:[function(require,module,exports){
+},{}],130:[function(require,module,exports){
 /*!
  * The buffer module from node.js, for the browser.
  *
@@ -14434,7 +14800,7 @@ function numberIsNaN (obj) {
   return obj !== obj // eslint-disable-line no-self-compare
 }
 
-},{"base64-js":127,"ieee754":129}],129:[function(require,module,exports){
+},{"base64-js":129,"ieee754":131}],131:[function(require,module,exports){
 exports.read = function (buffer, offset, isLE, mLen, nBytes) {
   var e, m
   var eLen = (nBytes * 8) - mLen - 1
@@ -14520,7 +14886,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
   buffer[offset + i - d] |= s * 128
 }
 
-},{}],130:[function(require,module,exports){
+},{}],132:[function(require,module,exports){
 // shim for using process in browser
 var process = module.exports = {};
 
diff --git a/dist/firephp.build/scripts/devtools/dist/index.html b/dist/firephp.build/scripts/devtools/dist/index.html
new file mode 100644
index 0000000..dc870c5
--- /dev/null
+++ b/dist/firephp.build/scripts/devtools/dist/index.html
@@ -0,0 +1,27 @@
+
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <script src="../../../lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/pinf-loader-full.browser.js"></script>
+        <script src="../../../lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/loader.js"></script>
+        <script src="../../../lib/eventemitter3.js"></script>
+        <script src="./dist/riot.csp.js"></script>
+        <script src="../../../lib.js"></script>        
+        <link rel="stylesheet" href="../../../default.css">
+
+        <script src="../../../lib/codemirror.js"></script>
+<link rel="stylesheet" href="../../../lib/codemirror.css">
+<script src="../../../lib/codemirror/addon/selection/active-line.js"></script>
+<script src="../../../lib/codemirror/mode/xml/xml.js"></script>
+<script src="../../../lib/codemirror/mode/javascript/javascript.js"></script>
+<script src="../../../lib/codemirror/mode/css/css.js"></script>
+<script src="../../../lib/codemirror/mode/htmlmixed/htmlmixed.js"></script>
+<script src="../../../lib/codemirror/mode/clike/clike.js"></script>
+<script src="../../../lib/codemirror/mode/php/php.js"></script>
+
+        <script src="./index.loader.js"></script>
+    </head>
+    <body>
+    </body>
+</html>
diff --git a/dist/firephp.build/scripts/devtools/index.js b/dist/firephp.build/scripts/devtools/dist/index.js
similarity index 96%
rename from dist/firephp.build/scripts/devtools/index.js
rename to dist/firephp.build/scripts/devtools/dist/index.js
index 644ec97..8b5d1ae 100644
--- a/dist/firephp.build/scripts/devtools/index.js
+++ b/dist/firephp.build/scripts/devtools/dist/index.js
@@ -17,12 +17,13 @@ if (WINDOW.document.body.innerHTML.replace(/[\s\n]*/g, "")) {
   console.error(new Error("Cannot render jsonrep page. Page 'body' element is not empty!"));
 } else {
   WINDOW.document.body.setAttribute("renderer", "jsonrep");
-  WINDOW.document.body.innerHTML = "{\"@layout\":{\"console\":{\"@console\":{\"@fireconsole\":{}}},\"menu\":{\"@menu\":{}},\"settings\":{\"@enabler\":{}},\"inspector\":{\"@inspector\":{}},\"manage\":{\"@manage\":{\"settings\":{\"@settings\":{}}}}}}";
+  WINDOW.document.body.innerHTML = "{\"@layout\":{\"console\":{\"@console\":{\"@fireconsole\":{}}},\"menu\":{\"@menu\":{}},\"settings\":{\"@enabler\":{}},\"inspector\":{\"@inspector\":{}},\"editor\":{\"@editor\":{}},\"manage\":{\"@manage\":{\"settings\":{\"@settings\":{}}}}}}";
   WINDOW.document.body.style.visibility = "hidden";
-  WINDOW.pmodule = pmodule;
 
   try {
     var JSONREP = require("./jsonrep");
+
+    JSONREP.options.ourBaseUri = bundle.module.filename.replace(/\/[^\/]+\/[^\/]+$/, '/dist');
   } catch (err) {
     console.error(err);
     throw err;
@@ -110,7 +111,7 @@ function makeExports(exports) {
     var keys = Object.keys(node);
 
     if (keys.length === 1 && /^@/.test(keys[0])) {
-      uri = keys[0].replace(/^@/, "") + ".rep";
+      uri = "/../" + keys[0].replace(/^@/, "") + ".rep";
       node = node[keys[0]];
     } else {
       if (exports.options.defaultRenderer) {
@@ -128,14 +129,10 @@ function makeExports(exports) {
           }
         });
       } else {
-        uri = "dist/insight.rep.js";
+        uri = "/reps/insight.rep.js";
       }
     }
 
-    if (/^dist\//.test(uri) && exports.options.ourBaseUri) {
-      uri = exports.options.ourBaseUri + "/" + uri;
-    }
-
     return exports.loadRenderer(uri).then(function (renderer) {
       return renderer.main(exports, node, {
         renderer: {
@@ -157,27 +154,30 @@ function makeExports(exports) {
       } else {
         exports.PINF = WINDOW.PINF;
       }
+    }
 
-      if (WINDOW.document) {
+    exports.getBaseUri = function () {
+      if (!exports.options.ourBaseUri && typeof WINDOW.document !== "undefined") {
         if (!exports.options.ourBaseUri) {
           var ourBaseUri = Array.from(WINDOW.document.querySelectorAll('SCRIPT[src]')).filter(function (tag) {
             return /\/jsonrep(\.min)?\.js$/.test(tag.getAttribute("src"));
           });
 
-          if (!ourBaseUri.length) {
-            exports.options.ourBaseUri = "";
-          } else {
-            exports.options.ourBaseUri = ourBaseUri[0].getAttribute("src").split("/").slice(0, -2).join("/");
+          if (ourBaseUri.length) {
+            exports.options.ourBaseUri = ourBaseUri[0].getAttribute("src").replace(/\/[^\/]+$/, '');
           }
         }
       }
-    }
 
-    exports.loadStyle = function (uri) {
-      if (WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
-        uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
+      if (!exports.options.ourBaseUri) {
+        throw new Error("[jsonrep] Could not determine 'ourBaseUri' by looking at 'options' and injected script tags!");
       }
 
+      return exports.options.ourBaseUri;
+    };
+
+    exports.loadStyle = function (uri) {
+      uri = "".concat(exports.getBaseUri(), "/").concat(uri).replace(/\/\.?\//g, '/');
       return new Promise(function (resolve, reject) {
         var link = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "link") : window.document.createElement("link");
         link.rel = "stylesheet";
@@ -193,10 +193,7 @@ function makeExports(exports) {
     };
 
     exports.loadRenderer = function (uri) {
-      if (WINDOW && typeof WINDOW.pmodule !== "undefined" && !/^\//.test(uri)) {
-        uri = [WINDOW.pmodule.filename.replace(/\/([^\/]*)\/([^\/]*)$/, ""), uri].join("/").replace(/\/\.?\//g, "/");
-      }
-
+      uri = "".concat(exports.getBaseUri(), "/").concat(uri).replace(/\/\.?\//g, '/');
       return new Promise(function (resolve, reject) {
         exports.PINF.sandbox(uri, resolve, reject);
       });
@@ -224,7 +221,7 @@ function makeExports(exports) {
             if (block._format === 'json') {
               var cssConfig = JSON.parse(block.getCode());
               el.setAttribute("_cssid", cssConfig._cssid);
-              exports.loadStyle(cssConfig.repUri + '.rep.css');
+              exports.loadStyle('../' + cssConfig.repUri + '.rep.css');
             } else if (block._format === 'css') {
               css = block.getCode();
             }
@@ -283,8 +280,6 @@ function makeExports(exports) {
 
   if (typeof sandbox !== "undefined") {
     init(sandbox);
-  } else if (WINDOW) {
-    WINDOW.jsonrep = init({});
   } else if (typeof exports !== "undefined") {
     init(exports);
   } else {
@@ -409,7 +404,7 @@ Codeblock.prototype.getCode = function () {
 **/
 
 (function (exports) {
- 
+
 // The global `require` for the 'external' (to the loader) environment.
 var Loader = function (global) {
 
@@ -456,7 +451,7 @@ var Loader = function (global) {
 	}
 
 	// @credit https://github.com/unscriptable/curl/blob/62caf808a8fd358ec782693399670be6806f1845/src/curl.js#L319-360
-	function loadInBrowser (uri, loadedCallback) {
+	function loadInBrowser (uri, loadedCallback, sandboxOptions) {
 		try {
 			// See if we are in a web worker.
 			if (typeof importScripts !== "undefined") {
@@ -484,7 +479,9 @@ var Loader = function (global) {
 				if (ev.type === "load" || readyStates[this.readyState]) {
 					this.onload = this.onreadystatechange = this.onerror = null;
 					loadedCallback(null, function () {
-						element.parentNode.removeChild(element);
+						if (!sandboxOptions || sandboxOptions.keepScriptTags !== true) {
+							element.parentNode.removeChild(element);
+						}
 					});
 				}
 			}
@@ -597,7 +594,8 @@ var Loader = function (global) {
 										cleanupCallback();
 									}
 								});
-							}
+							},
+							sandboxOptions
 						);
 					}
 				}
@@ -649,7 +647,8 @@ var Loader = function (global) {
 									function () {
 										pending -= 1;
 										finalize();
-									}
+									},
+									sandboxOptions
 								);
 							}
 						}
@@ -1138,7 +1137,7 @@ var Loader = function (global) {
 					loadedCallback(null);
 					return;
 				}
-				return fallbackLoad(uri, loadedCallback);
+				return fallbackLoad(uri, loadedCallback, options);
 			}
 			programIdentifier = bundle.uri || "#pinf:" + Math.random().toString(36).substr(2, 9);
 		}
diff --git a/dist/firephp.build/scripts/devtools/index.loader.js b/dist/firephp.build/scripts/devtools/dist/index.loader.js
similarity index 100%
rename from dist/firephp.build/scripts/devtools/index.loader.js
rename to dist/firephp.build/scripts/devtools/dist/index.loader.js
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.preview.htm
deleted file mode 100644
index 07a6460..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/array.preview.htm
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<span __dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48" __dtid="insight.domplate.reps/default/array" class="array "><span>array(</span><div class="element "><span class="value "></span><span class="separator ">,</span></div><div class="element "><span class="value "></span></div><span>)</span></span>
-
-<br/>
-
-<h2>collapsedTag</h2>
-
-<span __dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48" __dtid="insight.domplate.reps/default/array" class="array "><span>array(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48" __dtid="insight.domplate.reps/default/array" class="array "><span>array(</span><span class="element "><span class="value "></span><span class="separator ">,</span></span><span class="element "><span class="value "></span></span><span>)</span></span>
-
-<br/>
-
-<h2>expandableStub</h2>
-
-
-
-<br/>
-
-<h2>expandedStub</h2>
-
-
-
-<br/>
-
-<h2>moreTag</h2>
-
-<span __dbid="c8e051a0d8ba6235ed889e184aae731c7e813f48" __dtid="insight.domplate.reps/default/array"> ... </span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.preview.htm
deleted file mode 100644
index 3615324..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/dictionary.preview.htm
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="insight.domplate.reps/default/dictionary" class="dictionary "><span>dictionary(</span><div class="member "><span decorator="" class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></div><div class="member "><span decorator="" class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></div><span>)</span></span>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="insight.domplate.reps/default/dictionary" class="dictionary "><span>dictionary(</span><span class="member "><span class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></span><span class="member "><span class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></span><span>)</span></span>
-
-<br/>
-
-<h2>collapsedTag</h2>
-
-<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="insight.domplate.reps/default/dictionary" class="dictionary "><span>dictionary(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
-
-<br/>
-
-<h2>expandableStub</h2>
-
-
-
-<br/>
-
-<h2>expandedStub</h2>
-
-
-
-<br/>
-
-<h2>moreTag</h2>
-
-<span __dbid="91497e8f62a95c8946215eb31aaa2ffc2bf5cc84" __dtid="insight.domplate.reps/default/dictionary" class="more "> ... </span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.preview.htm
deleted file mode 100644
index 2a688a7..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/map.preview.htm
+++ /dev/null
@@ -1,28 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="insight.domplate.reps/default/map" class="map "><span>map(</span><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></div><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></div><span>)</span></span>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="insight.domplate.reps/default/map" class="map "><span>map(</span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></span><span>)</span></span>
-
-<br/>
-
-<h2>collapsedTag</h2>
-
-<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="insight.domplate.reps/default/map" class="map "><span>map(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
-
-<br/>
-
-<h2>moreTag</h2>
-
-<span __dbid="3dd180ce224195ebd5ae4041dd5384730e8c6534" __dtid="insight.domplate.reps/default/map"> ... </span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.preview.htm
deleted file mode 100644
index 7324bdd..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.preview.htm
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<span __dbid="bc26bd236f1fdd412abbeac376be6d7301b0432e" __dtid="insight.domplate.reps/default/string" class="string ">Hello World</span>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="bc26bd236f1fdd412abbeac376be6d7301b0432e" __dtid="insight.domplate.reps/default/string" class="string ">Hello World</span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.rep.css b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.rep.css
deleted file mode 100644
index 8b67b54..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/string.rep.css
+++ /dev/null
@@ -1,4 +0,0 @@
-
-SPAN[__dbid="bc26bd236f1fdd412abbeac376be6d7301b0432e"] {
-    color: black;
-}
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.preview.htm
deleted file mode 100644
index dc9aab8..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/table.preview.htm
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<div __dbid="39ff1ebd3d2ba584e6cc7b584b5b45791c136b55" __dtid="insight.domplate.reps/default/table" class="table "><table cellpadding="3" cellspacing="0"><tbody><tr class=" "><th class="header "></th><th class="header "></th></tr><tr><td class="cell "></td><td class="cell "></td></tr><tr><td class="cell "></td><td class="cell "></td></tr></tbody></table></div>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="39ff1ebd3d2ba584e6cc7b584b5b45791c136b55" __dtid="insight.domplate.reps/default/table" class="table "></span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.preview.htm
deleted file mode 100644
index 2671d6b..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/default/trace.preview.htm
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<div __dbid="02ce81bf834058ce5150d7be4d01e547baa84a2c" __dtid="insight.domplate.reps/default/trace" class="structures-trace "><table cellpadding="3" cellspacing="0"><tbody><tr><th class="header-file ">File</th><th class="header-line ">Line</th><th class="header-inst ">Instruction</th></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">10</td><td class="cell-inst "><div>Class(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">5</td><td class="cell-inst "><div>Function(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr></tbody></table></div>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="02ce81bf834058ce5150d7be4d01e547baa84a2c" __dtid="insight.domplate.reps/default/trace" class="structures-trace "></span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.preview.htm
deleted file mode 100644
index 0071d2e..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-associative.preview.htm
+++ /dev/null
@@ -1,28 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="insight.domplate.reps/php/array-associative" class="map "><span>map(</span><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></div><div class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></div><span>)</span></span>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="insight.domplate.reps/php/array-associative" class="map "><span>map(</span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span><span class="separator ">,</span></span><span class="pair "><span class="delimiter ">=&gt;</span><span class="value "></span></span><span>)</span></span>
-
-<br/>
-
-<h2>collapsedTag</h2>
-
-<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="insight.domplate.reps/php/array-associative" class="map "><span>map(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
-
-<br/>
-
-<h2>moreTag</h2>
-
-<span __dbid="4a95f694649c73f58a7f6356cfd1437e0be74823" __dtid="insight.domplate.reps/php/array-associative"> ... </span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.preview.htm
deleted file mode 100644
index f2d43d2..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/array-indexed.preview.htm
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="insight.domplate.reps/php/array-indexed" class="array "><span>array(</span><div class="element "><span class="value "></span><span class="separator ">,</span></div><div class="element "><span class="value "></span></div><span>)</span></span>
-
-<br/>
-
-<h2>collapsedTag</h2>
-
-<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="insight.domplate.reps/php/array-indexed" class="array "><span>array(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="insight.domplate.reps/php/array-indexed" class="array "><span>array(</span><span class="element "><span class="value "></span><span class="separator ">,</span></span><span class="element "><span class="value "></span></span><span>)</span></span>
-
-<br/>
-
-<h2>expandableStub</h2>
-
-
-
-<br/>
-
-<h2>expandedStub</h2>
-
-
-
-<br/>
-
-<h2>moreTag</h2>
-
-<span __dbid="e7edf46c5cf053a32cad8a256bef09bba0a1db61" __dtid="insight.domplate.reps/php/array-indexed"> ... </span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.preview.htm
deleted file mode 100644
index a560e9c..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/exception.preview.htm
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<div __dbid="900280dccb9220e71264efb50a24debff867f7f3" __dtid="insight.domplate.reps/php/exception" class="structures-trace "><table cellpadding="3" cellspacing="0"><tbody><tr><th class="header-file ">File</th><th class="header-line ">Line</th><th class="header-inst ">Instruction</th></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">10</td><td class="cell-inst "><div>Class(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr><tr><td class="cell-file ">File ...</td><td class="cell-line ">5</td><td class="cell-inst "><div>Function(<div class="arg "><span class="separator ">,</span></div><div class="arg "></div>)</div></td></tr></tbody></table></div>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="900280dccb9220e71264efb50a24debff867f7f3" __dtid="insight.domplate.reps/php/exception" class="structures-trace "></span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.preview.htm
deleted file mode 100644
index 1688447..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/object.preview.htm
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="insight.domplate.reps/php/object" class="dictionary "><span>Foo(</span><div class="member "><span decorator="" class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></div><div class="member "><span decorator="" class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></div><span>)</span></span>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="insight.domplate.reps/php/object" class="dictionary "><span>Foo(</span><span class="member "><span class="name ">key1</span><span class="delimiter ">:</span><span class="value "></span><span class="separator ">,</span></span><span class="member "><span class="name ">key2</span><span class="delimiter ">:</span><span class="value "></span></span><span>)</span></span>
-
-<br/>
-
-<h2>collapsedTag</h2>
-
-<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="insight.domplate.reps/php/object" class="dictionary "><span>Foo(</span><span class="collapsed ">... 2 ...</span><span>)</span></span>
-
-<br/>
-
-<h2>expandableStub</h2>
-
-
-
-<br/>
-
-<h2>expandedStub</h2>
-
-
-
-<br/>
-
-<h2>moreTag</h2>
-
-<span __dbid="fc2f04314c99af4367ff734e927c773cb9b1251b" __dtid="insight.domplate.reps/php/object" class="more "> ... </span>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.preview.htm
deleted file mode 100644
index 7b24740..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/php/unknown.preview.htm
+++ /dev/null
@@ -1,22 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<div __dbid="859944ec3afd0136c208c76b33fd54c9f08cb1a5" __dtid="insight.domplate.reps/php/unknown" class="unknown ">UNKNOWN EXPANDED</div>
-
-<br/>
-
-<h2>collapsedTag</h2>
-
-<div __dbid="859944ec3afd0136c208c76b33fd54c9f08cb1a5" __dtid="insight.domplate.reps/php/unknown" class="unknown ">UNKNOWN COLLAPSED</div>
-
-<br/>
-
-<h2>shortTag</h2>
-
-<div __dbid="859944ec3afd0136c208c76b33fd54c9f08cb1a5" __dtid="insight.domplate.reps/php/unknown" class="unknown ">UNKNOWN SHORT</div>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.preview.htm
deleted file mode 100644
index 77b738f..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.preview.htm
+++ /dev/null
@@ -1,22 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<div expandable="true" expanded="false" __dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9" __dtid="insight.domplate.reps/wrappers/console" class="console-message "><div hideonexpand="true" class="header "><div class="expander "></div><div class="actions "><div class="inspect  "></div><div class="file hidden "></div></div><span allowtagexpand="false" class="summary "><span class="label "></span></span><span class="fileline "><div></div></span></div><div class="body "></div></div>
-
-<br/>
-
-<h2>shortTag</h2>
-
-
-
-<br/>
-
-<h2>groupNoMessagesTag</h2>
-
-<div __dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9" __dtid="insight.domplate.reps/wrappers/console" class="group-no-messages ">No Messages</div>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.rep.css b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.rep.css
deleted file mode 100644
index baf87d7..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/console.rep.css
+++ /dev/null
@@ -1,181 +0,0 @@
-
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] {
-    position: relative;
-    margin: 0;
-    border-bottom: 1px solid #D7D7D7;
-    padding: 0px;
-    background-color: #FFFFFF;
-}
-DIV.console-message.selected[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] {
-    background-color: #35FC03 !important;
-}
-DIV.console-message-group[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expanded=true] {
-    background-color: #77CDD9;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header {
-    position: relative;
-    padding-left: 34px;
-    padding-right: 10px;
-    padding-top: 3px;
-    padding-bottom: 4px;
-    cursor: pointer;
-    min-height: 16px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expanded=true] > DIV.header > SPAN.summary {
-    opacity: 0.4;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expanded=false] > DIV.header:hover {
-    background-color: #ffc73d;
-}
-DIV.console-message-group[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header {
-    background: url(images/document_page_next.png) no-repeat;
-    background-position: 2px 3px;
-    font-weight: bold;
-    background-color: #77CDD9;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header-priority-info {
-    background: url(images/information.png) no-repeat;
-    background-position: 2px 3px;
-    background-color: #c6eeff;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header-priority-warn {
-    background: url(images/exclamation-diamond.png) no-repeat;
-    background-position: 2px 3px;
-    background-color: #ffe68d;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header-priority-error {
-    background: url(images/exclamation-red.png) no-repeat;
-    background-position: 2px 3px;
-    background-color: #ffa7a7;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > DIV.expander {
-    background-color: black;
-    width: 18px;
-    height: 18px;
-    display: inline-block;
-    float: left;
-    position: relative;
-    top: -1px;
-    margin-left: -14px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > DIV.expander:hover {
-    cursor: pointer;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expanded=false] > DIV.header > DIV.expander {
-    background: url(images/plus-small-white.png) no-repeat;
-    background-position: 0px 1px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expanded=true] > DIV.header > DIV.expander {
-    background: url(images/minus-small-white.png) no-repeat;
-    background-position: 0px 1px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > SPAN.summary > SPAN.label > SPAN, DIV.console-message > DIV.header > SPAN.fileline > DIV > DIV.label {
-    margin-right: 5px;
-    background-color: rgba(69,68,60,1);
-    padding-left: 5px;
-    padding-right: 5px;
-    color: white;
-    vertical-align: top;
-    margin-top: 1px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > SPAN.fileline > DIV > DIV.label {
-    float: left;
-    margin-top: 0px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > SPAN.summary > SPAN > SPAN.count {
-    color: #525252;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > SPAN.fileline {
-    color: #525252;
-    word-wrap: break-word;
-    float: right;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expandedSummary=true] > DIV.header > SPAN.summary {
-    display: none;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][keeptitle=true] > DIV.header, DIV.console-message-group > DIV.header {
-    text-align: left !important;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][keeptitle=true] > DIV.header > SPAN.fileline, DIV.console-message-group > DIV.header > SPAN.fileline {
-    display: none !important;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][keeptitle=true] > DIV.header > SPAN.summary, DIV.console-message-group > DIV.header > SPAN.summary {
-    display: inline !important;
-}
-DIV.console-message-group[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > DIV.actions {
-    display: none !important;
-}
-DIV.console-message-group[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > SPAN.summary > SPAN > SPAN.count {
-    color: #ffffff !important;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expanded=false] > DIV.header > SPAN.fileline {
-    display: none;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > DIV.actions {
-    display: inline-block;
-    position: relative;
-    top: 0px;
-    left: 10px;
-    float: right;
-    margin-left: 0px;
-    margin-right: 5px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > DIV.actions DIV.inspect {
-    display: inline-block;
-    background: url(images/node-magnifier.png) no-repeat;
-    width: 16px;
-    height: 16px;
-    margin-right: 4px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > DIV.actions > DIV.file {
-    display: inline-block;
-    background: url(images/document-binary.png) no-repeat;
-    width: 16px;
-    height: 16px;
-    margin-right: 4px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.header > DIV.actions > DIV.inspect:hover, DIV.console-message > DIV.header > DIV.actions > DIV.file:hover {
-    cursor: pointer;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.body {
-    padding: 6px;
-    margin: 3px;
-    margin-top: 0px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"][expanded=false] > DIV.body {
-    display: none;
-}
-DIV.console-message-group[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.body {
-    padding: 0px;
-    margin: 0px;
-    margin-left: 20px;
-    border-top: 1px solid #000000;
-    border-left: 1px solid #000000;
-    margin-bottom: -1px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.body-priority-info {
-    border: 3px solid #c6eeff;
-    margin: 0px;
-    border-top: 0px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.body-priority-warn {
-    border: 3px solid #ffe68d;
-    margin: 0px;
-    border-top: 0px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.body-priority-error {
-    border: 3px solid #ffa7a7;
-    margin: 0px;
-    border-top: 0px;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] > DIV.body > DIV.group-no-messages {
-    background-color: white;
-    padding-left: 4px;
-    padding-right: 4px;
-    padding-top: 3px;
-    padding-bottom: 3px;
-    color: gray;
-}
-DIV.console-message[__dbid="3a7bebf32f91801fb7aab559c5946d27a0b717b9"] .hidden {
-    display: none !important;
-}
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.preview.htm
deleted file mode 100644
index 4ce6a59..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/request.preview.htm
+++ /dev/null
@@ -1,10 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<div expanded="true" __dbid="42e45d15f38354db25f807078ed4de450a918dea" __dtid="insight.domplate.reps/wrappers/request" class="console-request "><div class="header "><div class="expander "></div><div class="labels ">AJAX</div><span class="summary "></span></div><div class="body "></div></div>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.preview.htm b/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.preview.htm
deleted file mode 100644
index 2b350d9..0000000
--- a/dist/firephp.build/scripts/devtools/dist/insight.domplate.reps/wrappers/viewer.preview.htm
+++ /dev/null
@@ -1,10 +0,0 @@
-<html>
-<body>
-
-<h2>tag</h2>
-
-<div __dbid="81dd6cc349cc607697b22148aa1809050c0fcda4" __dtid="insight.domplate.reps/wrappers/viewer" class="viewer-harness "></div>
-
-
-</body>
-</html>
\ No newline at end of file
diff --git a/dist/firephp.build/scripts/devtools/inspector.rep.css b/dist/firephp.build/scripts/devtools/dist/inspector.rep.css
similarity index 53%
rename from dist/firephp.build/scripts/devtools/inspector.rep.css
rename to dist/firephp.build/scripts/devtools/dist/inspector.rep.css
index d4bb1e3..575380b 100644
--- a/dist/firephp.build/scripts/devtools/inspector.rep.css
+++ b/dist/firephp.build/scripts/devtools/dist/inspector.rep.css
@@ -1,9 +1,9 @@
 
-[_cssid="28a5003f8fc18a24ec32c4aa8527cfc59ac5db1d"].inspector {
+[_cssid="9c9be6e55e2065132117ad31082315bbfe861e49"].inspector {
     padding: 10px;
 }
 
-[_cssid="28a5003f8fc18a24ec32c4aa8527cfc59ac5db1d"].inspector > .close {
+[_cssid="9c9be6e55e2065132117ad31082315bbfe861e49"].inspector > .close {
     border: 1px solid #dcdcdc;
     font-weight: bold;
     float: right;
@@ -12,6 +12,6 @@
     line-height: 10px;
 }
 
-[_cssid="28a5003f8fc18a24ec32c4aa8527cfc59ac5db1d"].inspector > .viewer {
+[_cssid="9c9be6e55e2065132117ad31082315bbfe861e49"].inspector > .viewer {
     height: 100%;
 }                    
diff --git a/dist/firephp.build/scripts/devtools/inspector.rep.js b/dist/firephp.build/scripts/devtools/dist/inspector.rep.js
similarity index 87%
rename from dist/firephp.build/scripts/devtools/inspector.rep.js
rename to dist/firephp.build/scripts/devtools/dist/inspector.rep.js
index 9f82e96..170365d 100644
--- a/dist/firephp.build/scripts/devtools/inspector.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/inspector.rep.js
@@ -313,6 +313,7 @@ function debounce(func, wait, options) {
       }
       if (maxing) {
         // Handle invocations in a tight loop.
+        clearTimeout(timerId);
         timerId = setTimeout(timerExpired, wait);
         return invokeFunc(lastCallTime);
       }
@@ -526,7 +527,7 @@ exports.main = function (JSONREP, node, options) {
   return JSONREP.makeRep('<div class="inspector">' + '<div class="close" style="display: none;">x</div>' + '<div class="viewer"></div>' + '</div>', {
     css: {
       ".@": "github.com~0ink~codeblock/codeblock:Codeblock",
-      "_code": "{\"_cssid\":\"28a5003f8fc18a24ec32c4aa8527cfc59ac5db1d\",\"repUri\":\"inspector\"}",
+      "_code": "{\"_cssid\":\"9c9be6e55e2065132117ad31082315bbfe861e49\",\"repUri\":\"inspector\"}",
       "_format": "json",
       "_args": [],
       "_compiled": false
@@ -584,9 +585,8 @@ exports.main = function (JSONREP, node, options) {
           showMessage(info.node);
         });
         window.FC.on("inspectFile", function (info) {
-          showMessage({
-            type: "string",
-            value: "Viewing of files is not yet implemented."
+          comp.showView("editor", {
+            show: info
           });
         });
       }
@@ -603,6 +603,7 @@ var DEBOUNCE = require('lodash/debounce');
 
 exports.for = function (ctx) {
   var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
   events.currentContext = null;
   setImmediate(function () {
     onContextMessage(null);
@@ -625,38 +626,52 @@ exports.for = function (ctx) {
     }
   }
 
-  ctx.browser.runtime.onMessage.addListener(function (message) {
+  events.handleBroadcastMessage = function (message) {
     try {
-      if (!ctx.browser || !ctx.browser.devtools || !ctx.browser.devtools.inspectedWindow || !message.context || message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId) {
-        return;
-      }
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
 
-      if (message.to === "message-listener") {
-        if (message.event === "currentContext" && typeof message.context !== "undefined") {
-          onContextMessage(message.context);
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
         }
-
-        events.emit("message", message);
       }
     } catch (err) {
       console.error(err);
     }
-  });
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
   ctx.browser.storage.onChanged.addListener(function (changes, area) {
     try {
       if (!events.currentContext) {
         return;
       }
 
-      var prefix = "domain[".concat(events.currentContext.hostname, "].");
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
 
       for (var item of Object.keys(changes)) {
-        if (!/^domain\[.+\]\..+$/.test(item)) continue;
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
         if (item.substring(0, prefix.length) === prefix) {
           var name = item.substring(prefix.length);
-          events.emit("setting." + name, changes[item].newValue);
-          events.emit("changed.setting", name, changes[item].newValue);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
         }
       }
     } catch (err) {
@@ -670,7 +685,7 @@ exports.for = function (ctx) {
     });
   }, 250);
 
-  events.getSetting = function (name) {
+  events.getSetting = async function (name) {
     if (!events.currentContext) {
       return Promise.resolve(null);
     }
@@ -678,21 +693,47 @@ exports.for = function (ctx) {
     return events._getSettingForHostname(events.currentContext.hostname, name);
   };
 
-  events._getSettingForHostname = function (hostname, name) {
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
 
-    var key = "domain[" + hostname + "]." + name;
+    var key = "hostname[" + hostname + "]." + name;
     return ctx.browser.storage.local.get(key).then(function (value) {
-      return value[key] || false;
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setSetting = function (name, value) {
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
     if (!events.currentContext) {
       throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
     }
@@ -700,7 +741,7 @@ exports.for = function (ctx) {
     return events._setSettingForHostname(events.currentContext.hostname, name, value);
   };
 
-  events._setSettingForHostname = function (hostname, name, value) {
+  events._setSettingForHostname = async function (hostname, name, value) {
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
@@ -711,7 +752,9 @@ exports.for = function (ctx) {
       }
 
       var obj = {};
-      obj["domain[" + hostname + "]." + name] = value;
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -719,9 +762,9 @@ exports.for = function (ctx) {
     });
   };
 
-  events.getGlobalSetting = function (name) {
+  events.getGlobalSetting = async function (name) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     var defaultValue;
@@ -740,15 +783,18 @@ exports.for = function (ctx) {
       }
 
       return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setGlobalSetting = function (name, value) {
+  events.setGlobalSetting = async function (name, value) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     return events.getGlobalSetting(name).then(function (existingValue) {
@@ -758,6 +804,7 @@ exports.for = function (ctx) {
 
       var obj = {};
       obj[name] = value;
+      globalSettings[name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -765,14 +812,54 @@ exports.for = function (ctx) {
     });
   };
 
-  events.isConfigured = function () {
-    return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return enableUserAgentHeader || enableFirePHPHeader || enableChromeLoggerData;
-        });
-      });
-    });
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
   };
 
   events.reloadBrowser = function () {
@@ -792,15 +879,41 @@ exports.for = function (ctx) {
     });
   };
 
-  events.showView = function (name) {
+  events.showView = function (name, args) {
     if (name === "manage") {
       ctx.browser.runtime.sendMessage({
         to: "broadcast",
         event: "manage"
       });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
+    }
+  };
+
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
     }
   };
 
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
   return events;
 };
 }).call(this,require("timers").setImmediate)
diff --git a/dist/firephp.build/scripts/devtools/layout.rep.css b/dist/firephp.build/scripts/devtools/dist/layout.rep.css
similarity index 50%
rename from dist/firephp.build/scripts/devtools/layout.rep.css
rename to dist/firephp.build/scripts/devtools/dist/layout.rep.css
index 8ca616e..609659a 100644
--- a/dist/firephp.build/scripts/devtools/layout.rep.css
+++ b/dist/firephp.build/scripts/devtools/dist/layout.rep.css
@@ -4,72 +4,76 @@ BODY {
     overflow: hidden;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"].layout-views {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"].layout-views {
     height: 100%;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"].layout-views,
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"].layout-views TABLE {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"].layout-views,
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"].layout-views TABLE {
     font-family: Lucida Grande, Tahoma, sans-serif;
     font-size: 11px;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .layout TD {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .layout TD {
     vertical-align: top;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .layout TD.menu-panel {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .layout TD.menu-panel {
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .layout TD.settings-panel {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .layout TD.settings-panel {
     border-bottom: 1px solid #dcdcdc;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .layout TD.inspector-panel {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .layout TD.inspector-panel {
     min-width: 200px;
     overflow: auto;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .manage {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .manage {
     height: 100%;
     padding: 20px;
     background-color: #efefef;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .manage TABLE.header {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .editor {
+    height: 100%;
+}
+
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .manage TABLE.header {
     margin-bottom: 10px;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .manage TABLE.header TD {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .manage TABLE.header TD {
     height: 18px;
     min-height: 18px;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .manage TABLE.header TD.right {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .manage TABLE.header TD.right {
     text-align: right;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .manage TABLE.header .close-button {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .manage TABLE.header .close-button {
     display: none;
     cursor: pointer;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .manage TABLE.header .version {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .manage TABLE.header .version {
     display: none;
     color: #acacac;
 }
 
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .uninitialized {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .uninitialized {
     text-align: center;
     padding-top: 50px;
     height: 100%;
 }
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .uninitialized > P {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .uninitialized > P {
     font-weight: bold;
     font-size: 16px;
     color: #dcdcdc;
 }
-[_cssid="2f5dd500e1e4e62dff743510d9a36390d191d88e"] .uninitialized BUTTON {
+[_cssid="e6b4c093136308c5b33910848e833b17a8ffdc8a"] .uninitialized BUTTON {
     font-size: 16px;
     color: #dcdcdc;
     cursor: pointer;
diff --git a/dist/firephp.build/scripts/devtools/layout.rep.js b/dist/firephp.build/scripts/devtools/dist/layout.rep.js
similarity index 86%
rename from dist/firephp.build/scripts/devtools/layout.rep.js
rename to dist/firephp.build/scripts/devtools/dist/layout.rep.js
index 941fe0a..2db9644 100644
--- a/dist/firephp.build/scripts/devtools/layout.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/layout.rep.js
@@ -313,6 +313,7 @@ function debounce(func, wait, options) {
       }
       if (maxing) {
         // Handle invocations in a tight loop.
+        clearTimeout(timerId);
         timerId = setTimeout(timerExpired, wait);
         return invokeFunc(lastCallTime);
       }
@@ -550,14 +551,14 @@ exports.main = function (JSONREP, node, options) {
       },
       html: {
         ".@": "github.com~0ink~codeblock/codeblock:Codeblock",
-        "_code": "<div class=\"layout-views\">\\n    <div class=\"ui\" style=\"display: none;\">\\n        <table class=\"layout\" height=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\\n            <tr>\\n                <td class=\"console-panel\" width=\"100%\" height=\"100%\" rowspan=\"2\">\\n                    %%%variables.panels.console%%%\\n                </td>\\n            </tr>\\n            <tr>\\n                <td class=\"side-panel\">\\n                    <table class=\"layout\" height=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\\n                        <tr>\\n                            <td class=\"menu-panel\">\\n                                %%%variables.panels.menu%%%\\n                            </td>\\n                        </tr>\\n                        <tr>\\n                            <td class=\"settings-panel\">\\n                                %%%variables.panels.settings%%%\\n                            </td>\\n                        </tr>\\n                        <tr>\\n                            <td class=\"inspector-panel\" height=\"100%\">\\n                                %%%variables.panels.inspector%%%\\n                            </td>\\n                        </tr>\\n                    </table>\\n                </td>\\n            </tr>\\n        </table>\\n    </div>\\n    <div class=\"manage\" style=\"display: none;\">\\n        <table class=\"header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\\n            <tr>\\n                <td><button class=\"close-button\">Close</button></td>\\n                <td class=\"right\"><div class=\"version\" style=\"display: none;\"></div></td>\\n            </tr>\\n        </table>\\n        %%%variables.panels.manage%%%\\n    </div>\\n    <div class=\"uninitialized\" style=\"display: none;\">\\n        <p><button action=\"reload\">Reload</button> to initialize FirePHP</p>\\n    </div>\\n</div>",
+        "_code": "<div class=\"layout-views\">\\n    <div class=\"ui\" style=\"display: none;\">\\n        <table class=\"layout\" height=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\\n            <tr>\\n                <td class=\"console-panel\" width=\"100%\" height=\"100%\" rowspan=\"2\">\\n                    %%%variables.panels.console%%%\\n                </td>\\n            </tr>\\n            <tr>\\n                <td class=\"side-panel\">\\n                    <table class=\"layout\" height=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\\n                        <tr>\\n                            <td class=\"menu-panel\">\\n                                %%%variables.panels.menu%%%\\n                            </td>\\n                        </tr>\\n                        <tr>\\n                            <td class=\"settings-panel\">\\n                                %%%variables.panels.settings%%%\\n                            </td>\\n                        </tr>\\n                        <tr>\\n                            <td class=\"inspector-panel\" height=\"100%\">\\n                                %%%variables.panels.inspector%%%\\n                            </td>\\n                        </tr>\\n                    </table>\\n                </td>\\n            </tr>\\n        </table>\\n    </div>\\n    <div class=\"manage\" style=\"display: none;\">\\n        <table class=\"header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\\n            <tr>\\n                <td><button class=\"close-button\">Close</button></td>\\n                <td class=\"right\"><div class=\"version\" style=\"display: none;\"></div></td>\\n            </tr>\\n        </table>\\n        %%%variables.panels.manage%%%\\n    </div>\\n    <div class=\"uninitialized\" style=\"display: none;\">\\n        <p><button action=\"reload\">Reload</button> to initialize FirePHP</p>\\n    </div>\\n    <div class=\"editor\" style=\"display: none;\">\\n        %%%variables.panels.editor%%%\\n    </div>\\n</div>",
         "_format": "html",
         "_args": ["variables"],
         "_compiled": false
       },
       css: {
         ".@": "github.com~0ink~codeblock/codeblock:Codeblock",
-        "_code": "{\"_cssid\":\"2f5dd500e1e4e62dff743510d9a36390d191d88e\",\"repUri\":\"layout\"}",
+        "_code": "{\"_cssid\":\"e6b4c093136308c5b33910848e833b17a8ffdc8a\",\"repUri\":\"layout\"}",
         "_format": "json",
         "_args": [],
         "_compiled": false
@@ -567,20 +568,33 @@ exports.main = function (JSONREP, node, options) {
           var COMPONENT = require("./component");
 
           var forceManage = false;
+          var forceEditor = false;
           var comp = COMPONENT.for({
             browser: WINDOW.crossbrowser
           });
           comp.on("changed.context", function () {
             comp.contextChangeAcknowledged();
+            forceEditor = false;
             sync();
           });
           comp.on("changed.setting", function (name, enabled) {
             sync();
           });
           comp.on("message", function (message) {
+            console.log("message in layout", message);
+
             if (message.event === "manage") {
               forceManage = true;
               sync();
+            } else if (message.event === "editor") {
+              if (typeof message.value !== 'undefined') {
+                forceEditor = message.value;
+              } else {
+                forceEditor = true;
+              }
+
+              console.log("forceEditor in layout", forceEditor);
+              sync();
             }
           });
           var persistLogs = false;
@@ -604,13 +618,15 @@ exports.main = function (JSONREP, node, options) {
 
                 if (forceManage) {
                   view = "manage";
-                } else if (!configured && !enabled && !persistLogs) {
-                    view = "manage";
-                  } else if (persistLogs && enabled && !configured) {
+                } else if (forceEditor) {
+                    view = "editor";
+                  } else if (!configured && !enabled && !persistLogs) {
                       view = "manage";
-                    } else {
-                      view = "console";
-                    }
+                    } else if (persistLogs && enabled && !configured) {
+                        view = "manage";
+                      } else {
+                        view = "console";
+                      }
               } else {
                 view = "uninitialized";
               }
@@ -618,7 +634,8 @@ exports.main = function (JSONREP, node, options) {
               var toggles = {
                 ui: false,
                 manage: false,
-                uninitialized: false
+                uninitialized: false,
+                editor: false
               };
               var versionEl = el.querySelector("DIV.manage TABLE.header DIV.version");
               versionEl.style.display = "none";
@@ -637,6 +654,8 @@ exports.main = function (JSONREP, node, options) {
                 }
               } else if (view === "console") {
                 toggles.ui = true;
+              } else if (view === "editor") {
+                toggles.editor = true;
               } else {
                 throw new Error("'view' with value '".concat(view, "' not implemented!"));
               }
@@ -644,6 +663,7 @@ exports.main = function (JSONREP, node, options) {
               el.querySelector("DIV.ui").style.display = toggles.ui ? "block" : "none";
               el.querySelector("DIV.manage").style.display = toggles.manage ? "block" : "none";
               el.querySelector("DIV.uninitialized").style.display = toggles.uninitialized ? "block" : "none";
+              el.querySelector("DIV.editor").style.display = toggles.editor ? "block" : "none";
             } catch (err) {
               console.error("Error during sync():", err.message || err.stack || err);
               throw err;
@@ -682,6 +702,7 @@ var DEBOUNCE = require('lodash/debounce');
 
 exports.for = function (ctx) {
   var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
   events.currentContext = null;
   setImmediate(function () {
     onContextMessage(null);
@@ -704,38 +725,52 @@ exports.for = function (ctx) {
     }
   }
 
-  ctx.browser.runtime.onMessage.addListener(function (message) {
+  events.handleBroadcastMessage = function (message) {
     try {
-      if (!ctx.browser || !ctx.browser.devtools || !ctx.browser.devtools.inspectedWindow || !message.context || message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId) {
-        return;
-      }
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
 
-      if (message.to === "message-listener") {
-        if (message.event === "currentContext" && typeof message.context !== "undefined") {
-          onContextMessage(message.context);
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
         }
-
-        events.emit("message", message);
       }
     } catch (err) {
       console.error(err);
     }
-  });
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
   ctx.browser.storage.onChanged.addListener(function (changes, area) {
     try {
       if (!events.currentContext) {
         return;
       }
 
-      var prefix = "domain[".concat(events.currentContext.hostname, "].");
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
 
       for (var item of Object.keys(changes)) {
-        if (!/^domain\[.+\]\..+$/.test(item)) continue;
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
         if (item.substring(0, prefix.length) === prefix) {
           var name = item.substring(prefix.length);
-          events.emit("setting." + name, changes[item].newValue);
-          events.emit("changed.setting", name, changes[item].newValue);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
         }
       }
     } catch (err) {
@@ -749,7 +784,7 @@ exports.for = function (ctx) {
     });
   }, 250);
 
-  events.getSetting = function (name) {
+  events.getSetting = async function (name) {
     if (!events.currentContext) {
       return Promise.resolve(null);
     }
@@ -757,21 +792,47 @@ exports.for = function (ctx) {
     return events._getSettingForHostname(events.currentContext.hostname, name);
   };
 
-  events._getSettingForHostname = function (hostname, name) {
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
 
-    var key = "domain[" + hostname + "]." + name;
+    var key = "hostname[" + hostname + "]." + name;
     return ctx.browser.storage.local.get(key).then(function (value) {
-      return value[key] || false;
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setSetting = function (name, value) {
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
     if (!events.currentContext) {
       throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
     }
@@ -779,7 +840,7 @@ exports.for = function (ctx) {
     return events._setSettingForHostname(events.currentContext.hostname, name, value);
   };
 
-  events._setSettingForHostname = function (hostname, name, value) {
+  events._setSettingForHostname = async function (hostname, name, value) {
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
@@ -790,7 +851,9 @@ exports.for = function (ctx) {
       }
 
       var obj = {};
-      obj["domain[" + hostname + "]." + name] = value;
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -798,9 +861,9 @@ exports.for = function (ctx) {
     });
   };
 
-  events.getGlobalSetting = function (name) {
+  events.getGlobalSetting = async function (name) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     var defaultValue;
@@ -819,15 +882,18 @@ exports.for = function (ctx) {
       }
 
       return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setGlobalSetting = function (name, value) {
+  events.setGlobalSetting = async function (name, value) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     return events.getGlobalSetting(name).then(function (existingValue) {
@@ -837,6 +903,7 @@ exports.for = function (ctx) {
 
       var obj = {};
       obj[name] = value;
+      globalSettings[name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -844,14 +911,54 @@ exports.for = function (ctx) {
     });
   };
 
-  events.isConfigured = function () {
-    return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return enableUserAgentHeader || enableFirePHPHeader || enableChromeLoggerData;
-        });
-      });
-    });
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
   };
 
   events.reloadBrowser = function () {
@@ -871,15 +978,41 @@ exports.for = function (ctx) {
     });
   };
 
-  events.showView = function (name) {
+  events.showView = function (name, args) {
     if (name === "manage") {
       ctx.browser.runtime.sendMessage({
         to: "broadcast",
         event: "manage"
       });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
     }
   };
 
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
+    }
+  };
+
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
   return events;
 };
 }).call(this,require("timers").setImmediate)
diff --git a/dist/firephp.build/scripts/devtools/manage.rep.css b/dist/firephp.build/scripts/devtools/dist/manage.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/manage.rep.css
rename to dist/firephp.build/scripts/devtools/dist/manage.rep.css
diff --git a/dist/firephp.build/scripts/devtools/manage.rep.js b/dist/firephp.build/scripts/devtools/dist/manage.rep.js
similarity index 86%
rename from dist/firephp.build/scripts/devtools/manage.rep.js
rename to dist/firephp.build/scripts/devtools/dist/manage.rep.js
index 4f99d73..041c638 100644
--- a/dist/firephp.build/scripts/devtools/manage.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/manage.rep.js
@@ -313,6 +313,7 @@ function debounce(func, wait, options) {
       }
       if (maxing) {
         // Handle invocations in a tight loop.
+        clearTimeout(timerId);
         timerId = setTimeout(timerExpired, wait);
         return invokeFunc(lastCallTime);
       }
@@ -533,26 +534,28 @@ exports.main = function (JSONREP, node, options) {
           html: "<div></div>",
           "on": {
             "mount": function (el) {
-              riot.util.styleManager.add = function (cssText, name) {
-                if (!cssText) {
-                  return;
-                }
-
-                if (window.document.createStyleSheet) {
-                  var sheet = window.document.createStyleSheet();
-                  sheet.cssText = cssText;
-                } else {
-                  var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
-                  style.appendChild(window.document.createTextNode(cssText));
-                  var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
-                  head.appendChild(style);
-                }
-              };
-
-              riot.util.styleManager.inject = function () {
-                if (!options) return;
-                JSONREP.loadStyle(options.renderer.uri + '.css');
-              };
+              if ("1" === "1") {
+                riot.util.styleManager.inject = function () {
+                  if (!options) return;
+                  JSONREP.loadStyle(options.renderer.uri + '.css');
+                };
+              } else {
+                riot.util.styleManager.add = function (cssText, name) {
+                  if (!cssText) {
+                    return;
+                  }
+
+                  if (window.document.createStyleSheet) {
+                    var sheet = window.document.createStyleSheet();
+                    sheet.cssText = cssText;
+                  } else {
+                    var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
+                    style.appendChild(window.document.createTextNode(cssText));
+                    var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+                    head.appendChild(style);
+                  }
+                };
+              }
 
               riot.tag('raw', '<div></div>', function (opts) {
                 this.set = function () {
@@ -606,6 +609,7 @@ var DEBOUNCE = require('lodash/debounce');
 
 exports.for = function (ctx) {
   var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
   events.currentContext = null;
   setImmediate(function () {
     onContextMessage(null);
@@ -628,38 +632,52 @@ exports.for = function (ctx) {
     }
   }
 
-  ctx.browser.runtime.onMessage.addListener(function (message) {
+  events.handleBroadcastMessage = function (message) {
     try {
-      if (!ctx.browser || !ctx.browser.devtools || !ctx.browser.devtools.inspectedWindow || !message.context || message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId) {
-        return;
-      }
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
 
-      if (message.to === "message-listener") {
-        if (message.event === "currentContext" && typeof message.context !== "undefined") {
-          onContextMessage(message.context);
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
         }
-
-        events.emit("message", message);
       }
     } catch (err) {
       console.error(err);
     }
-  });
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
   ctx.browser.storage.onChanged.addListener(function (changes, area) {
     try {
       if (!events.currentContext) {
         return;
       }
 
-      var prefix = "domain[".concat(events.currentContext.hostname, "].");
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
 
       for (var item of Object.keys(changes)) {
-        if (!/^domain\[.+\]\..+$/.test(item)) continue;
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
         if (item.substring(0, prefix.length) === prefix) {
           var name = item.substring(prefix.length);
-          events.emit("setting." + name, changes[item].newValue);
-          events.emit("changed.setting", name, changes[item].newValue);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
         }
       }
     } catch (err) {
@@ -673,7 +691,7 @@ exports.for = function (ctx) {
     });
   }, 250);
 
-  events.getSetting = function (name) {
+  events.getSetting = async function (name) {
     if (!events.currentContext) {
       return Promise.resolve(null);
     }
@@ -681,21 +699,47 @@ exports.for = function (ctx) {
     return events._getSettingForHostname(events.currentContext.hostname, name);
   };
 
-  events._getSettingForHostname = function (hostname, name) {
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
 
-    var key = "domain[" + hostname + "]." + name;
+    var key = "hostname[" + hostname + "]." + name;
     return ctx.browser.storage.local.get(key).then(function (value) {
-      return value[key] || false;
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setSetting = function (name, value) {
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
     if (!events.currentContext) {
       throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
     }
@@ -703,7 +747,7 @@ exports.for = function (ctx) {
     return events._setSettingForHostname(events.currentContext.hostname, name, value);
   };
 
-  events._setSettingForHostname = function (hostname, name, value) {
+  events._setSettingForHostname = async function (hostname, name, value) {
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
@@ -714,7 +758,9 @@ exports.for = function (ctx) {
       }
 
       var obj = {};
-      obj["domain[" + hostname + "]." + name] = value;
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -722,9 +768,9 @@ exports.for = function (ctx) {
     });
   };
 
-  events.getGlobalSetting = function (name) {
+  events.getGlobalSetting = async function (name) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     var defaultValue;
@@ -743,15 +789,18 @@ exports.for = function (ctx) {
       }
 
       return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setGlobalSetting = function (name, value) {
+  events.setGlobalSetting = async function (name, value) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     return events.getGlobalSetting(name).then(function (existingValue) {
@@ -761,6 +810,7 @@ exports.for = function (ctx) {
 
       var obj = {};
       obj[name] = value;
+      globalSettings[name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -768,14 +818,54 @@ exports.for = function (ctx) {
     });
   };
 
-  events.isConfigured = function () {
-    return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return enableUserAgentHeader || enableFirePHPHeader || enableChromeLoggerData;
-        });
-      });
-    });
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
   };
 
   events.reloadBrowser = function () {
@@ -795,15 +885,41 @@ exports.for = function (ctx) {
     });
   };
 
-  events.showView = function (name) {
+  events.showView = function (name, args) {
     if (name === "manage") {
       ctx.browser.runtime.sendMessage({
         to: "broadcast",
         event: "manage"
       });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
     }
   };
 
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
+    }
+  };
+
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
   return events;
 };
 }).call(this,require("timers").setImmediate)
diff --git a/dist/firephp.build/scripts/devtools/menu.rep.css b/dist/firephp.build/scripts/devtools/dist/menu.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/menu.rep.css
rename to dist/firephp.build/scripts/devtools/dist/menu.rep.css
diff --git a/dist/firephp.build/scripts/devtools/menu.rep.js b/dist/firephp.build/scripts/devtools/dist/menu.rep.js
similarity index 86%
rename from dist/firephp.build/scripts/devtools/menu.rep.js
rename to dist/firephp.build/scripts/devtools/dist/menu.rep.js
index 66260e0..ee079d3 100644
--- a/dist/firephp.build/scripts/devtools/menu.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/menu.rep.js
@@ -313,6 +313,7 @@ function debounce(func, wait, options) {
       }
       if (maxing) {
         // Handle invocations in a tight loop.
+        clearTimeout(timerId);
         timerId = setTimeout(timerExpired, wait);
         return invokeFunc(lastCallTime);
       }
@@ -531,26 +532,28 @@ exports.main = function (JSONREP, node, options) {
         html: "<div></div>",
         "on": {
           "mount": function (el) {
-            riot.util.styleManager.add = function (cssText, name) {
-              if (!cssText) {
-                return;
-              }
-
-              if (window.document.createStyleSheet) {
-                var sheet = window.document.createStyleSheet();
-                sheet.cssText = cssText;
-              } else {
-                var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
-                style.appendChild(window.document.createTextNode(cssText));
-                var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
-                head.appendChild(style);
-              }
-            };
-
-            riot.util.styleManager.inject = function () {
-              if (!options) return;
-              JSONREP.loadStyle(options.renderer.uri + '.css');
-            };
+            if ("1" === "1") {
+              riot.util.styleManager.inject = function () {
+                if (!options) return;
+                JSONREP.loadStyle(options.renderer.uri + '.css');
+              };
+            } else {
+              riot.util.styleManager.add = function (cssText, name) {
+                if (!cssText) {
+                  return;
+                }
+
+                if (window.document.createStyleSheet) {
+                  var sheet = window.document.createStyleSheet();
+                  sheet.cssText = cssText;
+                } else {
+                  var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
+                  style.appendChild(window.document.createTextNode(cssText));
+                  var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+                  head.appendChild(style);
+                }
+              };
+            }
 
             riot.tag('raw', '<div></div>', function (opts) {
               this.set = function () {
@@ -609,6 +612,7 @@ var DEBOUNCE = require('lodash/debounce');
 
 exports.for = function (ctx) {
   var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
   events.currentContext = null;
   setImmediate(function () {
     onContextMessage(null);
@@ -631,38 +635,52 @@ exports.for = function (ctx) {
     }
   }
 
-  ctx.browser.runtime.onMessage.addListener(function (message) {
+  events.handleBroadcastMessage = function (message) {
     try {
-      if (!ctx.browser || !ctx.browser.devtools || !ctx.browser.devtools.inspectedWindow || !message.context || message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId) {
-        return;
-      }
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
 
-      if (message.to === "message-listener") {
-        if (message.event === "currentContext" && typeof message.context !== "undefined") {
-          onContextMessage(message.context);
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
         }
-
-        events.emit("message", message);
       }
     } catch (err) {
       console.error(err);
     }
-  });
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
   ctx.browser.storage.onChanged.addListener(function (changes, area) {
     try {
       if (!events.currentContext) {
         return;
       }
 
-      var prefix = "domain[".concat(events.currentContext.hostname, "].");
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
 
       for (var item of Object.keys(changes)) {
-        if (!/^domain\[.+\]\..+$/.test(item)) continue;
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
         if (item.substring(0, prefix.length) === prefix) {
           var name = item.substring(prefix.length);
-          events.emit("setting." + name, changes[item].newValue);
-          events.emit("changed.setting", name, changes[item].newValue);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
         }
       }
     } catch (err) {
@@ -676,7 +694,7 @@ exports.for = function (ctx) {
     });
   }, 250);
 
-  events.getSetting = function (name) {
+  events.getSetting = async function (name) {
     if (!events.currentContext) {
       return Promise.resolve(null);
     }
@@ -684,21 +702,47 @@ exports.for = function (ctx) {
     return events._getSettingForHostname(events.currentContext.hostname, name);
   };
 
-  events._getSettingForHostname = function (hostname, name) {
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
 
-    var key = "domain[" + hostname + "]." + name;
+    var key = "hostname[" + hostname + "]." + name;
     return ctx.browser.storage.local.get(key).then(function (value) {
-      return value[key] || false;
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setSetting = function (name, value) {
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
     if (!events.currentContext) {
       throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
     }
@@ -706,7 +750,7 @@ exports.for = function (ctx) {
     return events._setSettingForHostname(events.currentContext.hostname, name, value);
   };
 
-  events._setSettingForHostname = function (hostname, name, value) {
+  events._setSettingForHostname = async function (hostname, name, value) {
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
@@ -717,7 +761,9 @@ exports.for = function (ctx) {
       }
 
       var obj = {};
-      obj["domain[" + hostname + "]." + name] = value;
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -725,9 +771,9 @@ exports.for = function (ctx) {
     });
   };
 
-  events.getGlobalSetting = function (name) {
+  events.getGlobalSetting = async function (name) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     var defaultValue;
@@ -746,15 +792,18 @@ exports.for = function (ctx) {
       }
 
       return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setGlobalSetting = function (name, value) {
+  events.setGlobalSetting = async function (name, value) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     return events.getGlobalSetting(name).then(function (existingValue) {
@@ -764,6 +813,7 @@ exports.for = function (ctx) {
 
       var obj = {};
       obj[name] = value;
+      globalSettings[name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -771,14 +821,54 @@ exports.for = function (ctx) {
     });
   };
 
-  events.isConfigured = function () {
-    return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return enableUserAgentHeader || enableFirePHPHeader || enableChromeLoggerData;
-        });
-      });
-    });
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
   };
 
   events.reloadBrowser = function () {
@@ -798,15 +888,41 @@ exports.for = function (ctx) {
     });
   };
 
-  events.showView = function (name) {
+  events.showView = function (name, args) {
     if (name === "manage") {
       ctx.browser.runtime.sendMessage({
         to: "broadcast",
         event: "manage"
       });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
     }
   };
 
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
+    }
+  };
+
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
   return events;
 };
 }).call(this,require("timers").setImmediate)
diff --git a/dist/firephp.build/scripts/devtools/settings.rep.css b/dist/firephp.build/scripts/devtools/dist/settings.rep.css
similarity index 61%
rename from dist/firephp.build/scripts/devtools/settings.rep.css
rename to dist/firephp.build/scripts/devtools/dist/settings.rep.css
index 024169d..7a5d810 100644
--- a/dist/firephp.build/scripts/devtools/settings.rep.css
+++ b/dist/firephp.build/scripts/devtools/dist/settings.rep.css
@@ -1,16 +1,16 @@
 
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD {
         vertical-align: top;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] UL {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] UL {
         list-style-type: none;
         margin: 0px;
         padding: 0px;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.settings > UL > LI {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.settings > UL > LI {
         white-space: nowrap;
         border: 1px solid black;
         margin: 0px;
@@ -20,20 +20,20 @@
         padding-right: 20px;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.settings > UL > LI:hover {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.settings > UL > LI:hover {
         background-color: #ebebeb;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.settings > UL > LI > H2 {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.settings > UL > LI > H2 {
         maring-top: 0px;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.settings > UL > LI > UL {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.settings > UL > LI > UL {
         list-style-type: none;
         margin-bottom: 10px;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.info {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.info {
         margin-left: 10px;
         border: 1px solid #bcbcbc;
         background-color: #efefef;
@@ -42,15 +42,15 @@
         padding-right: 20px;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.info {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.info {
         float: right;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.info H2 {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.info H2 {
         maring-top: 0px;
     }
 
-    DIV[data-is="tag_43a760ed7a01836899073d10ea4fe6b68958a287"] TABLE TD DIV.info > OL {
+    DIV[data-is="tag_3f5445734da54c78a500a6f4139cdfda541db5fd"] TABLE TD DIV.info > OL {
         margin: 0px;
         padding: 0px;
         margin-left: 20px;
diff --git a/dist/firephp.build/scripts/devtools/settings.rep.js b/dist/firephp.build/scripts/devtools/dist/settings.rep.js
similarity index 83%
rename from dist/firephp.build/scripts/devtools/settings.rep.js
rename to dist/firephp.build/scripts/devtools/dist/settings.rep.js
index 28ecfe6..226c0d6 100644
--- a/dist/firephp.build/scripts/devtools/settings.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/settings.rep.js
@@ -313,6 +313,7 @@ function debounce(func, wait, options) {
       }
       if (maxing) {
         // Handle invocations in a tight loop.
+        clearTimeout(timerId);
         timerId = setTimeout(timerExpired, wait);
         return invokeFunc(lastCallTime);
       }
@@ -531,26 +532,28 @@ exports.main = function (JSONREP, node, options) {
         html: "<div></div>",
         "on": {
           "mount": function (el) {
-            riot.util.styleManager.add = function (cssText, name) {
-              if (!cssText) {
-                return;
-              }
-
-              if (window.document.createStyleSheet) {
-                var sheet = window.document.createStyleSheet();
-                sheet.cssText = cssText;
-              } else {
-                var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
-                style.appendChild(window.document.createTextNode(cssText));
-                var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
-                head.appendChild(style);
-              }
-            };
+            if ("1" === "1") {
+              riot.util.styleManager.inject = function () {
+                if (!options) return;
+                JSONREP.loadStyle(options.renderer.uri + '.css');
+              };
+            } else {
+              riot.util.styleManager.add = function (cssText, name) {
+                if (!cssText) {
+                  return;
+                }
 
-            riot.util.styleManager.inject = function () {
-              if (!options) return;
-              JSONREP.loadStyle(options.renderer.uri + '.css');
-            };
+                if (window.document.createStyleSheet) {
+                  var sheet = window.document.createStyleSheet();
+                  sheet.cssText = cssText;
+                } else {
+                  var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
+                  style.appendChild(window.document.createTextNode(cssText));
+                  var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+                  head.appendChild(style);
+                }
+              };
+            }
 
             riot.tag('raw', '<div></div>', function (opts) {
               this.set = function () {
@@ -560,7 +563,7 @@ exports.main = function (JSONREP, node, options) {
               this.on('update', this.set);
               this.on('mount', this.set);
             });
-            riot.tag2('tag_43a760ed7a01836899073d10ea4fe6b68958a287', '<div> <table> <tr> <td width="75%"> <h2>Global Settings</h2> <ul> <li><input type="checkbox" name="reloadOnEnable" scope="global" onchange="{syncCheckbox}"> Reload page on <b>Enable</b></li> </ul> <h2>Settings for: {hostname}</h2> <div class="settings"> <ul> <li> <h2><a href="https://github.com/firephp/firephp-core" target="_blank">FirePHPCore</a></h2> <p>Only one type of request header needs to be sent.</p> <ul> <li><input type="checkbox" name="enableUserAgentHeader" onchange="{syncCheckbox}"> Enable <b>UserAgent Request Header</b> - Modifies the <i>User-Agent</i> request header by appending <i>FirePHP/0.5</i>.</li> <li><input type="checkbox" name="enableFirePHPHeader" onchange="{syncCheckbox}"> Enable <b>FirePHP Request Header</b> - Adds a <i>X-FirePHP-Version: 0.4</i> request header.</li> </ul> </li> <li> <h2><a href="https://github.com/ccampbell/chromelogger" target="_blank">Chrome Logger</a></h2> <ul> <li><input type="checkbox" name="enableChromeLoggerData" onchange="{syncCheckbox}"> Render <i>X-ChromeLogger-Data</i> response headers to the console.</li> </ul> </li> </ul> </div> </td> <td width="25%"> <div class="info"> <h2>How to get setup</h2> <ol> <li>Choose an integration method.</li> <li>Integrate the server library into your project.</li> <li>Check the relevant box.</li> <li><b>Close</b> the <i>Settings</i> and <b>Enable</b> the tool.</li> </ol> <p><i>FirePHP is <a target="_blank" href="https://github.com/firephp/firephp-for-firefox-devtools">Open Source with code on Github</a></i></p> <p><a target="_blank" href="https://github.com/firephp/firephp-for-firefox-devtools/issues">Report an Issue or Suggest a Feature</a></p> </div> </td> </tr> </table> </div>', '', '', function (opts) {
+            riot.tag2('tag_3f5445734da54c78a500a6f4139cdfda541db5fd', '<div> <table> <tr> <td width="75%"> <h2>Global Settings</h2> <ul> <li><input type="checkbox" name="reloadOnEnable" scope="global" onchange="{syncCheckbox}"> Reload page on <b>Enable</b></li> </ul> <h2>Settings for: {hostname}</h2> <p>For a list of supported server libraries see <a href="http://firephp.org/">firephp.org</a>. There are also many framework plugins out there.</p> <div class="settings"> <ul> <li> <h2><a href="https://github.com/firephp/firephp-core" target="_blank">FirePHP (Wildfire based)</a> Protocol</h2> <p>Only one type of request header needs to be sent.</p> <ul> <li><input type="checkbox" name="enableUserAgentHeader" onchange="{syncCheckbox}"> Enable <b>UserAgent Request Header</b> - Modifies the <i>User-Agent</i> request header by appending <i>FirePHP/0.5</i>.</li> <li><input type="checkbox" name="enableFirePHPHeader" onchange="{syncCheckbox}"> Enable <b>FirePHP Request Header</b> - Adds a <i>X-FirePHP-Version: 0.4</i> request header.</li> </ul> </li> <li> <h2><a href="https://github.com/ccampbell/chromelogger" target="_blank">Chrome Logger</a> Protocol</h2> <ul> <li><input type="checkbox" name="enableChromeLoggerData" onchange="{syncCheckbox}"> Render <i>X-ChromeLogger-Data</i> response headers to the console.</li> </ul> </li> </ul> </div> </td> <td width="25%"> <div class="info"> <h2>How to get setup</h2> <ol> <li>Choose a <a href="http://firephp.org/">server library</a>.</li> <li>Integrate the server library into your project.</li> <li>Check the relevant protocol box to your left.</li> <li><b>Close</b> the <i>Settings</i> and <b>Enable</b> the tool.</li> </ol> <p><i>FirePHP is <a target="_blank" href="https://github.com/firephp/firephp-for-firefox-devtools">Open Source with code on Github</a></i></p> <p><a target="_blank" href="https://github.com/firephp/firephp-for-firefox-devtools/issues">Report an Issue or Suggest a Feature</a></p> </div> </td> </tr> </table> </div>', '', '', function (opts) {
               var COMPONENT = require("./component");
 
               var tag = this;
@@ -614,7 +617,7 @@ exports.main = function (JSONREP, node, options) {
                 }
               };
             });
-            riot.mount(el, 'tag_43a760ed7a01836899073d10ea4fe6b68958a287', context);
+            riot.mount(el, 'tag_3f5445734da54c78a500a6f4139cdfda541db5fd', context);
           }
         }
       };
@@ -631,6 +634,7 @@ var DEBOUNCE = require('lodash/debounce');
 
 exports.for = function (ctx) {
   var events = new EVENTS.EventEmitter();
+  events.browser = ctx.browser;
   events.currentContext = null;
   setImmediate(function () {
     onContextMessage(null);
@@ -653,38 +657,52 @@ exports.for = function (ctx) {
     }
   }
 
-  ctx.browser.runtime.onMessage.addListener(function (message) {
+  events.handleBroadcastMessage = function (message) {
     try {
-      if (!ctx.browser || !ctx.browser.devtools || !ctx.browser.devtools.inspectedWindow || !message.context || message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId) {
-        return;
-      }
+      if (message.context && (ctx.getOwnTabId && message.context.tabId === ctx.getOwnTabId() || ctx.browser && ctx.browser.devtools && ctx.browser.devtools.inspectedWindow && message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId)) {
+        if (message.to === "message-listener") {
+          if (message.event === "currentContext" && typeof message.context !== "undefined") {
+            onContextMessage(message.context);
+          }
 
-      if (message.to === "message-listener") {
-        if (message.event === "currentContext" && typeof message.context !== "undefined") {
-          onContextMessage(message.context);
+          events.emit("message", message);
+        } else if (message.to === "protocol") {
+          if (ctx.handlers && ctx.handlers[message.message.receiver]) {
+            message.message.meta = JSON.parse(message.message.meta);
+            message.message.data = JSON.parse(message.message.data);
+            ctx.handlers[message.message.receiver](message.message);
+          }
         }
-
-        events.emit("message", message);
       }
     } catch (err) {
       console.error(err);
     }
-  });
+  };
+
+  ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+  var globalSettings = {};
+  var domainSettings = {};
   ctx.browser.storage.onChanged.addListener(function (changes, area) {
     try {
       if (!events.currentContext) {
         return;
       }
 
-      var prefix = "domain[".concat(events.currentContext.hostname, "].");
+      var prefix = "hostname[".concat(events.currentContext.hostname, "].");
 
       for (var item of Object.keys(changes)) {
-        if (!/^domain\[.+\]\..+$/.test(item)) continue;
+        if (!/^hostname\[.+\]\..+$/.test(item)) {
+          globalSettings[item] = changes[item].newValue;
+          continue;
+        }
 
         if (item.substring(0, prefix.length) === prefix) {
           var name = item.substring(prefix.length);
-          events.emit("setting." + name, changes[item].newValue);
-          events.emit("changed.setting", name, changes[item].newValue);
+          domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+          domainSettings[events.currentContext.hostname][name] = changes[item].newValue || false;
+          delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];
+          events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+          events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
         }
       }
     } catch (err) {
@@ -698,7 +716,7 @@ exports.for = function (ctx) {
     });
   }, 250);
 
-  events.getSetting = function (name) {
+  events.getSetting = async function (name) {
     if (!events.currentContext) {
       return Promise.resolve(null);
     }
@@ -706,21 +724,47 @@ exports.for = function (ctx) {
     return events._getSettingForHostname(events.currentContext.hostname, name);
   };
 
-  events._getSettingForHostname = function (hostname, name) {
+  events._getSettingForHostname = async function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
 
-    var key = "domain[" + hostname + "]." + name;
+    var key = "hostname[" + hostname + "]." + name;
     return ctx.browser.storage.local.get(key).then(function (value) {
-      return value[key] || false;
+      if (value[key] === null || typeof value[key] === "undefined") {
+        return defaultValue;
+      }
+
+      return value[key];
+    }).then(function (value) {
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setSetting = function (name, value) {
+  events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+    if (typeof defaultValue === "undefined") {
+      defaultValue = false;
+    }
+
+    if (!domainSettings[hostname] || typeof domainSettings[hostname][name] === "undefined") {
+      events._getSettingForHostname(hostname, name, defaultValue);
+
+      return defaultValue;
+    }
+
+    return domainSettings[hostname][name];
+  };
+
+  events.setSetting = async function (name, value) {
     if (!events.currentContext) {
       throw new Error("Cannot set setting for name '".concat(name, "' due to no 'currentContext'!"));
     }
@@ -728,7 +772,7 @@ exports.for = function (ctx) {
     return events._setSettingForHostname(events.currentContext.hostname, name, value);
   };
 
-  events._setSettingForHostname = function (hostname, name, value) {
+  events._setSettingForHostname = async function (hostname, name, value) {
     if (typeof ctx.browser === "undefined") {
       return Promise.resolve(null);
     }
@@ -739,7 +783,9 @@ exports.for = function (ctx) {
       }
 
       var obj = {};
-      obj["domain[" + hostname + "]." + name] = value;
+      obj["hostname[" + hostname + "]." + name] = value;
+      domainSettings[hostname] = domainSettings[hostname] || {};
+      domainSettings[hostname][name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -747,9 +793,9 @@ exports.for = function (ctx) {
     });
   };
 
-  events.getGlobalSetting = function (name) {
+  events.getGlobalSetting = async function (name) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     var defaultValue;
@@ -768,15 +814,18 @@ exports.for = function (ctx) {
       }
 
       return value[name];
+    }).then(function (value) {
+      globalSettings[name] = value;
+      return value;
     }).catch(function (err) {
       console.error(err);
       throw err;
     });
   };
 
-  events.setGlobalSetting = function (name, value) {
+  events.setGlobalSetting = async function (name, value) {
     if (typeof ctx.browser === "undefined") {
-      return Promise.resolve(null);
+      return null;
     }
 
     return events.getGlobalSetting(name).then(function (existingValue) {
@@ -786,6 +835,7 @@ exports.for = function (ctx) {
 
       var obj = {};
       obj[name] = value;
+      globalSettings[name] = value;
       return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
     }).catch(function (err) {
       console.error(err);
@@ -793,14 +843,54 @@ exports.for = function (ctx) {
     });
   };
 
-  events.isConfigured = function () {
-    return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-      return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-        return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-          return enableUserAgentHeader || enableFirePHPHeader || enableChromeLoggerData;
-        });
-      });
-    });
+  events.isConfigured = async function () {
+    if (!events.currentContext) {
+      throw new Error("Cannot get settings due to no 'currentContext'!");
+    }
+
+    var settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+    return settings._configured;
+  };
+
+  events._getHostnameSettingsFor = async function (hostname) {
+    var settings = {
+      enabled: await events._getSettingForHostname(hostname, "enabled", false),
+      enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+      enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+      enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+    };
+    settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+    return settings;
+  };
+
+  events._getHostnameSettingsForSync = function (hostname) {
+    if (!events._getHostnameSettingsForSync._cache[hostname]) {
+      var settings = {
+        enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+        enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+        enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+        enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+      };
+      settings._configured = settings.enableUserAgentHeader || settings.enableFirePHPHeader || settings.enableChromeLoggerData;
+      events._getHostnameSettingsForSync._cache[hostname] = settings;
+    }
+
+    return events._getHostnameSettingsForSync._cache[hostname];
+  };
+
+  events._getHostnameSettingsForSync._cache = {};
+
+  events.isEnabled = async function () {
+    if (!events.currentContext) {
+      return false;
+    }
+
+    return events._isEnabledForHostname(events.currentContext.hostname);
+  };
+
+  events._isEnabledForHostname = async function (hostname) {
+    var settings = await events._getHostnameSettingsFor(hostname);
+    return settings.enabled && settings._configured;
   };
 
   events.reloadBrowser = function () {
@@ -820,15 +910,41 @@ exports.for = function (ctx) {
     });
   };
 
-  events.showView = function (name) {
+  events.showView = function (name, args) {
     if (name === "manage") {
       ctx.browser.runtime.sendMessage({
         to: "broadcast",
         event: "manage"
       });
+    } else if (name === "editor") {
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        args: args
+      });
+    }
+  };
+
+  events.hideView = function (name) {
+    if (name === "editor") {
+      console.log("broadcast hide view: editor");
+      ctx.browser.runtime.sendMessage({
+        to: "broadcast",
+        event: "editor",
+        value: false
+      });
     }
   };
 
+  events.loadFile = function (file, line) {
+    ctx.browser.runtime.sendMessage({
+      to: "background",
+      event: "load-file",
+      file: file,
+      line: line
+    });
+  };
+
   return events;
 };
 }).call(this,require("timers").setImmediate)
diff --git a/dist/firephp.build/scripts/devtools/summary.rep.css b/dist/firephp.build/scripts/devtools/dist/summary.rep.css
similarity index 100%
rename from dist/firephp.build/scripts/devtools/summary.rep.css
rename to dist/firephp.build/scripts/devtools/dist/summary.rep.css
diff --git a/dist/firephp.build/scripts/devtools/summary.rep.js b/dist/firephp.build/scripts/devtools/dist/summary.rep.js
similarity index 68%
rename from dist/firephp.build/scripts/devtools/summary.rep.js
rename to dist/firephp.build/scripts/devtools/dist/summary.rep.js
index aa1ed35..b037055 100644
--- a/dist/firephp.build/scripts/devtools/summary.rep.js
+++ b/dist/firephp.build/scripts/devtools/dist/summary.rep.js
@@ -22,26 +22,28 @@ exports.main = function (JSONREP, node, options) {
         html: "<div></div>",
         "on": {
           "mount": function (el) {
-            riot.util.styleManager.add = function (cssText, name) {
-              if (!cssText) {
-                return;
-              }
-
-              if (window.document.createStyleSheet) {
-                var sheet = window.document.createStyleSheet();
-                sheet.cssText = cssText;
-              } else {
-                var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
-                style.appendChild(window.document.createTextNode(cssText));
-                var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
-                head.appendChild(style);
-              }
-            };
+            if ("1" === "1") {
+              riot.util.styleManager.inject = function () {
+                if (!options) return;
+                JSONREP.loadStyle(options.renderer.uri + '.css');
+              };
+            } else {
+              riot.util.styleManager.add = function (cssText, name) {
+                if (!cssText) {
+                  return;
+                }
 
-            riot.util.styleManager.inject = function () {
-              if (!options) return;
-              JSONREP.loadStyle(options.renderer.uri + '.css');
-            };
+                if (window.document.createStyleSheet) {
+                  var sheet = window.document.createStyleSheet();
+                  sheet.cssText = cssText;
+                } else {
+                  var style = window.document.createElementNS ? window.document.createElementNS("http://www.w3.org/1999/xhtml", "style") : window.document.createElement("style");
+                  style.appendChild(window.document.createTextNode(cssText));
+                  var head = window.document.getElementsByTagName("head")[0] || window.document.documentElement;
+                  head.appendChild(style);
+                }
+              };
+            }
 
             riot.tag('raw', '<div></div>', function (opts) {
               this.set = function () {
diff --git a/dist/firephp.build/scripts/devtools/index.html b/dist/firephp.build/scripts/devtools/index.html
deleted file mode 100644
index ae9ff7b..0000000
--- a/dist/firephp.build/scripts/devtools/index.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-<!DOCTYPE html>
-<html lang="en">
-    <head>
-        <meta charset="utf-8">
-        <script src="../../lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/lib/pinf-loader-full.browser.js"></script>
-        <script src="../../lib/github.com~pinf~pinf-for-mozilla-web-ext/scripts/loader.js"></script>
-        <script src="../../lib/eventemitter3.js"></script>
-        <script src="./dist/riot.csp.js"></script>
-        <script src="../../lib.js"></script>        
-        <link rel="stylesheet" href="../../default.css">
-
-        <script src="./index.loader.js"></script>
-    </head>
-    <body>
-    </body>
-</html>
diff --git a/dist/firephp.xpi b/dist/firephp.xpi
deleted file mode 100644
index 1d9e692..0000000
Binary files a/dist/firephp.xpi and /dev/null differ
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index ae4ac08..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,1401 +0,0 @@
-{
-  "name": "firephp-for-browser-devtools",
-  "version": "1.3.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "@pinf-it/inf": {
-      "version": "0.1.26",
-      "resolved": "https://registry.npmjs.org/@pinf-it/inf/-/inf-0.1.26.tgz",
-      "integrity": "sha512-f4df6QEYKFwZMqGj4SqH11N3vUO0z9w7SpHVaPMvQ3Zfuoav4IJsukmyOfZUy/GJI1Ax845hPJX091i3zcwTKw==",
-      "dev": true,
-      "requires": {
-        "bluebird": "^3.5.5",
-        "clarinet": "^0.12.3",
-        "codeblock": "^0.3.15",
-        "colors": "^1.3.3",
-        "escape-regexp": "0.0.1",
-        "fs-extra": "^8.1.0",
-        "glob": "^7.1.4",
-        "json-stable-stringify": "^1.0.1",
-        "lib.json": "^0.0.4",
-        "lodash": "^4.17.15",
-        "memorystream": "^0.3.1",
-        "minimist": "^1.2.0",
-        "pinf-loader-js": "^1.0.4",
-        "resolve": "^1.12.0",
-        "runbash": "^0.0.9",
-        "traverse": "^0.6.6"
-      },
-      "dependencies": {
-        "fs-extra": {
-          "version": "8.1.0",
-          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
-          "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.2.0",
-            "jsonfile": "^4.0.0",
-            "universalify": "^0.1.0"
-          }
-        },
-        "lib.json": {
-          "version": "0.0.4",
-          "resolved": "https://registry.npmjs.org/lib.json/-/lib.json-0.0.4.tgz",
-          "integrity": "sha512-QQJICulXpwuwioeDCl9tSgI8ynxvM2PF1DeGTP4IIE1GZQJ6An3nXj7b2jMlS0f6NL7BZ3GB0ioIqwEOW1lJWg==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0",
-            "resolve": "^1.10.0"
-          }
-        },
-        "runbash": {
-          "version": "0.0.9",
-          "resolved": "https://registry.npmjs.org/runbash/-/runbash-0.0.9.tgz",
-          "integrity": "sha512-i++z5QpTfJQcbOWqVrN4TeE1u+Bdy/V1eEYCjYRWxFcEC1/TZFEN3Dp9xtxubRqBE3nJgMcJaBdWlvUdSUf/xA==",
-          "dev": true,
-          "requires": {
-            "bash.origin": "^0.2.0-pre.0",
-            "bluebird": "^3.5.0"
-          }
-        }
-      }
-    },
-    "JSV": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
-      "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=",
-      "dev": true
-    },
-    "accepts": {
-      "version": "1.3.7",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
-      "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
-      "dev": true,
-      "requires": {
-        "mime-types": "~2.1.24",
-        "negotiator": "0.6.2"
-      }
-    },
-    "ajv": {
-      "version": "6.10.2",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
-      "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
-      "dev": true,
-      "requires": {
-        "fast-deep-equal": "^2.0.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
-      }
-    },
-    "ansi-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-      "dev": true
-    },
-    "ansi-styles": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
-      "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=",
-      "dev": true
-    },
-    "array-flatten": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
-      "dev": true
-    },
-    "asn1": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
-      "dev": true,
-      "requires": {
-        "safer-buffer": "~2.1.0"
-      }
-    },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-      "dev": true
-    },
-    "asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
-    },
-    "aws-sign2": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
-      "dev": true
-    },
-    "aws4": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
-      "dev": true
-    },
-    "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
-    },
-    "bash.origin": {
-      "version": "0.2.0-pre.1",
-      "resolved": "https://registry.npmjs.org/bash.origin/-/bash.origin-0.2.0-pre.1.tgz",
-      "integrity": "sha512-xvTGE7tpnTo4mGyrJ0T4PvtkgIfK79bStiHs23AqI1V9bchlXcVZYLhjHEVixM1hawFkqPIaci0jvwgA53R2dw==",
-      "dev": true,
-      "requires": {
-        "minimist": "^1.2.0"
-      }
-    },
-    "bash.origin.express": {
-      "version": "0.2.0-pre.0",
-      "resolved": "https://registry.npmjs.org/bash.origin.express/-/bash.origin.express-0.2.0-pre.0.tgz",
-      "integrity": "sha512-qOv6q36m7gOCshCcK5cH//XlA6yAK2EcuT9a5Rso5MSa1wqhIJr+6BaE4zeTm10tSgbsN0YE3/s6uneaWSrZPw==",
-      "dev": true,
-      "requires": {
-        "bash.origin.lib": "^0.4.0-pre.0",
-        "http-shutdown": "^1.2.0",
-        "lib.json": "^0.0.3"
-      },
-      "dependencies": {
-        "lib.json": {
-          "version": "0.0.3",
-          "resolved": "https://registry.npmjs.org/lib.json/-/lib.json-0.0.3.tgz",
-          "integrity": "sha512-ukZg8XhteUcF3pboatsVwbBhiZBReiIUqRaix8dwSXGoybuvlIw2ZJJDrjUUQCIEG46Yr0k2t3R2ZnqFjgw8xw==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0",
-            "resolve": "^1.10.0"
-          }
-        }
-      }
-    },
-    "bash.origin.lib": {
-      "version": "0.4.0-pre.3",
-      "resolved": "https://registry.npmjs.org/bash.origin.lib/-/bash.origin.lib-0.4.0-pre.3.tgz",
-      "integrity": "sha512-oHy5c/6MJ0NmwxeBUw6BpHOTUGu9uWbKvAWjZGzQk535PWYm6PXClNYhOYjE2OvDJ7E6KuZEinXLizFFBf0oEQ==",
-      "dev": true,
-      "requires": {
-        "@pinf-it/inf": "^0.1.25",
-        "bash.origin": "^0.2.0-pre.0",
-        "bash.origin.express": "^0.2.0-pre.0",
-        "bash.origin.modules": "^0.2.0-pre.3",
-        "bash.origin.test": "^0.2.0-pre.2",
-        "bluebird": "^3.5.2",
-        "body-parser": "^1.18.3",
-        "codeblock": "^0.3.15",
-        "colors": "^1.3.2",
-        "escape-regexp": "^0.0.1",
-        "express": "^4.16.4",
-        "fs-extra": "^7.0.0",
-        "get-port": "^4.0.0",
-        "lib.json": "^0.0.5",
-        "lodash": "^4.17.11",
-        "mime-types": "^2.1.20",
-        "minimist": "^1.2.0",
-        "morgan": "^1.9.1",
-        "pinf-loader-js": "^1.0.1",
-        "request": "^2.88.0",
-        "resolve": "^1.10.0",
-        "runbash": "^0.1.0-pre.0",
-        "uuid": "^3.3.2",
-        "uuidgen": "^1.0.0",
-        "waitfor": "^0.1.3"
-      }
-    },
-    "bash.origin.modules": {
-      "version": "0.2.0-pre.3",
-      "resolved": "https://registry.npmjs.org/bash.origin.modules/-/bash.origin.modules-0.2.0-pre.3.tgz",
-      "integrity": "sha512-TPw1hGeNYrIq0XlgfM2fJCWjTdf8IcFEzAe/Dl44nb+g6n/QIc1PjTsyewSi9S2CdTcZc+U5YVsloYXwDDGDFw==",
-      "dev": true,
-      "requires": {
-        "bluebird": "^3.5.2",
-        "codeblock": "^0.3.15",
-        "escape-regexp": "^0.0.1",
-        "fs-extra": "^7.0.0",
-        "minimist": "^1.2.0",
-        "uuid": "^3.3.2"
-      }
-    },
-    "bash.origin.test": {
-      "version": "0.2.0-pre.3",
-      "resolved": "https://registry.npmjs.org/bash.origin.test/-/bash.origin.test-0.2.0-pre.3.tgz",
-      "integrity": "sha512-T1m5Dz5N5easx5PmFvuSXRm7FxyT8mG0OGovrDKuzkQyDRc1LSGYHQ36O35E8p4g6KG84GvskwYhbq46YnPY4g==",
-      "dev": true,
-      "requires": {
-        "bash.origin.lib": "^0.4.0-pre.3",
-        "cli-table3": "^0.5.0"
-      }
-    },
-    "basic-auth": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
-      "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "5.1.2"
-      }
-    },
-    "bcrypt-pbkdf": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
-      "dev": true,
-      "requires": {
-        "tweetnacl": "^0.14.3"
-      }
-    },
-    "bluebird": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz",
-      "integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==",
-      "dev": true
-    },
-    "body-parser": {
-      "version": "1.19.0",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
-      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
-      "dev": true,
-      "requires": {
-        "bytes": "3.1.0",
-        "content-type": "~1.0.4",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "http-errors": "1.7.2",
-        "iconv-lite": "0.4.24",
-        "on-finished": "~2.3.0",
-        "qs": "6.7.0",
-        "raw-body": "2.4.0",
-        "type-is": "~1.6.17"
-      }
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "bytes": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
-      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
-      "dev": true
-    },
-    "caseless": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
-      "dev": true
-    },
-    "chalk": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
-      "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "~1.0.0",
-        "has-color": "~0.1.0",
-        "strip-ansi": "~0.1.0"
-      }
-    },
-    "clarinet": {
-      "version": "0.12.3",
-      "resolved": "https://registry.npmjs.org/clarinet/-/clarinet-0.12.3.tgz",
-      "integrity": "sha512-Vdv6MsBQWppWUCe/5EGa6JSYln0coSK98NsLAAbdqM3uTXmySZoGQXAMDgOAOlf5wjSIdeHVx9jhjkXOVDvoIA==",
-      "dev": true
-    },
-    "cli-table3": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
-      "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
-      "dev": true,
-      "requires": {
-        "colors": "^1.1.2",
-        "object-assign": "^4.1.0",
-        "string-width": "^2.1.1"
-      }
-    },
-    "codeblock": {
-      "version": "0.3.15",
-      "resolved": "https://registry.npmjs.org/codeblock/-/codeblock-0.3.15.tgz",
-      "integrity": "sha512-9bAJn3f2gTKP15fjnefcbS42gtqOPUi+IKrWNKpQnlbWutoZp9nEEqvJWtb0YN3hrVRV73kp/jLYfh2OBHojlw==",
-      "dev": true,
-      "requires": {
-        "colors": "^1.1.2",
-        "escape-string-regexp": "^1.0.5",
-        "esprima": "^3.1.3",
-        "jsonlint": "^1.6.2",
-        "resolve": "^1.3.2",
-        "traverse": "^0.6.6"
-      }
-    },
-    "colors": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
-      "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
-      "dev": true
-    },
-    "combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
-      "requires": {
-        "delayed-stream": "~1.0.0"
-      }
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
-    },
-    "content-disposition": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
-      "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "5.1.2"
-      }
-    },
-    "content-type": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
-      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
-      "dev": true
-    },
-    "cookie": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
-      "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
-      "dev": true
-    },
-    "cookie-signature": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
-      "dev": true
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "dev": true
-    },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "requires": {
-        "ms": "2.0.0"
-      }
-    },
-    "delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
-    },
-    "depd": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
-      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
-      "dev": true
-    },
-    "destroy": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
-      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
-      "dev": true
-    },
-    "ecc-jsbn": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
-      "dev": true,
-      "requires": {
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.1.0"
-      }
-    },
-    "ee-first": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
-      "dev": true
-    },
-    "encodeurl": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
-      "dev": true
-    },
-    "escape-html": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
-      "dev": true
-    },
-    "escape-regexp": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/escape-regexp/-/escape-regexp-0.0.1.tgz",
-      "integrity": "sha1-9EvaEtRbvfnLf4Yu5+SCez3TIlQ=",
-      "dev": true
-    },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true
-    },
-    "esprima": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
-      "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
-      "dev": true
-    },
-    "etag": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
-      "dev": true
-    },
-    "eventemitter2": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz",
-      "integrity": "sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=",
-      "dev": true
-    },
-    "express": {
-      "version": "4.17.1",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
-      "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
-      "dev": true,
-      "requires": {
-        "accepts": "~1.3.7",
-        "array-flatten": "1.1.1",
-        "body-parser": "1.19.0",
-        "content-disposition": "0.5.3",
-        "content-type": "~1.0.4",
-        "cookie": "0.4.0",
-        "cookie-signature": "1.0.6",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "finalhandler": "~1.1.2",
-        "fresh": "0.5.2",
-        "merge-descriptors": "1.0.1",
-        "methods": "~1.1.2",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.3",
-        "path-to-regexp": "0.1.7",
-        "proxy-addr": "~2.0.5",
-        "qs": "6.7.0",
-        "range-parser": "~1.2.1",
-        "safe-buffer": "5.1.2",
-        "send": "0.17.1",
-        "serve-static": "1.14.1",
-        "setprototypeof": "1.1.1",
-        "statuses": "~1.5.0",
-        "type-is": "~1.6.18",
-        "utils-merge": "1.0.1",
-        "vary": "~1.1.2"
-      }
-    },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
-      "dev": true
-    },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
-      "dev": true
-    },
-    "fast-deep-equal": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-      "dev": true
-    },
-    "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
-      "dev": true
-    },
-    "finalhandler": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
-      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
-      "dev": true,
-      "requires": {
-        "debug": "2.6.9",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "on-finished": "~2.3.0",
-        "parseurl": "~1.3.3",
-        "statuses": "~1.5.0",
-        "unpipe": "~1.0.0"
-      }
-    },
-    "fireconsole.rep.js": {
-      "version": "0.1.0-pre.0",
-      "resolved": "https://registry.npmjs.org/fireconsole.rep.js/-/fireconsole.rep.js-0.1.0-pre.0.tgz",
-      "integrity": "sha512-kKyl97ry4KZlN2L3NgVMi2+vjJzxrfcwYWUO9Uvk+1hqVFKNOcZE9eGM4qJt3DcbBzxSv+flvy51Bk9AQke11g==",
-      "dev": true
-    },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
-      "dev": true
-    },
-    "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
-      "dev": true,
-      "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
-        "mime-types": "^2.1.12"
-      }
-    },
-    "forwarded": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
-      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
-      "dev": true
-    },
-    "fp-modules-for-nodejs": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/fp-modules-for-nodejs/-/fp-modules-for-nodejs-0.2.2.tgz",
-      "integrity": "sha1-MtLpgNLZMf6jm0Ictaq81B0OPG4=",
-      "dev": true,
-      "requires": {
-        "q": "~1.1.2"
-      }
-    },
-    "fresh": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
-      "dev": true
-    },
-    "fs-extra": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
-      "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "jsonfile": "^4.0.0",
-        "universalify": "^0.1.0"
-      }
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
-    },
-    "get-port": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz",
-      "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==",
-      "dev": true
-    },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "glob": {
-      "version": "7.1.4",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
-      "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
-      "dev": true,
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "graceful-fs": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz",
-      "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==",
-      "dev": true
-    },
-    "har-schema": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-      "dev": true
-    },
-    "har-validator": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
-      "dev": true,
-      "requires": {
-        "ajv": "^6.5.5",
-        "har-schema": "^2.0.0"
-      }
-    },
-    "has-color": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
-      "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=",
-      "dev": true
-    },
-    "http-errors": {
-      "version": "1.7.2",
-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
-      "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
-      "dev": true,
-      "requires": {
-        "depd": "~1.1.2",
-        "inherits": "2.0.3",
-        "setprototypeof": "1.1.1",
-        "statuses": ">= 1.5.0 < 2",
-        "toidentifier": "1.0.0"
-      },
-      "dependencies": {
-        "inherits": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
-        }
-      }
-    },
-    "http-shutdown": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.1.tgz",
-      "integrity": "sha512-EVttBpDbynTLq1Xz4Y2/AfGv+7HbARU6TmwqtecgUulY3zFvPsgWW3MHMgAU9nwNZeJwgrjCIwk/1g9Be2vVPQ==",
-      "dev": true
-    },
-    "http-signature": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
-      }
-    },
-    "iconv-lite": {
-      "version": "0.4.24",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-      "dev": true,
-      "requires": {
-        "safer-buffer": ">= 2.1.2 < 3"
-      }
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
-    },
-    "insight-for-js": {
-      "version": "0.2.0-pre.0",
-      "resolved": "https://registry.npmjs.org/insight-for-js/-/insight-for-js-0.2.0-pre.0.tgz",
-      "integrity": "sha512-eGJ0cNQ8FoQUSKDdcoQNqvnCd2RI0GM8Z7QtiUzv93aXSQRME12MaBaTRnHCCbKSf53Z67o56A/TnwrCLN0evg==",
-      "dev": true,
-      "requires": {
-        "fp-modules-for-nodejs": "^0.2.2",
-        "wildfire-for-js": "^0.1.4"
-      }
-    },
-    "ipaddr.js": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
-      "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==",
-      "dev": true
-    },
-    "is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-      "dev": true
-    },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
-      "dev": true
-    },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
-      "dev": true
-    },
-    "jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-      "dev": true
-    },
-    "json-schema": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
-      "dev": true
-    },
-    "json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true
-    },
-    "json-stable-stringify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
-      "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
-      "dev": true,
-      "requires": {
-        "jsonify": "~0.0.0"
-      }
-    },
-    "json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
-      "dev": true
-    },
-    "jsonfile": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-      "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.6"
-      }
-    },
-    "jsonify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
-      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
-      "dev": true
-    },
-    "jsonlint": {
-      "version": "1.6.3",
-      "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.3.tgz",
-      "integrity": "sha512-jMVTMzP+7gU/IyC6hvKyWpUU8tmTkK5b3BPNuMI9U8Sit+YAWLlZwB6Y6YrdCxfg2kNz05p3XY3Bmm4m26Nv3A==",
-      "dev": true,
-      "requires": {
-        "JSV": "^4.0.x",
-        "nomnom": "^1.5.x"
-      }
-    },
-    "jsprim": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.2.3",
-        "verror": "1.10.0"
-      }
-    },
-    "lib.json": {
-      "version": "0.0.5",
-      "resolved": "https://registry.npmjs.org/lib.json/-/lib.json-0.0.5.tgz",
-      "integrity": "sha512-QnG33e2M8EMNSLS2Uh/G/l8opZb9iJziBucNKQKKMQ7U7iB/vUkP7j/4tGDJN0n1a9jocBlKGfMz6Wnjf5h7Gg==",
-      "dev": true,
-      "requires": {
-        "minimist": "^1.2.0",
-        "resolve": "^1.10.0"
-      }
-    },
-    "lodash": {
-      "version": "4.17.15",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
-      "dev": true
-    },
-    "media-typer": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
-      "dev": true
-    },
-    "memorystream": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
-      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
-      "dev": true
-    },
-    "merge-descriptors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
-      "dev": true
-    },
-    "methods": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
-      "dev": true
-    },
-    "mime": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
-      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
-      "dev": true
-    },
-    "mime-db": {
-      "version": "1.40.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
-      "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
-      "dev": true
-    },
-    "mime-types": {
-      "version": "2.1.24",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
-      "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
-      "dev": true,
-      "requires": {
-        "mime-db": "1.40.0"
-      }
-    },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "minimist": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-      "dev": true
-    },
-    "morgan": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
-      "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
-      "dev": true,
-      "requires": {
-        "basic-auth": "~2.0.0",
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "on-finished": "~2.3.0",
-        "on-headers": "~1.0.1"
-      }
-    },
-    "ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true
-    },
-    "negotiator": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
-      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
-      "dev": true
-    },
-    "nomnom": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
-      "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=",
-      "dev": true,
-      "requires": {
-        "chalk": "~0.4.0",
-        "underscore": "~1.6.0"
-      }
-    },
-    "oauth-sign": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
-      "dev": true
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true
-    },
-    "on-finished": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
-      "dev": true,
-      "requires": {
-        "ee-first": "1.1.1"
-      }
-    },
-    "on-headers": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
-      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
-      "dev": true
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "parseurl": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
-      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
-      "dev": true
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "path-parse": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
-    },
-    "path-to-regexp": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
-      "dev": true
-    },
-    "performance-now": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true
-    },
-    "pinf-loader-js": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/pinf-loader-js/-/pinf-loader-js-1.0.4.tgz",
-      "integrity": "sha512-1NYozWCZQqSfxAMvDexYmKA/bEcyOFXkVATzhMnZ4toi1wj7M+F0hIRpDjdc9DFjFVOFIrRMAQTSU4NTgzMcHw==",
-      "dev": true
-    },
-    "proxy-addr": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
-      "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
-      "dev": true,
-      "requires": {
-        "forwarded": "~0.1.2",
-        "ipaddr.js": "1.9.0"
-      }
-    },
-    "psl": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz",
-      "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==",
-      "dev": true
-    },
-    "punycode": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-      "dev": true
-    },
-    "q": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz",
-      "integrity": "sha1-Y1fikSBnAdmfGXq4TlforRlvKok=",
-      "dev": true
-    },
-    "qs": {
-      "version": "6.7.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
-      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
-      "dev": true
-    },
-    "range-parser": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
-      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
-      "dev": true
-    },
-    "raw-body": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
-      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
-      "dev": true,
-      "requires": {
-        "bytes": "3.1.0",
-        "http-errors": "1.7.2",
-        "iconv-lite": "0.4.24",
-        "unpipe": "1.0.0"
-      }
-    },
-    "request": {
-      "version": "2.88.0",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
-      "dev": true,
-      "requires": {
-        "aws-sign2": "~0.7.0",
-        "aws4": "^1.8.0",
-        "caseless": "~0.12.0",
-        "combined-stream": "~1.0.6",
-        "extend": "~3.0.2",
-        "forever-agent": "~0.6.1",
-        "form-data": "~2.3.2",
-        "har-validator": "~5.1.0",
-        "http-signature": "~1.2.0",
-        "is-typedarray": "~1.0.0",
-        "isstream": "~0.1.2",
-        "json-stringify-safe": "~5.0.1",
-        "mime-types": "~2.1.19",
-        "oauth-sign": "~0.9.0",
-        "performance-now": "^2.1.0",
-        "qs": "~6.5.2",
-        "safe-buffer": "^5.1.2",
-        "tough-cookie": "~2.4.3",
-        "tunnel-agent": "^0.6.0",
-        "uuid": "^3.3.2"
-      },
-      "dependencies": {
-        "qs": {
-          "version": "6.5.2",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
-          "dev": true
-        }
-      }
-    },
-    "resolve": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
-      "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
-      "dev": true,
-      "requires": {
-        "path-parse": "^1.0.6"
-      }
-    },
-    "runbash": {
-      "version": "0.1.0-pre.0",
-      "resolved": "https://registry.npmjs.org/runbash/-/runbash-0.1.0-pre.0.tgz",
-      "integrity": "sha512-tLzh4E2UJSYfyic3idVLHrpZf7h6jerQ+VVaCkwr7NYcMt3XHMMPUIIcwHQixdnKVKj3Q/wID7/0I/jU0zV/tw==",
-      "dev": true,
-      "requires": {
-        "bash.origin": "^0.2.0-pre.0",
-        "bluebird": "^3.5.0"
-      }
-    },
-    "safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "dev": true
-    },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
-      "dev": true
-    },
-    "send": {
-      "version": "0.17.1",
-      "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
-      "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
-      "dev": true,
-      "requires": {
-        "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "destroy": "~1.0.4",
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "etag": "~1.8.1",
-        "fresh": "0.5.2",
-        "http-errors": "~1.7.2",
-        "mime": "1.6.0",
-        "ms": "2.1.1",
-        "on-finished": "~2.3.0",
-        "range-parser": "~1.2.1",
-        "statuses": "~1.5.0"
-      },
-      "dependencies": {
-        "ms": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
-          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
-          "dev": true
-        }
-      }
-    },
-    "serve-static": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
-      "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
-      "dev": true,
-      "requires": {
-        "encodeurl": "~1.0.2",
-        "escape-html": "~1.0.3",
-        "parseurl": "~1.3.3",
-        "send": "0.17.1"
-      }
-    },
-    "setimmediate": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
-      "dev": true
-    },
-    "setprototypeof": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
-      "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
-      "dev": true
-    },
-    "sshpk": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
-      "dev": true,
-      "requires": {
-        "asn1": "~0.2.3",
-        "assert-plus": "^1.0.0",
-        "bcrypt-pbkdf": "^1.0.0",
-        "dashdash": "^1.12.0",
-        "ecc-jsbn": "~0.1.1",
-        "getpass": "^0.1.1",
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.0.2",
-        "tweetnacl": "~0.14.0"
-      }
-    },
-    "statuses": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
-      "dev": true
-    },
-    "string-width": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-      "dev": true,
-      "requires": {
-        "is-fullwidth-code-point": "^2.0.0",
-        "strip-ansi": "^4.0.0"
-      },
-      "dependencies": {
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        }
-      }
-    },
-    "strip-ansi": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
-      "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=",
-      "dev": true
-    },
-    "toidentifier": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
-      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
-      "dev": true
-    },
-    "tough-cookie": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
-      "dev": true,
-      "requires": {
-        "psl": "^1.1.24",
-        "punycode": "^1.4.1"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-          "dev": true
-        }
-      }
-    },
-    "traverse": {
-      "version": "0.6.6",
-      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
-      "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=",
-      "dev": true
-    },
-    "tunnel-agent": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "tweetnacl": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
-      "dev": true
-    },
-    "type-is": {
-      "version": "1.6.18",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
-      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
-      "dev": true,
-      "requires": {
-        "media-typer": "0.3.0",
-        "mime-types": "~2.1.24"
-      }
-    },
-    "underscore": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
-      "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=",
-      "dev": true
-    },
-    "universalify": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
-      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
-      "dev": true
-    },
-    "unpipe": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
-      "dev": true
-    },
-    "uri-js": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
-      "dev": true,
-      "requires": {
-        "punycode": "^2.1.0"
-      }
-    },
-    "utils-merge": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
-      "dev": true
-    },
-    "uuid": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
-      "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
-      "dev": true
-    },
-    "uuidgen": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/uuidgen/-/uuidgen-1.0.0.tgz",
-      "integrity": "sha1-QSixhV8/gQWklbbojbhEFo3gIe4=",
-      "dev": true
-    },
-    "vary": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
-      "dev": true
-    },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
-      }
-    },
-    "waitfor": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/waitfor/-/waitfor-0.1.3.tgz",
-      "integrity": "sha1-TcWjVAb7uYnnb/NYZ1cqKjWlqog=",
-      "dev": true,
-      "requires": {
-        "setimmediate": "~1.0.1"
-      }
-    },
-    "wildfire-for-js": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/wildfire-for-js/-/wildfire-for-js-0.1.4.tgz",
-      "integrity": "sha512-tIuyYssHidAhahJfHLCW7BrGq7FE+39Cz65BA8Tl+IH1WLurOmKmOF21Dv8HmLU6JLE67mJ31wQTmFyxtfp2jQ==",
-      "dev": true,
-      "requires": {
-        "fp-modules-for-nodejs": "^0.2.2"
-      }
-    },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
-    }
-  }
-}
diff --git a/package.json b/package.json
index ab7c5f7..d212078 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,19 @@
 {
   "name": "firephp-for-browser-devtools",
-  "version": "1.3.0",
+  "version": "1.3.1",
   "devDependencies": {
+    "codemirror": "^5.49.0",
     "eventemitter2": "^4.1.2",
     "insight-for-js": "^0.2.0-pre.0",
     "wildfire-for-js": "^0.1.4",
     "fireconsole.rep.js": "^0.1.0-pre.0",
-    "bash.origin.lib": "^0.4.0-pre.3"
+    "bash.origin.lib": "^0.4.0-pre.20"
   },
   "scripts": {
-    "start": "./workspace.sh run",
-    "test": "bash.origin.test tests",
-    "build": "./workspace.sh build",
-    "build-via-docker": "./src/docker/build.sh"
+    "test": "pinf.it ./#!inf.json test",
+    "dev": "pinf.it ./#!inf.json dev --watch",
+    "build": "pinf.it ./#!inf.json build",
+    "build-via-docker": "./src/docker/build.sh",
+    "publish": "pinf.it ./#!inf.json publish"
   }
-}
+}
\ No newline at end of file
diff --git a/src/adapters/http-request-observer.js b/src/adapters/http-request-observer.js
index f705d63..a453f4c 100644
--- a/src/adapters/http-request-observer.js
+++ b/src/adapters/http-request-observer.js
@@ -1,101 +1,89 @@
 
-exports.for = function (API) {
+exports.forAPI = function (API) {
 
-    var processor = null;
-    var requestIndex = 0;
+    class RequestObserver {
 
+        constructor (onRequestHandler) {
+            const self = this;
 
-    // Firefox allows returning a promise since version 52
-    // @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest
-    // Chrome requires a sync return
-    // @see https://developer.chrome.com/extensions/webRequest#event-onBeforeSendHeaders
-    function onRequest (request) {
+            API.on("destroy", function () {
+                self.ensureUnhooked();
+            });
 
-//console.log("MAKE REQUEST ... add headers", request);
+            let requestIndex = 0;
+            let isHooked = false;
 
-        var requestId = null;
+            // Firefox allows returning a promise since version 52
+            // @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest
+            // Chrome requires a sync return
+            // @see https://developer.chrome.com/extensions/webRequest#event-onBeforeSendHeaders
+            function onRequest (request) {
 
-        var headers = {};
-        request.requestHeaders.forEach(function (header) {
-            if (header.name.toLowerCase() === "x-request-id") {
-                requestId = header.value;
-            }
-            headers[header.name] = header.value;
-        });
-
-//console.log("register processor", processor);
-
-        var result = processor({
-            "id": requestId || "id:" + request.url + ":" + requestIndex,
-            "url": request.url,
-            "hostname": request.url.replace(/^https?:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
-            "port": request.url.replace(/^https?:\/\/[^:]+:?(\d+)?\/.*?$/, "$1") || 80,
-            "method": request.method,
-            "headers": headers
-            /*
-            setRequestHeader: function (name, value) {
-
-//console.log("SET REQUEST HEADER", "name, value", name, value);
-
-                request.requestHeaders.filter(function (header) {
-                    return (header.name === name);
-                })[0].value = value;
-            }
-            */
-        });
-
-        if (!result) {
-            return {};
-        }
-
-        const changes = result;
-        //return Promise.resolve(result).then(function (changes) {
+                if (API.VERBOSE) console.log("[http-request-observer] onRequest (request):", request);
 
-            //if (!changes) {
-            //    return {};
-            //}
-            
-            var ret = {};
+                let requestId = null;
+                let headers = {};
+                request.requestHeaders.forEach(function (header) {
+                    if (header.name.toLowerCase() === "x-request-id") {
+                        requestId = header.value;
+                    }
+                    headers[header.name] = header.value;
+                });
 
-            if (changes.requestHeaders) {
-                var headers = [];
-                Object.keys(changes.requestHeaders).forEach(function (name) {
-                    headers.push({
-                        name: name,
-                        value: changes.requestHeaders[name]
-                    });
+                requestIndex += 1;
+        
+                let result = onRequestHandler({
+                    "id": requestId || "id:" + request.url + ":" + requestIndex,
+                    "url": request.url,
+                    "hostname": request.url.replace(/^https?:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
+                    "port": request.url.replace(/^https?:\/\/[^:]+:?(\d+)?\/.*?$/, "$1") || 80,
+                    "method": request.method,
+                    "headers": headers
                 });
-                ret.requestHeaders = headers;
+        
+                if (
+                    !result ||
+                    !result.requestHeaders
+                ) {
+                    return {};
+                }
+        
+                return {
+                    requestHeaders: Object.keys(result.requestHeaders).map(function (name) {
+                        return {
+                            name: name,
+                            value: result.requestHeaders[name]
+                        };
+                    })
+                };
+            }            
+
+
+            self.ensureHooked = function () {
+                if (!isHooked) {    
+                    API.BROWSER.webRequest.onBeforeSendHeaders.addListener(
+                        onRequest,
+                        {
+                            urls: [
+                                "<all_urls>"
+                            ]
+                        },
+                        [
+                            "blocking",
+                            "requestHeaders"
+                        ]
+                    );
+                    isHooked = true;
+                }        
+            }
+    
+            self.ensureUnhooked = function () {
+                API.BROWSER.webRequest.onBeforeSendHeaders.removeListener(onRequest);
+                isHooked = false;
             }
 
-//console.log("onBeforeSendHeaders:", ret);
-
-            return ret;    
-        //});
+        }
     }
 
-
-    API.BROWSER.webRequest.onBeforeSendHeaders.addListener(
-        onRequest,
-        {
-            urls: [
-                "<all_urls>"
-            ]
-        },
-        [
-            "blocking",
-            "requestHeaders"
-        ]
-    );
-
-    API.on("destroy", function () {
-
-        API.BROWSER.webRequest.onBeforeSendHeaders.removeListener(onRequest);
-    });
-
-    return {
-        register: function (_processor) {
-            processor = _processor;
-        }
-    };
+    return RequestObserver;
 }
diff --git a/src/adapters/http-response-observer.js b/src/adapters/http-response-observer.js
index 40169a5..99fb15b 100644
--- a/src/adapters/http-response-observer.js
+++ b/src/adapters/http-response-observer.js
@@ -1,83 +1,96 @@
 
-exports.for = function (API) {
 
-    // TODO: Clear old records
-    let pageUrlByTabId = {};
-    let pageTimestampByTabId = {};
+exports.forAPI = function (API) {
 
+    class ResponseObserver {
 
-    function onHeadersReceived (response) {
-//        if (API.VERBOSE)
-//console.log("[http-response-observer] onHeadersReceived (response):", response);
+        constructor (onResponseHandler) {
+            const self = this;
 
-        var pageUrl = response.documentUrl || response.url;
-        var pageTimeStamp = response.timeStamp;
-        if (response.parentFrameId !== -1) {
-            pageUrl = pageUrlByTabId[response.tabId] || null;
-            pageTimeStamp = pageTimestampByTabId[response.tabId] || null;
-        } else
-        if (
-            response.type === "main_frame"// ||
-//            response.type === "sub_frame"
-        ) {
-            pageUrlByTabId[response.tabId] = pageUrl;
-            pageTimestampByTabId[response.tabId] = pageTimeStamp;
-        }
+            API.on("destroy", function () {
+                self.ensureUnhooked();
+            });            
+
+            let isHooked = false;
+
+            let pageUrlByTabId = {};
+            let pageTimestampByTabId = {};
 
-//console.log("pageUrlByFrameId :::", pageUrlByFrameId);
+            function onHeadersReceived (response) {
 
-//console.log("Make pageUrl", pageUrl, "from response", response);
+                if (API.VERBOSE) console.log("[http-response-observer] onHeadersReceived (response):", response);
 
-        API.emit("http.response", {
-            "request": {
-                "id": response.requestId,
-                //"url": response.url,
-                //"hostname": response.url,
-                //"port": response.url,
-                //"method": response.method,
-                //"headers": [],
-                "context": {
-                    frameId: response.frameId,
-                    tabId: response.tabId,
-                    url: response.url,
-                    hostname: response.url.replace(/^[^:]+:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
-                    requestId: response.requestId,
-                    requestType: response.type,
-                    documentUrl: response.documentUrl,
-                    timeStamp: response.timeStamp,
-                    pageUrl: pageUrl,
-                    pageTimeStamp: pageTimeStamp,
-                    pageUid: JSON.stringify({
-                        url: pageUrl,
-                        tabId: response.tabId
-                    }),
-                    requestUid: JSON.stringify({
-                        url: response.url,
-                        timeStamp: response.timeStamp,
-                        frameId: response.frameId,
-                        tabId: response.tabId
-                    })
+                let pageUrl = response.documentUrl || response.url;
+                let pageTimeStamp = response.timeStamp;
+                if (response.parentFrameId !== -1) {
+                    pageUrl = pageUrlByTabId[response.tabId] || null;
+                    pageTimeStamp = pageTimestampByTabId[response.tabId] || null;
+                } else
+                if (
+                    response.type === "main_frame"// ||
+                    //response.type === "sub_frame"
+                ) {
+                    pageUrlByTabId[response.tabId] = pageUrl;
+                    pageTimestampByTabId[response.tabId] = pageTimeStamp;
                 }
-            },
-            "status": response.statusCode,
-            //"contentType": contentType,
-            "headers": response.responseHeaders
-        });
+
+                onResponseHandler({
+                    "request": {
+                        "id": response.requestId,
+                        "context": {
+                            frameId: response.frameId,
+                            tabId: response.tabId,
+                            url: response.url,
+                            hostname: response.url.replace(/^[^:]+:\/\/([^:\/]+)(:\d+)?\/.*?$/, "$1"),
+                            requestId: response.requestId,
+                            requestType: response.type,
+                            documentUrl: response.documentUrl,
+                            timeStamp: response.timeStamp,
+                            pageUrl: pageUrl,
+                            pageTimeStamp: pageTimeStamp,
+                            pageUid: JSON.stringify({
+                                url: pageUrl,
+                                tabId: response.tabId
+                            }),
+                            requestUid: JSON.stringify({
+                                url: response.url,
+                                timeStamp: response.timeStamp,
+                                frameId: response.frameId,
+                                tabId: response.tabId
+                            })
+                        }
+                    },
+                    "status": response.statusCode,
+                    "headers": response.responseHeaders
+                });
+            }
+
+            self.ensureHooked = function () {
+                if (!isHooked) {    
+                    API.BROWSER.webRequest.onHeadersReceived.addListener(
+                        onHeadersReceived,
+                        {
+                            urls: [
+                                "<all_urls>"
+                            ]
+                        },
+                        [
+                            "responseHeaders"
+                        ]
+                    );
+                    isHooked = true;
+                }
+            }
+
+            self.ensureUnhooked = function () {
+                API.BROWSER.webRequest.onHeadersReceived.removeListener(onHeadersReceived);
+                isHooked = false;
+                pageUrlByTabId = {};
+                pageTimestampByTabId = {};
+            }
+        }
+
     }
-    API.BROWSER.webRequest.onHeadersReceived.addListener(
-        onHeadersReceived,
-        {
-            urls: [
-                "<all_urls>"
-            ]
-        },
-        [
-            "responseHeaders"
-        ]
-    );
-    API.on("destroy", function () {
-        API.BROWSER.webRequest.onHeadersReceived.removeListener(onHeadersReceived);
-    });
 
-    return {};
+    return ResponseObserver;
 }
diff --git a/src/adapters/page-browser-api.js b/src/adapters/page-browser-api.js
deleted file mode 100644
index ebbe701..0000000
--- a/src/adapters/page-browser-api.js
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-module.exports = {
-    runtime: {
-        onMessage: {
-            addListener: function (listener) {
-
-console.log("[page-browser] browser.runtime.onMessage.addListener (listener):", listener);                    
-                
-            }
-        },
-        sendMessage: function (message) {
-
-console.log("[page-browser] browser.runtime.sendMessage (message):", message);                    
-
-        }
-    },
-    storage: {
-        onChanged: {
-            addListener: function (listener) {
-                
-            }
-        },
-        local: {
-            get: function (name) {
-                return Promise.resolve(false);
-            }
-        }
-    },
-    devtools: {
-        inspectedWindow: {
-            tabId: 1
-        }
-    }
-};
diff --git a/src/background.js b/src/background.js
index 7cc7f93..780d78e 100644
--- a/src/background.js
+++ b/src/background.js
@@ -4,14 +4,31 @@ const WILDFIRE = exports.WILDFIRE = require("./wildfire");
 
 const LIB = require("./lib");
 
-WILDFIRE.VERBOSE = true;
 
+const COMPONENT = require("./component");
 
-WILDFIRE.once("error", function (err) {
+const comp = COMPONENT.for({
+    browser: WINDOW.crossbrowser,
+    getOwnTabId: function () {
+        if (!currentContext) {
+            return null;
+        }
+        return currentContext.tabId;
+    }
+});
+
+
+const wildfire = new WILDFIRE.Client(comp, {
+    verbose: false
+});
+
+wildfire.once("error", function (err) {
     console.error(err);
 });
 
 
+
+
 async function initCurrentContext () {
     if (currentContext) {
         // Already initialized
@@ -43,23 +60,50 @@ setImmediate(initCurrentContext);
 
 
 // TODO: Emit destroy when unloaded to proactively cleanup. Do we need to do that?
-//WILDFIRE.emit("destroy");
+//wildfire.emit("destroy");
 
 
+let serverUrl = null;
+
 function broadcastForContext (context, message) {
     message.context = context;
-    message.to = "message-listener";
+    message.to = message.forceTo || "message-listener";
 
+    comp.handleBroadcastMessage(message);
     return LIB.browser.runtime.sendMessage(message).catch(function (err) {
-        if (WILDFIRE.VERBOSE) console.log("WARNING", err);
+        if (wildfire.VERBOSE) console.log("WARNING", err);
     });
 }
 
-WILDFIRE.on("message.firephp", function (message) {
-    
-    if (WILDFIRE.VERBOSE) console.log("[background] WILDFIRE.on -| message.firephp (message):", message);
+wildfire.on("message.firephp", function (message) {
+    if (wildfire.VERBOSE) console.log("[background] WILDFIRE.on -| message.firephp (message):", message);
+
+    if (message.context.serverUrl) {
+        serverUrl = message.context.serverUrl;
+    }
+
+    broadcastForContext(message.context, {
+        message: {
+            sender: message.sender,
+            receiver: message.receiver,
+            meta: message.meta,
+            data: message.data            
+        }
+    });
+});
+
+wildfire.on("message.insight.selective", function (message) {    
+    if (wildfire.VERBOSE) console.log("[background] WILDFIRE.on -| message.insight.selective (message):", message);
+
+});
+
+wildfire.on("message.insight.request", function (message) {
+    if (wildfire.VERBOSE) console.log("[background] WILDFIRE.on -| message.insight.request (message):", message);
+
+//console.log("REQUEST message:", message);
 
     broadcastForContext(message.context, {
+        forceTo: "protocol",
         message: {
             sender: message.sender,
             receiver: message.receiver,
@@ -70,6 +114,7 @@ WILDFIRE.on("message.firephp", function (message) {
 });
 
 
+
 let currentContext = null;
 //let broadcastCurrentContext = false;
 
@@ -78,7 +123,10 @@ let lastDetailsForTabId = {};
 function setCurrentContextFromDetails (details, clearIfNew) {
     if (!details) {
         if (currentContext) {
+console.log("CLEAR CONTEXT", "reset serverUrl");            
+
             currentContext = null;
+            serverUrl = null;
             broadcastForContext(currentContext, {
                 event: "currentContext"
             });
@@ -98,6 +146,8 @@ function setCurrentContextFromDetails (details, clearIfNew) {
                 newCtx.pageUid !== currentContext.pageUid
             )
         ) {
+console.log("NEW CONTEXT", "reset serverUrl", currentContext, newCtx);            
+            serverUrl = null;
 
 //console.log("NEW CONTEXT", newCtx, details);
 
@@ -124,7 +174,7 @@ function setCurrentContextFromDetails (details, clearIfNew) {
 
 async function runtime_onMessage (message) {
 
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.runtime -| onMessage (message):", message);
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.runtime -| onMessage (message):", message);
 
     if (message.to === "broadcast") {
         if (message.event === "currentContext") {
@@ -139,6 +189,7 @@ async function runtime_onMessage (message) {
             ) {
                 await initCurrentContext();
             }
+//console.log("FORWARD in BACKGROUD", message);            
             broadcastForContext(message.context || currentContext || null, message);
         }
     } else
@@ -147,25 +198,70 @@ async function runtime_onMessage (message) {
             LIB.browser.tabs.reload(message.context.tabId, {
                 bypassCache: true
             });
+        } else
+        if (message.event === "load-file") {
+
+
+console.log("LOAD FILE FROM:::", serverUrl);
+
+            const file = message.file;
+            const line = message.line;
+
+            if (!serverUrl) {
+
+console.log("SLIP LOAD FILE FROM::: DUE TO NO serverUrl");
+
+                // TODO: Show error 'Server URL not available!' in UI
+                return;
+            }
+
+            try {
+                const response = await wildfire.callServer(serverUrl, {
+                    target: 'Insight_Plugin_FileViewer',
+                    action: 'GetFile',
+                    args: {
+                        path: file
+                    }
+                });
+
+console.log("SERVER response:", response);
+
+                if (!response) {
+                    return;
+                }
+
+                broadcastForContext(currentContext || null, {
+                    action: "show-file",
+                    args: {
+                        file: file,
+                        line: line,
+                        content: response
+                    }
+                });
+
+            } catch (err) {
+                // TODO: Show error message in UI
+                console.error("Error calling server:", err);
+            }
         }
     }
 }
 BROWSER.runtime.onMessage.addListener(runtime_onMessage);
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
     BROWSER.runtime.onMessage.removeListener(runtime_onMessage);
 });
 
 
 
 function webNavigation_onBeforeNavigate (details) {
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.webNavigation -| onBeforeNavigate (details):", details);
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.webNavigation -| onBeforeNavigate (details):", details);
 
     // We only care about the page frame event.
     if (details.parentFrameId !== -1) {
         return;
     }
 
-//console.log("ON BEFORE NAVIGATE", details);
+console.log("ON BEFORE NAVIGATE", details);
 
     setCurrentContextFromDetails(details);
 }
@@ -174,13 +270,14 @@ BROWSER.webNavigation.onBeforeNavigate.addListener(webNavigation_onBeforeNavigat
         {}
     ]
 });
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
     BROWSER.webNavigation.onBeforeNavigate.removeListener(webNavigation_onBeforeNavigate);
 });
 
 
+
 function webRequest_onBeforeRequest (details) {
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.webRequest -| onBeforeRequest (details):", details);
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.webRequest -| onBeforeRequest (details):", details);
 
     // We only care about the page frame event.
     if (
@@ -196,7 +293,7 @@ function webRequest_onBeforeRequest (details) {
         return;
     }
 
-//console.log("ON BEFORE REQUEST", details);
+console.log("ON BEFORE REQUEST", details);
 
     setCurrentContextFromDetails(details, true);
 }
@@ -205,14 +302,40 @@ BROWSER.webRequest.onBeforeRequest.addListener(webRequest_onBeforeRequest, {
         "<all_urls>"
     ]
 });
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
     BROWSER.webRequest.onBeforeRequest.removeListener(webRequest_onBeforeRequest);
 });
 
 
+function tabs_onRemoved (tabId) {
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.tabs -| onRemoved (tabId):", tabId);
+    
+    if (
+        currentContext &&
+        currentContext.tabId == tabId
+    ) {
+        setCurrentContextFromDetails(null);
+    }
+
+    return broadcastForContext({
+        tabId: tabId
+    }, {
+        event: "destroyContext"
+    });
+}
+BROWSER.tabs.onRemoved.addListener(tabs_onRemoved);
+wildfire.on("destroy", function () {
+    BROWSER.tabs.onRemoved.removeListener(tabs_onRemoved);
+});
+
+
+
+
+
+
 /*
 function webNavigation_onCommitted (details) {
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.webNavigation -| onCommitted (details):", details);
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.webNavigation -| onCommitted (details):", details);
 
     // We only care about the page frame event.
     if (details.parentFrameId !== -1) {
@@ -228,7 +351,7 @@ BROWSER.webNavigation.onCommitted.addListener(webNavigation_onCommitted, {
         {}
     ]
 });
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
     BROWSER.webNavigation.onCommitted.removeListener(webNavigation_onCommitted);
 });
 */
@@ -237,42 +360,18 @@ WILDFIRE.on("destroy", function () {
 
 /*
 function tabs_onActivated (details) {
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.tabs -| onActivated (details):", details);
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.tabs -| onActivated (details):", details);
 
 console.log("TABS on ACTIVATE", details);
 
     setCurrentContextFromDetails(lastDetailsForTabId[details.tabId] || null);
 }
 BROWSER.tabs.onActivated.addListener(tabs_onActivated);
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
     BROWSER.tabs.onActivated.removeListener(tabs_onActivated);
 });
 */
 
-
-function tabs_onRemoved (tabId) {
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.tabs -| onRemoved (tabId):", tabId);
-    
-    if (
-        currentContext &&
-        currentContext.tabId == tabId
-    ) {
-        setCurrentContextFromDetails(null);
-    }
-
-    return broadcastForContext({
-        tabId: tabId
-    }, {
-        event: "destroyContext"
-    });
-}
-BROWSER.tabs.onRemoved.addListener(tabs_onRemoved);
-WILDFIRE.on("destroy", function () {
-    BROWSER.tabs.onRemoved.removeListener(tabs_onRemoved);
-});
-
-
-
 /*
 function webNavigation_onBeforeNavigate (details) {
 
@@ -280,7 +379,7 @@ function webNavigation_onBeforeNavigate (details) {
         return;
     }
 
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.webNavigation -| onBeforeNavigate (details):", details);
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.webNavigation -| onBeforeNavigate (details):", details);
 
     // We only care about the page frame event.
     if (details.parentFrameId !== -1) {
@@ -305,7 +404,7 @@ BROWSER.webNavigation.onBeforeNavigate.addListener(webNavigation_onBeforeNavigat
         {}
     ]
 });
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
     BROWSER.webNavigation.onBeforeNavigate.removeListener(webNavigation_onBeforeNavigate);
 });
 
@@ -315,7 +414,7 @@ function webNavigation_onDOMContentLoaded (details) {
         return;        
     }
     
-    if (WILDFIRE.VERBOSE) console.log("[background] BROWSER.webNavigation -| onDOMContentLoaded (details):", details);
+    if (wildfire.VERBOSE) console.log("[background] BROWSER.webNavigation -| onDOMContentLoaded (details):", details);
 
     // We only care about the page frame event.
     if (details.parentFrameId !== -1) {
@@ -335,7 +434,7 @@ BROWSER.webNavigation.onDOMContentLoaded.addListener(webNavigation_onDOMContentL
         {}
     ]
 });
-WILDFIRE.on("destroy", function () {
+wildfire.on("destroy", function () {
     BROWSER.webNavigation.onDOMContentLoaded.removeListener(webNavigation_onDOMContentLoaded);
 });
 */
\ No newline at end of file
diff --git a/src/component.js b/src/component.js
index c7b3d29..0c4d605 100644
--- a/src/component.js
+++ b/src/component.js
@@ -5,6 +5,7 @@ const DEBOUNCE = require('lodash/debounce');
 exports.for = function (ctx) {
 
     const events = new EVENTS.EventEmitter();
+    events.browser = ctx.browser;
 
     events.currentContext = null;
 //    events.settings = {};
@@ -38,30 +39,51 @@ exports.for = function (ctx) {
         }
     }
 
-    ctx.browser.runtime.onMessage.addListener(function (message) {
+    events.handleBroadcastMessage = function (message) {
         try {
             if (
-                !ctx.browser ||
-                !ctx.browser.devtools ||
-                !ctx.browser.devtools.inspectedWindow ||
-                !message.context ||
-                message.context.tabId != ctx.browser.devtools.inspectedWindow.tabId
+                message.context &&
+                (
+                    (
+                        ctx.getOwnTabId &&
+                        message.context.tabId === ctx.getOwnTabId()
+                    ) ||
+                    (
+                        ctx.browser &&
+                        ctx.browser.devtools &&
+                        ctx.browser.devtools.inspectedWindow &&
+                        message.context.tabId === ctx.browser.devtools.inspectedWindow.tabId
+                    )
+                )
             ) {
-                return;
-            }
-            if (message.to === "message-listener") {
-                if (
-                    message.event === "currentContext" &&
-                    typeof message.context !== "undefined"
-                ) {
-                    onContextMessage(message.context);
+                if (message.to === "message-listener") {
+                    if (
+                        message.event === "currentContext" &&
+                        typeof message.context !== "undefined"
+                    ) {
+                        onContextMessage(message.context);
+                    }
+                    events.emit("message", message);
+                } else
+                if (message.to === "protocol") {
+                    if (
+                        ctx.handlers &&
+                        ctx.handlers[message.message.receiver]
+                    ) {
+                        message.message.meta = JSON.parse(message.message.meta);
+                        message.message.data = JSON.parse(message.message.data);
+                        ctx.handlers[message.message.receiver](message.message);
+                    }
                 }
-                events.emit("message", message);
             }
         } catch (err) {
             console.error(err);
         }
-    });
+    }
+    ctx.browser.runtime.onMessage.addListener(events.handleBroadcastMessage);
+
+    const globalSettings = {};
+    const domainSettings = {};
 
     ctx.browser.storage.onChanged.addListener(function (changes, area) {
         try {
@@ -69,9 +91,12 @@ exports.for = function (ctx) {
                 return;
             }
 
-            const prefix = `domain[${events.currentContext.hostname}].`;
+            const prefix = `hostname[${events.currentContext.hostname}].`;
             for (var item of Object.keys(changes)) {
-                if (!/^domain\[.+\]\..+$/.test(item)) continue;
+                if (!/^hostname\[.+\]\..+$/.test(item)) {
+                    globalSettings[item] = changes[item].newValue;
+                    continue;
+                }
 
                 if (item.substring(0, prefix.length) === prefix) {
     //console.log("[component] Updated domain settings '" + item + "':", events.settings[name]);
@@ -80,8 +105,13 @@ exports.for = function (ctx) {
     //                if (events.settings[name] !== changes[item].newValue) {
     //                    events.settings[name] = changes[item].newValue;
 
-                        events.emit("setting." + name, changes[item].newValue);
-                        events.emit("changed.setting", name, changes[item].newValue);
+                        domainSettings[events.currentContext.hostname] = domainSettings[events.currentContext.hostname] || {};
+                        domainSettings[events.currentContext.hostname][name] = (changes[item].newValue || false);
+
+                        delete events._getHostnameSettingsForSync._cache[events.currentContext.hostname];                        
+
+                        events.emit("setting." + name, domainSettings[events.currentContext.hostname][name]);
+                        events.emit("changed.setting", name, domainSettings[events.currentContext.hostname][name]);
     //                }
                 }
             }
@@ -97,31 +127,60 @@ exports.for = function (ctx) {
         });
     }, 250);
 
-    events.getSetting = function (name) {
+    // Get a setting for the current context's hostname
+    events.getSetting = async function (name) {
         if (!events.currentContext) {
             return Promise.resolve(null);
         }
         return events._getSettingForHostname(events.currentContext.hostname, name);
     }
-    events._getSettingForHostname = function (hostname, name) {
+    events._getSettingForHostname = async function (hostname, name, defaultValue) {
+        if (typeof defaultValue === "undefined") {
+            defaultValue = false;
+        }
         if (typeof ctx.browser === "undefined") {
             return Promise.resolve(null);
         }
-        var key = "domain[" + hostname + "]." + name;
+        var key = "hostname[" + hostname + "]." + name;
         return ctx.browser.storage.local.get(key).then(function (value) {
-            return (value[key] || false);
+            if (
+                value[key] === null ||
+                typeof value[key] === "undefined"
+            ) {
+                return defaultValue;
+            }
+            return value[key];
+        }).then(function (value) {
+            domainSettings[hostname] = domainSettings[hostname] || {};
+            domainSettings[hostname][name] = value;
+            return value;            
         }).catch(function (err) {
             console.error(err);
             throw err;
         });
-    }    
-    events.setSetting = function (name, value) {
+    }
+
+    events._getSettingForHostnameSync = function (hostname, name, defaultValue) {
+        if (typeof defaultValue === "undefined") {
+            defaultValue = false;
+        }
+        if (
+            !domainSettings[hostname] ||
+            typeof domainSettings[hostname][name] === "undefined"
+        ) {
+            events._getSettingForHostname(hostname, name, defaultValue);
+            return defaultValue;
+        }
+        return domainSettings[hostname][name];
+    }
+
+    events.setSetting = async function (name, value) {
         if (!events.currentContext) {
             throw new Error(`Cannot set setting for name '${name}' due to no 'currentContext'!`);
         }
         return events._setSettingForHostname(events.currentContext.hostname, name, value);
     }
-    events._setSettingForHostname = function (hostname, name, value) {
+    events._setSettingForHostname = async function (hostname, name, value) {
         if (typeof ctx.browser === "undefined") {
             return Promise.resolve(null);
         }
@@ -131,7 +190,9 @@ exports.for = function (ctx) {
                 return;
             }
             var obj = {};
-            obj["domain[" + hostname + "]." + name] = value;
+            obj["hostname[" + hostname + "]." + name] = value;
+            domainSettings[hostname] = domainSettings[hostname] || {};
+            domainSettings[hostname][name] = value;
             return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
         }).catch(function (err) {
             console.error(err);
@@ -139,9 +200,9 @@ exports.for = function (ctx) {
         });
     }
 
-    events.getGlobalSetting = function (name) {
+    events.getGlobalSetting = async function (name) {
         if (typeof ctx.browser === "undefined") {
-            return Promise.resolve(null);
+            return null;
         }
         var defaultValue;
         if (name === "reloadOnEnable") {
@@ -155,14 +216,17 @@ exports.for = function (ctx) {
                 return null;
             }
             return value[name];
+        }).then(function (value) {
+            globalSettings[name] = value;
+            return value;
         }).catch(function (err) {
             console.error(err);
             throw err;
         });
     }
-    events.setGlobalSetting = function (name, value) {
+    events.setGlobalSetting = async function (name, value) {
         if (typeof ctx.browser === "undefined") {
-            return Promise.resolve(null);
+            return null;
         }
         return events.getGlobalSetting(name).then(function (existingValue) {
             if (value === existingValue) {
@@ -171,6 +235,7 @@ exports.for = function (ctx) {
             }
             var obj = {};
             obj[name] = value;
+            globalSettings[name] = value;
             return ctx.browser.storage.local.set(obj).then(broadcastCurrentContext);
         }).catch(function (err) {
             console.error(err);
@@ -178,20 +243,66 @@ exports.for = function (ctx) {
         });
     }
 
-    events.isConfigured = function () {
-        return events.getSetting("enableUserAgentHeader").then(function (enableUserAgentHeader) {
-            return events.getSetting("enableFirePHPHeader").then(function (enableFirePHPHeader) {
-                return events.getSetting("enableChromeLoggerData").then(function (enableChromeLoggerData) {
-                    return (
-                        enableUserAgentHeader ||
-                        enableFirePHPHeader ||
-                        enableChromeLoggerData
-                    );
-                });                            
-            });                            
-        });                            
+    events.isConfigured = async function () {
+        if (!events.currentContext) {
+            throw new Error(`Cannot get settings due to no 'currentContext'!`);
+        }
+        const settings = await events._getHostnameSettingsFor(events.currentContext.hostname);
+        return settings._configured;
+    }
+
+    events._getHostnameSettingsFor = async function (hostname) {
+        const settings = {
+            enabled: await events._getSettingForHostname(hostname, "enabled", false),
+            enableUserAgentHeader: await events._getSettingForHostname(hostname, "enableUserAgentHeader", false),
+            enableFirePHPHeader: await events._getSettingForHostname(hostname, "enableFirePHPHeader", false),
+            enableChromeLoggerData: await events._getSettingForHostname(hostname, "enableChromeLoggerData", false)
+        };
+
+        settings._configured = (
+            settings.enableUserAgentHeader ||
+            settings.enableFirePHPHeader ||
+            settings.enableChromeLoggerData
+        );
+
+        return settings;
     }
 
+    // Callable in high volume.
+    events._getHostnameSettingsForSync = function (hostname) {
+        if (!events._getHostnameSettingsForSync._cache[hostname]) {
+            const settings = {
+                enabled: events._getSettingForHostnameSync(hostname, "enabled", false),
+                enableUserAgentHeader: events._getSettingForHostnameSync(hostname, "enableUserAgentHeader", false),
+                enableFirePHPHeader: events._getSettingForHostnameSync(hostname, "enableFirePHPHeader", false),
+                enableChromeLoggerData: events._getSettingForHostnameSync(hostname, "enableChromeLoggerData", false)
+            };
+            settings._configured = (
+                settings.enableUserAgentHeader ||
+                settings.enableFirePHPHeader ||
+                settings.enableChromeLoggerData
+            );
+            events._getHostnameSettingsForSync._cache[hostname] = settings;
+        }
+        return events._getHostnameSettingsForSync._cache[hostname];
+    }
+    events._getHostnameSettingsForSync._cache = {};
+
+    events.isEnabled = async function () {
+        if (!events.currentContext) {
+            return false;
+        }
+        return events._isEnabledForHostname(events.currentContext.hostname);
+    }
+
+    events._isEnabledForHostname = async function (hostname) {
+        const settings = await events._getHostnameSettingsFor(hostname);
+        return (
+            settings.enabled &&
+            settings._configured
+        );
+    }
+    
     events.reloadBrowser = function () {
         ctx.browser.runtime.sendMessage({
             to: "background",
@@ -212,7 +323,7 @@ exports.for = function (ctx) {
         });
     }
 
-    events.showView = function (name) {
+    events.showView = function (name, args) {
         if (name === "manage") {
             ctx.browser.runtime.sendMessage({
                 to: "broadcast",
@@ -221,8 +332,35 @@ exports.for = function (ctx) {
                     tabId: ctx.browser.devtools.inspectedWindow.tabId
                 }*/
             });
+        } else
+        if (name === "editor") {
+            ctx.browser.runtime.sendMessage({
+                to: "broadcast",
+                event: "editor",
+                args: args
+            });
+        }
+    }
+
+    events.hideView = function (name) {
+        if (name === "editor") {
+console.log("broadcast hide view: editor");            
+            ctx.browser.runtime.sendMessage({
+                to: "broadcast",
+                event: "editor",
+                value: false
+            });
         }
     }
 
+    events.loadFile = function (file, line) {
+        ctx.browser.runtime.sendMessage({
+            to: "background",
+            event: "load-file",
+            file: file,
+            line: line
+        });
+    }
+
     return events;
 }
diff --git a/src/console.rep.js b/src/console.rep.js
index 06ebfce..e9befde 100644
--- a/src/console.rep.js
+++ b/src/console.rep.js
@@ -157,7 +157,6 @@ exports.main = function (JSONREP, node, options) {
                         try {
 /*                            
                             if (message.event === "currentContext") {
-
                                 lastRequestConsole = getConsoleForContext(message.context);
                                 if (persistLogs) {
                                     if (!persistentConsole) {
@@ -170,7 +169,6 @@ console.log("CLEAR lastRequestConsole CONSOLE!!");
 
                                     }
                                 }
-
                             } else         
 */
                             if (message.message) {
@@ -181,13 +179,21 @@ console.log("CLEAR lastRequestConsole CONSOLE!!");
 
 //console.log("APPEND MESSAGE TO REQUEST CONSOLE");
 
+console.log("LOG TO CONSOLE 1", message.message);
+                                    message.message.meta = (
+                                        message.message.meta &&
+                                        JSON.parse(message.message.meta)
+                                    ) || {};
+                                    message.message.meta.console = message.message.meta.console || {};
+                                    message.message.meta.console.enableFileInspect = true;
+
                                     api.send(message.message);
                                     scrollIfBottom();
                                 });
                             } else
                             if (message.event === "prepare") {
 
-console.log("PREPARE CONSOLE!!!", message.context);
+//console.log("PREPARE CONSOLE!!!", message.context);
 
                                 let fc = getConsoleForContext(message.context);
                                 if (!persistLogs) {
@@ -209,7 +215,7 @@ console.log("PREPARE CONSOLE!!!", message.context);
 
                             } else
                             if (message.event === "clear") {
-console.log("CLEAR CONSOLE!!!", message.context);
+//console.log("CLEAR CONSOLE!!!", message.context);
 
                                 Object.keys(consoles).forEach(function (id) {
 //console.log("console id", id);                                    
@@ -221,7 +227,7 @@ console.log("CLEAR CONSOLE!!!", message.context);
                                 scrollIfBottom();
                             } else
                             if (message.event === "destroyContext") {
-console.log("DESTROY CONTEXT!!!", message.context);
+//console.log("DESTROY CONTEXT!!!", message.context);
 
                                 Object.keys(consoles).forEach(function (id) {
                                     if (id == message.context.pageUid) {
diff --git a/src/editor.rep.js b/src/editor.rep.js
new file mode 100644
index 0000000..86ea2b4
--- /dev/null
+++ b/src/editor.rep.js
@@ -0,0 +1,210 @@
+
+exports.main = function (JSONREP, node, options) {    
+
+    return JSONREP.makeRep2({
+        "config": {
+            "node": node
+        },
+        "code": (riotjs:makeRep () >>>
+
+            <table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0">
+                <tr>
+                    <td class="menu">
+                    </td>
+                    <td class="menu menu-right">
+                        <button onclick={triggerClose}>Close</button>
+                    </td>
+                </tr>
+                <tr>
+                    <td class="filetree" width="20%">
+                        <div class="filetree">
+                        </div>
+                    </td>
+                    <td class="editor" width="80%">
+                        <textarea class="editor"></textarea>
+                    </td>
+                </tr>
+            </table>
+
+            <style>
+
+                :scope DIV TABLE {
+                    height: 100vh;
+                    width: 100vw;
+                }
+
+                :scope DIV TD.menu {
+                    background-color: #efefef;
+                    border-bottom: 1px solid #bcbcbc;
+                    padding: 3px;
+                    padding-left: 10px;
+                    padding-right: 10px;
+                }
+
+                :scope DIV TD.menu-right {
+                    text-align: right;
+                }
+
+                :scope DIV TD.filetree {
+                    border-right: 1px solid #bcbcbc;
+                    padding: 10px;
+                    vertical-align: top;
+                    height: 100%;
+                    min-width: 20%;
+                }
+
+                :scope DIV TD.editor {
+                    min-width: 80%;
+                    vertical-align: top;
+                    position: relative;
+                    box-sizing: border-box;
+                }
+
+                :scope DIV DIV.CodeMirror {
+                    position: absolute;
+                }
+
+            </style>
+
+            <script>
+                const COMPONENT = require("./component");
+
+                const tag = this;
+                let editor = null;
+
+                const comp = COMPONENT.for({
+                    browser: window.crossbrowser,
+                    handlers: {
+                        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/request/0": function (message) {
+
+console.log("MESSAGE in EDITOR ::::", message);
+
+                            const filetreeTag = tag.root.querySelector('DIV.filetree');
+
+                            WINDOW.FC.fireconsole.repRenderer.renderNodeInto({
+                                meta: {
+                                    "lang": "elements",
+                                    "lang.type": "pathtree"
+                                },
+                                value: message.data
+                            }, filetreeTag);
+
+                            window.FC.on("click", function (args) {
+
+console.log("EDITOR FC CLICK", args);
+
+                                if (args.rep === "insight.domplate.reps/elements/pathtree") {
+
+                                    comp.loadFile(args.node.value.id);
+
+/*
+                                    editor.getDoc().setValue(JSON.stringify({
+                                        "load file": args.node.value.id
+                                    }, null, 4));
+*/
+                                }
+                            });
+
+                            if (editor) {
+                                editor.getDoc().setValue(JSON.stringify({
+                                    "foo": "bar"
+                                }, null, 4));
+                            }
+                        }
+                    }
+                });
+
+                comp.on("changed.context", function (context) {
+                    comp.contextChangeAcknowledged();
+
+// TODO: Clear editor
+
+                });
+
+                comp.on("message", function (message) {
+                    if (message.action === "show-file") {
+
+console.log("EDITOR show-file::", message);
+
+                        editor.getDoc().setValue(message.args.content);
+// TODO: Position to line
+                        if (typeof message.args.line !== "undefined") {
+
+console.log("set cursor to", message.args.line || 0);             
+
+                            setTimeout(function () {
+                                editor.getDoc().setCursor((message.args.line-1) || 0, 0);
+                                editor.scrollIntoView(null, 100);
+                            }, 10);
+                        }
+
+                    } else
+                    if (message.event === "editor") {
+                        let forceEditor = false;
+                        if (typeof message.value !== 'undefined') {
+                            forceEditor = message.value;
+                        } else {
+                            forceEditor = true;
+                        }
+                        if (forceEditor) {
+                            setTimeout(function () {
+                                syncSize();
+                            }, 100);
+                        }
+
+console.log("SHOW EDITOR::", message);
+
+                        if (
+                            message.args &&
+                            message.args.show &&
+                            message.args.show.file
+                        ) {
+                            comp.loadFile(
+                                message.args.show.file,
+                                message.args.show.line || 0
+                            );
+                        }
+                    }
+                });
+
+                // TODO: Debounce
+                function syncSize () {
+                    if (!editor) {
+                        return;
+                    }
+                    const editorTableTag = tag.root.querySelector("TD.editor");
+                    editor.setSize(
+                        editorTableTag.getBoundingClientRect().width,
+                        editorTableTag.getBoundingClientRect().height
+                    );
+                    editor.refresh();
+                }
+                
+                tag.on("mount", function () {
+
+                    editor = CodeMirror.fromTextArea(tag.root.querySelector("TEXTAREA.editor"), {
+                        value: "",
+                        lineNumbers: true,
+                        mode: "application/x-httpd-php",
+                        autofocus: true,
+                        autoRefresh: true,
+                        styleActiveLine: true,
+                        styleActiveSelected: true
+                    });
+
+                    window.addEventListener("resize", function () {
+                        syncSize();
+                    }, false);
+                    syncSize();
+
+                });
+
+                tag.triggerClose = function (event) {
+                    comp.hideView("editor");
+                }
+
+            </script>
+
+        <<<)
+    }, options);
+};
diff --git a/src/enabler.rep.js b/src/enabler.rep.js
index 963a8f1..69984c4 100644
--- a/src/enabler.rep.js
+++ b/src/enabler.rep.js
@@ -29,8 +29,8 @@ exports.main = function (JSONREP, node, options) {
         "code": (riotjs:makeRep () >>>
 
             <div>
-                <button if={enabled === false} onclick={triggerEnable} class="enable">Enable</button>
-                <button if={enabled === true} onclick={triggerDisable} class="disable">Disable</button>
+                <button if={enabled === false} onclick={triggerEnable} class="enable">Click to Enable</button>
+                <button if={enabled === true} onclick={triggerDisable} class="disable">Click to Disable</button>
             </div>
 
             <style>
diff --git a/src/inspector.rep.js b/src/inspector.rep.js
index 5e284c6..dcaf324 100644
--- a/src/inspector.rep.js
+++ b/src/inspector.rep.js
@@ -98,9 +98,9 @@ exports.main = function (JSONREP, node, options) {
                     });
                         
                     window.FC.on("inspectFile", function (info) {
-                        showMessage({
-                            type: "string",
-                            value: "Viewing of files is not yet implemented."
+
+                        comp.showView("editor", {
+                            show: info
                         });
                     });
 
diff --git a/src/layout.rep.js b/src/layout.rep.js
index 011f517..6f28b44 100644
--- a/src/layout.rep.js
+++ b/src/layout.rep.js
@@ -99,6 +99,9 @@ exports.main = function (JSONREP, node, options) {
                     <div class="uninitialized" style="display: none;">
                         <p><button action="reload">Reload</button> to initialize FirePHP</p>
                     </div>
+                    <div class="editor" style="display: none;">
+                        %%%variables.panels.editor%%%
+                    </div>
                 </div>
             <<<),
             css: (css () >>>
@@ -140,6 +143,10 @@ exports.main = function (JSONREP, node, options) {
                     background-color: #efefef;
                 }
 
+                :scope .editor {
+                    height: 100%;
+                }
+
                 :scope .manage TABLE.header {
                     margin-bottom: 10px;
                 }
@@ -194,6 +201,7 @@ exports.main = function (JSONREP, node, options) {
                     const COMPONENT = require("./component");
 
                     let forceManage = false;
+                    let forceEditor = false;
 
                     const comp = COMPONENT.for({
                         browser: WINDOW.crossbrowser
@@ -202,6 +210,7 @@ exports.main = function (JSONREP, node, options) {
 
                     comp.on("changed.context", function () {
                         comp.contextChangeAcknowledged();
+                        forceEditor = false;
                         sync();
                     });
 
@@ -210,10 +219,20 @@ exports.main = function (JSONREP, node, options) {
                     });
 
                     comp.on("message", function (message) {
+console.log("message in layout", message);                            
                         if (message.event === "manage") {
                             forceManage = true;
                             sync();
-                        }
+                        } else
+                        if (message.event === "editor") {
+                            if (typeof message.value !== 'undefined') {
+                                forceEditor = message.value;
+                            } else {
+                                forceEditor = true;
+                            }
+console.log("forceEditor in layout", forceEditor);                            
+                            sync();
+                        }                        
                     });
 
 
@@ -241,6 +260,10 @@ exports.main = function (JSONREP, node, options) {
                                 if (forceManage) {
                                     view = "manage";
                                 } else
+                                // The editor should be open
+                                if (forceEditor) {
+                                    view = "editor";
+                                } else
                                 // Default state when first opening
                                 if (
                                     !configured &&
@@ -262,13 +285,13 @@ exports.main = function (JSONREP, node, options) {
                             } else {
                                 view = "uninitialized";                            
                             }
-
 //console.log("VIEW DETAIS", view, "configured", configured, "enabled", enabled);
 
                             var toggles = {
                                 ui: false,
                                 manage: false,
-                                uninitialized: false
+                                uninitialized: false,
+                                editor: false
                             };
 
                             const versionEl = el.querySelector("DIV.manage TABLE.header DIV.version");
@@ -295,6 +318,9 @@ exports.main = function (JSONREP, node, options) {
                             } else
                             if (view === "console") {
                                 toggles.ui = true;
+                            } else
+                            if (view === "editor") {
+                                toggles.editor = true;
                             } else {
                                 throw new Error(`'view' with value '${view}' not implemented!`);
                             }
@@ -302,6 +328,7 @@ exports.main = function (JSONREP, node, options) {
                             el.querySelector("DIV.ui").style.display = toggles.ui ? "block" : "none";
                             el.querySelector("DIV.manage").style.display = toggles.manage ? "block" : "none";
                             el.querySelector("DIV.uninitialized").style.display = toggles.uninitialized ? "block" : "none";
+                            el.querySelector("DIV.editor").style.display = toggles.editor ? "block" : "none";
 
                         } catch (err) {
                             console.error("Error during sync():", err.message || err.stack || err);
diff --git a/src/receivers/firephp.js b/src/receivers/firephp.js
deleted file mode 100644
index 59c039f..0000000
--- a/src/receivers/firephp.js
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-exports.for = function (API) {
-
-
-    var transportReceiver1 = API.WILDFIRE.Receiver();
-    transportReceiver1.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
-    transportReceiver1.addListener({
-	    onMessageReceived: function(request, message) {
-
-//	        try {
-                //message.requestUrl = request.url;
-                message.context = request.context;
-    
-//	        	API.console.log("FirebugConsole onMessageReceived 1", message);
-
-                if (
-                    API.on &&
-                    API.on.firePHPMessage
-                ) {
-                    API.on.firePHPMessage(message);
-                }
-
-//	        } catch (err) {
-//                API.console.error(err);
-//	        }
-	    }
-	});
-    API.httpHeaderChannel.addReceiver(transportReceiver1);
-
-
-    var transportReceiver2 = API.WILDFIRE.Receiver();
-    transportReceiver2.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1");
-    transportReceiver2.addListener({
-        onMessageReceived: function(request, message) {
-
-//	        try {
-
-                //message.requestUrl = request.url;
-                message.context = request.context;
-                
-//	        	API.console.log("FirePHP onMessageReceived", message);
-
-                if (
-                    API.on &&
-                    API.on.firePHPMessage
-                ) {
-                    API.on.firePHPMessage(message);
-                }
-
-//	        } catch (err) {
-//	        	API.console.error(err);
-//	        }
-	    }
-	});
-    API.httpHeaderChannel.addReceiver(transportReceiver2);
-
-
-/*
-// FirePHP 0.x compatibility
-"http://registry.pinf.org/cadorn.org/github/fireconsole/@meta/receiver/console/0.1.0"
-
-    var receiver = API.WILDFIRE.Receiver();
-    receiver.setId("http://github.com/fireconsole/@meta/receivers/wildfire/fireconsole/0");
-    receiver.addListener({
-
-        onMessageReceived: function(request, message) {
-
-            try {
-
-                var data = JSON.decode(message.getData());
-
-                if (data.method = "callApi") {
-
-                    return context.callApi(data.args[0], data.args[1] || {});
-
-                } else {
-                    throw new Error("Method '" + data.method + "' not found!");
-                }
-
-            } catch (err) {
-                console.error(err);
-            }
-        }
-
-    });
-*/
-
-    // FirePHP 0.x compatibility
-/*
-    var transportReceiver = API.WILDFIRE.Receiver();
-    transportReceiver.setId("http://registry.pinf.org/cadorn.org/github/fireconsole/@meta/receiver/console/0.1.0");
-    transportReceiver.addListener({
-	    onMessageReceived: function(request, message) {
-
-	        try {
-
-	        	API.console.log("FireConsole onMessageReceived FirePHP!: " + message);
-
-            if (
-                API.on &&
-                API.on.message
-            ) {
-                API.on.message(message);
-            }
-
-	        } catch (err) {
-	        	API.console.error(err);
-	        }
-	    }
-  	});
-    API.httpHeaderChannel.addReceiver(transportReceiver);
-*/
-
-	return {};
-}
diff --git a/src/receivers/insight.js b/src/receivers/insight.js
deleted file mode 100644
index a444179..0000000
--- a/src/receivers/insight.js
+++ /dev/null
@@ -1,54 +0,0 @@
-
-exports.for = function (API) {
-
-    var receivers = {
-        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/controller/0": {},
-        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/plugin/0": {},
-        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/package/0": {},
-        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/selective/0": {},
-        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/request/0": {},
-        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/page/0": {
-            messageHandler: "insightMessage"
-        },
-        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/process/0": {}
-    };
-
-    Object.keys(receivers).forEach(function (uri) {
-
-        var receiver = API.WILDFIRE.Receiver();
-        receiver.setId(uri);
-        receiver.addListener({
-      	    onMessageReceived: function (request, message) {
-
-//      	        try {
-
-                  //message.requestUrl = request.url;
-                  message.context = request.context;
-                  
-                  //API.console.log("INSIGHT MESSAGE [" + uri + "] onMessageReceived !1", message);
-                  //API.console.log("INSIGHT MESSAGE API.on", API.on);
-
-                  if (
-                      receivers[uri].messageHandler &&
-                      API.on &&
-                      API.on[receivers[uri].messageHandler]
-                  ) {
-                      API.on[receivers[uri].messageHandler](message);
-                  } else {
-
-console.log("IGNORING insight MESSAGE:", message);
-
-                  }
-
-//      	        } catch (err) {
-//      	        	API.console.error(err);
-//      	        }
-      	    }
-    	  });
-        API.httpHeaderChannel.addReceiver(receiver);
-
-    });
-
-
-	  return {};
-}
diff --git a/src/settings.js b/src/settings.js
deleted file mode 100644
index 7b1eb3f..0000000
--- a/src/settings.js
+++ /dev/null
@@ -1,127 +0,0 @@
-
-const LIB = require("./lib");
-
-
-
-var domainSettingsCache = {};
-var settingsByHostnameCache = {};
-if (LIB.browser) {
-    LIB.browser.storage.onChanged.addListener(function (changes, area) {
-        for (var item of Object.keys(changes)) {
-            if (!/^domain\[.+\]\..+$/.test(item)) continue;
-            domainSettingsCache[item] = changes[item].newValue;
-
-            const m = item.match(/^domain\[([^\]]+)\]\.(.+)$/);
-            if (m) {
-                if (!settingsByHostnameCache[m[1]]) {
-                    settingsByHostnameCache[m[1]] = {
-                        "enabled": false,
-                        "enableUserAgentHeader": false,
-                        "enableFirePHPHeader": false,
-                        "enableChromeLoggerData": false
-                    };
-                }
-                settingsByHostnameCache[m[1]][m[2]] = changes[item].newValue;
-            }
-
-    //console.log("[background] Updated domain settings '" + item + "':", domainSettingsCache[item]);
-            
-        }
-    });
-}
-exports.getSetting = function (name) {
-    function get () {
-        if (!LIB.browser) return Promise.resolve(null);
-        return LIB.browser.storage.local.get(name).then(function (value) {
-
-            const m = name.match(/^domain\[([^\]]+)\]\.(.+)$/);
-            
-            if (m) {
-                if (!settingsByHostnameCache[m[1]]) {
-                    settingsByHostnameCache[m[1]] = {
-                        "enabled": false,
-                        "enableUserAgentHeader": false,
-                        "enableFirePHPHeader": false,
-                        "enableChromeLoggerData": false
-                    };
-                }
-                settingsByHostnameCache[m[1]][m[2]] = value[name] || false;
-            }
-
-            return (domainSettingsCache[name] = value[name]);
-        });
-    }
-    if (typeof domainSettingsCache[name] === "undefined") {
-        get();
-        return Promise.resolve(false);
-    }
-    return get();
-}
-exports.setSetting = function (name, value) {
-    if (!LIB.browser) return Promise.resolve(null);
-    return LIB.browser.storage.local.set(name, value);
-}
-
-
-// TODO: Speed this up by removing promises as much as possible
-
-exports.getDomainSettingsForDomain = function (domain) {
-    return exports.getSetting("domain[" + domain + "].enabled").then(function (enabled) {
-        return exports.getSetting("domain[" + domain + "].enableUserAgentHeader").then(function (enableUserAgentHeader) {            
-            return exports.getSetting("domain[" + domain + "].enableFirePHPHeader").then(function (enableFirePHPHeader) {            
-                return exports.getSetting("domain[" + domain + "].enableChromeLoggerData").then(function (enableChromeLoggerData) {            
-
-                    return Promise.resolve({
-                        "enabled": enabled,
-                        "enableUserAgentHeader": enableUserAgentHeader,
-                        "enableFirePHPHeader": enableFirePHPHeader,
-                        "enableChromeLoggerData": enableChromeLoggerData
-                    });
-                });
-            });
-        });
-    });
-}
-
-exports.isEnabledForDomain = function (domain) {
-    return exports.getDomainSettingsForDomain(domain).then(function (settings) {
-        return (
-            settings.enabled &&
-            (
-                settings.enableUserAgentHeader ||
-                settings.enableFirePHPHeader ||
-                settings.enableChromeLoggerData
-            )
-        );
-    });
-}    
-
-
-exports.getDomainSettingsForRequest = function (request) {
-    return exports.getDomainSettingsForDomain(request.hostname).then(function (settings) {
-//        console.log("Domain settings for '" + request.hostname + "':", settings);
-        return settings;
-    });
-}
-
-exports.getDomainSettingsForRequestSync = function (request) {
-    if (!exports.getDomainSettingsForRequestSync._fetchedOnce) {
-        exports.getDomainSettingsForRequestSync._fetchedOnce = {};
-    }
-    if (!exports.getDomainSettingsForRequestSync._fetchedOnce[request.hostname]) {
-        exports.getDomainSettingsForRequestSync._fetchedOnce[request.hostname] = true;
-        // kick off settings fetch
-        exports.getDomainSettingsForRequest(request);
-    }
-    // use last cached settings
-    if (!settingsByHostnameCache[request.hostname]) {
-        settingsByHostnameCache[request.hostname] = {
-            "enabled": false,
-            "enableUserAgentHeader": false,
-            "enableFirePHPHeader": false,
-            "enableChromeLoggerData": false
-        };
-    }
-
-    return settingsByHostnameCache[request.hostname];
-}
diff --git a/src/settings.rep.js b/src/settings.rep.js
index 86cfd6f..ad33f89 100644
--- a/src/settings.rep.js
+++ b/src/settings.rep.js
@@ -20,10 +20,12 @@ exports.main = function (JSONREP, node, options) {
             
                             <h2>Settings for: { hostname }</h2>
 
+                            <p>For a list of supported server libraries see <a href="http://firephp.org/">firephp.org</a>. There are also many framework plugins out there.</p>
+
                             <div class="settings">
                                 <ul>
                                     <li>
-                                        <h2><a href="https://github.com/firephp/firephp-core" target="_blank">FirePHPCore</a></h2>
+                                        <h2><a href="https://github.com/firephp/firephp-core" target="_blank">FirePHP (Wildfire based)</a> Protocol</h2>
                                         <p>Only one type of request header needs to be sent.</p>
                                         <ul>
                                             <li><input type="checkbox" name="enableUserAgentHeader" onchange={syncCheckbox}/> Enable <b>UserAgent Request Header</b> - Modifies the <i>User-Agent</i> request header by appending <i>FirePHP/0.5</i>.</li>
@@ -31,7 +33,7 @@ exports.main = function (JSONREP, node, options) {
                                         </ul>
                                     </li>
                                     <li>
-                                        <h2><a href="https://github.com/ccampbell/chromelogger" target="_blank">Chrome Logger</a></h2>
+                                        <h2><a href="https://github.com/ccampbell/chromelogger" target="_blank">Chrome Logger</a> Protocol</h2>
                                         <ul>
                                             <li><input type="checkbox" name="enableChromeLoggerData" onchange={syncCheckbox}/> Render <i>X-ChromeLogger-Data</i> response headers to the console.</li>
                                         </ul>
@@ -43,9 +45,9 @@ exports.main = function (JSONREP, node, options) {
                             <div class="info">
                                 <h2>How to get setup</h2>
                                 <ol>
-                                    <li>Choose an integration method.</li>
+                                    <li>Choose a <a href="http://firephp.org/">server library</a>.</li>
                                     <li>Integrate the server library into your project.</li>
-                                    <li>Check the relevant box.</li>
+                                    <li>Check the relevant protocol box to your left.</li>
                                     <li><b>Close</b> the <i>Settings</i> and <b>Enable</b> the tool.</li>
                                 </ol>
                                 <p><i>FirePHP is <a target="_blank" href="https://github.com/firephp/firephp-for-firefox-devtools">Open Source with code on Github</a></i></p>
diff --git a/src/wildfire.js b/src/wildfire.js
index e6e5127..56d8c43 100644
--- a/src/wildfire.js
+++ b/src/wildfire.js
@@ -1,344 +1,510 @@
-    
-const EVENTS = require("eventemitter2");
 
 
-var API = module.exports = new EVENTS();
-
-const BROWSER = (typeof browser != "undefined") ? browser : chrome;
+const EVENTS = require("eventemitter2");
+const ENCODER = require("insight-for-js/lib/encoder/default");
 
+exports.Client = function (comp, options) {
+    options = options || {};
 
-API.console = console;
-API.BROWSER = BROWSER;
-API.WILDFIRE = require("wildfire-for-js");
+    const API = new EVENTS();
 
+    API.console = console;
+    API.BROWSER = comp.browser;
+    API.VERBOSE = options.verbose || false;
+    API.WILDFIRE = require("wildfire-for-js");
 
-const REQUEST_OBSERVER = require("./adapters/http-request-observer").for(API);
-const RESPONSE_OBSERVER = require("./adapters/http-response-observer").for(API);
+    const REQUEST_OBSERVER = require("./adapters/http-request-observer").forAPI(API);
+    const RESPONSE_OBSERVER = require("./adapters/http-response-observer").forAPI(API);
 
 
-const ENCODER = require("insight-for-js/lib/encoder/default");
+    let forceEnabled = false;
+    API.forcedEnable = function (oo) {
+        forceEnabled = oo;
+    }
 
 
-var encoder = ENCODER.Encoder();
-encoder.setOption("maxObjectDepth", 1000);
-encoder.setOption("maxArrayDepth", 1000);
-encoder.setOption("maxOverallDepth", 1000);
+    comp.on("changed.context", function () {
+        comp.contextChangeAcknowledged();
+        syncListeners();
+    });    
+    comp.on("changed.setting", function () {
+        syncListeners();
+    });
+    async function syncListeners () {
+        const enabled = await comp.isEnabled();
+        if (enabled) {
+            ensureListenersHooked();
+        } else {
+            ensureListenersUnhooked();
+        }
+    }
 
 
-const SETTINGS = require("./settings");
+    function ensureListenersHooked () {
+        requestObserver.ensureHooked();
+        responseObserver.ensureHooked();
+    }
 
+    function ensureListenersUnhooked () {
+        requestObserver.ensureUnhooked();
+        responseObserver.ensureUnhooked();
+    }
 
-var forceEnabled = false;
-API.forcedEnable = function (oo) {
-    forceEnabled = oo;
-}
 
+    const encoder = ENCODER.Encoder();
+    encoder.setOption("maxObjectDepth", 1000);
+    encoder.setOption("maxArrayDepth", 1000);
+    encoder.setOption("maxOverallDepth", 1000);
 
-API.on.chromeLoggerMessage = function (message, context) {
-    try {
-        let i,ic,j,jc;
-        for (i=0,ic=message.rows.length ; i<ic ; i++) {
-            let log = {};
-            for (j=0,jc=message.columns.length ; j<jc ; j++) {
-                log[message.columns[j]] = message.rows[i][j];        
-            }
-            const meta = {
-                "msg.preprocessor": "FirePHPCoreCompatibility",
-                "lang.id" : "registry.pinf.org/cadorn.org/github/renderers/packages/php/master",
-                "priority": log.type
-            };
-            if (log.backtrace) {
-                let m = log.backtrace.match(/^([^:]+?)(\s*:\s*(\d+))?$/);
-                if (m) {
-                    meta.file = m[1];
-                    if (m[3] !== '') {
-                        meta.line = parseInt(m[3]);
+    function onChromeLoggerMessage (message, context) {
+        try {
+            let i,ic,j,jc;
+            for (i=0,ic=message.rows.length ; i<ic ; i++) {
+                let log = {};
+                for (j=0,jc=message.columns.length ; j<jc ; j++) {
+                    log[message.columns[j]] = message.rows[i][j];        
+                }
+                const meta = {
+                    "msg.preprocessor": "FirePHPCoreCompatibility",
+                    "lang.id" : "registry.pinf.org/cadorn.org/github/renderers/packages/php/master",
+                    "priority": log.type
+                };
+                if (log.backtrace) {
+                    let m = log.backtrace.match(/^([^:]+?)(\s*:\s*(\d+))?$/);
+                    if (m) {
+                        meta.file = m[1];
+                        if (m[3] !== '') {
+                            meta.line = parseInt(m[3]);
+                        }
                     }
                 }
+                if (log.log.length === 1) {
+                    log.log = log.log[0];
+                }
+                let dataNode = encoder.encode(log.log, {
+                    "lang": "php"
+                }, {
+                    "jsonEncode": false
+                });
+                let node = dataNode.origin;
+                Object.keys(meta).forEach(function (name) {
+                    node.meta[name] = meta[name];
+                });
+                let msg = {
+                    "context": context,
+                    "sender": "https://github.com/ccampbell/chromelogger",
+                    "receiver": "https://gi0.FireConsole.org/rep.js/InsightTree/0.1",
+                    "meta": "{}",
+                    "data": node
+                };
+                API.emit("message.firephp", msg);
             }
-            if (log.log.length === 1) {
-                log.log = log.log[0];
-            }
-            let dataNode = encoder.encode(log.log, {
-                "lang": "php"
-            }, {
-                "jsonEncode": false
-            });
-            let node = dataNode.origin;
-            Object.keys(meta).forEach(function (name) {
-                node.meta[name] = meta[name];
-            });
-            let msg = {
-                "context": context,
-                "sender": "https://github.com/ccampbell/chromelogger",
-                "receiver": "https://gi0.FireConsole.org/rep.js/InsightTree/0.1",
-                "meta": "{}",
-                "data": node
-            };
-            API.emit("message.firephp", msg);
+        } catch (err) {
+            console.error("Error formatting chromelogger message:", err);
         }
-    } catch (err) {
-        console.error("Error formatting chromelogger message:", err);
     }
-}
-    
-API.on.insightMessage = function (message) {
-
-//    console.log("LOG MESSAGE to insightMessage ::", message);
 
-    API.emit("message.insight", message);
-    
-//		console.log("DECODED", JSON.parse(message.getMeta()), message.getData());
-//consoleWidget1.loader.callApi("console.log.firephp", [JSON.parse(message.getMeta()), message.getData()]);
-}
-API.on.firePHPMessage = function (message) {
 
-//    console.log("LOG MESSAGE to firePHPMessage ::", message);
+    API.on.transport = async function (message, request) {
+        let info = {
+            request: request,
+            data: JSON.parse(message.data)
+        };
 
-//    console.log("DECODED", message.getMeta(), message.getData());
+        API.emit("message.transport", info);
 
-    API.emit("message.firephp", message);
-
-    //console.log("DECODED", JSON.parse(message.getMeta() || "{}"), message.getData());
-
-        
-//var encoder = ENCODER.Encoder();
-//var og = encoder.encode(message.getData(), {}, {});
-//consoleWidget1.loader.callApi("console.log.firephp", [JSON.parse(message.getMeta()), og]);
-}
-API.on.transport = function (info) {
-
-    API.emit("message.transport", info);
-    
 //console.log("make backend request", JSON.stringify(info, null, 4));
 
-/*
-var jqxhr = JQUERY.ajax({
-"method": "POST",
-"url": info.url,
-crossDomain: true,
-headers: info.headers,
-data: {
-payload: JSON.stringify(info.payload)
-}
-}).done(function (data) {
-
-//console.log("SUCCESS DATA", data);
-
-RECEIVERS_EXPORTS.parseReceived(data);
+        info.request.context.serverUrl = info.data.url;
 
+        let url = info.data.url;
+        if (url.indexOf("x-insight=transport") !== -1) {
+            return;
+        }
 
-}).fail(function (jqXHR, textStatus) {
+        if (url.indexOf("?") === -1) {
+            url += "?";
+        } else {
+            url += "&";
+        }
+        url += "x-insight=transport";
+        const response = await fetch(url, {
+            method: 'POST',
+            mode: 'same-origin',
+            cache: 'no-cache',
+            credentials: 'include',
+            headers: {
+                'Content-Type': 'application/json',
+                'x-insight': 'transport'
+            },
+            redirect: 'follow',
+            referrer: 'no-referrer',
+            body: JSON.stringify(info.data.payload)
+        });
 
-console.error("REQUEST ERROR", jqXHR, textStatus);
-});
-*/
-}
+        const body = await response.text();
+
+console.log("BODY", body);
+//console.log("info.request", info.request);
+
+        /*
+        info.request: {
+            "id": "80",
+            "context": {
+                "frameId": 0,
+                "tabId": 1,
+                "url": "http://127.0.0.1:8080/FirePHP.php",
+                "hostname": "127.0.0.1",
+                "requestId": "80",
+                "requestType": "main_frame",
+                "timeStamp": 1570337910009,
+                "pageUrl": "http://127.0.0.1:8080/FirePHP.php",
+                "pageTimeStamp": 1570337910009,
+                "pageUid": "{\"url\":\"http://127.0.0.1:8080/FirePHP.php\",\"tabId\":1}",
+                "requestUid": "{\"url\":\"http://127.0.0.1:8080/FirePHP.php\",\"timeStamp\":1570337910009,\"frameId\":0,\"tabId\":1}"
+            },
+            "status": 200
+        },
+        */
+
+        httpHeaderChannel.parseReceived(body, {
+            "id": info.request.id,
+            "url": info.request.context.url,
+            "hostname": info.request.context.hostname,
+            "context": info.request.context,
+            "port": 0,
+            "method": "",
+            "status": "",
+            "contentType": "",
+            "requestHeaders": ""
+        });
+    }
 
 
+    API.callServer = async function (serverUrl, payload) {
+        let url = serverUrl;
+        if (url.indexOf("?") === -1) {
+            url += "?";
+        } else {
+            url += "&";
+        }
+        url += "x-insight=serve";
 
-function isEnabled () {
-    return true;
-}
+console.log("Sending", payload, "to", url);
 
+        var announceMessage = getAnnounceMessageForRequest(serverUrl.replace(/^https?:\/\/([^\/]+)\/.*$/, '$1'));
+console.log("announceMessage", announceMessage);        
 
-var httpHeaderChannel = API.httpHeaderChannel = API.WILDFIRE.HttpHeaderChannel({
-    "enableTransport": false,
-    onError: function (err) {
-        console.error("HttpHeaderChannel error:", err);
-        API.emit("error", err);
-    }
-});
-httpHeaderChannel.setNoReceiverCallback(function(id) {
-    API.console.error("trying to log to unknown receiver (extension): " + id);
-});
+        var headers = {
+            'Content-Type': 'application/json',
+            'x-insight': 'serve'
+        };
 
+        if (announceMessage) {
 
-httpHeaderChannel.addListener({
-    afterChannelOpen: function (context) {
+            // dispatch announce message
+            announceDispatcher.dispatch(announceMessage);
 
-        if (API.VERBOSE) console.log("[wildfire] httpHeaderChannel -| afterChannelOpen (context):", context);
+            // flush channel
+            httpHeaderChannel.flush({
+                setMessagePart: function (name, value) {
+                    headers[name] = ('' + value);
+                },
+                getMessagePart: function (name) {
+                    return headers[name];
+                }
+            });            
+        }
 
-        API.emit("response", {
-            context: context.context
+console.log("headers::", headers);
+
+        /*
+        curl \
+            --header "Content-Type: application/json" \
+            --header "x-insight: serve" \
+            --header "x-request-id: 157336719818551" \
+            --header "x-wf-1-1: 875||{\"authkey\":\"mykey\",\"receivers\":[\"http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1\",\"http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1\",\"http://registry.pinf.org/cadorn.org/wildfire/@meta/receiver/transport/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/controller/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/plugin/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/package/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/selective/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/request/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/page/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/process/0\",\"http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/firephp/0\"]}|" \
+            --header "x-wf-1-index: 1" \
+            --header "x-wf-protocol-1: http://registry.pinf.org/cadorn.org/wildfire/@meta/protocol/announce/0.1.0" \
+            --request POST \
+            --data '{"target": "Insight_Plugin_FileViewer", "action": "GetFile", "args": {"path": "/christoph/projects/gi0.FirePHP.org/insight/demo/FirePHP.php"}}' \
+            127.0.0.1:8080/FirePHP.php?x-insight=serve
+        */
+
+        const response = await fetch(url, {
+            method: 'POST',
+            mode: 'same-origin',
+            cache: 'no-cache',
+            credentials: 'include',
+            headers: headers,
+            redirect: 'follow',
+            referrer: 'no-referrer',
+            body: JSON.stringify(payload)
         });
-    }
-});
-
 
-// TODO: Load dynamically
-require("./receivers/firephp").for(API);
-require("./receivers/insight").for(API);
+console.log("response in wildfire", response);
 
+        const body = await response.text();
 
-var announceDispatcher = API.WILDFIRE.Dispatcher();
-announceDispatcher.setProtocol('http://registry.pinf.org/cadorn.org/wildfire/@meta/protocol/announce/0.1.0');
-announceDispatcher.setChannel(httpHeaderChannel);
+console.log("BODY", body);
 
-function getAnnounceMessageForRequest (request) {
-
-    if (!getAnnounceMessageForRequest._forHostnames) {
-        getAnnounceMessageForRequest._forHostnames = {};
+        return body;
     }
-    var cache = getAnnounceMessageForRequest._forHostnames;
-
-    // TODO: Don't just use `request.hostname` for cache.
-    // TODO: If config changes this announceMessage needs to be reset
 
-    if (cache[request.hostname]) {
-        return cache[request.hostname];
-    }
 
-    cache[request.hostname] = new API.WILDFIRE.Message();
-    cache[request.hostname].setData(JSON.stringify({
-        // TODO: Populate crypto-based key
-        "authkey": "mykey",
-        "receivers": httpHeaderChannel.receivers.map(function (receiver) {
-            return receiver.getId();
-        })
-    }));
+    var httpHeaderChannel = API.httpHeaderChannel = API.WILDFIRE.HttpHeaderChannel({
+        "enableTransport": false,
+        onError: function (err) {
+            console.error("HttpHeaderChannel error:", err);
+            API.emit("error", err);
+        }
+    });
+    httpHeaderChannel.setNoReceiverCallback(function(id) {
+        API.console.error("trying to log to unknown receiver (extension): " + id);
+    });
 
-    return cache[request.hostname];
-}
+    httpHeaderChannel.addListener({
+        afterChannelOpen: function (context) {
+    
+            if (API.VERBOSE) console.log("[wildfire] httpHeaderChannel -| afterChannelOpen (context):", context);
+    
+            API.emit("response", {
+                context: context.context
+            });
+        }
+    });
+    
 
 
 
-var hostnameSettings = {};
+    var transportReceiver1 = API.WILDFIRE.Receiver();
+    transportReceiver1.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1");
+    transportReceiver1.addListener({
+	    onMessageReceived: function(request, message) {
+            message.context = request.context;
+            API.emit("message.firephp", message);
+        }
+	});
+    API.httpHeaderChannel.addReceiver(transportReceiver1);
+
+
+    var transportReceiver2 = API.WILDFIRE.Receiver();
+    transportReceiver2.setId("http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1");
+    transportReceiver2.addListener({
+        onMessageReceived: function(request, message) {
+            message.context = request.context;
+            API.emit("message.firephp", message);
+	    }
+	});
+    API.httpHeaderChannel.addReceiver(transportReceiver2);
+
+
+
+    const receivers = {
+        "http://registry.pinf.org/cadorn.org/wildfire/@meta/receiver/transport/0": {
+            messageHandler: "transport"
+        },
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/controller/0": {},
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/plugin/0": {},
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/package/0": {},
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/insight/selective/0": {
+            messageEvent: "message.insight.selective"
+        },
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/request/0": {
+            messageEvent: "message.insight.request"
+        },
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/page/0": {
+            messageEvent: "message.firephp"
+        },
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/process/0": {},
+
+        "http://registry.pinf.org/cadorn.org/insight/@meta/receiver/console/firephp/0": {
+            messageEvent: "message.firephp"
+        }
+    };
+    Object.keys(receivers).forEach(function (uri) {
 
-API.hostnameSettings = hostnameSettings;
+        var receiver = API.WILDFIRE.Receiver();
+        receiver.setId(uri);
+        receiver.addListener({
+            onMessageReceived: function (request, message) {
+                try {
+                    message.context = request.context;
+                    //API.console.log("INSIGHT MESSAGE [" + uri + "] onMessageReceived !1", message);
+                    //API.console.log("INSIGHT MESSAGE API.on", API.on);
+                    if (
+                        receivers[uri].messageHandler &&
+                        API.on &&
+                        API.on[receivers[uri].messageHandler]
+                    ) {
+                        API.on[receivers[uri].messageHandler](message, request);
+                    } else
+                    if (receivers[uri].messageEvent) {
+                        API.emit(receivers[uri].messageEvent, message);
+                    } else {
+    console.log("IGNORING insight MESSAGE:", uri, message);
+                    }
+                } catch (err) {
+                    API.console.error(err);
+                }
+            }
+        });
+        API.httpHeaderChannel.addReceiver(receiver);
+    });
 
-REQUEST_OBSERVER.register(function (request) {
-    // Firefox allows returning a promise since version 52
-    // @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest
-    // Chrome requires a sync return
-    // @see https://developer.chrome.com/extensions/webRequest#event-onBeforeSendHeaders
 
-    if (!isEnabled()) {
+    // FireConsole 0.x compatibility
+    var announceDispatcher = API.WILDFIRE.Dispatcher();
+    announceDispatcher.setProtocol('http://registry.pinf.org/cadorn.org/wildfire/@meta/protocol/announce/0.1.0');
+    announceDispatcher.setChannel(httpHeaderChannel);
+    function getAnnounceMessageForRequest (request) {
 
-        if (API.VERBOSE) console.log("[wildfire] REQUEST_OBSERVER handler: not enabled");
+        if (!getAnnounceMessageForRequest._forHostnames) {
+            getAnnounceMessageForRequest._forHostnames = {};
+        }
+        var cache = getAnnounceMessageForRequest._forHostnames;
 
-        return null;
-    }
+        // TODO: Don't just use `request.hostname` for cache.
+        // TODO: If config changes this announceMessage needs to be reset
 
-    const settings = SETTINGS.getDomainSettingsForRequestSync(request);
+        if (cache[request.hostname]) {
+            return cache[request.hostname];
+        }
 
-    if (API.VERBOSE) console.log("[wildfire] forceEnabled:", forceEnabled);
-    if (API.VERBOSE) console.log("[wildfire] request domain settings for '" + request.hostname + "':", settings);
+        cache[request.hostname] = new API.WILDFIRE.Message();
+        cache[request.hostname].setData(JSON.stringify({
+            // TODO: Populate crypto-based key
+            "authkey": "mykey",
+            "receivers": httpHeaderChannel.receivers.map(function (receiver) {
+                return receiver.getId();
+            })
+        }));
 
-    hostnameSettings[request.hostname] = settings;
-    
-    if (
-        !forceEnabled &&
-        !settings.enabled
-    ) {
-        return {};
+        return cache[request.hostname];
     }
+    
+    
+    let hostnameSettings = {};  
+//    API.hostnameSettings = hostnameSettings;
+    
 
-    if (
-        forceEnabled ||
-        settings.enableUserAgentHeader
-    ) {
-        if (!request.headers["User-Agent"].match(/\sFirePHP\/([\.|\d]*)\s?/)) {
-            request.headers["User-Agent"] = request.headers["User-Agent"] + " FirePHP/0.5";
+    const requestObserver = new REQUEST_OBSERVER(function (request) {
+        // Firefox allows returning a promise since version 52
+        // @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest
+        // Chrome requires a sync return
+        // @see https://developer.chrome.com/extensions/webRequest#event-onBeforeSendHeaders
+    
+        const settings = comp._getHostnameSettingsForSync(request.hostname);
+    
+        if (API.VERBOSE) console.log("[wildfire] forceEnabled:", forceEnabled);
+        if (API.VERBOSE) console.log("[wildfire] request domain settings for '" + request.hostname + "':", settings);
+    
+        hostnameSettings[request.hostname] = settings;
+        
+        if (
+            !forceEnabled &&
+            !settings.enabled
+        ) {
+            return {};
+        }
+    
+        if (
+            forceEnabled ||
+            settings.enableUserAgentHeader
+        ) {
+            if (!request.headers["User-Agent"].match(/\sFirePHP\/([\.|\d]*)\s?/)) {
+                request.headers["User-Agent"] = request.headers["User-Agent"] + " FirePHP/0.5";
+            }
+        }
+    
+        if (
+            forceEnabled ||
+            settings.enableFirePHPHeader
+        ) {
+            request.headers["X-FirePHP-Version"] = "0.4";
         }
-    }
-
-    if (
-        forceEnabled ||
-        settings.enableFirePHPHeader
-    ) {
-        request.headers["X-FirePHP-Version"] = "0.4";
-    }
 
-    if (API.VERBOSE) console.log("[wildfire] updated request headers:", request.headers);
+        if (API.VERBOSE) console.log("[wildfire] updated request headers:", request.headers);
 
-    return {
-        requestHeaders: request.headers
-    };
 
+        // FireConsole 0.x compatibility
+        var announceMessage = getAnnounceMessageForRequest(request);
+        if (announceMessage) {
 
-/*
-// TODO: Implement wildfire messaging
-    var announceMessage = getAnnounceMessageForRequest(request);
-    if (announceMessage) {
-        // dispatch announce message
-        announceDispatcher.dispatch(announceMessage);
+            // dispatch announce message
+            announceDispatcher.dispatch(announceMessage);
 
-        // flush channel
-        httpHeaderChannel.flush({
-            setMessagePart: function(name, value) {
-                request.httpChannel.setRequestHeader(name, value, false);
-            },
-            getMessagePart: function(name) {
-                if (request.httpChannel.getRequestHeader) {
-                    return null;
-                }
-                // HACK: Do not use exception for flow control
-                try {
-                    return request.httpChannel.getRequestHeader(name);
-                } catch (err) {
-                    return null;
+            // flush channel
+            httpHeaderChannel.flush({
+                setMessagePart: function (name, value) {
+                    request.headers[name] = ('' + value);
+                },
+                getMessagePart: function (name) {
+                    return request.headers[name];
                 }
-            }
-        });
-    } else {
-        // TODO: Use a proper unique ID + counter.
-        request.httpChannel.setRequestHeader("x-request-id", ""+(new Date().getTime()) + "" + Math.floor(Math.random()*1000+1), false);
-    }
+            });
 
-    // API.console.log("REQUEST", request);
-*/
-});
+        //} else {
+        //    // TODO: Use a proper unique ID + counter.
+        //    request.httpChannel.setRequestHeader("x-request-id", ""+(new Date().getTime()) + "" + Math.floor(Math.random()*1000+1), false);
+        }
 
-API.on("http.response", function (response) {
-    if (!isEnabled()) {
-        return;
-    }
+        return {
+            requestHeaders: request.headers
+        };
+    });
 
-    var settings = hostnameSettings[response.request.context.hostname];
 
-    if (
-        !settings ||
-        (
-            !forceEnabled &&
-            !settings.enabled
-        )
-    ) {
-        return;
-    }
+    const responseObserver = new RESPONSE_OBSERVER(function (response) {
+    
+        var settings = hostnameSettings[response.request.context.hostname];
+    
+        if (
+            !settings ||
+            (
+                !forceEnabled &&
+                !settings.enabled
+            )
+        ) {
+            return;
+        }
 
-    if (API.VERBOSE) console.log("[wildfire] response domain settings for '" + response.request.context.hostname + "':", settings);
+        if (API.VERBOSE) console.log("[wildfire] response domain settings for '" + response.request.context.hostname + "':", settings);
 
-    var chromeLoggerMessage = response.headers.filter(function (header) {
-        return (header.name === "X-ChromeLogger-Data");
-    });
-    if (chromeLoggerMessage.length > 0) {
-        chromeLoggerMessage.forEach(function (header) {
-            try {
-                // @see https://craig.is/writing/chrome-logger/techspecs
-                var message = decodeURIComponent(escape(atob(header.value)));
-                message = JSON.parse(message);
-
-                API.on.chromeLoggerMessage(message, response.request.context);
-            } catch (err) {
-                console.error("header", header);
-                console.error("Error processing message:", err);
+        if (settings.enableChromeLoggerData) {
+            let chromeLoggerMessage = response.headers.filter(function (header) {
+                return (header.name === "X-ChromeLogger-Data");
+            });
+            if (chromeLoggerMessage.length > 0) {
+                chromeLoggerMessage.forEach(function (header) {
+                    try {
+                        // @see https://craig.is/writing/chrome-logger/techspecs
+                        var message = decodeURIComponent(escape(atob(header.value)));
+                        message = JSON.parse(message);
+        
+                        onChromeLoggerMessage(message, response.request.context);
+                    } catch (err) {
+                        console.error("header", header);
+                        console.error("Error processing message:", err);
+                    }
+                });
             }
+        }
+    
+        //dump("RESPONSE IN EXTENSION2", response);
+        //console.log("RESPONSE IN EXTENSION4", response);
+        httpHeaderChannel.parseReceived(response.headers, {
+            "id": response.request.id,
+            "url": response.request.url,
+            "hostname": response.request.hostname,
+            "context": response.request.context,
+            "port": response.request.port,
+            "method": response.request.method,
+            "status": response.status,
+            "contentType": response.contentType,
+            "requestHeaders": response.request.headers
         });
-    }
-
-    //dump("RESPONSE IN EXTENSION2", response);
-    //console.log("RESPONSE IN EXTENSION4", response);
-    httpHeaderChannel.parseReceived(response.headers, {
-        "id": response.request.id,
-        "url": response.request.url,
-        "hostname": response.request.hostname,
-        "context": response.request.context,
-        "port": response.request.port,
-        "method": response.request.method,
-        "status": response.status,
-        "contentType": response.contentType,
-        "requestHeaders": response.request.headers
     });
 
-});
+    return API;
+}
diff --git a/tests/01-Messages-ChromeLogger/#!inf.json b/tests/01-Messages-ChromeLogger/#!inf.json
new file mode 100644
index 0000000..f666a65
--- /dev/null
+++ b/tests/01-Messages-ChromeLogger/#!inf.json
@@ -0,0 +1,97 @@
+{
+    "#": "gi0.PINF.it/core/v0",
+
+    "#": {
+        "/.extension.built": "to.pinf.org.mozilla.web-ext",
+        "runner": "it.pinf.org.mozilla.web-ext"
+    },
+    ":builder:": "/.extension.built @ builder/v1",
+    ":runner:": "runner @ runner/v0",
+
+    "gi0.PINF.it/build/v0 @ # /.extension.built": (inf () >>>
+        {
+            "gi0.PINF.it/build/v0 @ # :builder: build() /": {
+                "homepage": ":3000/",
+                "port": 3001,
+                "manifest": {
+                    "permissions": [
+                        "storage",
+                        "webRequest",
+                        "webRequestBlocking",
+                        "<all_urls>"
+                    ],
+                    "background": {
+                        "scripts": [
+                            {
+                                "worker.js": {
+                                    "gi0.PINF.it/build/v0 # /scripts # /": {
+                                        "@it.pinf.org.mochajs # router/v1": {
+                                            "suite": "worker",
+                                            "apiBaseUrl": "/tests",
+                                            "tests": {
+                                                "01-HelloWorld": function /* CodeBlock */ (build) {
+
+                                                    var WILDFIRE = require("../../src/wildfire");
+                                                    WILDFIRE.forcedEnable(true);
+
+                                                    describe('Wait for messages', function () {
+                                                        this.timeout(5 * 1000);
+                
+                                                        it('received', function (done) {
+                
+                                                            WILDFIRE.on("message.chromelogger", function (message) {
+                
+                                                                chai.assert.equal(message.rows.length, 3);
+                                                                chai.assert.equal(message.rows[0][0][0], "Hello console!");
+                                                                chai.assert.equal(message.rows[2][0][0], "something went wrong!");
+                                                                chai.assert.equal(message.rows[2][2], "warn");
+                
+                                                                done();
+                                                            });
+                
+                                                            browser.tabs.reload();
+                                                        });
+                                                    });
+                                                }
+                                            },
+                                            "babel": {
+                                                "presets": {
+                                                    "@babel/preset-env": {
+                                                        "targets": "last 1 Firefox versions"
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                },
+                "expect": {
+                    "exit": true,
+                    "conditions": [
+                        {
+                            "gi0.PINF.it/build/v0 # /testpage # /": {
+                                "@it.pinf.org.mochajs # expect/v1": {
+                                    "wait": true,
+                                    "suites": [
+                                        "worker"
+                                    ]
+                                }
+                            }
+                        }        
+                    ]
+                }                
+            }
+        }
+    <<<),
+
+    "gi0.PINF.it/build/v0 @ # onDone()": (inf () >>>
+        {
+            "gi0.PINF.it/core/v0 @ # :runner: set() extensionPath": ".extension.built",
+
+            "gi0.PINF.it/core/v0 @ # :runner: run()": ""
+        }
+    <<<)
+}
\ No newline at end of file
diff --git a/tests/01-Messages-ChromeLogger/main.sh b/tests/01-Messages-ChromeLogger/main.sh
index 80b8c74..ff0cc2c 100755
--- a/tests/01-Messages-ChromeLogger/main.sh
+++ b/tests/01-Messages-ChromeLogger/main.sh
@@ -1,9 +1,7 @@
 #!/usr/bin/env bash.origin.script
 
 depend {
-    "webext": "@com.github/pinf-to/to.pinf.org.mozilla.web-ext#s1",
-    "php": "@com.github/bash-origin/bash.origin.php#s1",
-    "process": "@com.github/bash-origin/bash.origin.process#s1"
+    "php": "bash.origin.php # runner/v0"
 }
 
 echo "TEST_MATCH_IGNORE>>>"
@@ -11,85 +9,13 @@ echo "TEST_MATCH_IGNORE>>>"
 CALL_php composer install
 
 
-local port="$(CALL_process free_port)"
-echo "PHP server port: ${port}"
+#local port="$(CALL_process free_port)"
+# TODO: Get port dynamically and pass to 'pinf.it .'
+echo "PHP server port: 3000"
 
-CALL_php start "${port}"
+CALL_php start "3000"
 
-
-CALL_webext run {
-    "homepage": ":${port}/",
-    "manifest": {
-        "permissions": [
-            "storage",
-            "webRequest",
-            "webRequestBlocking",            
-            "<all_urls>"
-        ],
-        "background": {
-            "scripts": [
-                {
-                    "worker.js": {
-                        "@it.pinf.org.mochajs#s1": {
-                            "suite": "worker",
-                            "apiBaseUrl": "/tests",
-                            "tests": {
-                                "01-HelloWorld": function /* CodeBlock */ () {
-
-                                    var WILDFIRE = require("$__DIRNAME__/../../src/wildfire");
-                                    WILDFIRE.forcedEnable(true);
-
-                                    describe('Wait for messages', function () {
-                                        this.timeout(5 * 1000);
-
-                                        it('received', function (done) {
-
-                                            WILDFIRE.on("message.chromelogger", function (message) {
-
-                                                chai.assert.equal(message.rows.length, 3);
-                                                chai.assert.equal(message.rows[0][0][0], "Hello console!");
-                                                chai.assert.equal(message.rows[2][0][0], "something went wrong!");
-                                                chai.assert.equal(message.rows[2][2], "warn");
-
-                                                done();
-                                            });
-
-                                            browser.tabs.reload();
-                                        });
-                                    });
-                                }
-                            },
-                            "babel": {
-                                "presets": {
-                                    "@babel/preset-env": {
-                                        "targets": "last 1 Firefox versions"
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            ]
-        }
-    },
-    "routes": {
-        "^/tests": {
-            "@it.pinf.org.mochajs#s1": {}
-        }
-    },
-    "expect": {
-        "exit": true,
-        "conditions": [
-            {
-                "@it.pinf.org.mochajs#s1": {
-                    "suites": [
-                        "worker"
-                    ]
-                }
-            }        
-        ]
-    }
-}
+pinf.it .
 
 echo "<<<TEST_MATCH_IGNORE"
 
@@ -97,4 +23,4 @@ echo "OK"
 
 echo "TEST_MATCH_IGNORE>>>"
 
-CALL_php stop "${port}"
+CALL_php stop "3000"
diff --git a/tests/08-Subrequests/main.sh b/tests/08-Subrequests/main.sh
index a522e52..6815372 100755
--- a/tests/08-Subrequests/main.sh
+++ b/tests/08-Subrequests/main.sh
@@ -21,7 +21,7 @@ CALL_webext run {
     "homepage": ":${port}/",
     "manifest": {
         "permissions": [
-            "tabs",
+            "activeTab",
             "storage",
             "webRequest",
             "webNavigation",
@@ -58,6 +58,13 @@ CALL_webext run {
                         "code": {
                             "@github.com~jsonrep~jsonrep#s1": {
                                 "externalizeCss": true,
+                                "include": {
+                                    "jquery": false,
+                                    "regenerator-runtime": false,
+                                    "riot.csp.js": true,
+                                    "riot.js": false,
+                                    "riot.min.js": false
+                                },
                                 "page": {
                                     "@layout": {
                                         "console": {
diff --git a/workspace.sh b/workspace.sh
deleted file mode 100755
index 21aa053..0000000
--- a/workspace.sh
+++ /dev/null
@@ -1,263 +0,0 @@
-#!/usr/bin/env bash.origin.script
-
-# TODO: Remove these once we lock versions for downloaded assets or include them.
-#export BO_ALLOW_DOWNLOADS=1
-#export BO_ALLOW_INSTALLS=1
-
-#export BO_SYSTEM_CACHE_DIR="$(node --eval 'process.stdout.write(require("bash.origin.workspace").node_modules);')"
-
-
-depend {
-    "webext": "@com.github/pinf-to/to.pinf.org.mozilla.web-ext#s1"
-}
-
-
-function do_run {
-
-    version="$(BO_run_recent_node --eval 'process.stdout.write(require("./package.json").version);')"
-
-    CALL_webext run {
-        "manifest": {
-            "dist": "$__DIRNAME__/dist/firephp.build",
-            "name": "FirePHP",
-            "version": "${version}",
-            "description": "Log from PHP to a devtools panel.",
-            "applications": {
-                "gecko": {
-                    "id": "FirePHPExtension-Build@firephp.org",
-                    "strict_min_version": "56.0"
-                }
-            },
-            "icons": {
-                "48": "$__DIRNAME__/src/skin/Logo.png"
-            },
-            "permissions": [
-                "activeTab",
-                "storage",
-                "webRequest",
-                "webNavigation",
-                "webRequestBlocking",            
-                "<all_urls>"
-            ],
-            "content_security_policy": "script-src 'self'; style-src 'self'; object-src 'self'; img-src 'self'",
-            "background": {
-                "scripts": [
-                    {
-                        "background.js": {
-                            "@it.pinf.org.browserify#s1": {
-                                "src": "$__DIRNAME__/src/background.js",
-                                "prime": true,
-                                "format": "pinf",
-                                "babel": {
-                                    "presets": {
-                                        "@babel/preset-env": {
-                                            "targets": "last 1 Firefox versions"
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                ]
-            },
-            "devtools": {
-                "panels": [
-                    {
-                        "devtools/index.js": {
-                            "label": "FirePHP",
-                            "icon": "$__DIRNAME__/src/skin/Logo.png",
-                            "code": {
-                                "@github.com~jsonrep~jsonrep#s1": {
-                                    "externalizeCss": true,
-                                    "include": {
-                                        "jquery": false,
-                                        "regenerator-runtime": false,
-                                        "riot.csp.js": true,
-                                        "riot.js": false,
-                                        "riot.min.js": false
-                                    },
-                                    "page": {
-                                        "@layout": {
-                                            "console": {
-                                                "@console": {
-                                                    "@fireconsole": {
-                                                    }
-                                                }
-                                            },
-                                            "menu": {
-                                                "@menu": {}
-                                            },
-                                            "settings": {
-                                                "@enabler": {}
-                                            },
-                                            "inspector": {
-                                                "@inspector": {}
-                                            },
-                                            "manage": {
-                                                "@manage": {
-                                                    "settings": {
-                                                        "@settings": {}
-                                                    }
-                                                }
-                                            }
-                                        }
-                                    },
-                                    "reps": {
-                                        "layout": "$__DIRNAME__/src/layout.rep.js",
-                                        "menu": "$__DIRNAME__/src/menu.rep.js",
-                                        "summary": "$__DIRNAME__/src/summary.rep.js",
-                                        "settings": "$__DIRNAME__/src/settings.rep.js",
-                                        "manage": "$__DIRNAME__/src/manage.rep.js",
-                                        "inspector": "$__DIRNAME__/src/inspector.rep.js",
-                                        "fireconsole": "fireconsole.rep.js/dist/fireconsole.rep.js",
-                                        "console": "$__DIRNAME__/src/console.rep.js",
-                                        "enabler": "$__DIRNAME__/src/enabler.rep.js"
-                                    },
-                                    "babel": {
-                                        "presets": {
-                                            "@babel/preset-env": {
-                                                "targets": "last 1 Firefox versions"
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                ]
-            }
-        },
-        "routes": {
-            "^/$": (javascript (API) >>>
-
-                return function (req, res, next) {
-
-                    if (
-                        req.headers["x-firephp-version"] ||
-                        /\sFirePHP\/([\.|\d]*)\s?/.test(req.headers["user-agent"])
-                    ) {
-
-                        function wrap (message) {
-                            return message.length + '|' + message + '|';
-                        }
-
-                        res.writeHead(200, {
-                            'X-Wf-Protocol-1': 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2',
-                            'X-Wf-1-Plugin-1': 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.0.0master1106021548',
-                            'X-Wf-1-Structure-1': 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1',
-
-                            // @see https://github.com/firephp/firephp/issues/16
-                            'X-Wf-1-1-1-1': wrap('[{"Type":"LOG","File":"/path/to/file","Line":10},"Hello World"]'),
-                            'X-Wf-1-1-1-2': wrap('[{"Type":"INFO","File":"\/christoph\/projects\/gi0.FireConsole.org\/rep.js\/examples\/03-FirePHPCore\/index.php","Line":75},"\\u0427\\u0442\\u043e-\\u0442\\u043e"]'),
-                            'X-Wf-1-1-1-3': wrap('[{"Type":"INFO","File":"\/christoph\/projects\/gi0.FireConsole.org\/rep.js\/examples\/03-FirePHPCore\/index.php","Line":76},"Od\\u00f3metro"]'),
-
-                            'X-Wf-1-Index': '3'
-                        });
-
-
-                        res.end("FirePHP Core formatted messages sent in HTTP response headers.");
-                    } else {
-
-                        res.end("No FirePHP HTTP request headers found.");
-                    }
-
-                    if (process.env.BO_TEST_FLAG_DEV) return;
-
-                    setTimeout(function () {
-                        API.SERVER.stop();
-                    }, 1000);
-                };
-            <<<),
-            "^/tests": {
-                "@it.pinf.org.mochajs#s1": {}
-            }
-        },
-        "postbuild": (javascript (LIB) >>>
-        
-            return async function () {
-                // Remove files that we do not need.
-                await Promise.all([
-                    'domplate-eval.browser.js',
-                    'domplate.browser.js',
-                    'pinf-loader-core.browser.js',
-                    'babel-regenerator-runtime.js'
-                ].map(async function (filename) {
-                    const paths = LIB.GLOB.sync('**/' + filename);
-                    await Promise.all(paths.map(async function (path) {
-                        await LIB.FS_EXTRA.removeSync(path);
-                    }));
-                }));
-            };
-        <<<)
-    } "$@"
-
-}
-
-function do_sign {
-
-    pushd "dist/firephp.build" > /dev/null
-
-        CALL_webext sign {
-            "dist": "$__DIRNAME__/dist/firephp.xpi",
-            "manifest": {
-            }
-        }
-
-    popd > /dev/null
-}
-
-function do_extract {
-
-    if [ ! -e "dist/firephp.xpi" ]; then
-        BO_exit_error "No xpi file to unbundle found! Run 'sign' first."
-    fi
-    rm -Rf "dist/firephp.xpi.extracted" || true
-    rm -Rf "dist/firephp.zip" || true
-    cp "dist/firephp.xpi" "dist/firephp.zip"
-    unzip "dist/firephp.zip" -d "dist/firephp.xpi.extracted/"
-    rm -Rf "dist/firephp.zip" || true
-
-    BO_cecho "Extracted extension can be found in: dist/firephp.extracted/" YELLOW BOLD
-}
-
-
-BO_parse_args "ARGS" "$@"
-
-
-if [ "$ARGS_1" == "build" ]; then
-
-    do_run "--build-only"
-
-    pushd "dist/firephp.build" > /dev/null
-        rm -f ../firephp.build.zip 2> /dev/null || true
-        zip -r ../firephp.build.zip *
-    popd > /dev/null
-
-    BO_cecho "Built extension can be found in: dist/firephp.build/" YELLOW BOLD
-
-elif [ "$ARGS_1" == "run" ]; then
-
-    export BO_TEST_FLAG_DEV=1
-    do_run
-
-elif [ "$ARGS_1" == "sign" ]; then
-
-    if [ "$ARGS_OPT_dev" == "true" ]; then
-        export BO_TEST_FLAG_DEV=1
-    fi
-
-    # unlisted mode
-    do_sign
-
-    #do_extract
-
-elif [ "$ARGS_1" == "publish" ]; then
-
-    if [ "$ARGS_OPT_dev" == "true" ]; then
-        export BO_TEST_FLAG_DEV=1
-    fi
-
-    MOZILLA_ADDONS_CHANNEL="listed" do_sign
-
-    #do_extract
-fi
