Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Mon, 08 Oct 2012 17:08:01 -0700
changeset 111110 53e083b077e7665ed5eaca904fa878f3550f1487
parent 111109 2eaa8cf6f4272b2e66eb9c04f99feef4fc287d07 (current diff)
parent 109793 aa5e3b4458103dbdc95ecc0b9e4d4ac1ee1a0457 (diff)
child 111111 72e05828334d4aeb018253e396ce7f4d535a9120
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
milestone19.0a1
Merge m-c to s-c.
browser/devtools/highlighter/InsideOutBox.jsm
browser/devtools/highlighter/TreePanel.jsm
browser/devtools/highlighter/domplate.jsm
browser/devtools/highlighter/inspector.html
browser/devtools/webconsole/HUDService-content.js
browser/devtools/webconsole/NetworkHelper.jsm
browser/devtools/webconsole/WebConsoleUtils.jsm
browser/themes/gnomestripe/devtools/htmlpanel.css
browser/themes/pinstripe/devtools/htmlpanel.css
browser/themes/winstripe/devtools/htmlpanel.css
services/sync/Makefile.in
toolkit/themes/gnomestripe/global/icons/panelarrow-light-vertical.svg
--- a/.hgtags
+++ b/.hgtags
@@ -82,8 +82,9 @@ b6627f28b7ec17e1b46a594df0f780d3a40847e4
 357da346ceb705d196a46574804c7c4ec44ac186 FIREFOX_AURORA_14_BASE
 26dcd1b1a20893ad99341c61c6b1239ff1523858 FIREFOX_AURORA_15_BASE
 0accd12a8e7e217836ea3f1ee7c411913fc75d8e FIREFOX_AURORA_16_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_16_BASE
 9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
 9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
 6fdf9985acfe6f939da584b2559464ab22264fe7 FIREFOX_AURORA_16_BASE
 fd72dbbd692012224145be1bf13df1d7675fd277 FIREFOX_AURORA_17_BASE
+2704e441363fe2a48e992dfac694482dfd82664a FIREFOX_AURORA_18_BASE
--- a/accessible/src/jsat/content-script.js
+++ b/accessible/src/jsat/content-script.js
@@ -41,17 +41,19 @@ function virtualCursorControl(aMessage) 
           if (details.action == 'moveNext')
             moved = vc.moveFirst(rule);
           else
             moved = vc.moveLast(rule);
         } else {
           moved = vc[details.action](rule);
         }
       } catch (x) {
-        moved = vc.moveNext(rule, content.document.activeElement, true);
+        let acc = Utils.AccRetrieval.
+          getAccessibleFor(content.document.activeElement);
+        moved = vc.moveNext(rule, acc, true);
       }
       break;
     case 'moveToPoint':
       moved = vc.moveToPoint(rule, details.x, details.y, true);
       break;
     case 'presentLastPivot':
       EventManager.presentLastPivot();
       break;
@@ -83,17 +85,17 @@ function forwardMessage(aVirtualCursor, 
       // XXX: OOP content's screen offset is 0,
       // so we remove the real screen offset here.
       aMessage.json.x -= content.mozInnerScreenX;
       aMessage.json.y -= content.mozInnerScreenY;
       mm.sendAsyncMessage(aMessage.name, aMessage.json);
       return true;
     }
   } catch (x) {
-    Logger.error(x);
+    // Frame may be hidden, we regard this case as false.
   }
   return false;
 }
 
 function activateCurrent(aMessage) {
   Logger.debug('activateCurrent');
   function activateAccessible(aAccessible) {
     if (aAccessible.actionCount > 0) {
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=18.0a1
+MOZ_APP_VERSION=19.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=1.0.0
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/chrome.manifest
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/chrome.manifest
@@ -1,18 +1,20 @@
 resource testpilot ./
 content testpilot content/
+locale testpilot en-US locale/en-US/
 skin testpilot skin skin/all/
 skin testpilot-os skin skin/linux/ os=Linux
 skin testpilot-os skin skin/linux/ os=SunOS
 skin testpilot-os skin skin/mac/ os=Darwin
 skin testpilot-os skin skin/win/ os=WINNT
 
 overlay chrome://browser/content/macBrowserOverlay.xul chrome://testpilot/content/browser.xul
 
 overlay chrome://browser/content/browser.xul chrome://testpilot/content/browser.xul
 
+style	chrome://global/content/customizeToolbar.xul	chrome://testpilot/content/browser.css
 # For the menubar on Mac
 overlay chrome://testpilot/content/all-studies-window.xul chrome://browser/content/macBrowserOverlay.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} os=Darwin
 
 component {e6e5e58f-7977-485a-b076-2f74bee2677b} components/TestPilot.js
 contract @mozilla.org/testpilot/service;1 {e6e5e58f-7977-485a-b076-2f74bee2677b}
 category profile-after-change testpilot @mozilla.org/testpilot/service;1
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css
@@ -18,63 +18,57 @@
 #feedback-menu-button .toolbarbutton-icon {
   display: none;
 }
 
 #feedback-menu-button .toolbarbutton-menu-dropmarker {
   -moz-padding-start: 5px;
 }
 
-
-#pilot-notifications-button {
-  margin-right: 10px;
+#tp-notification-popup-icon {
+  list-style-image: url("chrome://testpilot/skin/testpilot_16x16.png");
 }
 
 /* Popup Bounding Box */
 #pilot-notification-popup {
   -moz-appearance: none;
   -moz-window-shadow: none;
   background-color: transparent;
   margin-top: -6px;
   margin-right: -3px;
   width: 480px;
-}
-
-.tail-up {
- border-width: 26px 56px 22px 18px;
- border-style: solid;
- -moz-border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 fill round stretch;
+  /* FIXES: #725850  based on #717262
+  /* Needed whilst we support Gecko < 13 */
+  border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 / 26px 56px 22px 18px round stretch;
+  /* Supported in Gecko >= 13 */
+  border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 50 22 18 fill stretch;
+  border-width: 26px 56px 22px 18px;
+  border-style: solid;
 }
 
-/* tail-down uses the old styling; it doesn't look as good as the new styling,
-   but the new styling doesn't work on 3.6.
-   TODO: If someone is using 3.7.* or 4.* but is NOT on the beta channel and
-   installed Test Pilot from AMO, they should get the new styling, similar
-   to .tail-up! */
-.tail-down {
- border-width: 26px 50px 22px 18px;
- border-style: solid;
- -moz-border-image: url(chrome://testpilot/skin/notification-tail-down.png) 26 50 22 18 fill repeat;
- color: white;
-}
 
 .pilot-notification-popup-container {
   -moz-appearance: none;
   margin-right: -42px;
   padding: 0px 5px 5px 5px;
   font-size: 14px;
 }
 
 .pilot-notification-toprow {
   margin-bottom: 12px;
 }
 
-#pilot-notification-text,
+#pilot-notification-text {
+  margin-bottom: 5px;
+}
+
 #pilot-notification-link {
   margin-bottom: 5px;
+  text-decoration: underline;
+  cursor: pointer;
 }
 
 #pilot-notification-close {
   list-style-image: url("chrome://testpilot-os/skin/close_button.png");
   -moz-image-region: rect(0px, 14px, 14px, 0px);
   width: 14px;
   height: 14px;
 }
@@ -235,21 +229,16 @@ vbox.results-thumbnail {
 }
 
 image.results-thumbnail {
     max-height: 90px;
     max-width:  90px;
     margin: 10px;
 }
 
-.notification-link {
-    text-decoration: underline;
-    cursor: pointer;
-}
-
 prefpane .groupbox-body {
   -moz-appearance: none;
   padding: 8px 4px 4px 4px;
 }
 
 prefpane .groupbox-title {
   background: url("chrome://global/skin/50pct_transparent_grey.png") repeat-x bottom left;
   margin-bottom: 4px;
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.xul
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.xul
@@ -1,14 +1,15 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
+<?xml-stylesheet href="chrome://testpilot-os/skin/feedback.css" type="text/css"?>
 
 <!DOCTYPE overlay [
   <!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
     %testpilotDTD;
 ]>
 
 <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <script src="chrome://testpilot/content/browser.js"
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html
@@ -1,18 +1,33 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html> <head>
 <title>Test Pilot Debug Page</title>
+<script type="text/javascript" src="jquery.min.js"></script>
 <script src="experiment-page.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 
+  var numstartupattempts=0;
+  function startup() {
+      numstartupattempts += 1;
+      dump('numattempts: ' + numstartupattempts + "\n");
+      var dropsuccess = populateFileDropdown();
+      if (! dropsuccess) {
+          $('h1#status').fadeIn('slow','linear');
+          window.setTimeout(function() {startup()}, 2000); // try again!
+      } else {
+          showSelectedTaskStatus()
+          showIndexFileDropdown();
+          $('h1#status').fadeOut("slow", "linear");
+      };
+  };
+
   function getEid() {
     var selector = document.getElementById("task-selector");
     var i = selector.selectedIndex;
     return selector.options[i].getAttribute("value");
   }
 
   function setTaskStatus() {
     Components.utils.import("resource://testpilot/modules/setup.js");
@@ -25,17 +40,17 @@
   function reloadAllExperiments() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     TestPilotSetup.reloadRemoteExperiments(function(success) {
       let errors = TestPilotSetup._remoteExperimentLoader.getLoadErrors();
       let str;
       if (errors.length > 0) {
         str = "<ul>";
         for each (let errStr in errors) {
-          str += "<li>" + errStr + "</li>";          
+          str += "<li>" + errStr + "</li>";
         }
         str += "</ul>";
       } else {
         str = "All experiments reloaded, no errors.";
       }
       document.getElementById("debug").innerHTML = str;
     });
   }
@@ -57,28 +72,29 @@
     var jarStore = new jarStoreModule.JarStore();
 
     // OK now watch this!  It's gonna be awesome!
     var clientLoader = Cuddlefish.Loader(
       {fs: new SecurableModule.CompositeFileSystem(
          [jarStore, loader.fs])});
     dump("Debug page Requiring toolbar study.\n");
     var toolbarStudy = clientLoader.require("toolbar-study");
-    
+
   }
 
   function remindMe() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     TestPilotSetup._notifyUserOfTasks();
     //TestPilotSetup._doHousekeeping();
   }
 
   function getCodeStorage() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     var loader = TestPilotSetup._remoteExperimentLoader;
+    if (! loader) {return undefined;}
     return loader._jarStore;
   }
 
   function getSelectedFilename() {
     var selector = document.getElementById("file-selector");
     var i = selector.selectedIndex;
     return selector.options[i].text;
   }
@@ -131,16 +147,18 @@
     var task = TestPilotSetup.getTaskById(getEid());
     task.dataStore.nukeTable();
     var debug = document.getElementById("debug");
     debug.innerHTML = "Nuked!";
   }
 
   function populateFileDropdown() {
     var codeStore = getCodeStorage();
+    if (! codeStore ) { return false }
+
     var files = codeStore.listAllFiles();
     var selector = document.getElementById("file-selector");
     var opt, i;
     for (var i = 0; i < files.length; i++) {
       opt = document.createElement("option");
       opt.innerHTML = files[i];
       selector.appendChild(opt);
     }
@@ -150,16 +168,17 @@
     var title;
     for (i = 0; i < tasks.length; i++) {
       opt = document.createElement("option");
       title = tasks[i].title;
       opt.innerHTML = title;
       opt.setAttribute("value", tasks[i].id);
       selector.appendChild(opt);
     }
+    return true;
   }
 
   function showSelectedTaskStatus() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     var task = TestPilotSetup.getTaskById(getEid());
     document.getElementById("show-status-span").innerHTML = task.status;
     var selector = document.getElementById("status-selector");
     selector.selectedIndex = task.status;
@@ -211,25 +230,29 @@
                      get("ProfD", Components.interfaces.nsIFile);
     file.append("TestPilotExperimentFiles");
     file.append("index.json");
     if (file.exists()) {
       file.remove(false);
     }
   }
 
+
+   $(function(){startup()});
+
 </script>
 
 <style type="text/css">
       canvas { border: 1px solid black; }
     </style>
 
 </head>
 
-<body onload="populateFileDropdown();showSelectedTaskStatus();showIndexFileDropdown();">
+<body>
+<h1 id='status'><blink>loading experiments</blink></h1> <!-- take that, web standards! -->
 
 <fieldset>
 <p><select id="task-selector" onchange="showSelectedTaskStatus();"></select> Current Status = <span id="show-status-span"></span>.
 <button onclick="resetSelectedTask();showSelectedTaskStatus();">Reset Task</button>
 or set it to
 <select id="status-selector" onchange="setSelectedTaskStatus(); showSelectedTaskStatus();">
   <option value="0">0 (New)</option>
   <option value="1">1 (Pending)</option>
@@ -247,17 +270,17 @@ or set it to
 <button onclick="showMetaData();">Show Metadata</button>
 <button onclick="runUnitTests();">Run Tests</button>
 </fieldset>
 <fieldset>
 <p><button onclick="makeThereBeAPopup();">Show Dummy Popup</button>
 <button onclick="reloadAllExperiments();">Reload All Experiments</button>
 <button onclick="remindMe();">Notify Me</button>
 <button onclick="testJarStore();">Test Jar Store</button>
-Index file: 
+Index file:
 <select id="index-file-selector" onchange="setSelectedIndexFile();">
   <option value="index.json">index.json</option>
   <option value="index-dev.json">index-dev.json</option>
 </select>
 </p>
 </fieldset>
 <p><span id="debug"></span></p>
 
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js
@@ -153,17 +153,17 @@ var stringBundle;
             for (let j = 0; j < columnNames.length; j++) {
               csvString += "\"" + rawData[i][propertyNames[j]] + "\",";
             }
             csvString = csvString.substring(0, (csvString.length - 1));
             csvString += "\n";
           }
 
           // write, create, truncate
-          foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
+          foStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0664", 8), 0);
           converter.init(foStream, "UTF-8", 0, 0);
           converter.writeString(csvString);
           converter.close();
         });
       }
     };
 
     fp.init(window, null, nsIFilePicker.modeSave);
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js.orig
@@ -0,0 +1,428 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const PAGE_TYPE_STATUS = 0;
+const PAGE_TYPE_QUIT = 1;
+var stringBundle;
+
+  function showRawData(experimentId) {
+    window.openDialog(
+      "chrome://testpilot/content/raw-data-dialog.xul",
+      "TestPilotRawDataDialog", "chrome,centerscreen,resizable,scrollbars",
+      experimentId);
+  }
+
+  function getUrlParam(name) {
+    // from http://www.netlobo.com/url_query_string_javascript.html
+    name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+    var regexS = "[\\?&]"+name+"=([^&#]*)";
+    var regex = new RegExp(regexS);
+    var results = regex.exec(window.location.href);
+    if( results == null )
+      return "";
+    else
+      return results[1];
+  }
+
+  function uploadData() {
+    Components.utils.import("resource://testpilot/modules/setup.js");
+    let eid = getUrlParam("eid");
+    let task = TestPilotSetup.getTaskById(eid);
+
+    // If always-submit-checkbox is checked, set the pref
+    if (task._recursAutomatically) {
+      let checkBox = document.getElementById("always-submit-checkbox");
+      if (checkBox && checkBox.checked) {
+        task.setRecurPref(TaskConstants.ALWAYS_SUBMIT);
+      }
+    }
+
+    // Study web content must provide an element with id 'upload-status'.
+    // Fill it first with a message about data being uploaded; if there's
+    // an error, replace it with the error message.
+    let uploadStatus = document.getElementById("upload-status");
+    uploadStatus.innerHTML =
+      stringBundle.GetStringFromName("testpilot.statusPage.uploadingData");
+    task.upload( function(success) {
+      if (success) {
+        window.location =
+	  "chrome://testpilot/content/status.html?eid=" + eid;
+      } else {
+        // Replace 'now uploading' message
+        let errorParagraph = document.createElement("p");
+        errorParagraph.innerHTML = stringBundle.GetStringFromName("testpilot.statusPage.uploadErrorMsg");
+        let willRetryParagraph = document.createElement("p");
+        willRetryParagraph.innerHTML = stringBundle.GetStringFromName("testpilot.statusPage.willRetry");
+        uploadStatus.innerHTML = "";
+        uploadStatus.appendChild(errorParagraph);
+        uploadStatus.appendChild(willRetryParagraph);
+      }
+    });
+  }
+
+  function deleteData() {
+    Components.utils.import("resource://testpilot/modules/setup.js");
+    Components.utils.import("resource://testpilot/modules/tasks.js");
+    let eid = getUrlParam("eid");
+    let task = TestPilotSetup.getTaskById(eid);
+    task.dataStore.wipeAllData();
+    // reload the URL after wiping all data.
+    window.location = "chrome://testpilot/content/status.html?eid=" + eid;
+  }
+
+  function saveCanvas(canvas) {
+    const nsIFilePicker = Components.interfaces.nsIFilePicker;
+    let fp = Components.classes["@mozilla.org/filepicker;1"].
+             createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK ||
+          aResult == nsIFilePicker.returnReplace) {
+        const nsIWebBrowserPersist =
+          Components.interfaces.nsIWebBrowserPersist;
+        let file = fp.file;
+
+        // create a data url from the canvas and then create URIs of the
+        // source and targets
+        let io = Components.classes["@mozilla.org/network/io-service;1"].
+                 getService(Components.interfaces.nsIIOService);
+        let source = io.newURI(canvas.toDataURL("image/png"), "UTF8", null);
+        let target = io.newFileURI(file);
+
+        // prepare to save the canvas data
+        let persist = Components.classes[
+          "@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
+          createInstance(nsIWebBrowserPersist);
+        persist.persistFlags = nsIWebBrowserPersist.
+          PERSIST_FLAGS_REPLACE_EXISTING_FILES;
+        persist.persistFlags |= nsIWebBrowserPersist.
+          PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+        // displays a download dialog (remove these 3 lines for silent download)
+        let xfer = Components.classes["@mozilla.org/transfer;1"].
+                   createInstance(Components.interfaces.nsITransfer);
+        xfer.init(source, target, "", null, null, null, persist, false);
+        persist.progressListener = xfer;
+
+        // save the canvas data to the file
+        persist.saveURI(source, null, null, null, null, file, null);
+      }
+    };
+
+    fp.init(window, null, nsIFilePicker.modeSave);
+    fp.appendFilters(nsIFilePicker.filterImages | nsIFilePicker.filterAll);
+    fp.defaultString = "canvas.png";
+    fp.open(fpCallback);
+  }
+
+  function exportData() {
+    const nsIFilePicker = Components.interfaces.nsIFilePicker;
+    let eid = getUrlParam("eid");
+    let task = TestPilotSetup.getTaskById(eid);
+    let fp = Components.classes["@mozilla.org/filepicker;1"].
+             createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK ||
+          aResult == nsIFilePicker.returnReplace) {
+        const nsIWebBrowserPersist =
+          Components.interfaces.nsIWebBrowserPersist;
+        let foStream =
+          Components.classes["@mozilla.org/network/file-output-stream;1"].
+          createInstance(Components.interfaces.nsIFileOutputStream);
+        let converter =
+          Components.classes["@mozilla.org/intl/converter-output-stream;1"].
+          createInstance(Components.interfaces.nsIConverterOutputStream);
+        let file = fp.file;
+        let dataStore = task.dataStore;
+        let columnNames = dataStore.getHumanReadableColumnNames();
+        let propertyNames = dataStore.getPropertyNames();
+        let csvString = "";
+
+        // titles
+        for (let i = 0; i < columnNames.length; i++) {
+          csvString += "\"" + columnNames[i] + "\",";
+        }
+        if (csvString.length > 0) {
+          csvString = csvString.substring(0, (csvString.length - 1));
+          csvString += "\n";
+        }
+
+        dataStore.getAllDataAsJSON(true, function(rawData) {
+          // data
+          for (let i = 0; i < rawData.length; i++) {
+            for (let j = 0; j < columnNames.length; j++) {
+              csvString += "\"" + rawData[i][propertyNames[j]] + "\",";
+            }
+            csvString = csvString.substring(0, (csvString.length - 1));
+            csvString += "\n";
+          }
+
+          // write, create, truncate
+          foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
+          converter.init(foStream, "UTF-8", 0, 0);
+          converter.writeString(csvString);
+          converter.close();
+        });
+      }
+    };
+
+    fp.init(window, null, nsIFilePicker.modeSave);
+    fp.appendFilters(nsIFilePicker.filterImages | nsIFilePicker.filterAll);
+    fp.defaultString = task.title + ".csv";
+    fp.open(fpCallback);
+  }
+
+  function openLink(url) {
+    // open the link in the chromeless window
+    let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+                       .getService(Components.interfaces.nsIWindowMediator);
+    let recentWindow = wm.getMostRecentWindow("navigator:browser");
+
+    if (recentWindow) {
+      recentWindow.TestPilotWindowUtils.openInTab(url);
+    } else {
+      window.open(url);
+    }
+  }
+
+  function getTestEndingDate(experimentId) {
+    Components.utils.import("resource://testpilot/modules/setup.js");
+    var task = TestPilotSetup.getTaskById(experimentId);
+    var endDate = new Date(task.endDate);
+    var diff = (endDate - Date.now());
+    var span = document.getElementById("test-end-time");
+    if (!span) {
+      return;
+    }
+    if (diff < 0) {
+      span.innerHTML =
+        stringBundle.GetStringFromName("testpilot.statusPage.endedAlready");
+      return;
+    }
+    var hours = diff / (60 * 60 * 1000);
+    if (hours < 24) {
+      span.innerHTML =
+        stringBundle.formatStringFromName(
+	  "testpilot.statusPage.todayAt", [endDate.toLocaleTimeString()], 1);
+    } else {
+      span.innerHTML =
+        stringBundle.formatStringFromName(
+	  "testpilot.statusPage.endOn", [endDate.toLocaleString()], 1);
+    }
+  }
+
+  function showMetaData() {
+    Components.utils.import("resource://testpilot/modules/metadata.js");
+    Components.utils.import("resource://gre/modules/PluralForm.jsm");
+    MetadataCollector.getMetadata(function(md) {
+      var mdLocale = document.getElementById("md-locale");
+      if (mdLocale)
+        mdLocale.innerHTML = md.location;
+      var mdVersion = document.getElementById("md-version");
+      if (mdVersion)
+        mdVersion.innerHTML = md.version;
+      var mdOs = document.getElementById("md-os");
+      if (mdOs)
+        mdOs.innerHTML = md.operatingSystem;
+      var mdNumExt = document.getElementById("md-num-ext");
+      if (mdNumExt) {
+        // This computes the correctly localized singular or plural string
+        // of the number of extensions, e.g. "1 extension", "2 extensions", etc.
+        let str = stringBundle.GetStringFromName("testpilot.statusPage.numExtensions");
+        var numExt = md.extensions.length;
+        mdNumExt.innerHTML = PluralForm.get(numExt, str).replace("#1", numExt);
+      }
+    });
+  }
+
+  function onQuitPageLoad() {
+    Components.utils.import("resource://testpilot/modules/setup.js");
+    setStrings(PAGE_TYPE_QUIT);
+    let eid = getUrlParam("eid");
+    let task = TestPilotSetup.getTaskById(eid);
+    let header = document.getElementById("about-quit-title");
+    header.innerHTML =
+      stringBundle.formatStringFromName(
+	"testpilot.quitPage.aboutToQuit", [task.title], 1);
+
+    if (task._recursAutomatically) {
+      document.getElementById("recur-options").setAttribute("style", "");
+      document.getElementById("recur-checkbox-container").
+        setAttribute("style", "");
+    }
+  }
+
+  function quitExperiment() {
+    Components.utils.import("resource://testpilot/modules/setup.js");
+    Components.utils.import("resource://testpilot/modules/tasks.js");
+    let eid = getUrlParam("eid");
+    let reason = document.getElementById("reason-for-quit").value;
+    let task = TestPilotSetup.getTaskById(eid);
+    task.optOut(reason, function(success) {
+      // load the you-are-canceleed page.
+      window.location = "chrome://testpilot/content/status.html?eid=" + eid;
+    });
+
+    // If opt-out-forever checkbox is checked, opt out forever!
+    if (task._recursAutomatically) {
+      let checkBox = document.getElementById("opt-out-forever");
+      if (checkBox.checked) {
+        task.setRecurPref(TaskConstants.NEVER_SUBMIT);
+      }
+      // quit test so rescheduling
+      task._reschedule();
+    }
+  }
+
+  function updateRecurSettings() {
+    Components.utils.import("resource://testpilot/modules/setup.js");
+    let eid = getUrlParam("eid");
+    let experiment = TestPilotSetup.getTaskById(eid);
+    let recurSelector = document.getElementById("recur-selector");
+    let newValue = recurSelector.options[recurSelector.selectedIndex].value;
+    experiment.setRecurPref(parseInt(newValue));
+  }
+
+  function showRecurControls(experiment) {
+    Components.utils.import("resource://testpilot/modules/tasks.js");
+    let recurPrefSpan = document.getElementById("recur-pref");
+    if (!recurPrefSpan) {
+      return;
+    }
+    let days = experiment._recurrenceInterval;
+    recurPrefSpan.innerHTML =
+      stringBundle.formatStringFromName(
+	"testpilot.statusPage.recursEveryNumberOfDays", [days], 1);
+
+    let controls = document.getElementById("recur-controls");
+    let selector = document.createElement("select");
+    controls.appendChild(selector);
+    selector.setAttribute("onchange", "updateRecurSettings();");
+    selector.setAttribute("id", "recur-selector");
+
+    let option = document.createElement("option");
+    option.setAttribute("value", TaskConstants.ASK_EACH_TIME);
+    if (experiment.recurPref == TaskConstants.ASK_EACH_TIME) {
+      option.setAttribute("selected", "true");
+    }
+    option.innerHTML =
+      stringBundle.GetStringFromName(
+	"testpilot.statusPage.askMeBeforeSubmitData");
+    selector.appendChild(option);
+
+    option = document.createElement("option");
+    option.setAttribute("value", TaskConstants.ALWAYS_SUBMIT);
+    if (experiment.recurPref == TaskConstants.ALWAYS_SUBMIT) {
+      option.setAttribute("selected", "true");
+    }
+    option.innerHTML =
+      stringBundle.GetStringFromName(
+	"testpilot.statusPage.alwaysSubmitData");
+    selector.appendChild(option);
+
+    option = document.createElement("option");
+    option.setAttribute("value", TaskConstants.NEVER_SUBMIT);
+    if (experiment.recurPref == TaskConstants.NEVER_SUBMIT) {
+      option.setAttribute("selected", "true");
+    }
+    option.innerHTML =
+      stringBundle.GetStringFromName(
+	"testpilot.statusPage.neverSubmitData");
+    selector.appendChild(option);
+  }
+
+  function loadExperimentPage() {
+    Components.utils.import("resource://testpilot/modules/setup.js");
+    Components.utils.import("resource://testpilot/modules/tasks.js");
+    var contentDiv = document.getElementById("experiment-specific-text");
+    var dataPrivacyDiv = document.getElementById("data-privacy-text");
+    // Get experimentID from the GET args of page
+    var eid = getUrlParam("eid");
+    var experiment = TestPilotSetup.getTaskById(eid);
+    if (!experiment) {
+      // Possible that experiments aren't done loading yet.  Try again in
+      // a few seconds.
+      contentDiv.innerHTML =
+        stringBundle.GetStringFromName("testpilot.statusPage.loading");
+      window.setTimeout(function() { loadExperimentPage(); }, 2000);
+      return;
+    }
+
+    // Let the experiment fill in its web content (asynchronous)
+    experiment.getWebContent(function(webContent) {
+      contentDiv.innerHTML = webContent;
+
+      // Metadata and start/end date should be filled in for every experiment:
+      showMetaData();
+      getTestEndingDate(eid);
+      if (experiment._recursAutomatically &&
+        experiment.status != TaskConstants.STATUS_FINISHED) {
+        showRecurControls(experiment);
+      }
+
+      // Do whatever the experiment's web content wants done on load
+      // (Usually drawing a graph) - must be done after innerHTML is set.
+      experiment.webContent.onPageLoad(experiment, document, jQuery);
+    });
+
+    experiment.getDataPrivacyContent(function(dataPrivacyContent) {
+      if (dataPrivacyContent && dataPrivacyContent.length > 0) {
+        dataPrivacyDiv.innerHTML = dataPrivacyContent;
+        dataPrivacyDiv.removeAttribute("hidden");
+      }
+    });
+  }
+
+  function onStatusPageLoad() {
+    setStrings(PAGE_TYPE_STATUS);
+    /* An experiment ID (eid) must be provided in the url params. Show status
+     * for that experiment.*/
+    loadExperimentPage();
+  }
+
+  function setStrings(pageType) {
+    stringBundle =
+      Components.classes["@mozilla.org/intl/stringbundle;1"].
+        getService(Components.interfaces.nsIStringBundleService).
+	  createBundle("chrome://testpilot/locale/main.properties");
+    let map;
+    let mapLength;
+
+    if (pageType == PAGE_TYPE_STATUS) {
+      map = [
+	{ id: "page-title", stringKey: "testpilot.fullBrandName" },
+	{ id: "comments-and-discussions-link",
+	  stringKey: "testpilot.page.commentsAndDiscussions" },
+	{ id: "propose-test-link",
+	  stringKey: "testpilot.page.proposeATest" },
+	{ id: "testpilot-twitter-link",
+	  stringKey: "testpilot.page.testpilotOnTwitter" }
+      ];
+    } else if (pageType == PAGE_TYPE_QUIT) {
+      map = [
+	{ id: "page-title", stringKey: "testpilot.fullBrandName" },
+	{ id: "comments-and-discussions-link",
+	  stringKey: "testpilot.page.commentsAndDiscussions" },
+	{ id: "propose-test-link",
+	  stringKey: "testpilot.page.proposeATest" },
+	{ id: "testpilot-twitter-link",
+	  stringKey: "testpilot.page.testpilotOnTwitter" },
+	{ id: "optional-message",
+	  stringKey: "testpilot.quitPage.optionalMessage" },
+	{ id: "reason-text",
+	  stringKey: "testpilot.quitPage.reason" },
+	{ id: "recur-options",
+	  stringKey: "testpilot.quitPage.recurringStudy" },
+	{ id: "quit-forever-text",
+	  stringKey: "testpilot.quitPage.quitForever" },
+	{ id: "quit-study-link",
+	  stringKey: "testpilot.quitPage.quitStudy" }
+      ];
+    }
+    mapLength = map.length;
+    for (let i = 0; i < mapLength; i++) {
+      let entry = map[i];
+      document.getElementById(entry.id).innerHTML =
+        stringBundle.GetStringFromName(entry.stringKey);
+    }
+  }
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js.rej
@@ -0,0 +1,21 @@
+--- content/experiment-page.js
++++ content/experiment-page.js
+@@ -156,17 +156,17 @@
+           for (let j = 0; j < columnNames.length; j++) {
+ 	    csvString += "\"" + rawData[i][propertyNames[j]] + "\",";
+           }
+ 	  csvString = csvString.substring(0, (csvString.length - 1));
+           csvString += "\n";
+         }
+ 
+         // write, create, truncate
+-        foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
++        foStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0664", 8), 0);
+         converter.init(foStream, "UTF-8", 0, 0);
+         converter.writeString(csvString);
+         converter.close();
+       });
+     }
+   }
+ 
+   function openLink(url) {
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/feedback-browser.xul
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/feedback-browser.xul
@@ -1,16 +1,13 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
-<?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
-<?xml-stylesheet href="chrome://testpilot-os/skin/feedback.css" type="text/css"?>
-
 <!DOCTYPE overlay [
   <!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
     %testpilotDTD;
 ]>
 
 <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <menupopup id="menu_ToolsPopup">
   <menu id="pilot-menu" insertafter="menu_openAddons" />
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/jquery.min.js
@@ -0,0 +1,2 @@
+/*! jQuery v@1.8.0 jquery.com | jquery.org/license */
+(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d<e;d++)p.event.add(b,c,h[c][d])}g.data&&(g.data=p.extend({},g.data))}function bE(a,b){var c;if(b.nodeType!==1)return;b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?(b.parentNode&&(b.outerHTML=a.outerHTML),p.support.html5Clone&&a.innerHTML&&!p.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):c==="input"&&bv.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text),b.removeAttribute(p.expando)}function bF(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bG(a){bv.test(a.type)&&(a.defaultChecked=a.checked)}function bX(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=bV.length;while(e--){b=bV[e]+c;if(b in a)return b}return d}function bY(a,b){return a=b||a,p.css(a,"display")==="none"||!p.contains(a.ownerDocument,a)}function bZ(a,b){var c,d,e=[],f=0,g=a.length;for(;f<g;f++){c=a[f];if(!c.style)continue;e[f]=p._data(c,"olddisplay"),b?(!e[f]&&c.style.display==="none"&&(c.style.display=""),c.style.display===""&&bY(c)&&(e[f]=p._data(c,"olddisplay",cb(c.nodeName)))):(d=bH(c,"display"),!e[f]&&d!=="none"&&p._data(c,"olddisplay",d))}for(f=0;f<g;f++){c=a[f];if(!c.style)continue;if(!b||c.style.display==="none"||c.style.display==="")c.style.display=b?e[f]||"":"none"}return a}function b$(a,b,c){var d=bO.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function b_(a,b,c,d){var e=c===(d?"border":"content")?4:b==="width"?1:0,f=0;for(;e<4;e+=2)c==="margin"&&(f+=p.css(a,c+bU[e],!0)),d?(c==="content"&&(f-=parseFloat(bH(a,"padding"+bU[e]))||0),c!=="margin"&&(f-=parseFloat(bH(a,"border"+bU[e]+"Width"))||0)):(f+=parseFloat(bH(a,"padding"+bU[e]))||0,c!=="padding"&&(f+=parseFloat(bH(a,"border"+bU[e]+"Width"))||0));return f}function ca(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=!0,f=p.support.boxSizing&&p.css(a,"boxSizing")==="border-box";if(d<=0){d=bH(a,b);if(d<0||d==null)d=a.style[b];if(bP.test(d))return d;e=f&&(p.support.boxSizingReliable||d===a.style[b]),d=parseFloat(d)||0}return d+b_(a,b,c||(f?"border":"content"),e)+"px"}function cb(a){if(bR[a])return bR[a];var b=p("<"+a+">").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write("<!doctype html><html><body>"),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bR[a]=c,c}function ch(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||cd.test(a)?d(a,e):ch(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ch(a+"["+e+"]",b[e],c,d);else d(a,b)}function cy(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h<i;h++)d=g[h],f=/^\+/.test(d),f&&(d=d.substr(1)||"*"),e=a[d]=a[d]||[],e[f?"unshift":"push"](c)}}function cz(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h,i=a[f],j=0,k=i?i.length:0,l=a===cu;for(;j<k&&(l||!h);j++)h=i[j](c,d,e),typeof h=="string"&&(!l||g[h]?h=b:(c.dataTypes.unshift(h),h=cz(a,c,d,e,h,g)));return(l||!h)&&!g["*"]&&(h=cz(a,c,d,e,"*",g)),h}function cA(a,c){var d,e,f=p.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((f[d]?a:e||(e={}))[d]=c[d]);e&&p.extend(!0,a,e)}function cB(a,c,d){var e,f,g,h,i=a.contents,j=a.dataTypes,k=a.responseFields;for(f in k)f in d&&(c[k[f]]=d[f]);while(j[0]==="*")j.shift(),e===b&&(e=a.mimeType||c.getResponseHeader("content-type"));if(e)for(f in i)if(i[f]&&i[f].test(e)){j.unshift(f);break}if(j[0]in d)g=j[0];else{for(f in d){if(!j[0]||a.converters[f+" "+j[0]]){g=f;break}h||(h=f)}g=g||h}if(g)return g!==j[0]&&j.unshift(g),d[g]}function cC(a,b){var c,d,e,f,g=a.dataTypes.slice(),h=g[0],i={},j=0;a.dataFilter&&(b=a.dataFilter(b,a.dataType));if(g[1])for(c in a.converters)i[c.toLowerCase()]=a.converters[c];for(;e=g[++j];)if(e!=="*"){if(h!=="*"&&h!==e){c=i[h+" "+e]||i["* "+e];if(!c)for(d in i){f=d.split(" ");if(f[1]===e){c=i[h+" "+f[0]]||i["* "+f[0]];if(c){c===!0?c=i[d]:i[d]!==!0&&(e=f[0],g.splice(j--,0,e));break}}}if(c!==!0)if(c&&a["throws"])b=c(b);else try{b=c(b)}catch(k){return{state:"parsererror",error:c?k:"No conversion from "+h+" to "+e}}}h=e}return{state:"success",data:b}}function cK(){try{return new a.XMLHttpRequest}catch(b){}}function cL(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function cT(){return setTimeout(function(){cM=b},0),cM=p.now()}function cU(a,b){p.each(b,function(b,c){var d=(cS[b]||[]).concat(cS["*"]),e=0,f=d.length;for(;e<f;e++)if(d[e].call(a,b,c))return})}function cV(a,b,c){var d,e=0,f=0,g=cR.length,h=p.Deferred().always(function(){delete i.elem}),i=function(){var b=cM||cT(),c=Math.max(0,j.startTime+j.duration-b),d=1-(c/j.duration||0),e=0,f=j.tweens.length;for(;e<f;e++)j.tweens[e].run(d);return h.notifyWith(a,[j,d,c]),d<1&&f?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:p.extend({},b),opts:p.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:cM||cT(),duration:c.duration,tweens:[],createTween:function(b,c,d){var e=p.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(e),e},stop:function(b){var c=0,d=b?j.tweens.length:0;for(;c<d;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;cW(k,j.opts.specialEasing);for(;e<g;e++){d=cR[e].call(j,a,k,j.opts);if(d)return d}return cU(j,k),p.isFunction(j.opts.start)&&j.opts.start.call(a,j),p.fx.timer(p.extend(i,{anim:j,queue:j.opts.queue,elem:a})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}function cW(a,b){var c,d,e,f,g;for(c in a){d=p.camelCase(c),e=b[d],f=a[c],p.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=p.cssHooks[d];if(g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}}function cX(a,b,c){var d,e,f,g,h,i,j,k,l=this,m=a.style,n={},o=[],q=a.nodeType&&bY(a);c.queue||(j=p._queueHooks(a,"fx"),j.unqueued==null&&(j.unqueued=0,k=j.empty.fire,j.empty.fire=function(){j.unqueued||k()}),j.unqueued++,l.always(function(){l.always(function(){j.unqueued--,p.queue(a,"fx").length||j.empty.fire()})})),a.nodeType===1&&("height"in b||"width"in b)&&(c.overflow=[m.overflow,m.overflowX,m.overflowY],p.css(a,"display")==="inline"&&p.css(a,"float")==="none"&&(!p.support.inlineBlockNeedsLayout||cb(a.nodeName)==="inline"?m.display="inline-block":m.zoom=1)),c.overflow&&(m.overflow="hidden",p.support.shrinkWrapBlocks||l.done(function(){m.overflow=c.overflow[0],m.overflowX=c.overflow[1],m.overflowY=c.overflow[2]}));for(d in b){f=b[d];if(cO.exec(f)){delete b[d];if(f===(q?"hide":"show"))continue;o.push(d)}}g=o.length;if(g){h=p._data(a,"fxshow")||p._data(a,"fxshow",{}),q?p(a).show():l.done(function(){p(a).hide()}),l.done(function(){var b;p.removeData(a,"fxshow",!0);for(b in n)p.style(a,b,n[b])});for(d=0;d<g;d++)e=o[d],i=l.createTween(e,q?h[e]:0),n[e]=h[e]||p.style(a,e),e in h||(h[e]=i.start,q&&(i.end=i.start,i.start=e==="width"||e==="height"?1:0))}}function cY(a,b,c,d,e){return new cY.prototype.init(a,b,c,d,e)}function cZ(a,b){var c,d={height:a},e=0;for(;e<4;e+=2-b)c=bU[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function c_(a){return p.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}var c,d,e=a.document,f=a.location,g=a.navigator,h=a.jQuery,i=a.$,j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf,m=Object.prototype.toString,n=Object.prototype.hasOwnProperty,o=String.prototype.trim,p=function(a,b){return new p.fn.init(a,b,c)},q=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,r=/\S/,s=/\s+/,t=r.test(" ")?/^[\s\xA0]+|[\s\xA0]+$/g:/^\s+|\s+$/g,u=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.0",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i<j;i++)if((a=arguments[i])!=null)for(c in a){d=h[c],e=a[c];if(h===e)continue;k&&e&&(p.isPlainObject(e)||(f=p.isArray(e)))?(f?(f=!1,g=d&&p.isArray(d)?d:[]):g=d&&p.isPlainObject(d)?d:{},h[c]=p.extend(k,g,e)):e!==b&&(h[c]=e)}return h},p.extend({noConflict:function(b){return a.$===p&&(a.$=i),b&&a.jQuery===p&&(a.jQuery=h),p},isReady:!1,readyWait:1,holdReady:function(a){a?p.readyWait++:p.ready(!0)},ready:function(a){if(a===!0?--p.readyWait:p.isReady)return;if(!e.body)return setTimeout(p.ready,1);p.isReady=!0;if(a!==!0&&--p.readyWait>0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f<g;)if(c.apply(a[f++],d)===!1)break}else if(h){for(e in a)if(c.call(a[e],e,a[e])===!1)break}else for(;f<g;)if(c.call(a[f],f,a[f++])===!1)break;return a},trim:o?function(a){return a==null?"":o.call(a)}:function(a){return a==null?"":a.toString().replace(t,"")},makeArray:function(a,b){var c,d=b||[];return a!=null&&(c=p.type(a),a.length==null||c==="string"||c==="function"||c==="regexp"||p.isWindow(a)?j.call(d,a):p.merge(d,a)),d},inArray:function(a,b,c){var d;if(b){if(l)return l.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=c.length,e=a.length,f=0;if(typeof d=="number")for(;f<d;f++)a[e++]=c[f];else while(c[f]!==b)a[e++]=c[f++];return a.length=e,a},grep:function(a,b,c){var d,e=[],f=0,g=a.length;c=!!c;for(;f<g;f++)d=!!b(a[f],f),c!==d&&e.push(a[f]);return e},map:function(a,c,d){var e,f,g=[],h=0,i=a.length,j=a instanceof p||i!==b&&typeof i=="number"&&(i>0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h<i;h++)e=c(a[h],h,d),e!=null&&(g[g.length]=e);else for(f in a)e=c(a[f],f,d),e!=null&&(g[g.length]=e);return g.concat.apply([],g)},guid:1,proxy:function(a,c){var d,e,f;return typeof c=="string"&&(d=a[c],c=a,a=d),p.isFunction(a)?(e=k.call(arguments,2),f=function(){return a.apply(c,e.concat(k.call(arguments)))},f.guid=a.guid=a.guid||f.guid||p.guid++,f):b},access:function(a,c,d,e,f,g,h){var i,j=d==null,k=0,l=a.length;if(d&&typeof d=="object"){for(k in d)p.access(a,c,k,d[k],1,g,e);f=1}else if(e!==b){i=h===b&&p.isFunction(e),j&&(i?(i=c,c=function(a,b,c){return i.call(p(a),c)}):(c.call(a,e),c=null));if(c)for(;k<l;k++)c(a[k],d,i?e.call(a[k],k,c(a[k],d)):e,h);f=1}return f?a:j?c.call(a):l?c(a[0],d):g},now:function(){return(new Date).getTime()}}),p.ready.promise=function(b){if(!d){d=p.Deferred();if(e.readyState==="complete"||e.readyState!=="loading"&&e.addEventListener)setTimeout(p.ready,1);else if(e.addEventListener)e.addEventListener("DOMContentLoaded",D,!1),a.addEventListener("load",p.ready,!1);else{e.attachEvent("onreadystatechange",D),a.attachEvent("onload",p.ready);var c=!1;try{c=a.frameElement==null&&e.documentElement}catch(f){}c&&c.doScroll&&function g(){if(!p.isReady){try{c.doScroll("left")}catch(a){return setTimeout(g,50)}p.ready()}}()}}return d.promise(b)},p.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){E["[object "+b+"]"]=b.toLowerCase()}),c=p(e);var F={};p.Callbacks=function(a){a=typeof a=="string"?F[a]||G(a):p.extend({},a);var c,d,e,f,g,h,i=[],j=!a.once&&[],k=function(b){c=a.memory&&b,d=!0,h=f||0,f=0,g=i.length,e=!0;for(;i&&h<g;h++)if(i[h].apply(b[0],b[1])===!1&&a.stopOnFalse){c=!1;break}e=!1,i&&(j?j.length&&k(j.shift()):c?i=[]:l.disable())},l={add:function(){if(i){var b=i.length;(function d(b){p.each(b,function(b,c){p.isFunction(c)&&(!a.unique||!l.has(c))?i.push(c):c&&c.length&&d(c)})})(arguments),e?g=i.length:c&&(f=b,k(c))}return this},remove:function(){return i&&p.each(arguments,function(a,b){var c;while((c=p.inArray(b,i,c))>-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b<d;b++)c[b]&&p.isFunction(c[b].promise)?c[b].promise().done(g(b,j,c)).fail(f.reject).progress(g(b,i,h)):--e}return e||f.resolveWith(j,c),f.promise()}}),p.support=function(){var b,c,d,f,g,h,i,j,k,l,m,n=e.createElement("div");n.setAttribute("className","t"),n.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="<div></div>",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e<f;e++)delete d[b[e]];if(!(c?K:p.isEmptyObject)(d))return}}if(!c){delete h[i].data;if(!K(h[i]))return}g?p.cleanData([a],!0):p.support.deleteExpando||h!=h.window?delete h[i]:h[i]=null},_data:function(a,b,c){return p.data(a,b,c,!0)},acceptData:function(a){var b=a.nodeName&&p.noData[a.nodeName.toLowerCase()];return!b||b!==!0&&a.getAttribute("classid")===b}}),p.fn.extend({data:function(a,c){var d,e,f,g,h,i=this[0],j=0,k=null;if(a===b){if(this.length){k=p.data(i);if(i.nodeType===1&&!p._data(i,"parsedAttrs")){f=i.attributes;for(h=f.length;j<h;j++)g=f[j].name,g.indexOf("data-")===0&&(g=p.camelCase(g.substring(5)),J(i,g,k[g]));p._data(i,"parsedAttrs",!0)}}return k}return typeof a=="object"?this.each(function(){p.data(this,a)}):(d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!",p.access(this,function(c){if(c===b)return k=this.triggerHandler("getData"+e,[d[0]]),k===b&&i&&(k=p.data(i,a),k=J(i,a,k)),k===b&&d[1]?this.data(d[0]):k;d[1]=c,this.each(function(){var b=p(this);b.triggerHandler("setData"+e,d),p.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.shift(),e=p._queueHooks(a,b),f=function(){p.dequeue(a,b)};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),delete e.stop,d.call(a,f,e)),!c.length&&e&&e.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length<d?p.queue(this[0],a):c===b?this:this.each(function(){var b=p.queue(this,a,c);p._queueHooks(this,a),a==="fx"&&b[0]!=="inprogress"&&p.dequeue(this,a)})},dequeue:function(a){return this.each(function(){p.dequeue(this,a)})},delay:function(a,b){return a=p.fx?p.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){var d,e=1,f=p.Deferred(),g=this,h=this.length,i=function(){--e||f.resolveWith(g,[g])};typeof a!="string"&&(c=a,a=b),a=a||"fx";while(h--)(d=p._data(g[h],a+"queueHooks"))&&d.empty&&(e++,d.empty.add(i));return i(),f.promise(c)}});var L,M,N,O=/[\t\r\n]/g,P=/\r/g,Q=/^(?:button|input)$/i,R=/^(?:button|input|object|select|textarea)$/i,S=/^a(?:rea|)$/i,T=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,U=p.support.getSetAttribute;p.fn.extend({attr:function(a,b){return p.access(this,p.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{f=" "+e.className+" ";for(g=0,h=b.length;g<h;g++)~f.indexOf(" "+b[g]+" ")||(f+=b[g]+" ");e.className=p.trim(f)}}}return this},removeClass:function(a){var c,d,e,f,g,h,i;if(p.isFunction(a))return this.each(function(b){p(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(s);for(h=0,i=this.length;h<i;h++){e=this[h];if(e.nodeType===1&&e.className){d=(" "+e.className+" ").replace(O," ");for(f=0,g=c.length;f<g;f++)while(d.indexOf(" "+c[f]+" ")>-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(O," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c<d;c++){e=h[c];if(e.selected&&(p.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!p.nodeName(e.parentNode,"optgroup"))){b=p(e).val();if(i)return b;g.push(b)}}return i&&!g.length&&h.length?p(h[f]).val():g},set:function(a,b){var c=p.makeArray(b);return p(a).find("option").each(function(){this.selected=p.inArray(p(this).val(),c)>=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g<d.length;g++)e=d[g],e&&(c=p.propFix[e]||e,f=T.test(e),f||p.attr(a,e,""),a.removeAttribute(U?e:c),f&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(Q.test(a.nodeName)&&a.parentNode)p.error("type property can't be changed");else if(!p.support.radioValue&&b==="radio"&&p.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}},value:{get:function(a,b){return L&&p.nodeName(a,"button")?L.get(a,b):b in a?a.value:null},set:function(a,b,c){if(L&&p.nodeName(a,"button"))return L.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,f,g,h=a.nodeType;if(!a||h===3||h===8||h===2)return;return g=h!==1||!p.isXMLDoc(a),g&&(c=p.propFix[c]||c,f=p.propHooks[c]),d!==b?f&&"set"in f&&(e=f.set(a,d,c))!==b?e:a[c]=d:f&&"get"in f&&(e=f.get(a,c))!==null?e:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):R.test(a.nodeName)||S.test(a.nodeName)&&a.href?0:b}}}}),M={get:function(a,c){var d,e=p.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;return b===!1?p.removeAttr(a,c):(d=p.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase())),c}},U||(N={name:!0,id:!0,coords:!0},L=p.valHooks.button={get:function(a,c){var d;return d=a.getAttributeNode(c),d&&(N[c]?d.value!=="":d.specified)?d.value:b},set:function(a,b,c){var d=a.getAttributeNode(c);return d||(d=e.createAttribute(c),a.setAttributeNode(d)),d.value=b+""}},p.each(["width","height"],function(a,b){p.attrHooks[b]=p.extend(p.attrHooks[b],{set:function(a,c){if(c==="")return a.setAttribute(b,"auto"),c}})}),p.attrHooks.contenteditable={get:L.get,set:function(a,b,c){b===""&&(b="false"),L.set(a,b,c)}}),p.support.hrefNormalized||p.each(["href","src","width","height"],function(a,c){p.attrHooks[c]=p.extend(p.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),p.support.style||(p.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),p.support.optSelected||(p.propHooks.selected=p.extend(p.propHooks.selected,{get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}})),p.support.enctype||(p.propFix.enctype="encoding"),p.support.checkOn||p.each(["radio","checkbox"],function(){p.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),p.each(["radio","checkbox"],function(){p.valHooks[this]=p.extend(p.valHooks[this],{set:function(a,b){if(p.isArray(b))return a.checked=p.inArray(p(a).val(),b)>=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j<c.length;j++){k=W.exec(c[j])||[],l=k[1],m=(k[2]||"").split(".").sort(),r=p.event.special[l]||{},l=(f?r.delegateType:r.bindType)||l,r=p.event.special[l]||{},n=p.extend({type:l,origType:k[1],data:e,handler:d,guid:d.guid,selector:f,namespace:m.join(".")},o),q=i[l];if(!q){q=i[l]=[],q.delegateCount=0;if(!r.setup||r.setup.call(a,e,m,h)===!1)a.addEventListener?a.addEventListener(l,h,!1):a.attachEvent&&a.attachEvent("on"+l,h)}r.add&&(r.add.call(a,n),n.handler.guid||(n.handler.guid=d.guid)),f?q.splice(q.delegateCount++,0,n):q.push(n),p.event.global[l]=!0}a=null},global:{},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,q,r=p.hasData(a)&&p._data(a);if(!r||!(m=r.events))return;b=p.trim(_(b||"")).split(" ");for(f=0;f<b.length;f++){g=W.exec(b[f])||[],h=i=g[1],j=g[2];if(!h){for(h in m)p.event.remove(a,h+b[f],c,d,!0);continue}n=p.event.special[h]||{},h=(d?n.delegateType:n.bindType)||h,o=m[h]||[],k=o.length,j=j?new RegExp("(^|\\.)"+j.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(l=0;l<o.length;l++)q=o[l],(e||i===q.origType)&&(!c||c.guid===q.guid)&&(!j||j.test(q.namespace))&&(!d||d===q.selector||d==="**"&&q.selector)&&(o.splice(l--,1),q.selector&&o.delegateCount--,n.remove&&n.remove.call(a,q));o.length===0&&k!==o.length&&((!n.teardown||n.teardown.call(a,j,r.handle)===!1)&&p.removeEvent(a,h,r.handle),delete m[h])}p.isEmptyObject(m)&&(delete r.handle,p.removeData(a,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,f,g){if(!f||f.nodeType!==3&&f.nodeType!==8){var h,i,j,k,l,m,n,o,q,r,s=c.type||c,t=[];if($.test(s+p.event.triggered))return;s.indexOf("!")>=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j<q.length&&!c.isPropagationStopped();j++)k=q[j][0],c.type=q[j][1],o=(p._data(k,"events")||{})[c.type]&&p._data(k,"handle"),o&&o.apply(k,d),o=m&&k[m],o&&p.acceptData(k)&&o.apply(k,d)===!1&&c.preventDefault();return c.type=s,!g&&!c.isDefaultPrevented()&&(!n._default||n._default.apply(f.ownerDocument,d)===!1)&&(s!=="click"||!p.nodeName(f,"a"))&&p.acceptData(f)&&m&&f[s]&&(s!=="focus"&&s!=="blur"||c.target.offsetWidth!==0)&&!p.isWindow(f)&&(l=f[m],l&&(f[m]=null),p.event.triggered=s,f[s](),p.event.triggered=b,l&&(f[m]=l)),c.result}return},dispatch:function(c){c=p.event.fix(c||a.event);var d,e,f,g,h,i,j,k,l,m,n,o=(p._data(this,"events")||{})[c.type]||[],q=o.delegateCount,r=[].slice.call(arguments),s=!c.exclusive&&!c.namespace,t=p.event.special[c.type]||{},u=[];r[0]=c,c.delegateTarget=this;if(t.preDispatch&&t.preDispatch.call(this,c)===!1)return;if(q&&(!c.button||c.type!=="click")){g=p(this),g.context=this;for(f=c.target;f!=this;f=f.parentNode||this)if(f.disabled!==!0||c.type!=="click"){i={},k=[],g[0]=f;for(d=0;d<q;d++)l=o[d],m=l.selector,i[m]===b&&(i[m]=g.is(m)),i[m]&&k.push(l);k.length&&u.push({elem:f,matches:k})}}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d<u.length&&!c.isPropagationStopped();d++){j=u[d],c.currentTarget=j.elem;for(e=0;e<j.matches.length&&!c.isImmediatePropagationStopped();e++){l=j.matches[e];if(s||!c.namespace&&!l.namespace||c.namespace_re&&c.namespace_re.test(l.namespace))c.data=l.data,c.handleObj=l,h=((p.event.special[l.origType]||{}).handle||l.handler).apply(j.elem,r),h!==b&&(c.result=h,h===!1&&(c.preventDefault(),c.stopPropagation()))}}return t.postDispatch&&t.postDispatch.call(this,c),c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,c){var d,f,g,h=c.button,i=c.fromElement;return a.pageX==null&&c.clientX!=null&&(d=a.target.ownerDocument||e,f=d.documentElement,g=d.body,a.pageX=c.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=c.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?c.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0),a}},fix:function(a){if(a[p.expando])return a;var b,c,d=a,f=p.event.fixHooks[a.type]||{},g=f.props?this.props.concat(f.props):this.props;a=p.Event(d);for(b=g.length;b;)c=g[--b],a[c]=d[c];return a.target||(a.target=d.srcElement||e),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,f.filter?f.filter(a,d):a},special:{ready:{setup:p.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){p.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=p.extend(new p.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?p.event.trigger(e,null,b):p.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},p.event.handle=p.event.dispatch,p.removeEvent=e.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]=="undefined"&&(a[d]=null),a.detachEvent(d,c))},p.Event=function(a,b){if(this instanceof p.Event)a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?bb:ba):this.type=a,b&&p.extend(this,b),this.timeStamp=a&&a.timeStamp||p.now(),this[p.expando]=!0;else return new p.Event(a,b)},p.Event.prototype={preventDefault:function(){this.isDefaultPrevented=bb;var a=this.originalEvent;if(!a)return;a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=bb;var a=this.originalEvent;if(!a)return;a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=bb,this.stopPropagation()},isDefaultPrevented:ba,isPropagationStopped:ba,isImmediatePropagationStopped:ba},p.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){p.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj,g=f.selector;if(!e||e!==d&&!p.contains(d,e))a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b;return c}}}),p.support.submitBubbles||(p.event.special.submit={setup:function(){if(p.nodeName(this,"form"))return!1;p.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=p.nodeName(c,"input")||p.nodeName(c,"button")?c.form:b;d&&!p._data(d,"_submit_attached")&&(p.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),p._data(d,"_submit_attached",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&p.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(p.nodeName(this,"form"))return!1;p.event.remove(this,"._submit")}}),p.support.changeBubbles||(p.event.special.change={setup:function(){if(V.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")p.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),p.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),p.event.simulate("change",this,a,!0)});return!1}p.event.add(this,"beforeactivate._change",function(a){var b=a.target;V.test(b.nodeName)&&!p._data(b,"_change_attached")&&(p.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&p.event.simulate("change",this.parentNode,a,!0)}),p._data(b,"_change_attached",!0))})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){return p.event.remove(this,"._change"),V.test(this.nodeName)}}),p.support.focusinBubbles||p.each({focus:"focusin",blur:"focusout"},function(a,b){var c=0,d=function(a){p.event.simulate(b,a.target,p.event.fix(a),!0)};p.event.special[b]={setup:function(){c++===0&&e.addEventListener(a,d,!0)},teardown:function(){--c===0&&e.removeEventListener(a,d,!0)}}}),p.fn.extend({on:function(a,c,d,e,f){var g,h;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(h in a)this.on(h,c,d,a[h],f);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=ba;else if(!e)return this;return f===1&&(g=e,e=function(a){return p().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=p.guid++)),this.each(function(){p.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){var e,f;if(a&&a.preventDefault&&a.handleObj)return e=a.handleObj,p(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler),this;if(typeof a=="object"){for(f in a)this.off(f,c,a[f]);return this}if(c===!1||typeof c=="function")d=c,c=b;return d===!1&&(d=ba),this.each(function(){p.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){return p(this.context).on(a,this.selector,b,c),this},die:function(a,b){return p(this.context).off(a,this.selector||"**",b),this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a||"**",c)},trigger:function(a,b){return this.each(function(){p.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return p.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||p.guid++,d=0,e=function(c){var e=(p._data(this,"lastToggle"+a.guid)||0)%d;return p._data(this,"lastToggle"+a.guid,e+1),c.preventDefault(),b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),p.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){p.fn[b]=function(a,c){return c==null&&(c=a,a=null),arguments.length>0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bd(a,b,c,d){var e=0,f=b.length;for(;e<f;e++)Z(a,b[e],c,d)}function be(a,b,c,d,e,f){var g,h=$.setFilters[b.toLowerCase()];return h||Z.error(b),(a||!(g=e))&&bd(a||"*",d,g=[],e),g.length>0?h(g,c,f):[]}function bf(a,c,d,e,f){var g,h,i,j,k,l,m,n,p=0,q=f.length,s=L.POS,t=new RegExp("^"+s.source+"(?!"+r+")","i"),u=function(){var a=1,c=arguments.length-2;for(;a<c;a++)arguments[a]===b&&(g[a]=b)};for(;p<q;p++){s.exec(""),a=f[p],j=[],i=0,k=e;while(g=s.exec(a)){n=s.lastIndex=g.index+g[0].length;if(n>i){m=a.slice(i,g.index),i=n,l=[c],B.test(m)&&(k&&(l=k),k=e);if(h=H.test(m))m=m.slice(0,-5).replace(B,"$&*");g.length>1&&g[0].replace(t,u),k=be(m,g[1],g[2],l,k,h)}}k?(j=j.concat(k),(m=a.slice(i))&&m!==")"?B.test(m)?bd(m,j,d,e):Z(m,c,d,e?e.concat(k):k):o.apply(d,j)):Z(a,c,d,e)}return q===1?d:Z.uniqueSort(d)}function bg(a,b,c){var d,e,f,g=[],i=0,j=D.exec(a),k=!j.pop()&&!j.pop(),l=k&&a.match(C)||[""],m=$.preFilter,n=$.filter,o=!c&&b!==h;for(;(e=l[i])!=null&&k;i++){g.push(d=[]),o&&(e=" "+e);while(e){k=!1;if(j=B.exec(e))e=e.slice(j[0].length),k=d.push({part:j.pop().replace(A," "),captures:j});for(f in n)(j=L[f].exec(e))&&(!m[f]||(j=m[f](j,b,c)))&&(e=e.slice(j.shift().length),k=d.push({part:f,captures:j}));if(!k)break}}return k||Z.error(a),g}function bh(a,b,e){var f=b.dir,g=m++;return a||(a=function(a){return a===e}),b.first?function(b,c){while(b=b[f])if(b.nodeType===1)return a(b,c)&&b}:function(b,e){var h,i=g+"."+d,j=i+"."+c;while(b=b[f])if(b.nodeType===1){if((h=b[q])===j)return b.sizset;if(typeof h=="string"&&h.indexOf(i)===0){if(b.sizset)return b}else{b[q]=j;if(a(b,e))return b.sizset=!0,b;b.sizset=!1}}}}function bi(a,b){return a?function(c,d){var e=b(c,d);return e&&a(e===!0?c:e,d)}:b}function bj(a,b,c){var d,e,f=0;for(;d=a[f];f++)$.relative[d.part]?e=bh(e,$.relative[d.part],b):(d.captures.push(b,c),e=bi(e,$.filter[d.part].apply(null,d.captures)));return e}function bk(a){return function(b,c){var d,e=0;for(;d=a[e];e++)if(d(b,c))return!0;return!1}}var c,d,e,f,g,h=a.document,i=h.documentElement,j="undefined",k=!1,l=!0,m=0,n=[].slice,o=[].push,q=("sizcache"+Math.random()).replace(".",""),r="[\\x20\\t\\r\\n\\f]",s="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",t=s.replace("w","w#"),u="([*^$|!~]?=)",v="\\["+r+"*("+s+")"+r+"*(?:"+u+r+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+t+")|)|)"+r+"*\\]",w=":("+s+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",x=":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",y=r+"*([\\x20\\t\\r\\n\\f>+~])"+r+"*",z="(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|"+v+"|"+w.replace(2,7)+"|[^\\\\(),])+",A=new RegExp("^"+r+"+|((?:^|[^\\\\])(?:\\\\.)*)"+r+"+$","g"),B=new RegExp("^"+y),C=new RegExp(z+"?(?="+r+"*,|$)","g"),D=new RegExp("^(?:(?!,)(?:(?:^|,)"+r+"*"+z+")*?|"+r+"*(.*?))(\\)|$)"),E=new RegExp(z.slice(19,-6)+"\\x20\\t\\r\\n\\f>+~])+|"+y,"g"),F=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,G=/[\x20\t\r\n\f]*[+~]/,H=/:not\($/,I=/h\d/i,J=/input|select|textarea|button/i,K=/\\(?!\\)/g,L={ID:new RegExp("^#("+s+")"),CLASS:new RegExp("^\\.("+s+")"),NAME:new RegExp("^\\[name=['\"]?("+s+")['\"]?\\]"),TAG:new RegExp("^("+s.replace("[-","[-\\*")+")"),ATTR:new RegExp("^"+v),PSEUDO:new RegExp("^"+w),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+r+"*(even|odd|(([+-]|)(\\d*)n|)"+r+"*(?:([+-]|)"+r+"*(\\d+)|))"+r+"*\\)|)","i"),POS:new RegExp(x,"ig"),needsContext:new RegExp("^"+r+"*[>+~]|"+x,"i")},M={},N=[],O={},P=[],Q=function(a){return a.sizzleFilter=!0,a},R=function(a){return function(b){return b.nodeName.toLowerCase()==="input"&&b.type===a}},S=function(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}},T=function(a){var b=!1,c=h.createElement("div");try{b=a(c)}catch(d){}return c=null,b},U=T(function(a){a.innerHTML="<select></select>";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),V=T(function(a){a.id=q+0,a.innerHTML="<a name='"+q+"'></a><div name='"+q+"'></div>",i.insertBefore(a,i.firstChild);var b=h.getElementsByName&&h.getElementsByName(q).length===2+h.getElementsByName(q+0).length;return g=!h.getElementById(q),i.removeChild(a),b}),W=T(function(a){return a.appendChild(h.createComment("")),a.getElementsByTagName("*").length===0}),X=T(function(a){return a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!==j&&a.firstChild.getAttribute("href")==="#"}),Y=T(function(a){return a.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!a.getElementsByClassName||a.getElementsByClassName("e").length===0?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length!==1)}),Z=function(a,b,c,d){c=c||[],b=b||h;var e,f,g,i,j=b.nodeType;if(j!==1&&j!==9)return[];if(!a||typeof a!="string")return c;g=ba(b);if(!g&&!d)if(e=F.exec(a))if(i=e[1]){if(j===9){f=b.getElementById(i);if(!f||!f.parentNode)return c;if(f.id===i)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(i))&&bb(b,f)&&f.id===i)return c.push(f),c}else{if(e[2])return o.apply(c,n.call(b.getElementsByTagName(a),0)),c;if((i=e[3])&&Y&&b.getElementsByClassName)return o.apply(c,n.call(b.getElementsByClassName(i),0)),c}return bm(a,b,c,d,g)},$=Z.selectors={cacheLength:50,match:L,order:["ID","TAG"],attrHandle:{},createPseudo:Q,find:{ID:g?function(a,b,c){if(typeof b.getElementById!==j&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==j&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==j&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:W?function(a,b){if(typeof b.getElementsByTagName!==j)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(K,""),a[3]=(a[4]||a[5]||"").replace(K,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||Z.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&Z.error(a[0]),a},PSEUDO:function(a){var b,c=a[4];return L.CHILD.test(a[0])?null:(c&&(b=D.exec(c))&&b.pop()&&(a[0]=a[0].slice(0,b[0].length-c.length-1),c=b[0].slice(0,-1)),a.splice(2,3,c||a[3]),a)}},filter:{ID:g?function(a){return a=a.replace(K,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(K,""),function(b){var c=typeof b.getAttributeNode!==j&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(K,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=M[a];return b||(b=M[a]=new RegExp("(^|"+r+")"+a+"("+r+"|$)"),N.push(a),N.length>$.cacheLength&&delete M[N.shift()]),function(a){return b.test(a.className||typeof a.getAttribute!==j&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=Z.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return Z.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=m++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[q]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[q]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e=$.pseudos[a]||$.pseudos[a.toLowerCase()];return e||Z.error("unsupported pseudo: "+a),e.sizzleFilter?e(b,c,d):e}},pseudos:{not:Q(function(a,b,c){var d=bl(a.replace(A,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!$.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:Q(function(a){return function(b){return(b.textContent||b.innerText||bc(b)).indexOf(a)>-1}}),has:Q(function(a){return function(b){return Z(a,b).length>0}}),header:function(a){return I.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:R("radio"),checkbox:R("checkbox"),file:R("file"),password:R("password"),image:R("image"),submit:S("submit"),reset:S("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return J.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},odd:function(a,b,c){var d=[],e=c?0:1,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},lt:function(a,b,c){return c?a.slice(+b):a.slice(0,+b)},gt:function(a,b,c){return c?a.slice(0,+b+1):a.slice(+b+1)},eq:function(a,b,c){var d=a.splice(+b,1);return c?a:d}}};$.setFilters.nth=$.setFilters.eq,$.filters=$.pseudos,X||($.attrHandle={href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}}),V&&($.order.push("NAME"),$.find.NAME=function(a,b){if(typeof b.getElementsByName!==j)return b.getElementsByName(a)}),Y&&($.order.splice(1,0,"CLASS"),$.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!==j&&!c)return b.getElementsByClassName(a)});try{n.call(i.childNodes,0)[0].nodeType}catch(_){n=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}var ba=Z.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},bb=Z.contains=i.compareDocumentPosition?function(a,b){return!!(a.compareDocumentPosition(b)&16)}:i.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc=Z.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=bc(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=bc(b);return c};Z.attr=function(a,b){var c,d=ba(a);return d||(b=b.toLowerCase()),$.attrHandle[b]?$.attrHandle[b](a):U||d?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},Z.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},[0,0].sort(function(){return l=0}),i.compareDocumentPosition?e=function(a,b){return a===b?(k=!0,0):(!a.compareDocumentPosition||!b.compareDocumentPosition?a.compareDocumentPosition:a.compareDocumentPosition(b)&4)?-1:1}:(e=function(a,b){if(a===b)return k=!0,0;if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],g=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return f(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)g.unshift(j),j=j.parentNode;c=e.length,d=g.length;for(var l=0;l<c&&l<d;l++)if(e[l]!==g[l])return f(e[l],g[l]);return l===c?f(a,g[l],-1):f(e[l],b,1)},f=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),Z.uniqueSort=function(a){var b,c=1;if(e){k=l,a.sort(e);if(k)for(;b=a[c];c++)b===a[c-1]&&a.splice(c--,1)}return a};var bl=Z.compile=function(a,b,c){var d,e,f,g=O[a];if(g&&g.context===b)return g;e=bg(a,b,c);for(f=0;d=e[f];f++)e[f]=bj(d,b,c);return g=O[a]=bk(e),g.context=b,g.runs=g.dirruns=0,P.push(a),P.length>$.cacheLength&&delete O[P.shift()],g};Z.matches=function(a,b){return Z(a,null,null,b)},Z.matchesSelector=function(a,b){return Z(b,null,null,[a]).length>0};var bm=function(a,b,e,f,g){a=a.replace(A,"$1");var h,i,j,k,l,m,p,q,r,s=a.match(C),t=a.match(E),u=b.nodeType;if(L.POS.test(a))return bf(a,b,e,f,s);if(f)h=n.call(f,0);else if(s&&s.length===1){if(t.length>1&&u===9&&!g&&(s=L.ID.exec(t[0]))){b=$.find.ID(s[1],b,g)[0];if(!b)return e;a=a.slice(t.shift().length)}q=(s=G.exec(t[0]))&&!s.index&&b.parentNode||b,r=t.pop(),m=r.split(":not")[0];for(j=0,k=$.order.length;j<k;j++){p=$.order[j];if(s=L[p].exec(m)){h=$.find[p]((s[1]||"").replace(K,""),q,g);if(h==null)continue;m===r&&(a=a.slice(0,a.length-r.length)+m.replace(L[p],""),a||o.apply(e,n.call(h,0)));break}}}if(a){i=bl(a,b,g),d=i.dirruns++,h==null&&(h=$.find.TAG("*",G.test(a)&&b.parentNode||b));for(j=0;l=h[j];j++)c=i.runs++,i(l,b)&&e.push(l)}return e};h.querySelectorAll&&function(){var a,b=bm,c=/'|\\/g,d=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,e=[],f=[":active"],g=i.matchesSelector||i.mozMatchesSelector||i.webkitMatchesSelector||i.oMatchesSelector||i.msMatchesSelector;T(function(a){a.innerHTML="<select><option selected></option></select>",a.querySelectorAll("[selected]").length||e.push("\\["+r+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="<p test=''></p>",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+r+"*(?:\"\"|'')"),a.innerHTML="<input type='hidden'>",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bm=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return o.apply(f,n.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j=d.getAttribute("id"),k=j||q,l=G.test(a)&&d.parentNode||d;j?k=k.replace(c,"\\$&"):d.setAttribute("id",k);try{return o.apply(f,n.call(l.querySelectorAll(a.replace(C,"[id='"+k+"'] $&")),0)),f}catch(i){}finally{j||d.removeAttribute("id")}}return b(a,d,f,g,h)},g&&(T(function(b){a=g.call(b,"div");try{g.call(b,"[test!='']:sizzle"),f.push($.match.PSEUDO)}catch(c){}}),f=new RegExp(f.join("|")),Z.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!ba(b)&&!f.test(c)&&(!e||!e.test(c)))try{var h=g.call(b,c);if(h||a||b.document&&b.document.nodeType!==11)return h}catch(i){}return Z(c,null,null,[b]).length>0})}(),Z.attr=p.attr,p.find=Z,p.expr=Z.selectors,p.expr[":"]=p.expr.pseudos,p.unique=Z.uniqueSort,p.text=Z.getText,p.isXMLDoc=Z.isXML,p.contains=Z.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b<c;b++)if(p.contains(h[b],this))return!0});g=this.pushStack("","find",a);for(b=0,c=this.length;b<c;b++){d=g.length,p.find(a,this[b],g);if(b>0)for(e=d;e<g.length;e++)for(f=0;f<d;f++)if(g[f]===g[e]){g.splice(e--,1);break}}return g},has:function(a){var b,c=p(a,this),d=c.length;return this.filter(function(){for(b=0;b<d;b++)if(p.contains(this,c[b]))return!0})},not:function(a){return this.pushStack(bj(this,a,!1),"not",a)},filter:function(a){return this.pushStack(bj(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?bf.test(a)?p(a,this.context).index(this[0])>=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d<e;d++){c=this[d];while(c&&c.ownerDocument&&c!==b&&c.nodeType!==11){if(g?g.index(c)>-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/<tbody/i,br=/<|&#?\w+;/,bs=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,bu=new RegExp("<(?:"+bl+")[\\s/>]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,bz={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X<div>","</div>"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(f){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){return bh(this[0])?this.length?this.pushStack(p(p.isFunction(a)?a():a),"replaceWith",a):this:p.isFunction(a)?this.each(function(b){var c=p(this),d=c.html();c.replaceWith(a.call(this,b,d))}):(typeof a!="string"&&(a=p(a).detach()),this.each(function(){var b=this.nextSibling,c=this.parentNode;p(this).remove(),b?p(b).before(a):p(c).append(a)}))},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){a=[].concat.apply([],a);var e,f,g,h,i=0,j=a[0],k=[],l=this.length;if(!p.support.checkClone&&l>1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i<l;i++)d.call(c&&p.nodeName(this[i],"table")?bC(this[i],"tbody"):this[i],i===h?g:p.clone(g,!0,!0))}g=f=null,k.length&&p.each(k,function(a,b){b.src?p.ajax?p.ajax({url:b.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):p.error("no ajax"):p.globalEval((b.text||b.textContent||b.innerHTML||"").replace(by,"")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),p.buildFragment=function(a,c,d){var f,g,h,i=a[0];return c=c||e,c=(c[0]||c).ownerDocument||c[0]||c,typeof c.createDocumentFragment=="undefined"&&(c=e),a.length===1&&typeof i=="string"&&i.length<512&&c===e&&i.charAt(0)==="<"&&!bt.test(i)&&(p.support.checkClone||!bw.test(i))&&(p.support.html5Clone||!bu.test(i))&&(g=!0,f=p.fragments[i],h=f!==b),f||(f=c.createDocumentFragment(),p.clean(a,c,f,d),g&&(p.fragments[i]=h&&f)),{fragment:f,cacheable:g}},p.fragments={},p.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){p.fn[a]=function(c){var d,e=0,f=[],g=p(c),h=g.length,i=this.length===1&&this[0].parentNode;if((i==null||i&&i.nodeType===11&&i.childNodes.length===1)&&h===1)return g[b](this[0]),this;for(;e<h;e++)d=(e>0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=0,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(g=b===e&&bA;(h=a[s])!=null;s++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{g=g||bk(b),l=l||g.appendChild(b.createElement("div")),h=h.replace(bo,"<$1></$2>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]==="<table>"&&!m?l.childNodes:[];for(f=n.length-1;f>=0;--f)p.nodeName(n[f],"tbody")&&!n[f].childNodes.length&&n[f].parentNode.removeChild(n[f])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l=g.lastChild}h.nodeType?t.push(h):t=p.merge(t,h)}l&&(g.removeChild(l),h=l=g=null);if(!p.support.appendChecked)for(s=0;(h=t[s])!=null;s++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(s=0;(h=t[s])!=null;s++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[s+1,0].concat(r)),s+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^margin/,bO=new RegExp("^("+q+")(.*)$","i"),bP=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bQ=new RegExp("^([-+])=("+q+")","i"),bR={},bS={position:"absolute",visibility:"hidden",display:"block"},bT={letterSpacing:0,fontWeight:400,lineHeight:1},bU=["Top","Right","Bottom","Left"],bV=["Webkit","O","Moz","ms"],bW=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return bZ(this,!0)},hide:function(){return bZ(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bW.apply(this,arguments):this.each(function(){(c?a:bY(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bX(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bQ.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bX(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bT&&(f=bT[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(a,b){var c,d,e,f,g=getComputedStyle(a,null),h=a.style;return g&&(c=g[b],c===""&&!p.contains(a.ownerDocument.documentElement,a)&&(c=p.style(a,b)),bP.test(c)&&bN.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=c,c=g.width,h.width=d,h.minWidth=e,h.maxWidth=f)),c}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bP.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0||bH(a,"display")!=="none"?ca(a,b,d):p.swap(a,bS,function(){return ca(a,b,d)})},set:function(a,c,d){return b$(a,c,d?b_(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bP.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bU[d]+b]=e[d]||e[d-2]||e[0];return f}},bN.test(a)||(p.cssHooks[a+b].set=b$)});var cc=/%20/g,cd=/\[\]$/,ce=/\r?\n/g,cf=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,cg=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||cg.test(this.nodeName)||cf.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(ce,"\r\n")}}):{name:b.name,value:c.replace(ce,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ch(d,a[d],c,f);return e.join("&").replace(cc,"+")};var ci,cj,ck=/#.*$/,cl=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cm=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,cn=/^(?:GET|HEAD)$/,co=/^\/\//,cp=/\?/,cq=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,cr=/([?&])_=[^&]*/,cs=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,ct=p.fn.load,cu={},cv={},cw=["*/"]+["*"];try{ci=f.href}catch(cx){ci=e.createElement("a"),ci.href="",ci=ci.href}cj=cs.exec(ci.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&ct)return ct.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("<div>").append(a.replace(cq,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cA(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cA(a,b),a},ajaxSettings:{url:ci,isLocal:cm.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cw},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cy(cu),ajaxTransport:cy(cv),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cB(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cC(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cl.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(ck,"").replace(co,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=cs.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==cj[1]&&i[2]==cj[2]&&(i[3]||(i[1]==="http:"?80:443))==(cj[3]||(cj[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cz(cu,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!cn.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cp.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cr,"$1_="+z);l.url=A+(A===l.url?(cp.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cw+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cz(cv,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cD=[],cE=/\?/,cF=/(=)\?(?=&|$)|\?\?/,cG=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cD.pop()||p.expando+"_"+cG++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cF.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cF.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cF,"$1"+f):m?c.data=i.replace(cF,"$1"+f):k&&(c.url+=(cE.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cD.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cH,cI=a.ActiveXObject?function(){for(var a in cH)cH[a](0,1)}:!1,cJ=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cK()||cL()}:cK,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cI&&delete cH[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cJ,cI&&(cH||(cH={},p(a).unload(cI)),cH[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cM,cN,cO=/^(?:toggle|show|hide)$/,cP=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cQ=/queueHooks$/,cR=[cX],cS={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cP.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cV,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d<e;d++)c=a[d],cS[c]=cS[c]||[],cS[c].unshift(b)},prefilter:function(a,b){b?cR.unshift(a):cR.push(a)}}),p.Tween=cY,cY.prototype={constructor:cY,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(p.cssNumber[c]?"":"px")},cur:function(){var a=cY.propHooks[this.prop];return a&&a.get?a.get(this):cY.propHooks._default.get(this)},run:function(a){var b,c=cY.propHooks[this.prop];return this.pos=b=p.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration),this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):cY.propHooks._default.set(this),this}},cY.prototype.init.prototype=cY.prototype,cY.propHooks={_default:{get:function(a){var b;return a.elem[a.prop]==null||!!a.elem.style&&a.elem.style[a.prop]!=null?(b=p.css(a.elem,a.prop,!1,""),!b||b==="auto"?0:b):a.elem[a.prop]},set:function(a){p.fx.step[a.prop]?p.fx.step[a.prop](a):a.elem.style&&(a.elem.style[p.cssProps[a.prop]]!=null||p.cssHooks[a.prop])?p.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},cY.propHooks.scrollTop=cY.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},p.each(["toggle","show","hide"],function(a,b){var c=p.fn[b];p.fn[b]=function(d,e,f){return d==null||typeof d=="boolean"||!a&&p.isFunction(d)&&p.isFunction(e)?c.apply(this,arguments):this.animate(cZ(b,!0),d,e,f)}}),p.fn.extend({fadeTo:function(a,b,c,d){return this.filter(bY).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=p.isEmptyObject(a),f=p.speed(b,c,d),g=function(){var b=cV(this,p.extend({},a),f);e&&b.stop(!0)};return e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,c,d){var e=function(a){var b=a.stop;delete a.stop,b(d)};return typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,c=a!=null&&a+"queueHooks",f=p.timers,g=p._data(this);if(c)g[c]&&g[c].stop&&e(g[c]);else for(c in g)g[c]&&g[c].stop&&cQ.test(c)&&e(g[c]);for(c=f.length;c--;)f[c].elem===this&&(a==null||f[c].queue===a)&&(f[c].anim.stop(d),b=!1,f.splice(c,1));(b||!d)&&p.dequeue(this,a)})}}),p.each({slideDown:cZ("show"),slideUp:cZ("hide"),slideToggle:cZ("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){p.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),p.speed=function(a,b,c){var d=a&&typeof a=="object"?p.extend({},a):{complete:c||!c&&b||p.isFunction(a)&&a,duration:a,easing:c&&b||b&&!p.isFunction(b)&&b};d.duration=p.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in p.fx.speeds?p.fx.speeds[d.duration]:p.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";return d.old=d.complete,d.complete=function(){p.isFunction(d.old)&&d.old.call(this),d.queue&&p.dequeue(this,d.queue)},d},p.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},p.timers=[],p.fx=cY.prototype.init,p.fx.tick=function(){var a,b=p.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||p.fx.stop()},p.fx.timer=function(a){a()&&p.timers.push(a)&&!cN&&(cN=setInterval(p.fx.tick,p.fx.interval))},p.fx.interval=13,p.fx.stop=function(){clearInterval(cN),cN=null},p.fx.speeds={slow:600,fast:200,_default:400},p.fx.step={},p.expr&&p.expr.filters&&(p.expr.filters.animated=function(a){return p.grep(p.timers,function(b){return a===b.elem}).length});var c$=/^(?:body|html)$/i;p.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){p.offset.setOffset(this,a,b)});var c,d,e,f,g,h,i,j,k,l,m=this[0],n=m&&m.ownerDocument;if(!n)return;return(e=n.body)===m?p.offset.bodyOffset(m):(d=n.documentElement,p.contains(d,m)?(c=m.getBoundingClientRect(),f=c_(n),g=d.clientTop||e.clientTop||0,h=d.clientLeft||e.clientLeft||0,i=f.pageYOffset||d.scrollTop,j=f.pageXOffset||d.scrollLeft,k=c.top+i-g,l=c.left+j-h,{top:k,left:l}):{top:0,left:0})},p.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;return p.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(p.css(a,"marginTop"))||0,c+=parseFloat(p.css(a,"marginLeft"))||0),{top:b,left:c}},setOffset:function(a,b,c){var d=p.css(a,"position");d==="static"&&(a.style.position="relative");var e=p(a),f=e.offset(),g=p.css(a,"top"),h=p.css(a,"left"),i=(d==="absolute"||d==="fixed")&&p.inArray("auto",[g,h])>-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c$.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c$.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=c_(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window);
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/notificationBindings.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE bindings [
+<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
+%notificationDTD;
+]>
+
+<bindings id="popupBindings"
+   xmlns="http://www.mozilla.org/xbl"
+   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+   xmlns:xbl="http://www.mozilla.org/xbl">
+
+  <binding id="testpilot-notification"
+           extends="chrome://global/content/bindings/notification.xml#popup-notification">
+
+    <!-- Remember to keep this up to date with the changes to base binding
+         at http://hg.mozilla.org/mozilla-central/file/tip/toolkit/content/widgets/notification.xml-->
+    <content align="start">
+      <xul:image class="popup-notification-icon" anonid="notification-icon"
+                 xbl:inherits="popupid"/>
+      <xul:vbox flex="1">
+        <xul:label anonid="notification-title" class="testpilot-notification-title"/>
+        <xul:description class="popup-notification-description"
+                         xbl:inherits="xbl:text=label"/>
+        <xul:spacer flex="1"/>
+        <xul:hbox class="popup-notification-button-container"
+                  pack="end" align="center">
+          <xul:button anonid="button"
+                      class="popup-notification-menubutton"
+                      type="menu-button"
+                      xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
+            <xul:menupopup anonid="menupopup"
+                           xbl:inherits="oncommand=menucommand">
+              <children/>
+              <xul:menuitem class="menuitem-iconic popup-notification-closeitem"
+                            label="&closeNotificationItem.label;"
+                            xbl:inherits="oncommand=closeitemcommand"/>
+            </xul:menupopup>
+          </xul:button>
+
+        </xul:hbox>
+      </xul:vbox>
+      <xul:vbox pack="start">
+        <xul:toolbarbutton anonid="closebutton"
+                           class="messageCloseButton popup-notification-closebutton tabbable"
+                           xbl:inherits="oncommand=closebuttoncommand"
+                           tooltiptext="&closeNotification.tooltip;"/>
+      </xul:vbox>
+    </content>
+    <implementation>
+      <constructor><![CDATA[
+        this.title.value = this.notification.options.title;
+        this.icon.classList.add(this.notification.options.iconClass);
+        this.closebutton.addEventListener("command",
+                                          this.notification.options.closeButtonFunc,
+                                          false);
+        ]]>
+      </constructor>
+      <field name="title" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "notification-title");
+      </field>
+      <field name="icon" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "notification-icon");
+      </field>
+    </implementation>
+  </binding>
+</bindings>
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/screen.css
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/screen.css
@@ -1,179 +1,158 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html {
-	padding-bottom: 0px; 
+	padding-bottom: 0px;
 	padding-top: 20px;
 	padding-left: 0px;
 	padding-right: 0px;
 }
 
-body { 
+body {
 	font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif;
-	font-family: 'DroidSans';
 	background: #fff url('chrome://testpilot/skin/bg.jpg') repeat-x top center;
-	padding: 0px; 
+	padding: 0px;
 	color:#787878;
 	font-size:12px;
 	line-height: 18px;
 	margin: 0 auto;
-	
+
 }
 
-	h1 {font-family: 'DroidSans'; color: #3a3a3a; font-size: 28px; font-weight: normal; letter-spacing: -1px}
-	h2 {font-family: 'DroidSans'; color: #54717b; font-size: 24px; line-height: 22px;font-weight: normal; letter-spacing: -1px}
-	
+	h1 {font-family: 'sans-serif'; color: #3a3a3a; font-size: 28px; font-weight: normal; letter-spacing: -1px}
+	h2 {font-family: 'sans-serif'; color: #54717b; font-size: 24px; line-height: 22px;font-weight: normal; letter-spacing: -1px}
+
 	p, ul {font-size: 12px;}
-	
+
 	a:link, a:hover, a:active, a:focus, a:visited {color: #9f423b; text-decoration: none;}
 	a:hover {color: #9f423b; text-decoration: none;}
-	
-	.bold {font-family: 'DroidSans-Bold'; color:#3a3a3a;}
+
+	.bold {color:#3a3a3a;}
 	.address {margin-left: 20px;}
 	.inactive {color:#ccc;}
-	
+
 	li {list-style-type: circle;}
 
 	.spacer {height: 75px;}
 
 
-@font-face{
-font-family: 'DroidSans'; 
-src: url('chrome://testpilot/skin/fonts/DroidSans.ttf') format('truetype');
-}
-
-@font-face{
-font-family: 'DroidSans-Bold'; 
-src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
-}
-
 #container {
 	margin: 0px auto;
 	width: 950px;
 
 }
 
 #logo {
 	margin-left: 20px;
 }
 
 #contentWelcome {
 	margin-top: 120px;
 	padding: 8px 24px 24px 24px;
-	font-family: 'DroidSans';
+	font-family: 'sans-serif';
 	text-align: left;
 }
 
 #content {
 	margin-top: 60px;
 	padding: 8px 24px 24px 24px;
-	font-family: 'DroidSans';
+	font-family: 'sans-serif';
 	text-align: left;
 }
 
 #download {
 	width: 300px;
 	margin-left: 410px;
 	margin-top: 240px;
 }
 
-.downloadButton {
-	margin-bottom: -10px;
-	margin-top: -2px;
-	margin-right: 12px;
-}
-
-.downloadH1 {font-family: 'DroidSans-bold'; color: #3a3a3a; font-size: 24px; font-weight: normal; letter-spacing: -1px; margin-right: 8px;}
-
-.downloadH2 {font-family: 'DroidSans'; color: #3a3a3a; font-size: 22px; font-weight: normal; letter-spacing: -1px; line-height: 28px; margin-left: 46px;}
-
 #intro {
 	float: left;
 	width: 500px;
 	margin-top: 30px;
 }
 
 #links {
 	float: right;
 	padding: 24px 0px;
 	margin-right: 0px;
 	margin-top: 30px;
 }
 
 .button {
-	font-family: 'DroidSans';
+	font-family: 'sans-serif';
 	font-size: 16px;
 	padding: 8px 12px;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
-	box-shadow: 
+	box-shadow:
 		inset rgba(0, 0, 0, 0.2) 0 1px 1px,
 		inset rgba(255, 255, 255, 1) 0 3px 1px,
 		inset rgba(255, 255, 255, 0.3) 0 16px 0px,
 		inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
-		inset rgba(0, 0, 0, 0.1) 0 -2px 1px, 
+		inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
 		rgba(255, 255, 255, 1) 0 1px,
 		rgba(133, 153, 166, 0.3) 0px 1px 12px;
 	background-color: #e7eaec;
-	//display: inline;
+	/*display: inline;*/
 }
 
 .home_button {
-	font-family: 'DroidSans';
+	font-family: 'sans-serif';
 	font-size: 16px;
 	padding: 8px 12px;
 	width: 240px;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
-	box-shadow: 
+	box-shadow:
 		inset rgba(0, 0, 0, 0.2) 0 1px 1px,
 		inset rgba(255, 255, 255, 1) 0 3px 1px,
 		inset rgba(255, 255, 255, 0.3) 0 16px 0px,
 		inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
-		inset rgba(0, 0, 0, 0.1) 0 -2px 1px, 
+		inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
 		rgba(255, 255, 255, 1) 0 1px,
 		rgba(133, 153, 166, 0.3) 0px 1px 12px;
 	background-color: #e7eaec;
-	//display: inline;
+	/*display: inline;*/
 }
 
 
 .callout {
-	font-family: 'DroidSans';
+	font-family: 'sans-serif';
 	font-size: 16px;
 	padding: 8px 24px;
 	margin: 24px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/callout.png') no-repeat top center;
-	box-shadow: 
+	box-shadow:
 		inset rgba(185, 221, 234, 0.2) 0 -10px 12px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 12px;
-	//display: inline;
+	/*display: inline;*/
 }
 
 .home_callout {
-	font-family: 'DroidSans';
+	font-family: 'sans-serif';
 	font-size: 16px;
 	vertical-align: middle;
 	width: 240px;
 	padding: 8px 24px;
 	margin: 8px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/callout.png') no-repeat top center;
-	box-shadow: 
+	box-shadow:
 		inset rgba(185, 221, 234, 0.2) 0 -10px 12px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 12px;
-	//display: inline;
+	/*display: inline;*/
 }
 
 .homeIcon {
 	margin-top: -32px;
 	margin-bottom: -32px;
 	margin-right: 12px;
 }
 
@@ -184,32 +163,32 @@ src: url('chrome://testpilot/skin/fonts/
 
 }
 
 .mozLogo {
 	margin-bottom: -10px;
 	margin-right: 6px;
 }
 
-/* ------- MENU ------- 
+/* ------- MENU -------
 
 #menu {
 	margin: 20px auto;
 	max-width: 800px;
 	padding: 4px 40px;
 	width: 800px;
 	text-align: left;
 	border-radius: 0.25em;
 	border-top: 1px solid #adb6ba;
 	border-left: 1px solid #adb6ba;
 	border-right: 1px solid #adb6ba;
 	border-bottom: 3px solid #adb6ba;
 	-moz-border-bottom-colors:#adb6ba #e7eaec #e7eaec;
 	background-color: #fff;
-} 
+}
 
 */
 
 .menuItem {
 	margin-right: 30px;
 	margin-bottom: 40px;
 	font-size: 14px;
 	text-shadow: 1px 1px 1px rgba(173, 182, 186, 0.6);
@@ -217,17 +196,17 @@ src: url('chrome://testpilot/skin/fonts/
 }
 
 .menuOn {
 	margin-right: 30px;
 	margin-bottom: 40px;
 	font-size: 14px;
 	text-shadow: 1px 1px 1px rgba(173, 182, 186, 1);
 	background-color: rgba(173, 182, 186, 0.3);
-	box-shadow: 
+	box-shadow:
 		inset rgba(0, 0, 0, 0.2) 0 -10px 12px;
 	padding: 9px 8px 8px 8px;
 }
 
 .menuItem a {color: #9f423b; text-decoration: none;}
 .menuItem a:hover {color: #9f423b; text-decoration: none; border-bottom: 1px dotted #9f423b;}
 
 
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/tp-browser-customNotifications.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+  <!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
+    %testpilotDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<statusbar id="status-bar">
+  <statusbarpanel id="pilot-notifications-button"
+                  class="statusbarpanel-iconic"
+                  insertbefore="security-button"
+                  onmousedown="
+                    event.preventDefault();
+                    TestPilotMenuUtils.onMenuButtonMouseDown();"
+                  image="chrome://testpilot/skin/testpilot_16x16.png"/>
+
+  <panel id="pilot-notification-popup" hidden="true" noautofocus="true"
+    level="parent" position="after_start">
+    <vbox class="pilot-notification-popup-container">
+      <hbox class="pilot-notification-toprow">
+        <image id="pilot-notification-icon" />
+        <vbox pack="center">
+          <label id="pilot-notification-title" class="pilot-title" />
+        </vbox>
+        <spacer flex="1" />
+        <vbox pack="start">
+          <image id="pilot-notification-close"
+            tooltiptext="&testpilot.notification.close.tooltip;" />
+        </vbox>
+      </hbox>
+      <description id="pilot-notification-text" />
+      <hbox align="right"><label id="pilot-notification-link" /></hbox>
+      <hbox>
+        <checkbox id="pilot-notification-always-submit-checkbox"
+          label="&testpilot.settings.alwaysSubmitData.shortLabel;" />
+        <spacer flex="1" />
+      </hbox>
+      <hbox align="right">
+        <button id="pilot-notification-submit" />
+      </hbox>
+    </vbox>
+  </panel>
+</statusbar>
+</overlay>
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/tp-browser-popupNotifications.xul
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://testpilot/content/browser.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+  <!ENTITY % testpilotDTD SYSTEM "chrome://testpilot/locale/main.dtd">
+    %testpilotDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <!-- anchor for the fx4 style notifications -->
+  <toolbar id="nav-bar">
+    <box id="tp-notification-popup-box" hidden="true" align="center">
+      <image id="tp-notification-popup-icon" class="notification-anchor-icon" role="button"/>
+    </box>
+    <panel id="testpilot-notification-popup" type="arrow" position="after_start"
+           hidden="true" orient="vertical" role="alert">
+    </panel>
+  </toolbar>
+</overlay>
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/tp-browser.xul
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/tp-browser.xul
@@ -49,45 +49,38 @@
     </menu>
     <menuitem label="&testpilot.allYourStudies.label;"
               oncommand="TestPilotWindowUtils.openAllStudiesWindow();"/>
     <menuitem label="&testpilot.about.label;"
               oncommand="TestPilotWindowUtils.openHomepage();"/>
   </menupopup>
 </menu>
 
-<statusbar id="status-bar">
-  <statusbarpanel id="pilot-notifications-button"
-                  class="statusbarpanel-iconic"
-                  insertbefore="security-button"
-                  onmousedown="
-                    event.preventDefault();
-                    TestPilotMenuUtils.onMenuButtonMouseDown();"
-                  image="chrome://testpilot/skin/testpilot_16x16.png"/>
-
+<toolbar id="nav-bar">
+  <image id="tp-notification-popup-icon" role="button" hidden="true"/>
   <panel id="pilot-notification-popup" hidden="true" noautofocus="true"
     level="parent" position="after_start">
     <vbox class="pilot-notification-popup-container">
       <hbox class="pilot-notification-toprow">
         <image id="pilot-notification-icon" />
         <vbox pack="center">
           <label id="pilot-notification-title" class="pilot-title" />
         </vbox>
         <spacer flex="1" />
         <vbox pack="start">
           <image id="pilot-notification-close"
             tooltiptext="&testpilot.notification.close.tooltip;" />
         </vbox>
       </hbox>
       <description id="pilot-notification-text" />
-      <hbox align="right"><label id="pilot-notification-link" /></hbox>
+      <hbox align="right"><label id="pilot-notification-link"/></hbox>
       <hbox>
         <checkbox id="pilot-notification-always-submit-checkbox"
           label="&testpilot.settings.alwaysSubmitData.label;" />
         <spacer flex="1" />
       </hbox>
       <hbox align="right">
         <button id="pilot-notification-submit" />
       </hbox>
     </vbox>
   </panel>
-</statusbar>
+</toolbar>
 </overlay>
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/welcome-page.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/welcome-page.js
@@ -44,21 +44,21 @@ var TestPilotWelcomePage = {
         stringKey: "testpilot.welcomePage.pleaseTake" },
       { id: "background-survey-text",
         stringKey: "testpilot.welcomePage.backgroundSurvey" },
       { id: "open-studies-window-link",
         stringKey: "testpilot.welcomePage.clickToOpenStudiesWindow" },
       { id: "testpilot-addon-text",
         stringKey: "testpilot.welcomePage.testpilotAddon" },
       { id: "icon-explanation-text",
-        stringKey: "testpilot.welcomePage.iconExplanation" },
+        stringKey: "testpilot.welcomePage.iconExplanation2" },
       { id: "icon-explanation-more-text",
-        stringKey: "testpilot.welcomePage.moreIconExplanation" },
+        stringKey: "testpilot.welcomePage.moreIconExplanation2" },
       { id: "notification-info-text",
-	stringKey: "testpilot.welcomePage.notificationInfo" },
+	stringKey: "testpilot.welcomePage.notificationInfo2" },
       { id: "privacy-policy-link",
 	stringKey: "testpilot.welcomePage.privacyPolicy" },
       { id: "legal-notices-link",
 	stringKey: "testpilot.welcomePage.legalNotices" }
       ];
 
     let mapLength = map.length;
     for (let i = 0; i < mapLength; i++) {
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/install.rdf.in
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/install.rdf.in
@@ -5,29 +5,29 @@
 
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>testpilot@labs.mozilla.com</em:id>
-    <em:version>1.1.2</em:version>
+    <em:version>1.2.2</em:version>
     <em:type>2</em:type>
 
-    <!-- Target Application this extension can install into, 
-         with minimum and maximum supported versions. --> 
+    <!-- Target Application this extension can install into,
+         with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>3.5</em:minVersion>
         <em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
       </Description>
     </em:targetApplication>
-   
+
     <!-- Front End MetaData -->
     <em:name>Feedback</em:name>
     <em:description>Help make Firefox better by giving feedback.</em:description>
     <em:creator>Mozilla Corporation</em:creator>
     <em:homepageURL>http://testpilot.mozillalabs.com/</em:homepageURL>
     <em:iconURL>chrome://testpilot/skin/dino_32x32.png</em:iconURL>
   </Description>
 </RDF>
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/locale/en-US/main.dtd
@@ -0,0 +1,33 @@
+<!ENTITY testpilot.brand.label                      "Test Pilot">
+<!ENTITY testpilot.settings.label                   "Settings">
+<!ENTITY testpilot.settings.dataSubmission.label    "Data Submission">
+<!ENTITY testpilot.settings.notifications.label     "Notifications">
+<!ENTITY testpilot.settings.notifyWhen.label        "Notify me when…">
+<!ENTITY testpilot.settings.readyToSubmit.label     "A study is ready to submit">
+<!ENTITY testpilot.settings.newStudy.label          "There's a new study">
+<!ENTITY testpilot.settings.hasNewResults.label     "A study has new results">
+<!ENTITY testpilot.settings.alwaysSubmitData.label  "Automatically submit my data (don't ask me)">
+<!ENTITY testpilot.allYourStudies.label             "All Your User Studies…">
+<!ENTITY testpilot.about.label                      "About Test Pilot">
+
+<!-- all studies window -->
+<!ENTITY testpilot.studiesWindow.title                  "Your Test Pilot Studies">
+<!ENTITY testpilot.studiesWindow.currentStudies.label   "Current Studies">
+<!ENTITY testpilot.studiesWindow.finishedStudies.label  "Finished Studies">
+<!ENTITY testpilot.studiesWindow.studyFindings.label    "Study Findings">
+<!ENTITY testpilot.studiesWindow.settings.label         "Settings">
+<!ENTITY testpilot.studiesWindow.stillLoadingMessage    "Loading, please wait…">
+
+<!-- raw data dialog -->
+<!ENTITY testpilot.rawDataWindow.title                  "Test Pilot: Raw Data">
+
+<!-- notification -->
+<!ENTITY testpilot.notification.close.tooltip            "Close">
+
+<!-- Firefox 4 beta version UI -->
+<!ENTITY testpilot.enable.label                        "Turn On User Studies">
+<!ENTITY testpilot.feedbackbutton.label                "Feedback">
+<!ENTITY testpilot.happy.label        "Firefox Made Me Happy Because…">
+<!ENTITY testpilot.sad.label          "Firefox Made Me Sad Because…">
+<!ENTITY testpilot.broken.label      "Report this website as broken…">
+<!ENTITY testpilot.idea.label        "Give us a suggestion…">
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/locale/en-US/main.properties
@@ -0,0 +1,98 @@
+# description for add-on manager
+extensions.testpilot@labs.mozilla.com.description = Help make Firefox better by running user studies.
+
+# common
+testpilot.fullBrandName = Mozilla Labs Test Pilot
+testpilot.moreInfo = More Info
+testpilot.submit = Submit
+testpilot.takeSurvey = Take the Survey
+
+# Feedback button menu
+testpilot.turnOn = Turn On User Studies
+testpilot.turnOff = Turn Off User Studies
+
+# studies window
+testpilot.studiesWindow.noStudies = We are working on a new study now; it will knock on your door soon! Stay Tuned!
+testpilot.studiesWindow.uploading = Uploading…
+testpilot.studiesWindow.unableToReachServer = Unable to reach Mozilla; please try again later.
+testpilot.studiesWindow.thanksForContributing = Thanks for contributing!
+testpilot.studiesWindow.finishedOn = Finished on %S
+testpilot.studiesWindow.canceledStudy = (You canceled this study)
+testpilot.studiesWindow.missedStudy = (You missed this study)
+testpilot.studiesWindow.willStart = Will start on %S
+testpilot.studiesWindow.gatheringData = Currently gathering data.
+testpilot.studiesWindow.willFinish = Will finish on %S
+testpilot.studiesWindow.proposeStudy = Propose your own study
+
+# for pages
+testpilot.page.commentsAndDiscussions = Comments & Discussions »
+testpilot.page.proposeATest = Propose a Test »
+testpilot.page.testpilotOnTwitter = @MozTestPilot on Twitter »
+
+# status page
+testpilot.statusPage.uploadingData = Now uploading data…
+testpilot.statusPage.uploadErrorMsg = Oops! There was an error connecting to the Mozilla servers.  Maybe your network connection is down?
+testpilot.statusPage.willRetry = Test Pilot will retry automatically, so it's OK to close this page now.
+testpilot.statusPage.endedAlready = (It has already ended and you should not be seeing this page)
+testpilot.statusPage.todayAt =  today, at %S
+testpilot.statusPage.endOn = on %S
+# LOCALIZATION NOTE (numExtensions): Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 = number of extensions
+# example: "2 extensions"
+testpilot.statusPage.numExtensions = #1 extension;#1 extensions
+testpilot.statusPage.recursEveryNumberOfDays = This test recurs every %S days. Each time it completes:
+testpilot.statusPage.askMeBeforeSubmitData = Ask me whether I want to submit my data.
+testpilot.statusPage.alwaysSubmitData = Always submit my data, and don't ask me about it.
+testpilot.statusPage.neverSubmitData = Never submit my data, and don't ask me about it.
+testpilot.statusPage.loading = Loading, please wait a moment…
+
+# quit page
+testpilot.quitPage.aboutToQuit = You are about to quit the "%S" study.
+testpilot.quitPage.optionalMessage = (Optional) If you have a minute, please let us know why you have chosen to quit the study.
+testpilot.quitPage.reason = Reason:
+testpilot.quitPage.recurringStudy = This is a recurring study. Normally we will let you know the next time we run the study.  If you never want to hear about this study again, check the box below:
+testpilot.quitPage.quitForever = Quit this recurring study.
+testpilot.quitPage.quitStudy = Quit the Study »
+
+# welcome page
+testpilot.welcomePage.thankYou = Thank You for Joining the Test Pilot Team!
+testpilot.welcomePage.gettingStarted = Getting Started
+testpilot.welcomePage.pleaseTake = Please take the
+testpilot.welcomePage.backgroundSurvey = Pilot Background Survey
+testpilot.welcomePage.clickToOpenStudiesWindow = Click here to see the studies that are currently running.
+testpilot.welcomePage.testpilotAddon = Test Pilot Add-on
+testpilot.welcomePage.iconExplanation2 = « look for this icon in the Tools menu to find the Test Pilot sub-menu.
+testpilot.welcomePage.moreIconExplanation2 = Use the sub-menu to control settings and see what studies are running.
+testpilot.welcomePage.notificationInfo2 = Test Pilot will pop up a notification when a study needs your attention.
+testpilot.welcomePage.privacyPolicy = Privacy Policy
+testpilot.welcomePage.legalNotices = Legal Notices
+
+# survey page
+testpilot.surveyPage.saveAnswers = Save Answers
+testpilot.surveyPage.submitAnswers = Submit Answers
+testpilot.surveyPage.changeAnswers = Change Answers
+testpilot.surveyPage.loading = Loading, please wait a moment…
+testpilot.surveyPage.thankYouForFinishingSurvey = Thank you for finishing this survey. Your answers will be uploaded along with the next set of experimental data.
+testpilot.surveyPage.reviewOrChangeYourAnswers = If you would like to review or change your answers, you can do so at any time using the button below.
+
+# modules/task.js
+testpilot.finishedTask.finishedStudy = Excellent! You finished the "%S" Study!
+testpilot.finishedTask.allRelatedDataDeleted = All data related to this study has been removed from your computer.
+
+# modules/setup.js
+testpilot.notification.update = Update…
+testpilot.notification.thankYouForUploadingData = Thanks!
+testpilot.notification.thankYouForUploadingData.message = Thank you for uploading your data.
+testpilot.notification.readyToSubmit = Ready to Submit
+testpilot.notification.readyToSubmit.message = The Test Pilot "%S" study is finished gathering data and is ready to submit.
+testpilot.notification.newTestPilotStudy = New Test Pilot Study
+testpilot.notification.newTestPilotStudy.pre.message = The Test Pilot "%S" study is about to begin.
+testpilot.notification.newTestPilotSurvey = New Test Pilot Survey
+testpilot.notification.newTestPilotSurvey.message = %S
+testpilot.notification.newTestPilotResults = New Test Pilot Results
+testpilot.notification.newTestPilotResults.message = New results are now available for the Test Pilot "%S" study.
+testpilot.notification.autoUploadedData = Thank you!
+testpilot.notification.autoUploadedData.message = The Test Pilot "%S" study is completed and your data has been submitted!
+testpilot.notification.extensionUpdate = Extension Update
+testpilot.notification.extensionUpdate.message = One of your studies requires a newer version of Test Pilot. You can get the latest version using the Add-ons window.
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/interface.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/interface.js
@@ -10,35 +10,61 @@
  */
 
 // A lot of the stuff that's currently in browser.js can get moved here.
 
 EXPORTED_SYMBOLS = ["TestPilotUIBuilder"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cu = Components.utils;
 const UPDATE_CHANNEL_PREF = "app.update.channel";
+const POPUP_SHOW_ON_NEW = "extensions.testpilot.popup.showOnNewStudy";
+const POPUP_CHECK_INTERVAL = "extensions.testpilot.popup.delayAfterStartup";
 
 var TestPilotUIBuilder = {
-  __prefs: null,
   get _prefs() {
-    this.__prefs = Cc["@mozilla.org/preferences-service;1"]
+    delete this._prefs;
+    return this._prefs = Cc["@mozilla.org/preferences-service;1"]
       .getService(Ci.nsIPrefBranch);
-    return this.__prefs;
+  },
+
+  get _prefDefaultBranch() {
+    delete this._prefDefaultBranch;
+    return this._prefDefaultBranch = Cc["@mozilla.org/preferences-service;1"]
+      .getService(Ci.nsIPrefService).getDefaultBranch("");
+  },
+
+  get _comparator() {
+    delete this._comparator;
+    return this._comparator = Cc["@mozilla.org/xpcom/version-comparator;1"]
+      .getService(Ci.nsIVersionComparator);
+  },
+
+  get _appVersion() {
+    delete this._appVersion;
+    return this._appVersion = Cc["@mozilla.org/xre/app-info;1"]
+      .getService(Ci.nsIXULAppInfo).version;
   },
 
   buildTestPilotInterface: function(window) {
     // Don't need Feedback button: remove it
     let feedbackButton = window.document.getElementById("feedback-menu-button");
     if (!feedbackButton) {
       let toolbox = window.document.getElementById("navigator-toolbox");
       let palette = toolbox.palette;
       feedbackButton = palette.getElementsByAttribute("id", "feedback-menu-button").item(0);
     }
     feedbackButton.parentNode.removeChild(feedbackButton);
+
+    /* Default prefs for test pilot version - default to NOT notifying user about new
+     * studies starting. Note we're setting default values, not current values -- we
+     * want these to be overridden by any user set values!!*/
+    this._prefDefaultBranch.setBoolPref(POPUP_SHOW_ON_NEW, false);
+    this._prefDefaultBranch.setIntPref(POPUP_CHECK_INTERVAL, 180000);
   },
 
   buildFeedbackInterface: function(window) {
     /* If this is first run, and it's ffx4 beta version, and the feedback
      * button is not in the expected place, put it there!
      * (copied from MozReporterButtons extension) */
 
     /* Check if we've already done this customization -- if not, don't do it
@@ -57,59 +83,51 @@ var TestPilotUIBuilder = {
       window.document.persist("nav-bar", "currentset");
       this._prefs.setBoolPref(pref, true);
       // if you don't do the following call, funny things happen.
       try {
         window.BrowserToolboxCustomizeDone(true);
       } catch (e) {
       }
     }
+
+    /* Pref defaults for Feedback version: default to notifying user about new
+     * studies starting. Note we're setting default values, not current values -- we
+     * want these to be overridden by any user set values!!*/
+    this._prefDefaultBranch.setBoolPref(POPUP_SHOW_ON_NEW, true);
+    this._prefDefaultBranch.setIntPref(POPUP_CHECK_INTERVAL, 600000);
   },
 
-  isBetaChannel: function() {
+  channelUsesFeedback: function() {
     // Beta and aurora channels use feedback interface; nightly and release channels don't.
-    let channel = this._prefs.getCharPref(UPDATE_CHANNEL_PREF);
+    let channel = this._prefDefaultBranch.getCharPref(UPDATE_CHANNEL_PREF);
     return (channel == "beta") || (channel == "betatest") || (channel == "aurora");
   },
 
   appVersionIsFinal: function() {
     // Return true iff app version >= 4.0 AND there is no "beta" or "rc" in version string.
-    let appInfo = Cc["@mozilla.org/xre/app-info;1"]
-      .getService(Ci.nsIXULAppInfo);
-    let version = appInfo.version;
-    let versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
-      .getService(Components.interfaces.nsIVersionComparator);
-    if (versionChecker.compare(version, "4.0") >= 0) {
-      if (version.indexOf("b") == -1 && version.indexOf("rc") == -1) {
+    if (this._comparator.compare(this._appVersion, "4.0") >= 0) {
+      if (this._appVersion.indexOf("b") == -1 && this._appVersion.indexOf("rc") == -1) {
         return true;
       }
     }
     return false;
   },
 
   buildCorrectInterface: function(window) {
     let firefoxnav = window.document.getElementById("nav-bar");
     /* This is sometimes called for windows that don't have a navbar - in
      * that case, do nothing. TODO maybe this should be in onWindowLoad?*/
     if (!firefoxnav) {
       return;
     }
 
-    /* Overlay Feedback XUL if we're in the beta update channel, Test Pilot XUL otherwise.
-     * Once the overlay is complete, call buildFeedbackInterface() or buildTestPilotInterface(). */
-    let self = this;
-    if (this.isBetaChannel()) {
-      window.document.loadOverlay("chrome://testpilot/content/feedback-browser.xul",
-                                  {observe: function(subject, topic, data) {
-                                     if (topic == "xul-overlay-merged") {
-                                       self.buildFeedbackInterface(window);
-                                     }
-                                   }});
+    /* Overlay Feedback XUL if we're in the beta update channel, Test Pilot XUL otherwise, and
+     * call buildFeedbackInterface() or buildTestPilotInterface(). */
+    if (this.channelUsesFeedback()) {
+      window.document.loadOverlay("chrome://testpilot/content/feedback-browser.xul", null);
+      this.buildFeedbackInterface(window);
     } else {
-      window.document.loadOverlay("chrome://testpilot/content/tp-browser.xul",
-                                  {observe: function(subject, topic, data) {
-                                     if (topic == "xul-overlay-merged") {
-                                       self.buildTestPilotInterface(window);
-                                     }
-                                  }});
+      window.document.loadOverlay("chrome://testpilot/content/tp-browser.xul", null);
+      this.buildTestPilotInterface(window);
     }
   }
 };
\ No newline at end of file
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/jar-code-store.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/jar-code-store.js
@@ -66,17 +66,17 @@ JarStore.prototype = {
   _verifyJar: function(jarFile, expectedHash) {
     // Compare the jar file's hash to the expected hash from the
     // index file.
     // from https://developer.mozilla.org/en/nsICryptoHash#Computing_the_Hash_of_a_File
     console.info("Attempting to verify jarfile vs hash = " + expectedHash);
     let istream = Cc["@mozilla.org/network/file-input-stream;1"]
                         .createInstance(Ci.nsIFileInputStream);
     // open for reading
-    istream.init(jarFile, 0x01, 0444, 0);
+    istream.init(jarFile, 0x01, parseInt("0444", 8), 0);
     let ch = Cc["@mozilla.org/security/hash;1"]
                    .createInstance(Ci.nsICryptoHash);
     // Use SHA256, it's more secure than MD5:
     ch.init(ch.SHA256);
     // this tells updateFromStream to read the entire file
     const PR_UINT32_MAX = 0xffffffff;
     ch.updateFromStream(istream, PR_UINT32_MAX);
     // pass false here to get binary data back
@@ -107,17 +107,17 @@ JarStore.prototype = {
       // If a file of that name already exists, remove it!
       if (jarFile.exists()) {
         jarFile.remove(false);
       }
       // From https://developer.mozilla.org/en/Code_snippets/File_I%2f%2fO#Getting_special_files
       jarFile.create( Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
       let stream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
                       createInstance(Ci.nsIFileOutputStream);
-      stream.init(jarFile, 0x04 | 0x08 | 0x20, 0600, 0); // readwrite, create, truncate
+      stream.init(jarFile, 0x04 | 0x08 | 0x20, parseInt("0600", 8), 0); // readwrite, create, truncate
       stream.write(rawData, rawData.length);
       if (stream instanceof Ci.nsISafeOutputStream) {
         stream.finish();
       } else {
         stream.close();
       }
       // Verify hash; if it's good, index and set last modified time.
       // If not good, remove it.
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/lib/file.js
@@ -0,0 +1,73 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+(function(global) {
+   const Cc = Components.classes;
+   const Ci = Components.interfaces;
+   const Cu = Components.utils;
+   const Cr = Components.results;
+
+   var exports = {};
+
+   var dirsvc = Cc["@mozilla.org/file/directory_service;1"]
+                .getService(Ci.nsIProperties);
+
+   function MozFile(path) {
+     var file = Cc['@mozilla.org/file/local;1']
+                .createInstance(Ci.nsILocalFile);
+     file.initWithPath(path);
+     return {
+       get directoryEntries() {
+         try {
+           return file.directoryEntries;
+         } catch (e if e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
+           throw new Error("path does not exist: " + file.path);
+         }
+       },
+       __proto__: file
+     };
+   }
+
+   exports.join = function join(base) {
+     if (arguments.length < 2)
+       throw new Error("need at least 2 args");
+     base = MozFile(base);
+     for (var i = 1; i < arguments.length; i++)
+       base.append(arguments[i]);
+     return base.path;
+   };
+
+   exports.dirname = function dirname(path) {
+     return MozFile(path).parent.path;
+   };
+
+   exports.list = function list(path) {
+     var entries = MozFile(path).directoryEntries;
+     var entryNames = [];
+     while(entries.hasMoreElements()) {
+       var entry = entries.getNext();
+       entry.QueryInterface(Ci.nsIFile);
+       entryNames.push(entry.leafName);
+     }
+     return entryNames;
+   };
+
+   if (global.window) {
+     // We're being loaded in a chrome window, or a web page with
+     // UniversalXPConnect privileges.
+     global.File = exports;
+   } else if (global.exports) {
+     // We're being loaded in a SecurableModule.
+     for (name in exports) {
+       global.exports[name] = exports[name];
+     }
+   } else {
+     // We're being loaded in a JS module.
+     global.EXPORTED_SYMBOLS = [];
+     for (name in exports) {
+       global.EXPORTED_SYMBOLS.push(name);
+       global[name] = exports[name];
+     }
+   }
+ })(this);
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/lib/securable-module.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/lib/securable-module.js
@@ -83,17 +83,17 @@
      // Unless specified otherwise, use a principal with limited
      // privileges.
      this._defaultPrincipal = resolvePrincipal(defaultPrincipal,
                                                "http://www.mozilla.org");
    },
 
    exports.SandboxFactory.prototype = {
      createSandbox: function createSandbox(options) {
-       var principal = resolvePrincipal(options.principal,
+       var principal = resolvePrincipal('b' in options ? options.b : undefined,
                                         this._defaultPrincipal);
 
        return {
          _sandbox: new Cu.Sandbox(principal),
          _principal: principal,
          defineProperty: function defineProperty(name, value) {
            this._sandbox[name] = value;
          },
new file mode 100644
--- /dev/null
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/notifications.js
@@ -0,0 +1,216 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* The TestPilotSetup object will choose one of these implementations to instantiate.
+ * The interface for all NotificationManager implementations is:
+     showNotification: function(window, features, choices) {},
+     hideNotification: function(window) {}
+ */
+
+EXPORTED_SYMBOLS = ["CustomNotificationManager", "PopupNotificationManager"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+/* CustomNotificationManager: the one where notifications
+ * come up from the Test Pilot icon in the addon bar.  For Firefox 3.6. */
+function CustomNotificationManager(anchorId, tailIsUp) {
+  this._anchorId = anchorId;
+  this._tailIsUp = tailIsUp;
+}
+CustomNotificationManager.prototype = {
+  showNotification: function TP_OldNotfn_showNotification(window, features, choices) {
+    let doc = window.document;
+    let popup = doc.getElementById("pilot-notification-popup");
+    let textLabel = doc.getElementById("pilot-notification-text");
+    let titleLabel = doc.getElementById("pilot-notification-title");
+    let icon = doc.getElementById("pilot-notification-icon");
+    let button = doc.getElementById("pilot-notification-submit");
+    let closeBtn = doc.getElementById("pilot-notification-close");
+    let link = doc.getElementById("pilot-notification-link");
+    let checkbox = doc.getElementById("pilot-notification-always-submit-checkbox");
+    let self = this;
+    let buttonChoice = null;
+    let linkChoice = null;
+    let checkBoxChoice = null;
+
+    if (this._tailIsUp) {
+      popup.setAttribute("class", "tail-up");
+    } else {
+      popup.setAttribute("class", "tail-down");
+    }
+
+    popup.setAttribute("noautohide", !(features.fragile));
+    if (features.title) {
+      titleLabel.setAttribute("value", features.title);
+    }
+    while (textLabel.lastChild) {
+      textLabel.removeChild(textLabel.lastChild);
+    }
+    if (features.text) {
+      textLabel.appendChild(doc.createTextNode(features.text));
+    }
+    if (features.iconClass) {
+      // css will set the image url based on the class.
+      icon.setAttribute("class", features.iconClass);
+    }
+
+    /* Go through the specified choices and figure out which one to turn into a link, which one
+     * (if any) to turn into a button, and which one (if any) to turn into a check box. */
+    for (let i = 0; i < choices.length; i++) {
+      switch(choices[i].customUiType) {
+      case "button":
+        buttonChoice = choices[i];
+        break;
+      case "link":
+        linkChoice = choices[i];
+        break;
+      case "checkbox":
+        checkBoxChoice = choices[i];
+        break;
+      }
+    }
+    // Create check box if specified:
+    if (checkBoxChoice) {
+      checkbox.removeAttribute("hidden");
+      checkbox.setAttribute("label", checkBoxChoice.label);
+    } else {
+      checkbox.setAttribute("hidden", true);
+    }
+
+    // Create button if specified:
+    if (buttonChoice) {
+      button.setAttribute("label", buttonChoice.label);
+      button.onclick = function(event) {
+        if (event.button == 0) {
+          if (checkbox.checked && checkBoxChoice) {
+            checkBoxChoice.callback();
+          }
+          buttonChoice.callback();
+          self.hideNotification(window);
+          if (features.closeCallback) {
+            features.closeCallback();
+          }
+        }
+      };
+      button.removeAttribute("hidden");
+    } else {
+      button.setAttribute("hidden", true);
+    }
+
+    // Create the link if specified:
+    if (linkChoice) {
+      link.setAttribute("value", linkChoice.label);
+      link.setAttribute("class", "notification-link");
+      link.onclick = function(event) {
+        if (event.button == 0) {
+          linkChoice.callback();
+          self.hideNotification(window);
+          if (features.closeCallback) {
+            features.closeCallback();
+          }
+        }
+      };
+      link.removeAttribute("hidden");
+    } else {
+      link.setAttribute("hidden", true);
+    }
+
+    closeBtn.onclick = function() {
+      self.hideNotification(window);
+      if (features.closeCallback) {
+        features.closeCallback();
+      }
+    };
+
+    // Show the popup:
+    popup.hidden = false;
+    popup.setAttribute("open", "true");
+    let anchorElement = window.document.getElementById(this._anchorId);
+    popup.openPopup(anchorElement, "after_end");
+  },
+
+  hideNotification: function TP_OldNotfn_hideNotification(window) {
+    let popup = window.document.getElementById("pilot-notification-popup");
+    popup.removeAttribute("open");
+    popup.hidePopup();
+  }
+};
+
+// For Fx 4.0 + , uses the built-in doorhanger notification system (but with my own anchor icon)
+function PopupNotificationManager() {
+  /* In the future, we may want to anchor these to the Feedback button if present,
+   * but for now that option is unimplemented. */
+  this._popupModule = {};
+  Components.utils.import("resource://gre/modules/PopupNotifications.jsm", this._popupModule);
+  this._pn = null;
+}
+PopupNotificationManager.prototype = {
+  showNotification: function TP_NewNotfn_showNotification(window, features, choices) {
+    let self = this;
+    let tabbrowser = window.getBrowser();
+    let panel = window.document.getElementById("testpilot-notification-popup");
+    let iconBox = window.document.getElementById("tp-notification-popup-box");
+    let defaultChoice = null;
+    let additionalChoices = [];
+
+    // hide any existing notification so we don't get a weird stack
+    this.hideNotification();
+
+    // Create notifications object for window
+    this._pn = new this._popupModule.PopupNotifications(tabbrowser, panel, iconBox);
+
+    /* Add hideNotification() calls to the callbacks of each choice -- the client code shouldn't
+     * have to worry about hiding the notification in its callbacks.*/
+    for (let i = 0; i < choices.length; i++) {
+      let choice = choices[i];
+      let choiceWithHide = {
+        label: choice.label,
+        accessKey: choice.accessKey,
+        callback: function() {
+          self.hideNotification();
+          choice.callback();
+        }};
+      // Take the first one to be the default choice:
+      if (i == 0) {
+        defaultChoice = choiceWithHide;
+      } else {
+        additionalChoices.push(choiceWithHide);
+      }
+    }
+
+    this._notifRef = this._pn.show(tabbrowser.selectedBrowser,
+                             "testpilot",
+                             features.text,
+                             "tp-notification-popup-icon", // All TP notifications use this icon
+                             defaultChoice,
+                             additionalChoices,
+                             {persistWhileVisible: true,
+                              removeOnDismissal: features.fragile,
+                              title: features.title,
+                              iconClass: features.iconClass,
+                              closeButtonFunc: function() {
+                                self.hideNotification();
+                              },
+                              eventCallback: function(stateChange){
+                                /* Note - closeCallback() will be called AFTER the button handler,
+                                 * and will be called no matter whether the notification is closed via
+                                 * close button or a menu button item.
+                                 */
+                                if (stateChange == "removed" && features.closeCallback) {
+                                  features.closeCallback();
+                                }
+                                self._notifRef = null;
+                              }});
+    // See http://mxr.mozilla.org/mozilla-central/source/toolkit/content/PopupNotifications.jsm
+  },
+
+  hideNotification: function TP_NewNotfn_hideNotification() {
+    if (this._notifRef && this._pn) {
+      this._pn.remove(this._notifRef);
+      this._notifRef = null;
+    }
+  }
+};
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js
@@ -1,17 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const BASE_URL_PREF = "extensions.testpilot.indexBaseURL";
+const SSL_DOWNLOAD_REQUIRED_PREF = "extensions.testpilot.ssldownloadrequired";
+
 var Cuddlefish = require("cuddlefish");
 var resolveUrl = require("url").resolve;
 var SecurableModule = require("securable-module");
 let JarStore = require("jar-code-store").JarStore;
+let prefs = require("preferences-service");
 
 /* Security info should look like this:
  * Security Info:
 	Security state: secure
 	Security description: Authenticated by Equifax
 	Security error message: null
 
 Certificate Status:
@@ -95,21 +98,26 @@ function downloadFile(url, cb, lastModif
     req.setRequestHeader("If-Modified-Since", d.toGMTString());
     console.info("Setting if-modified-since header to " + d.toGMTString());
   }
   //Use binary mode to download jars TODO find a better switch
   if (url.indexOf(".jar") == url.length - 4) {
     console.info("Using binary mode to download jar file.");
     req.overrideMimeType('text/plain; charset=x-user-defined');
   }
-  req.addEventListener("readystatechange", function(aEvt) {
+  req.onreadystatechange = function(aEvt) {
     if (req.readyState == 4) {
       if (req.status == 200) {
-        // check security channel:
-        if (verifyChannelSecurity(req.channel)) {
+        // check security channel, unless the user is ignoring that.
+        let ssldownloadrequired= prefs.get(SSL_DOWNLOAD_REQUIRED_PREF,true);
+        if (!ssldownloadrequired) {
+            dump("not requiring ssl download for experiements.  use at your own risk!\n");
+            dump("change this with: " + SSL_DOWNLOAD_REQUIRED_PREF + "\n");
+        }
+        if (!ssldownloadrequired | verifyChannelSecurity(req.channel)) {
           cb(req.responseText);
         } else {
           cb(null);
         }
       } else if (req.status == 304) {
         // 304 is "Not Modified", which we can get because we send an
         // If-Modified-Since header.
         console.info("File " + url + " not modified; using cached version.");
@@ -117,17 +125,17 @@ function downloadFile(url, cb, lastModif
         // calling back with null lets the RemoteExperimentLoader know it should
         // keep using the old cached version of the code.
       } else {
         // Some kind of error.
         console.warn("Got a " + req.status + " error code downloading " + url);
 	cb(null);
       }
     }
-  }, false);
+  };
   req.send();
 }
 
 // example contents of extensions.testpilot.experiment.codeFs:
 // {'fs': {"bookmark01/experiment": "<plain-text code @ bookmarks.js>"}}
 // sample code
     // example data:
     // {'experiments': [{'name': 'Bookmark Experiment',
@@ -372,17 +380,17 @@ exports.RemoteExperimentLoader.prototype
       if (file.exists()) {
         file.remove(false);
       }
       file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
       // file is nsIFile, data is a string
       let foStream = Cc["@mozilla.org/network/file-output-stream;1"].
                                createInstance(Ci.nsIFileOutputStream);
 
-      foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
+      foStream.init(file, 0x02 | 0x08 | 0x20, parseInt("0666", 8), 0);
       // write, create, truncate
       let converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
                                 createInstance(Ci.nsIConverterOutputStream);
       converter.init(foStream, "UTF-8", 0, 0);
       converter.writeString(data);
       converter.close(); // this closes foStream too
     } catch(e) {
       console.warn("Error cacheing index file: " + e);
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/setup.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/setup.js
@@ -19,16 +19,18 @@ const POPUP_SHOW_ON_FINISH = "extensions
 const POPUP_SHOW_ON_RESULTS = "extensions.testpilot.popup.showOnNewResults";
 const POPUP_CHECK_INTERVAL = "extensions.testpilot.popup.delayAfterStartup";
 const POPUP_REMINDER_INTERVAL = "extensions.testpilot.popup.timeBetweenChecks";
 const ALWAYS_SUBMIT_DATA = "extensions.testpilot.alwaysSubmitData";
 const UPDATE_CHANNEL_PREF = "app.update.channel";
 const LOG_FILE_NAME = "TestPilotErrorLog.log";
 const RANDOM_DEPLOY_PREFIX = "extensions.testpilot.deploymentRandomizer";
 
+Cu.import("resource://testpilot/modules/interface.js");
+
 let TestPilotSetup = {
   didReminderAfterStartup: false,
   startupComplete: false,
   _shortTimer: null,
   _longTimer: null,
   _remoteExperimentLoader: null, // TODO make this a lazy initializer too?
   taskList: [],
   version: "",
@@ -146,48 +148,23 @@ let TestPilotSetup = {
   __obs: null,
   get _obs() {
     if (this.__obs == null) {
       this.__obs = this._loader.require("observer-service");
     }
     return this.__obs;
   },
 
-  _isBetaChannel: function TPS__isBetaChannel() {
-    // Beta and aurora channels use feedback interface; nightly and release channels don't.
-    let channel = this._prefs.getValue(UPDATE_CHANNEL_PREF, "");
-    return (channel == "beta") || (channel == "betatest") || (channel == "aurora");
-  },
-
-  _setPrefDefaultsForVersion: function TPS__setPrefDefaultsForVersion() {
-    /* A couple of preferences need different default values depending on
-     * whether we're in the Firefox 4 beta version or the standalone TP version
-     */
-    let ps = Cc["@mozilla.org/preferences-service;1"]
-                    .getService(Ci.nsIPrefService);
-    let prefBranch = ps.getDefaultBranch("");
-    /* note we're setting default values, not current values -- these
-     * get overridden by any user set values. */
-    if (this._isBetaChannel()) {
-      prefBranch.setBoolPref(POPUP_SHOW_ON_NEW, true);
-      prefBranch.setIntPref(POPUP_CHECK_INTERVAL, 600000);
-    } else {
-      prefBranch.setBoolPref(POPUP_SHOW_ON_NEW, false);
-      prefBranch.setIntPref(POPUP_CHECK_INTERVAL, 180000);
-    }
-  },
-
   globalStartup: function TPS__doGlobalSetup() {
     // Only ever run this stuff ONCE, on the first window restore.
     // Should get called by the Test Pilot component.
     let logger = this._logger;
     logger.trace("TestPilotSetup.globalStartup was called.");
 
     try {
-    this._setPrefDefaultsForVersion();
     if (!this._prefs.getValue(RUN_AT_ALL_PREF, true)) {
       logger.trace("Test Pilot globally disabled: Not starting up.");
       return;
     }
 
     // Set up observation for task state changes
     var self = this;
     this._obs.add("testpilot:task:changed", this.onTaskStatusChanged, self);
@@ -217,17 +194,17 @@ let TestPilotSetup = {
 	  });
       }}, this._prefs.getValue(POPUP_REMINDER_INTERVAL, 86400000),
       Ci.nsITimer.TYPE_REPEATING_SLACK);
 
       this.getVersion(function() {
         /* Show first run page (in front window) only the first time after install;
          * Don't show first run page in Feedback UI version. */
         if ((self._prefs.getValue(VERSION_PREF, "") == "") &&
-           (!self._interfaceBuilder.channelUsesFeedback())) {
+           (!TestPilotUIBuilder.channelUsesFeedback())) {
             self._prefs.setValue(VERSION_PREF, self.version);
             let browser = self._getFrontBrowserWindow().getBrowser();
             let url = self._prefs.getValue(FIRST_RUN_PREF, "");
             let tab = browser.addTab(url);
             browser.selectedTab = tab;
         }
 
         // Install tasks. (This requires knowing the version, so it is
@@ -338,26 +315,31 @@ let TestPilotSetup = {
      * object that you create and then call .show() on. */
 
     // If there are multiple windows, show notifications in the frontmost
     // window.
     let window = this._getFrontBrowserWindow();
     let doc = window.document;
     let popup = doc.getElementById("pilot-notification-popup");
 
-    let anchor;
-    if (this._isBetaChannel()) {
-      /* If we're in the Ffx4Beta version, popups come down from feedback
-       * button, but if we're in the standalone extension version, they
-       * come up from status bar icon. */
+    let anchor, xOffset;
+    if (TestPilotUIBuilder.channelUsesFeedback()) {
+      /* If we're in the Ffx4Beta version, popups hang down from the feedback
+       * button. In the standalone extension version, they hang down from
+       * a temporary Test Pilot icon that appears in the toolbar. */
       anchor = doc.getElementById("feedback-menu-button");
-      popup.setAttribute("class", "tail-up");
+      xOffset = 0;
     } else {
-      anchor = doc.getElementById("pilot-notifications-button");
-      popup.setAttribute("class", "tail-down");
+      anchor = doc.getElementById("tp-notification-popup-icon");
+      anchor.hidden = false;
+      /* By default, right edge of notification will line up with right edge of anchor.
+       * That looks fine for feedback button, but the test pilot icon is narrower and
+       * this alignment causes the pointy part of the arrow to miss the icon.  Fix this
+       * by shifting the notification to the right 24 pixels. */
+      xOffset = 24;
     }
     let textLabel = doc.getElementById("pilot-notification-text");
     let titleLabel = doc.getElementById("pilot-notification-title");
     let icon = doc.getElementById("pilot-notification-icon");
     let submitBtn = doc.getElementById("pilot-notification-submit");
     let closeBtn = doc.getElementById("pilot-notification-close");
     let link = doc.getElementById("pilot-notification-link");
     let alwaysSubmitCheckbox =
@@ -416,17 +398,16 @@ let TestPilotSetup = {
         };
       }
     }
     submitBtn.setAttribute("hidden", !showSubmit);
 
     // Create the link if specified:
     if (linkText && (linkUrl || task)) {
       link.setAttribute("value", linkText);
-      link.setAttribute("class", "notification-link");
       link.onclick = function(event) {
         if (event.button == 0) {
 	  if (task) {
             task.loadPage();
 	  } else {
             self._openChromeless(linkUrl);
 	  }
           self._hideNotification(window, onCloseCallback);
@@ -439,17 +420,17 @@ let TestPilotSetup = {
 
     closeBtn.onclick = function() {
       self._hideNotification(window, onCloseCallback);
     };
 
     // Show the popup:
     popup.hidden = false;
     popup.setAttribute("open", "true");
-    popup.openPopup( anchor, "after_end");
+    popup.openPopup( anchor, "after_end", xOffset, 0);
   },
 
   _openChromeless: function TPS__openChromeless(url) {
     let window = this._getFrontBrowserWindow();
     window.TestPilotWindowUtils.openChromeless(url);
   },
 
   _hideNotification: function TPS__hideNotification(window, onCloseCallback) {
@@ -457,16 +438,22 @@ let TestPilotSetup = {
      * window because the window order might have changed since the notification
      * appeared and we want to be sure we close the notification in the same
      * window as we opened it in! */
     let popup = window.document.getElementById("pilot-notification-popup");
     popup.hidden = true;
     popup.setAttribute("open", "false");
     popup.removeAttribute("tpisextensionupdate");
     popup.hidePopup();
+
+    if (!TestPilotUIBuilder.channelUsesFeedback()) {
+      // If we're using the temporary test pilot icon as an anchor, hide it now
+      let icon = window.document.getElementById("tp-notification-popup-icon");
+      icon.hidden = true;
+    }
     if (onCloseCallback) {
       onCloseCallback();
     }
   },
 
   _isShowingUpdateNotification : function() {
     let window = this._getFrontBrowserWindow();
     let popup = window.document.getElementById("pilot-notification-popup");
@@ -482,17 +469,17 @@ let TestPilotSetup = {
 
     // if showing extension update notification, don't do anything.
     if (this._isShowingUpdateNotification()) {
       return;
     }
 
     // Highest priority is if there is a finished test (needs a decision)
     if (this._prefs.getValue(POPUP_SHOW_ON_FINISH, false)) {
-      for (i = 0; i < this.taskList.length; i++) {
+      for (let i = 0; i < this.taskList.length; i++) {
         task = this.taskList[i];
         if (task.status == TaskConstants.STATUS_FINISHED) {
           if (!this._prefs.getValue(ALWAYS_SUBMIT_DATA, false)) {
             this._showNotification(
 	      task, false,
 	      this._stringBundle.formatStringFromName(
 		"testpilot.notification.readyToSubmit.message", [task.title],
 		1),
@@ -507,17 +494,17 @@ let TestPilotSetup = {
           }
         }
       }
     }
 
     // If there's no finished test, next highest priority is new tests that
     // are starting...
     if (this._prefs.getValue(POPUP_SHOW_ON_NEW, false)) {
-      for (i = 0; i < this.taskList.length; i++) {
+      for (let i = 0; i < this.taskList.length; i++) {
         task = this.taskList[i];
         if (task.status == TaskConstants.STATUS_PENDING ||
             task.status == TaskConstants.STATUS_NEW) {
           if (task.taskType == TaskConstants.TYPE_EXPERIMENT) {
 	    this._showNotification(
 	      task, false,
 	      this._stringBundle.formatStringFromName(
 		"testpilot.notification.newTestPilotStudy.pre.message",
@@ -533,32 +520,33 @@ let TestPilotSetup = {
                 TestPilotSetup.reloadRemoteExperiments();
               });
             return;
           } else if (task.taskType == TaskConstants.TYPE_SURVEY) {
 	    this._showNotification(
 	      task, false,
 	      this._stringBundle.formatStringFromName(
 		"testpilot.notification.newTestPilotSurvey.message",
-		[task.title], 1),
+    // in task.js summary falls back to title if undefined or empty, but we double make sure :)
+		[task.summary || task.title],1),
               this._stringBundle.GetStringFromName(
 		"testpilot.notification.newTestPilotSurvey"),
 	      "new-study", false, false,
-	      this._stringBundle.GetStringFromName("testpilot.moreInfo"),
+	      this._stringBundle.GetStringFromName("testpilot.takeSurvey"),
 	      task.defaultUrl);
             task.changeStatus(TaskConstants.STATUS_IN_PROGRESS, true);
             return;
           }
         }
       }
     }
 
     // And finally, new experiment results:
     if (this._prefs.getValue(POPUP_SHOW_ON_RESULTS, false)) {
-      for (i = 0; i < this.taskList.length; i++) {
+      for (let i = 0; i < this.taskList.length; i++) {
         task = this.taskList[i];
         if (task.taskType == TaskConstants.TYPE_RESULTS &&
             task.status == TaskConstants.STATUS_NEW) {
 	  this._showNotification(
 	    task, true,
 	    this._stringBundle.formatStringFromName(
 	      "testpilot.notification.newTestPilotResults.message",
 	      [task.title], 1),
@@ -640,39 +628,42 @@ let TestPilotSetup = {
                    .compare(this._application.version, versionString);
     if (result < 0) {
       return true; // versionString is newer than Firefox
     } else {
       return false; // versionString is the same as or older than Firefox
     }
   },
 
-  _experimentRequirementsAreMet: function TPS__requirementsMet(experiment) {
-    /* Returns true if we we meet the requirements to run this experiment
-     * (e.g. meet the minimum Test Pilot version and Firefox version)
-     * false if not.
-     * Default is always to run the study - return true UNLESS the study
-     * specifies a requirement that we don't meet. */
+  _checkExperimentRequirements: function TPS__requirementsMet(experiment, callback) {
+    /* Async.
+     * Calls callback with a true if we we meet the requirements to run this
+     * experiment (e.g. meet the minimum Test Pilot version and Firefox version)
+     * calls callback with a false if not.
+     * All of the requirements that a study can specify - firefox version, test pilot
+     * version, filter function etc - default to true if not provided. callback(true)
+     * UNLESS the study specifies a requirement that we don't meet. */
     let logger = this._logger;
     try {
-      let minTpVer, minFxVer, expName, runOrNotFunc, randomDeployment;
+      let minTpVer, minFxVer, expName, filterFunc, randomDeployment;
       /* Could be an experiment, which specifies experimentInfo, or survey,
        * which specifies surveyInfo. */
       let info = experiment.experimentInfo ?
                    experiment.experimentInfo :
                    experiment.surveyInfo;
       if (!info) {
         // If neither one is supplied, study lacks metadata required to run
         logger.warn("Study lacks minimum metadata to run.");
-        return false;
+        callback(false);
+        return;
       }
       minTpVer = info.minTPVersion;
       minFxVer = info.minFXVersion;
       expName =  info.testName;
-      runOrNotFunc = info.runOrNotFunc;
+      filterFunc = info.filter;
       randomDeployment = info.randomDeployment;
 
       // Minimum test pilot version:
       if (minTpVer && this._isNewerThanMe(minTpVer)) {
         logger.warn("Not loading " + expName);
         logger.warn("Because it requires Test Pilot version " + minTpVer);
 
         // Let user know there is a newer version of Test Pilot available:
@@ -680,54 +671,64 @@ let TestPilotSetup = {
           this._showNotification(
 	    null, false,
 	    this._stringBundle.GetStringFromName(
 	      "testpilot.notification.extensionUpdate.message"),
 	    this._stringBundle.GetStringFromName(
 	      "testpilot.notification.extensionUpdate"),
 	    "update-extension", true, false, "", "", true);
 	}
-        return false;
+        callback(false);
+        return;
       }
 
       // Minimum firefox version:
       if (minFxVer && this._isNewerThanFirefox(minFxVer)) {
         logger.warn("Not loading " + expName);
         logger.warn("Because it requires Firefox version " + minFxVer);
-        return false;
+        callback(false);
+        return;
       }
 
       // Random deployment (used to give study to random subsample of users)
       if (randomDeployment) {
-        /* Roll a hundred-sided die. Remember what we roll for later reference.  A study
+        /* Roll a 100*uniform_random. Remember what we roll for later reference or reuse.  A study
          * using random subsample deployment will provide a range (say, 0 ~ 30) which means
-         * only users who roll within that range will run the study. */
+         * only users who roll not outside that range (inclusive) will run the study.
+         * ie., [0,1.5] -> 1.5% prob
+         */
         let prefName = RANDOM_DEPLOY_PREFIX + "." + randomDeployment.rolloutCode;
         let myRoll = this._prefs.getValue(prefName, null);
         if (myRoll == null) {
-          myRoll = Math.floor(Math.random()*100);
-          this._prefs.setValue(prefName, myRoll);
+          myRoll = Math.random()*100;
+          this._prefs.setValue(prefName, JSON.stringify(myRoll));
+        } else {
+          myRoll = Number(myRoll);  // cast it
         }
         if (myRoll < randomDeployment.minRoll) {
-          return false;
+          callback(false);
+          return;
         }
         if (myRoll > randomDeployment.maxRoll) {
-          return false;
+          callback(false);
+          return;
         }
       }
 
       /* The all-purpose, arbitrary code "Should this study run?" function - if
-       * provided, use its return value. */
-      if (runOrNotFunc) {
-        return runOrNotFunc();
+       * provided, use it.  (filterFunc must be asynchronous too!)*/
+      if (filterFunc) {
+        filterFunc(callback);
+        return;
       }
     } catch (e) {
       logger.warn("Error in requirements check " +  e);
+      callback(false); // if something went wrong, err on the side of not running the study
     }
-    return true;
+    callback(true);
   },
 
   checkForTasks: function TPS_checkForTasks(callback) {
     let logger = this._logger;
     if (! this._remoteExperimentLoader ) {
       logger.trace("Now requiring remote experiment loader:");
       let remoteLoaderModule = this._loader.require("remote-experiment-loader");
       logger.trace("Now instantiating remoteExperimentLoader:");
@@ -738,75 +739,81 @@ let TestPilotSetup = {
     let self = this;
     this._remoteExperimentLoader.checkForUpdates(
       function(success) {
         logger.info("Getting updated experiments... Success? " + success);
         // Actually, we do exactly the same thing whether we succeeded in
         // downloading new contents or not...
         let experiments = self._remoteExperimentLoader.getExperiments();
 
+        let numExperimentsProcessed = 0;
+        let numExperiments = Object.keys(experiments).length;
         for (let filename in experiments) {
-          if (!self._experimentRequirementsAreMet(experiments[filename])) {
-            continue;
-          }
-          try {
-            // The try-catch ensures that if something goes wrong in loading one
-            // experiment, the other experiments after that one still get loaded.
-            logger.trace("Attempting to load experiment " + filename);
-
-            let task;
-            // Could be a survey: check if surveyInfo is exported:
-            if (experiments[filename].surveyInfo != undefined) {
-              let sInfo = experiments[filename].surveyInfo;
-              // If it supplies questions, it's a built-in survey.
-              // If not, it's a web-based survey.
-              if (!sInfo.surveyQuestions) {
-                task = new self._taskModule.TestPilotWebSurvey(sInfo);
-              } else {
-                task = new self._taskModule.TestPilotBuiltinSurvey(sInfo);
+          self._checkExperimentRequirements(experiments[filename], function(requirementsMet) {
+            if (requirementsMet) {
+              try {
+                /* The try-catch ensures that if something goes wrong in loading one
+                 * experiment, the other experiments after that one still get loaded. */
+                logger.trace("Attempting to load experiment " + filename);
+                let task;
+                // Could be a survey: check if surveyInfo is exported:
+                if (experiments[filename].surveyInfo != undefined) {
+                  let sInfo = experiments[filename].surveyInfo;
+                  // If it supplies questions, it's a built-in survey.
+                  // If not, it's a web-based survey.
+                  if (!sInfo.surveyQuestions) {
+                    task = new self._taskModule.TestPilotWebSurvey(sInfo);
+                  } else {
+                    task = new self._taskModule.TestPilotBuiltinSurvey(sInfo);
+                  }
+                } else {
+                  // This one must be an experiment.
+                  let expInfo = experiments[filename].experimentInfo;
+                  let dsInfo = experiments[filename].dataStoreInfo;
+                  let dataStore = new self._dataStoreModule.ExperimentDataStore(
+                    dsInfo.fileName, dsInfo.tableName, dsInfo.columns );
+                  let webContent = experiments[filename].webContent;
+                  task = new self._taskModule.TestPilotExperiment(expInfo,
+                                                                  dataStore,
+                                                                  experiments[filename].handlers,
+                                                                  webContent);
+                }
+                self.addTask(task);
+                logger.info("Loaded task " + filename);
+              } catch (e) {
+                logger.warn("Failed to load task " + filename + ": " + e);
               }
-            } else {
-              // This one must be an experiment.
-              let expInfo = experiments[filename].experimentInfo;
-              let dsInfo = experiments[filename].dataStoreInfo;
-              let dataStore = new self._dataStoreModule.ExperimentDataStore(
-                dsInfo.fileName, dsInfo.tableName, dsInfo.columns );
-              let webContent = experiments[filename].webContent;
-              task = new self._taskModule.TestPilotExperiment(expInfo,
-                                                              dataStore,
-                                                              experiments[filename].handlers,
-                                                              webContent);
-            }
-            self.addTask(task);
-            logger.info("Loaded task " + filename);
-          } catch (e) {
-            logger.warn("Failed to load task " + filename + ": " + e);
-          }
-        } // end for filename in experiments
+            } // end if requirements met
+            // whether loading succeeded or failed, we're done processing that one; increment the count:
+            numExperimentsProcessed ++;
+            if (numExperimentsProcessed == numExperiments) {
+              // all done with experiments -- do results and legacy studies:
+              let results = self._remoteExperimentLoader.getStudyResults();
+              for (let r in results) {
+                let studyResult = new self._taskModule.TestPilotStudyResults(results[r]);
+                self.addTask(studyResult);
+              }
 
-        // Handling new results is much simpler:
-        let results = self._remoteExperimentLoader.getStudyResults();
-        for (let r in results) {
-          let studyResult = new self._taskModule.TestPilotStudyResults(results[r]);
-          self.addTask(studyResult);
-        }
+              /* Legacy studies = stuff we no longer have the code for, but
+               * if the user participated in it we want to keep that metadata. */
+              let legacyStudies = self._remoteExperimentLoader.getLegacyStudies();
+              for (let l in legacyStudies) {
+                let legacyStudy = new self._taskModule.TestPilotLegacyStudy(legacyStudies[l]);
+                self.addTask(legacyStudy);
+              }
 
-        /* Legacy studies = stuff we no longer have the code for, but
-         * if the user participated in it we want to keep that metadata. */
-        let legacyStudies = self._remoteExperimentLoader.getLegacyStudies();
-        for (let l in legacyStudies) {
-          let legacyStudy = new self._taskModule.TestPilotLegacyStudy(legacyStudies[l]);
-          self.addTask(legacyStudy);
-        }
-
-        if (callback) {
-          callback();
-        }
+              // Finally, call the callback if there is one
+              if (callback) {
+                callback();
+              }
+            } // end of if all experiments are processed
+          }); // end of call to check experiment requirements
+        } // end for filename in experiments
       }
-    );
+    ); // end of call to checkForUpdates
   },
 
   reloadRemoteExperiments: function TPS_reloadRemoteExperiments(callback) {
     for (let i = 0; i < this.taskList.length; i++) {
       this.taskList[i].onExperimentShutdown();
     }
 
     this.taskList = [];
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js
@@ -808,17 +808,17 @@ TestPilotExperiment.prototype = {
     self._logger.info("Posting data to url " + url + "\n");
     self._prependMetadataToJSON( function(dataString) {
       let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                   .createInstance( Ci.nsIXMLHttpRequest );
       req.open('POST', url, true);
       req.setRequestHeader("Content-type", "application/json");
       req.setRequestHeader("Content-length", dataString.length);
       req.setRequestHeader("Connection", "close");
-      req.addEventListener("readystatechange", function(aEvt) {
+      req.onreadystatechange = function(aEvt) {
         if (req.readyState == 4) {
           if (req.status == 200 || req.status == 201 || req.status == 202) {
             let location = req.getResponseHeader("Location");
   	    self._logger.info("DATA WAS POSTED SUCCESSFULLY " + location);
             if (self._uploadRetryTimer) {
               self._uploadRetryTimer.cancel(); // Stop retrying - it worked!
             }
             self.changeStatus(TaskConstants.STATUS_SUBMITTED);
@@ -849,17 +849,17 @@ TestPilotExperiment.prototype = {
               parseInt(Math.random() * Math.pow(2, retryCount) * interval);
             self._uploadRetryTimer.initWithCallback(
               { notify: function(timer) {
 		self.upload(callback, retryCount++);
 	      } }, (interval + delay), Ci.nsITimer.TYPE_ONE_SHOT);
             callback(false);
           }
         }
-      }, false);
+      };
       req.send(dataString);
     });
   },
 
   optOut: function TestPilotExperiment_optOut(reason, callback) {
     // Regardless of study ID, post the opt-out message to a special
     // database table of just opt-out messages; include study ID in metadata.
     let logger = this._logger;
@@ -881,31 +881,31 @@ TestPilotExperiment.prototype = {
       var req =
         Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
 	  createInstance(Ci.nsIXMLHttpRequest);
       logger.trace("Posting " + dataString + " to " + url);
       req.open('POST', url, true);
       req.setRequestHeader("Content-type", "application/json");
       req.setRequestHeader("Content-length", dataString.length);
       req.setRequestHeader("Connection", "close");
-      req.addEventListener("readystatechange", function(aEvt) {
+      req.onreadystatechange = function(aEvt) {
         if (req.readyState == 4) {
           if (req.status == 200 || req.status == 201 || req.status == 202) {
 	    logger.info("Quit reason posted successfully " + req.responseText);
             if (callback) {
               callback(true);
             }
 	  } else {
 	    logger.warn(req.status + " posting error " + req.responseText);
             if (callback) {
               callback(false);
             }
 	  }
 	}
-      }, false);
+      };
       logger.trace("Sending quit reason.");
       req.send(dataString);
     } else {
       if (callback) {
         callback(false);
       }
     }
   },
@@ -1031,17 +1031,17 @@ TestPilotBuiltinSurvey.prototype = {
       let req =
         Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
           createInstance(Ci.nsIXMLHttpRequest);
       let url = self.uploadUrl;
       req.open("POST", url, true);
       req.setRequestHeader("Content-type", "application/json");
       req.setRequestHeader("Content-length", params.length);
       req.setRequestHeader("Connection", "close");
-      req.addEventListener("readystatechange", function(aEvt) {
+      req.onreadystatechange = function(aEvt) {
         if (req.readyState == 4) {
           if (req.status == 200 || req.status == 201 ||
              req.status == 202) {
             self._logger.info(
 	    "DATA WAS POSTED SUCCESSFULLY " + req.responseText);
             if (self._uploadRetryTimer) {
               self._uploadRetryTimer.cancel(); // Stop retrying - it worked!
 	    }
@@ -1061,17 +1061,17 @@ TestPilotBuiltinSurvey.prototype = {
 	      parseInt(Math.random() * Math.pow(2, retryCount) * interval);
 	    self._uploadRetryTimer.initWithCallback(
               { notify: function(timer) {
 	          self._upload(callback, retryCount++);
 	        } }, (interval + delay), Ci.nsITimer.TYPE_ONE_SHOT);
 	    callback(false);
 	  }
         }
-      }, false);
+      };
       req.send(params);
     });
   }
 };
 TestPilotBuiltinSurvey.prototype.__proto__ = TestPilotTask;
 
 function TestPilotWebSurvey(surveyInfo) {
   this._init(surveyInfo);
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css
@@ -1,63 +1,61 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html {
-	padding-bottom: 40px; 
+	padding-bottom: 40px;
 	padding-top: 20px;
 	padding-left: 0px;
 	padding-right: 0px;
 }
 
-body { 
+body {
 	font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif;
-	background: #fff url('chrome://testpilot/skin/images/bg-status.jpg') repeat-x top center;	padding: 0px; 
+	background: #fff url('chrome://testpilot/skin/images/bg-status.jpg') repeat-x top center;	padding: 0px;
 	color:#787878;
 	font-size:12px;
 	line-height: 18px;
 	margin: 0 auto;
-	
+
 }
 
 	h1 {color: #3a3a3a; font-size: 28px; font-weight: normal; letter-spacing: -1px}
 	h2 {color: #54717b; font-size: 24px; line-height: 24px;font-weight: normal; letter-spacing: -1px}
 	h3 {color: #54717b; font-size: 18px; line-height: 18px;font-weight: normal; letter-spacing: -1px}
-	
+
 	p, ul {font-size: 12px;}
-	
+
 	a:link, a:hover, a:active, a:focus, a:visited {color: #9f423b; text-decoration: none;}
 	a:hover {color: #9f423b; text-decoration: none;}
-	
+
 	.bold {color:#3a3a3a;}
 	.address {margin-left: 20px;}
 	.inactive {color:#ccc;}
-	
+
 	li {list-style-type: circle;}
 
 	.spacer {height: 40px;}
-	
+
 	.center {text-align: center;}
 
 	.data {
-	
+
 	    background-color: #fff;
         margin-bottom: 6px;
         border: 1px solid rgba(133, 153, 166, 0.2);
         border-bottom: 4px solid rgba(133, 153, 166, 0.2);
 		-moz-border-bottom-colors:rgba(133, 153, 166, 0.3) rgba(133, 153, 166, 0.2) rgba(133, 153, 166, 0.2) rgba(133, 153, 166, 0.2);
         padding: 6px;
         box-shadow:
 			rgba(133, 153, 166, 0.4) 0px 1px 17px;
-		-webkit-box-shadow:
-			rgba(133, 153, 166, 0.4) 0px 1px 24px;
-		
+
 	}
-	
+
 	.dataBox {
 		font-size: 16px;
 		padding: 6px 20px 20px 20px;
 		border-radius: 0.5em;
 		background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
 		/* display: inline; */
 	}
 
@@ -84,26 +82,16 @@ body {
 }
 
 #download {
 	width: 300px;
 	margin-left: 410px;
 	margin-top: 240px;
 }
 
-.downloadButton {
-	margin-bottom: -10px;
-	margin-top: -2px;
-	margin-right: 12px;
-}
-
-.downloadH1 {color: #3a3a3a; font-size: 24px; font-weight: normal; letter-spacing: -1px; margin-right: 8px;}
-
-.downloadH2 {color: #3a3a3a; font-size: 22px; font-weight: normal; letter-spacing: -1px; line-height: 28px; margin-left: 46px;}
-
 #intro {
 	float: left;
 	width: 500px;
 	margin-top: 30px;
 }
 
 #links {
 	float: right;
@@ -112,54 +100,54 @@ body {
 	margin-top: 260px;
 }
 
 .button {
 	font-size: 16px;
 	padding: 8px 12px;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
-	box-shadow: 
+	box-shadow:
 		inset rgba(0, 0, 0, 0.2) 0 1px 1px,
 		inset rgba(255, 255, 255, 1) 0 3px 1px,
 		inset rgba(255, 255, 255, 0.3) 0 16px 0px,
 		inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
-		inset rgba(0, 0, 0, 0.1) 0 -2px 1px, 
+		inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
 		rgba(255, 255, 255, 1) 0 1px,
 		rgba(133, 153, 166, 0.3) 0px 1px 8.5px;
 	background-color: #e7eaec;
 	/* display: inline; */
 }
 
 .home_button {
 	font-size: 16px;
 	padding: 8px 12px;
 	width: 240px;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
-	box-shadow: 
+	box-shadow:
 		inset rgba(0, 0, 0, 0.2) 0 1px 1px,
 		inset rgba(255, 255, 255, 1) 0 3px 1px,
 		inset rgba(255, 255, 255, 0.3) 0 16px 0px,
 		inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
-		inset rgba(0, 0, 0, 0.1) 0 -2px 1px, 
+		inset rgba(0, 0, 0, 0.1) 0 -2px 1px,
 		rgba(255, 255, 255, 1) 0 1px,
 		rgba(133, 153, 166, 0.3) 0px 1px 8.5px;
 	background-color: #e7eaec;
 	/* display: inline; */
 }
 
 .callout {
 	font-size: 16px;
 	padding: 8px 24px;
 	margin: 24px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
-	box-shadow: 
+	box-shadow:
 		inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
 	/* display: inline; */
 }
 
 #data-privacy-text {
   width: 320px;
@@ -170,33 +158,33 @@ body {
 	font-size: 16px;
 	vertical-align: middle;
  	/* width: 280px;*/
 	padding: 8px 24px;
 	margin: 8px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
-	box-shadow: 
+	box-shadow:
 		inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
 	/* display: inline; */
 }
 
 .home_callout_continue {
 	font-size: 16px;
 	vertical-align: middle;
 	width: 280px;
 	padding: 8px 24px;
 	margin: 8px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout_continue.png') no-repeat top center;
-	box-shadow: 
+	box-shadow:
 		inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
 	/* display: inline; */
 }
 
 .home_callout_continue a {color: #6c9735; text-decoration: none;}
 .home_callout_continue a:hover {color: #6c9735; text-decoration: none; border-bottom: 1px dotted #6c9735;}
@@ -222,32 +210,32 @@ body {
 
 }
 
 .mozLogo {
 	margin-bottom: -10px;
 	margin-right: 6px;
 }
 
-/* ------- MENU ------- 
+/* ------- MENU -------
 
 #menu {
 	margin: 20px auto;
 	max-width: 800px;
 	padding: 4px 40px;
 	width: 800px;
 	text-align: left;
 	border-radius: 0.25em;
 	border-top: 1px solid #adb6ba;
 	border-left: 1px solid #adb6ba;
 	border-right: 1px solid #adb6ba;
 	border-bottom: 3px solid #adb6ba;
 	-moz-border-bottom-colors:#adb6ba #e7eaec #e7eaec;
 	background-color: #fff;
-} 
+}
 
 */
 
 .menuItem {
 	margin-right: 30px;
 	margin-bottom: 40px;
 	font-size: 14px;
 	text-shadow: 1px 1px 1px rgba(173, 182, 186, 0.6);
@@ -255,17 +243,17 @@ body {
 }
 
 .menuOn {
 	margin-right: 30px;
 	margin-bottom: 40px;
 	font-size: 14px;
 	text-shadow: 1px 1px 1px rgba(173, 182, 186, 1);
 	background-color: rgba(173, 182, 186, 0.3);
-	box-shadow: 
+	box-shadow:
 		inset rgba(0, 0, 0, 0.2) 0 -10px 8.5px;
 	padding: 9px 8px 8px 8px;
 }
 
 .menuItem a {color: #9f423b; text-decoration: none;}
 .menuItem a:hover {color: #9f423b; text-decoration: none; border-bottom: 1px dotted #9f423b;}
 
 
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css
@@ -1,27 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #pilot-notification-popup {
   -moz-appearance: none;
+  border-image: none;
   background-color: Menu;
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.2), transparent);
   box-shadow: inset 0 0 7px hsla(0,0%,100%,.2),
                    inset 0 1px 0 hsla(0,0%,100%,.3);
   border-radius: 4px;
   border: 1px solid Menu;
   margin: -6px 0 0 0;
   width: 480px;
 }
 
-.tail-up,
-.tail-down {
- -moz-border-image: none;
-}
-
 .pilot-notification-popup-container {
   -moz-appearance: none;
   margin: 0;
   padding: 10px;
   font-size: 14px;
 }
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/mac/feedback.css
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/mac/feedback.css
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Submit Button Style */
 
 #pilot-notification-submit {
   -moz-appearance: none;
-  background: #666 
+  background: #666
               -moz-linear-gradient(rgba(110,110,110,.9), rgba(70,70,70,.9) 49%,
                                    rgba(60,60,60,.9) 51%, rgba(50,50,50,.9));
   background-clip: padding-box;
   background-origin: padding-box;
   border-radius: 12px;
   border: 1px solid rgba(0,0,0,.65);
   box-shadow: inset 0 1px 0 rgba(255,255,255,.2),
                    inset 0 0 1px rgba(255,255,255,.1),
@@ -28,11 +28,11 @@
 }
 
 /* Text Colors */
 
 .pilot-notification-popup-container {
   color: #fff;
 }
 
-.notification-link {
+#pilot-notification-link {
   color: #fff;
 }
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js
@@ -402,17 +402,17 @@ StubHandlers.prototype = {
   uninstallAll: function() {
   }
 };
 
 function clearAllPrefsForStudy(studyId) {
   dump("Looking for prefs to delete...\n");
   let prefService = Cc["@mozilla.org/preferences-service;1"]
                      .getService(Ci.nsIPrefService)
-                     .QueryInterface(Ci.nsIPrefBranch);
+                     .QueryInterface(Ci.nsIPrefBranch2);
   let prefStem = "extensions.testpilot";
   let prefNames = prefService.getChildList(prefStem);
   for each (let prefName in prefNames) {
     if (prefName.indexOf(studyId) != -1) {
       dump("Clearing pref " + prefName + "\n");
       prefService.clearUserPref(prefName);
     }
   }
@@ -778,9 +778,9 @@ function testSameGUIDs() {
           cheapAssertEqual((exp2Guid != ""), true, "guid should be non-empty");
         });
       });
     });
   });
 }
 
 // TODO test for continuity of GUID with recurring study (longitudinal) - i don't think this
-// has actually been working so far because a new GUID is generted every time the study starts up...
+// has actually been working so far because a new GUID is generted every time the study starts up...
\ No newline at end of file
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1005,17 +1005,17 @@ pref("devtools.gcli.allowSet", false);
 pref("devtools.commands.dir", "");
 
 // Enable the Inspector
 pref("devtools.inspector.enabled", true);
 pref("devtools.inspector.htmlHeight", 112);
 pref("devtools.inspector.htmlPanelOpen", false);
 pref("devtools.inspector.sidebarOpen", false);
 pref("devtools.inspector.activeSidebar", "ruleview");
-pref("devtools.inspector.markupPreview", true);
+pref("devtools.inspector.markupPreview", false);
 
 // Enable the Layout View
 pref("devtools.layoutview.enabled", true);
 pref("devtools.layoutview.open", false);
 
 // Enable the Responsive UI tool
 pref("devtools.responsiveUI.enabled", true);
 
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -153,36 +153,17 @@ const gXPInstallObserver = {
           accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
           callback: function() {
             Application.restart();
           }
         };
       }
       else {
         messageString = gNavigatorBundle.getString("addonsInstalled");
-        action = {
-          label: gNavigatorBundle.getString("addonInstallManage"),
-          accessKey: gNavigatorBundle.getString("addonInstallManage.accesskey"),
-          callback: function() {
-            // Calculate the add-on type that is most popular in the list of
-            // installs
-            var types = {};
-            var bestType = null;
-            for (let install of installInfo.installs) {
-              if (install.type in types)
-                types[install.type]++;
-              else
-                types[install.type] = 1;
-              if (!bestType || types[install.type] > types[bestType])
-                bestType = install.type;
-            }
-
-            BrowserOpenAddonsMgr("addons://list/" + bestType);
-          }
-        };
+        action = null;
       }
 
       messageString = PluralForm.get(installInfo.installs.length, messageString);
       messageString = messageString.replace("#1", installInfo.installs[0].name);
       messageString = messageString.replace("#2", installInfo.installs.length);
       messageString = messageString.replace("#3", brandShortName);
 
       // Remove notificaion on dismissal, since it's possible to cancel the
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -479,17 +479,17 @@ var gPluginHandler = {
    },
 
   _showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser) {
     aBrowser._clickToPlayDoorhangerShown = true;
     let contentWindow = aBrowser.contentWindow;
 
     let messageString = gNavigatorBundle.getString("activatePluginsMessage.message");
     let mainAction = {
-      label: gNavigatorBundle.getString("activatePluginsMessage.label"),
+      label: gNavigatorBundle.getString("activateAllPluginsMessage.label"),
       accessKey: gNavigatorBundle.getString("activatePluginsMessage.accesskey"),
       callback: function() { gPluginHandler.activatePlugins(contentWindow); }
     };
     let centerActions = gPluginHandler._makeCenterActions(aBrowser);
     let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
     let haveVulnerablePlugin = cwu.plugins.some(function(plugin) {
       let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1,14 +1,14 @@
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 // The minimum sizes for the auto-resize panel code.
-const PANEL_MIN_HEIGHT = 300;
+const PANEL_MIN_HEIGHT = 100;
 const PANEL_MIN_WIDTH = 330;
 
 let SocialUI = {
   // Called on delayed startup to initialize UI
   init: function SocialUI_init() {
     Services.obs.addObserver(this, "social:pref-changed", false);
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
@@ -198,50 +198,58 @@ let SocialChatBar = {
       this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
   },
   update: function() {
     if (!this.canShow)
       this.chatbar.removeAll();
   }
 }
 
-function sizeSocialPanelToContent(iframe) {
+function sizeSocialPanelToContent(panel, iframe) {
   // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
   let doc = iframe.contentDocument;
   if (!doc || !doc.body) {
     return;
   }
   let body = doc.body;
   // offsetHeight/Width don't include margins, so account for that.
   let cs = doc.defaultView.getComputedStyle(body);
   let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
   let height = Math.max(computedHeight, PANEL_MIN_HEIGHT);
-  iframe.style.height = height + "px";
   let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
   let width = Math.max(computedWidth, PANEL_MIN_WIDTH);
+  let wDiff = width - iframe.getBoundingClientRect().width;
+  // A panel resize will move the right margin - if that is where the anchor
+  // arrow is, the arrow will be mis-aligned from the anchor.  So we move the
+  // popup to compensate for that.  See bug 799014.
+  if (wDiff !== 0 && panel.getAttribute("side") == "right") {
+    let box = panel.boxObject;
+    panel.moveTo(box.screenX - wDiff, box.screenY);
+  }
+  iframe.style.height = height + "px";
   iframe.style.width = width + "px";
 }
 
 function DynamicResizeWatcher() {
   this._mutationObserver = null;
 }
 
 DynamicResizeWatcher.prototype = {
-  start: function DynamicResizeWatcher_start(iframe) {
+  start: function DynamicResizeWatcher_start(panel, iframe) {
     this.stop(); // just in case...
     let doc = iframe.contentDocument;
     this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
-      sizeSocialPanelToContent(iframe);
+      sizeSocialPanelToContent(panel, iframe);
     });
     // Observe anything that causes the size to change.
     let config = {attributes: true, characterData: true, childList: true, subtree: true};
     this._mutationObserver.observe(doc, config);
     // and since this may be setup after the load event has fired we do an
     // initial resize now.
-    sizeSocialPanelToContent(iframe);
+    sizeSocialPanelToContent(panel, iframe);
   },
   stop: function DynamicResizeWatcher_stop() {
     if (this._mutationObserver) {
       try {
         this._mutationObserver.disconnect();
       } catch (ex) {
         // may get "TypeError: can't access dead object" which seems strange,
         // but doesn't seem to indicate a real problem, so ignore it...
@@ -304,29 +312,30 @@ let SocialFlyout = {
     let panel = this.panel;
     panel.hidePopup();
     if (!panel.firstChild)
       return
     panel.removeChild(panel.firstChild);
   },
 
   onShown: function(aEvent) {
-    let iframe = this.panel.firstChild;
+    let panel = this.panel;
+    let iframe = panel.firstChild;
     this._dynamicResizer = new DynamicResizeWatcher();
     iframe.docShell.isActive = true;
     iframe.docShell.isAppTab = true;
     if (iframe.contentDocument.readyState == "complete") {
-      this._dynamicResizer.start(iframe);
+      this._dynamicResizer.start(panel, iframe);
       this.dispatchPanelEvent("socialFrameShow");
     } else {
       // first time load, wait for load and dispatch after load
       iframe.addEventListener("load", function panelBrowserOnload(e) {
         iframe.removeEventListener("load", panelBrowserOnload, true);
         setTimeout(function() {
-          SocialFlyout._dynamicResizer.start(iframe);
+          SocialFlyout._dynamicResizer.start(panel, iframe);
           SocialFlyout.dispatchPanelEvent("socialFrameShow");
         }, 0);
       }, true);
     }
   },
 
   onHidden: function(aEvent) {
     this._dynamicResizer.stop();
@@ -361,17 +370,17 @@ let SocialFlyout = {
     else if (aCallback) {
       try {
         aCallback(iframe.contentWindow);
       } catch(e) {
         Cu.reportError(e);
       }
     }
 
-    sizeSocialPanelToContent(iframe);
+    sizeSocialPanelToContent(panel, iframe);
     let anchor = document.getElementById("social-sidebar-browser");
     if (panel.state == "open") {
       // this is painful - there is no way to say "move to a new anchor offset",
       // only "move to new screen pos".  So we remember the last yOffset,
       // calculate the adjustment needed to the new yOffset, then calc the
       // screen Y position.
       let yAdjust = yOffset - this.yOffset;
       let box = panel.boxObject;
@@ -619,31 +628,67 @@ var SocialToolbar = {
       notLoggedInLabel.hidden = false;
       userNameBtn.hidden = true;
     }
   },
 
   updateButton: function SocialToolbar_updateButton() {
     this.updateButtonHiddenState();
     let provider = Social.provider;
-    let iconNames = Object.keys(provider.ambientNotificationIcons);
+    let icons = provider.ambientNotificationIcons;
+    let iconNames = Object.keys(icons);
     let iconBox = document.getElementById("social-toolbar-item");
     let notifBox = document.getElementById("social-notification-box");
     let panel = document.getElementById("social-notification-panel");
     panel.hidden = false;
+
+    let command = document.getElementById("Social:ToggleNotifications");
+    command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
+
+    const CACHE_PREF_NAME = "social.cached.notificationIcons";
+    // provider.profile == undefined means no response yet from the provider
+    // to tell us whether the user is logged in or not.
+    if (!SocialUI.haveLoggedInUser() && provider.profile !== undefined) {
+      // The provider has responded with a profile and the user isn't logged
+      // in.  The icons etc have already been removed by
+      // updateButtonHiddenState, so we want to nuke any cached icons we
+      // have and get out of here!
+      Services.prefs.clearUserPref(CACHE_PREF_NAME);
+      return;
+    }
+    if (Social.provider.profile === undefined) {
+      // provider has not told us about the login state yet - see if we have
+      // a cached version for this provider.
+      let cached;
+      try {
+        cached = JSON.parse(Services.prefs.getCharPref(CACHE_PREF_NAME));
+      } catch (ex) {}
+      if (cached && cached.provider == Social.provider.origin && cached.data) {
+        icons = cached.data;
+        iconNames = Object.keys(icons);
+        // delete the counter data as it is almost certainly stale.
+        for each(let name in iconNames) {
+          icons[name].counter = '';
+        }
+      }
+    } else {
+      // We have a logged in user - save the current set of icons back to the
+      // "cache" so we can use them next startup.
+      Services.prefs.setCharPref(CACHE_PREF_NAME,
+                                 JSON.stringify({provider: Social.provider.origin,
+                                                 data: icons}));
+    }
+
     let notificationFrames = document.createDocumentFragment();
     let iconContainers = document.createDocumentFragment();
 
     let createdFrames = [];
 
-    let command = document.getElementById("Social:ToggleNotifications");
-    command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
-
     for each(let name in iconNames) {
-      let icon = provider.ambientNotificationIcons[name];
+      let icon = icons[name];
 
       let notificationFrameId = "social-status-" + icon.name;
       let notificationFrame = document.getElementById(notificationFrameId);
       if (!notificationFrame) {
         notificationFrame = document.createElement("iframe");
         notificationFrame.setAttribute("type", "content");
         notificationFrame.setAttribute("class", "social-panel-frame");
         notificationFrame.setAttribute("id", notificationFrameId);
@@ -751,23 +796,23 @@ var SocialToolbar = {
     });
 
     panel.addEventListener("popupshown", function onpopupshown() {
       panel.removeEventListener("popupshown", onpopupshown);
       aToolbarButtonBox.setAttribute("open", "true");
       notificationFrame.docShell.isActive = true;
       notificationFrame.docShell.isAppTab = true;
       if (notificationFrame.contentDocument.readyState == "complete") {
-        dynamicResizer.start(notificationFrame);
+        dynamicResizer.start(panel, notificationFrame);
         dispatchPanelEvent("socialFrameShow");
       } else {
         // first time load, wait for load and dispatch after load
         notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
           notificationFrame.removeEventListener("load", panelBrowserOnload, true);
-          dynamicResizer.start(notificationFrame);
+          dynamicResizer.start(panel, notificationFrame);
           setTimeout(function() {
             dispatchPanelEvent("socialFrameShow");
           }, 0);
         }, true);
       }
     });
 
     let imageId = aToolbarButtonBox.getAttribute("id") + "-image";
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4153,17 +4153,17 @@ var XULBrowserWindow = {
       }
 
       // Disable find commands in documents that ask for them to be disabled.
       if (aLocationURI &&
           (aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"))) {
         // Don't need to re-enable/disable find commands for same-document location changes
         // (e.g. the replaceStates in about:addons)
         if (!(aFlags & nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
-          if (content.document.readyState == 'interactive' || content.document.readyState == "complete")
+          if (content.document.readyState == "interactive" || content.document.readyState == "complete")
             disableFindCommands(shouldDisableFind(content.document));
           else {
             content.document.addEventListener("readystatechange", onContentRSChange);
           }
         }
       } else
         disableFindCommands(false);
 
--- a/browser/base/content/test/browser_bug553455.js
+++ b/browser/base/content/test/browser_bug553455.js
@@ -331,17 +331,16 @@ function test_restartless() {
     is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
     // Wait for the install confirmation dialog
     wait_for_install_dialog(function(aWindow) {
       // Wait for the complete notification
       wait_for_notification(function(aPanel) {
         let notification = aPanel.childNodes[0];
         is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
-        is(notification.button.label, "Open Add-ons Manager", "Should have seen the right button");
         is(notification.getAttribute("label"),
            "XPI Test has been installed successfully.",
            "Should have seen the right message");
 
         AddonManager.getAllInstalls(function(aInstalls) {
           is(aInstalls.length, 0, "Should be no pending installs");
 
           AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) {
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-18.0a1
+19.0a1
--- a/browser/devtools/commandline/CmdJsb.jsm
+++ b/browser/devtools/commandline/CmdJsb.jsm
@@ -23,112 +23,115 @@ gcli.addCommand({
   returnValue:'string',
   params: [
     {
       name: 'url',
       type: 'string',
       description: gcli.lookup('jsbUrlDesc')
     },
     {
-      name: 'indentSize',
-      type: 'number',
-      description: gcli.lookup('jsbIndentSizeDesc'),
-      manual: gcli.lookup('jsbIndentSizeManual'),
-      defaultValue: 2
-    },
-    {
-      name: 'indentChar',
-      type: {
-        name: 'selection',
-        lookup: [
-          { name: "space", value: " " },
-          { name: "tab", value: "\t" }
-        ]
-      },
-      description: gcli.lookup('jsbIndentCharDesc'),
-      manual: gcli.lookup('jsbIndentCharManual'),
-      defaultValue: ' ',
-    },
-    {
-      name: 'preserveNewlines',
-      type: 'boolean',
-      description: gcli.lookup('jsbPreserveNewlinesDesc'),
-      manual: gcli.lookup('jsbPreserveNewlinesManual')
-    },
-    {
-      name: 'preserveMaxNewlines',
-      type: 'number',
-      description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
-      manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
-      defaultValue: -1
-    },
-    {
-      name: 'jslintHappy',
-      type: 'boolean',
-      description: gcli.lookup('jsbJslintHappyDesc'),
-      manual: gcli.lookup('jsbJslintHappyManual')
-    },
-    {
-      name: 'braceStyle',
-      type: {
-        name: 'selection',
-        data: ['collapse', 'expand', 'end-expand', 'expand-strict']
-      },
-      description: gcli.lookup('jsbBraceStyleDesc'),
-      manual: gcli.lookup('jsbBraceStyleManual'),
-      defaultValue: "collapse"
-    },
-    {
-      name: 'spaceBeforeConditional',
-      type: 'boolean',
-      description: gcli.lookup('jsbSpaceBeforeConditionalDesc'),
-      manual: gcli.lookup('jsbSpaceBeforeConditionalManual')
-    },
-    {
-      name: 'unescapeStrings',
-      type: 'boolean',
-      description: gcli.lookup('jsbUnescapeStringsDesc'),
-      manual: gcli.lookup('jsbUnescapeStringsManual')
+      group: gcli.lookup("jsbOptionsDesc"),
+      params: [
+        {
+          name: 'indentSize',
+          type: 'number',
+          description: gcli.lookup('jsbIndentSizeDesc'),
+          manual: gcli.lookup('jsbIndentSizeManual'),
+          defaultValue: 2
+        },
+        {
+          name: 'indentChar',
+          type: {
+            name: 'selection',
+            lookup: [
+              { name: "space", value: " " },
+              { name: "tab", value: "\t" }
+            ]
+          },
+          description: gcli.lookup('jsbIndentCharDesc'),
+          manual: gcli.lookup('jsbIndentCharManual'),
+          defaultValue: ' ',
+        },
+        {
+          name: 'doNotPreserveNewlines',
+          type: 'boolean',
+          description: gcli.lookup('jsbDoNotPreserveNewlinesDesc')
+        },
+        {
+          name: 'preserveMaxNewlines',
+          type: 'number',
+          description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
+          manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
+          defaultValue: -1
+        },
+        {
+          name: 'jslintHappy',
+          type: 'boolean',
+          description: gcli.lookup('jsbJslintHappyDesc'),
+          manual: gcli.lookup('jsbJslintHappyManual')
+        },
+        {
+          name: 'braceStyle',
+          type: {
+            name: 'selection',
+            data: ['collapse', 'expand', 'end-expand', 'expand-strict']
+          },
+          description: gcli.lookup('jsbBraceStyleDesc'),
+          manual: gcli.lookup('jsbBraceStyleManual'),
+          defaultValue: "collapse"
+        },
+        {
+          name: 'noSpaceBeforeConditional',
+          type: 'boolean',
+          description: gcli.lookup('jsbNoSpaceBeforeConditionalDesc')
+        },
+        {
+          name: 'unescapeStrings',
+          type: 'boolean',
+          description: gcli.lookup('jsbUnescapeStringsDesc'),
+          manual: gcli.lookup('jsbUnescapeStringsManual')
+        }
+      ]
     }
   ],
   exec: function(args, context) {
     let opts = {
       indent_size: args.indentSize,
       indent_char: args.indentChar,
-      preserve_newlines: args.preserveNewlines,
+      preserve_newlines: !args.doNotPreserveNewlines,
       max_preserve_newlines: args.preserveMaxNewlines == -1 ?
                              undefined : args.preserveMaxNewlines,
       jslint_happy: args.jslintHappy,
       brace_style: args.braceStyle,
-      space_before_conditional: args.spaceBeforeConditional,
+      space_before_conditional: !args.noSpaceBeforeConditional,
       unescape_strings: args.unescapeStrings
-    }
+    };
 
     let xhr = new XMLHttpRequest();
 
     try {
       xhr.open("GET", args.url, true);
     } catch(e) {
       return gcli.lookup('jsbInvalidURL');
     }
 
     let promise = context.createPromise();
 
     xhr.onreadystatechange = function(aEvt) {
       if (xhr.readyState == 4) {
         if (xhr.status == 200 || xhr.status == 0) {
           let browserDoc = context.environment.chromeDocument;
           let browserWindow = browserDoc.defaultView;
-          let browser = browserWindow.gBrowser;
-  
-          browser.selectedTab = browser.addTab("data:text/plain;base64," +
-            browserWindow.btoa(js_beautify(xhr.responseText, opts)));
+          let gBrowser = browserWindow.gBrowser;
+          let result = js_beautify(xhr.responseText, opts);
+
+          browserWindow.Scratchpad.ScratchpadManager.openScratchpad({text: result});
+
           promise.resolve();
-        }
-        else {
+        } else {
           promise.resolve("Unable to load page to beautify: " + args.url + " " +
                           xhr.status + " " + xhr.statusText);
         }
       };
     }
     xhr.send(null);
     return promise;
   }
--- a/browser/devtools/commandline/commandline.css
+++ b/browser/devtools/commandline/commandline.css
@@ -29,8 +29,11 @@
 }
 
 .gcli-menu-name,
 .gcli-out-shortcut,
 .gcli-help-synopsis {
   direction: ltr;
 }
 
+.gcli-row-out .nowrap {
+  white-space: nowrap;
+}
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -3051,17 +3051,20 @@ function hash(str) {
  */
 exports.setContents = function(elem, contents) {
   if (typeof HTMLElement !== 'undefined' && contents instanceof HTMLElement) {
     exports.clearElement(elem);
     elem.appendChild(contents);
     return;
   }
 
-  if (exports.isXmlDocument(elem.ownerDocument)) {
+  if ('innerHTML' in elem) {
+    elem.innerHTML = contents;
+  }
+  else {
     try {
       var ns = elem.ownerDocument.documentElement.namespaceURI;
       if (!ns) {
         ns = exports.NS_XHTML;
       }
       exports.clearElement(elem);
       contents = '<div xmlns="' + ns + '">' + contents + '</div>';
       var range = elem.ownerDocument.createRange();
@@ -3071,19 +3074,16 @@ exports.setContents = function(elem, con
       }
     }
     catch (ex) {
       console.error('Bad XHTML', ex);
       console.trace();
       throw ex;
     }
   }
-  else {
-    elem.innerHTML = contents;
-  }
 };
 
 /**
  * Load some HTML into the given document and return a DOM element.
  * This utility assumes that the html has a single root (other than whitespace)
  */
 exports.toDom = function(document, html) {
   var div = exports.createElement(document, 'div');
@@ -5464,17 +5464,17 @@ function Requisition(environment, doc) {
     catch (ex) {
       // Ignore
     }
   }
 
   // The command that we are about to execute.
   // @see setCommandConversion()
   this.commandAssignment = new CommandAssignment();
-  this._setAssignment(this.commandAssignment, null, true);
+  this.setAssignment(this.commandAssignment, null);
 
   // The object that stores of Assignment objects that we are filling out.
   // The Assignment objects are stored under their param.name for named
   // lookup. Note: We make use of the property of Javascript objects that
   // they are not just hashmaps, but linked-list hashmaps which iterate in
   // insertion order.
   // _assignments excludes the commandAssignment.
   this._assignments = {};
@@ -5551,17 +5551,17 @@ Requisition.prototype._commandAssignment
 
   this._assignments = {};
 
   var command = this.commandAssignment.value;
   if (command) {
     for (var i = 0; i < command.params.length; i++) {
       var param = command.params[i];
       var assignment = new Assignment(param, i);
-      this._setAssignment(assignment, null, true);
+      this.setAssignment(assignment, null);
       assignment.onAssignmentChange.add(this._assignmentChanged, this);
       this._assignments[param.name] = assignment;
     }
   }
   this.assignmentCount = Object.keys(this._assignments).length;
 };
 
 /**
@@ -5674,36 +5674,32 @@ Requisition.prototype.getAssignments = f
   }
   Object.keys(this._assignments).forEach(function(name) {
     assignments.push(this.getAssignment(name));
   }, this);
   return assignments;
 };
 
 /**
- * Alter the given assignment using the given arg.
- * @param assignment The assignment to alter
- * @param arg The new value for the assignment. An instance of Argument, or an
- * instance of Conversion, or null to set the blank value.
- */
-Requisition.prototype.setAssignment = function(assignment, arg) {
-  this._setAssignment(assignment, arg, false);
-};
-
-/**
  * Internal function to alter the given assignment using the given arg.
  * @param assignment The assignment to alter
  * @param arg The new value for the assignment. An instance of Argument, or an
  * instance of Conversion, or null to set the blank value.
- * @param skipArgUpdate (default=false) Adjusts the args in this requisition to
- * keep things up to date. Args should only be skipped when setAssignment is
- * being called as part of the update process.
- */
-Requisition.prototype._setAssignment = function(assignment, arg, skipArgUpdate) {
-  if (!skipArgUpdate) {
+ * @param options There are a number of ways to customize how the assignment
+ * is made, including:
+ * - argUpdate: (default:false) Adjusts the args in this requisition to keep
+ *   things up to date. Args should only be skipped when setAssignment is being
+ *   called as part of the update process.
+ * - matchPadding: (default:false) If argUpdate=true, and matchPadding=true
+ *   then further take the step of altering the whitespace on the prefix and
+ *   suffix of the new argument to match that of the old argument.
+ */
+Requisition.prototype.setAssignment = function(assignment, arg, options) {
+  options = options || {};
+  if (options.argUpdate) {
     var originalArgs = assignment.arg.getArgs();
 
     // Update the args array
     var replacementArgs = arg.getArgs();
     var maxLen = Math.max(originalArgs.length, replacementArgs.length);
     for (var i = 0; i < maxLen; i++) {
       // If there are no more original args, or if the original arg was blank
       // (i.e. not typed by the user), we'll just need to add at the end
@@ -5719,16 +5715,26 @@ Requisition.prototype._setAssignment = f
       }
 
       // If there are no more replacement args, we just remove the original args
       // Otherwise swap original args and replacements
       if (i >= replacementArgs.length) {
         this._args.splice(index, 1);
       }
       else {
+        if (options.matchPadding) {
+          if (replacementArgs[i].prefix.length === 0 &&
+              this._args[index].prefix.length !== 0) {
+            replacementArgs[i].prefix = this._args[index].prefix;
+          }
+          if (replacementArgs[i].suffix.length === 0 &&
+              this._args[index].suffix.length !== 0) {
+            replacementArgs[i].suffix = this._args[index].suffix;
+          }
+        }
         this._args[index] = replacementArgs[i];
       }
     }
   }
 
   var conversion;
   if (arg == null) {
     conversion = assignment.param.type.getBlank();
@@ -5756,17 +5762,17 @@ Requisition.prototype._setAssignment = f
   });
 };
 
 /**
  * Reset all the assignments to their default values
  */
 Requisition.prototype.setBlankArguments = function() {
   this.getAssignments().forEach(function(assignment) {
-    this._setAssignment(assignment, null, true);
+    this.setAssignment(assignment, null);
   }, this);
 };
 
 /**
  * Complete the argument at <tt>cursor</tt>.
  * Basically the same as:
  *   assignment = getAssignmentAt(cursor);
  *   assignment.value = assignment.conversion.predictions[0];
@@ -5796,23 +5802,23 @@ Requisition.prototype.complete = functio
       this._addSpace(assignment);
     }
 
     // Also add a space if we are in the name part of an assignment, however
     // this time we don't want the 'push the space to the next assignment'
     // logic, so we don't use addSpace
     if (assignment.isInName()) {
       var newArg = assignment.conversion.arg.beget({ prefixPostSpace: true });
-      this.setAssignment(assignment, newArg);
+      this.setAssignment(assignment, newArg, { argUpdate: true });
     }
   }
   else {
     // Mutate this argument to hold the completion
     var arg = assignment.arg.beget({ text: prediction.name });
-    this.setAssignment(assignment, arg);
+    this.setAssignment(assignment, arg, { argUpdate: true });
 
     if (!prediction.incomplete) {
       // The prediction is complete, add a space to let the user move-on
       this._addSpace(assignment);
 
       // Bug 779443 - Remove or explain the reparse
       if (assignment instanceof UnassignedAssignment) {
         this.update(this.toString());
@@ -5827,41 +5833,41 @@ Requisition.prototype.complete = functio
 /**
  * Pressing TAB sometimes requires that we add a space to denote that we're on
  * to the 'next thing'.
  * @param assignment The assignment to which to append the space
  */
 Requisition.prototype._addSpace = function(assignment) {
   var arg = assignment.conversion.arg.beget({ suffixSpace: true });
   if (arg !== assignment.conversion.arg) {
-    this.setAssignment(assignment, arg);
+    this.setAssignment(assignment, arg, { argUpdate: true });
   }
 };
 
 /**
  * Replace the current value with the lower value if such a concept exists.
  */
 Requisition.prototype.decrement = function(assignment) {
   var replacement = assignment.param.type.decrement(assignment.conversion.value);
   if (replacement != null) {
     var str = assignment.param.type.stringify(replacement);
     var arg = assignment.conversion.arg.beget({ text: str });
-    this.setAssignment(assignment, arg);
+    this.setAssignment(assignment, arg, { argUpdate: true });
   }
 };
 
 /**
  * Replace the current value with the higher value if such a concept exists.
  */
 Requisition.prototype.increment = function(assignment) {
   var replacement = assignment.param.type.increment(assignment.conversion.value);
   if (replacement != null) {
     var str = assignment.param.type.stringify(replacement);
     var arg = assignment.conversion.arg.beget({ text: str });
-    this.setAssignment(assignment, arg);
+    this.setAssignment(assignment, arg, { argUpdate: true });
   }
 };
 
 /**
  * Extract a canonical version of the input
  */
 Requisition.prototype.toCanonicalString = function() {
   var line = [];
@@ -6509,17 +6515,17 @@ Requisition.prototype._split = function(
   // Handle the special case of the user typing { javascript(); }
   // We use the hidden 'eval' command directly rather than shift()ing one of
   // the parameters, and parse()ing it.
   var conversion;
   if (args[0].type === 'ScriptArgument') {
     // Special case: if the user enters { console.log('foo'); } then we need to
     // use the hidden 'eval' command
     conversion = new Conversion(evalCommand, new ScriptArgument());
-    this._setAssignment(this.commandAssignment, conversion, true);
+    this.setAssignment(this.commandAssignment, conversion);
     return;
   }
 
   var argsUsed = 1;
 
   while (argsUsed <= args.length) {
     var arg = (argsUsed === 1) ?
               args[0] :
@@ -6536,17 +6542,17 @@ Requisition.prototype._split = function(
     // Previously we needed a way to hide commands depending context.
     // We have not resurrected that feature yet, but if we do we should
     // insert code here to ignore certain commands depending on the
     // context/environment
 
     argsUsed++;
   }
 
-  this._setAssignment(this.commandAssignment, conversion, true);
+  this.setAssignment(this.commandAssignment, conversion);
 
   for (var i = 0; i < argsUsed; i++) {
     args.shift();
   }
 
   // This could probably be re-written to consume args as we go
 };
 
@@ -6583,17 +6589,17 @@ Requisition.prototype._assign = function
   }
 
   // Special case: if there is only 1 parameter, and that's of type
   // text, then we put all the params into the first param
   if (this.assignmentCount === 1) {
     var assignment = this.getAssignment(0);
     if (assignment.param.type instanceof StringType) {
       var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
-      this._setAssignment(assignment, arg, true);
+      this.setAssignment(assignment, arg);
       return;
     }
   }
 
   // Positional arguments can still be specified by name, but if they are
   // then we need to ignore them when working them out positionally
   var unassignedParams = this.getParameterNames();
 
@@ -6628,74 +6634,74 @@ Requisition.prototype._assign = function
           var arrayArg = arrayArgs[assignment.param.name];
           if (!arrayArg) {
             arrayArg = new ArrayArgument();
             arrayArgs[assignment.param.name] = arrayArg;
           }
           arrayArg.addArgument(arg);
         }
         else {
-          this._setAssignment(assignment, arg, true);
+          this.setAssignment(assignment, arg);
         }
       }
       else {
         // Skip this parameter and handle as a positional parameter
         i++;
       }
     }
   }, this);
 
   // What's left are positional parameters assign in order
   unassignedParams.forEach(function(name) {
     var assignment = this.getAssignment(name);
 
     // If not set positionally, and we can't set it non-positionally,
     // we have to default it to prevent previous values surviving
     if (!assignment.param.isPositionalAllowed) {
-      this._setAssignment(assignment, null, true);
+      this.setAssignment(assignment, null);
       return;
     }
 
     // If this is a positional array argument, then it swallows the
     // rest of the arguments.
     if (assignment.param.type instanceof ArrayType) {
       var arrayArg = arrayArgs[assignment.param.name];
       if (!arrayArg) {
         arrayArg = new ArrayArgument();
         arrayArgs[assignment.param.name] = arrayArg;
       }
       arrayArg.addArguments(args);
       args = [];
     }
     else {
       if (args.length === 0) {
-        this._setAssignment(assignment, null, true);
+        this.setAssignment(assignment, null);
       }
       else {
         var arg = args.splice(0, 1)[0];
         // --foo and -f are named parameters, -4 is a number. So '-' is either
         // the start of a named parameter or a number depending on the context
         var isIncompleteName = assignment.param.type instanceof NumberType ?
             /-[-a-zA-Z_]/.test(arg.text) :
             arg.text.charAt(0) === '-';
 
         if (isIncompleteName) {
           this._unassigned.push(new UnassignedAssignment(this, arg));
         }
         else {
-          this._setAssignment(assignment, arg, true);
+          this.setAssignment(assignment, arg);
         }
       }
     }
   }, this);
 
   // Now we need to assign the array argument (if any)
   Object.keys(arrayArgs).forEach(function(name) {
     var assignment = this.getAssignment(name);
-    this._setAssignment(assignment, arrayArgs[name], true);
+    this.setAssignment(assignment, arrayArgs[name]);
   }, this);
 
   // What's left is can't be assigned, but we need to extract
   this._addUnassignedArgs(args);
 };
 
 exports.Requisition = Requisition;
 
@@ -8061,18 +8067,20 @@ JavascriptField.prototype.setConversion 
     }
   }, this);
 
   this.menu.show(items);
   this.setMessage(conversion.message);
 };
 
 JavascriptField.prototype.itemClicked = function(ev) {
-  this.onFieldChange(ev);
-  this.setMessage(ev.conversion.message);
+  var conversion = this.type.parse(ev.arg);
+
+  this.onFieldChange({ conversion: conversion });
+  this.setMessage(conversion.message);
 };
 
 JavascriptField.prototype.onInputChange = function(ev) {
   this.item = ev.currentTarget.item;
   var conversion = this.getConversion();
   this.onFieldChange({ conversion: conversion });
   this.setMessage(conversion.message);
 };
@@ -8175,38 +8183,37 @@ Menu.prototype.destroy = function() {
 };
 
 /**
  * The default is to do nothing when someone clicks on the menu.
  * This is called from template.html
  * @param ev The click event from the browser
  */
 Menu.prototype.onItemClickInternal = function(ev) {
-  var name = ev.currentTarget.querySelector('.gcli-menu-name').innerHTML;
+  var name = ev.currentTarget.querySelector('.gcli-menu-name').textContent;
   var arg = new Argument(name);
   arg.suffix = ' ';
 
-  var conversion = this.type.parse(arg);
-  this.onItemClick({ conversion: conversion });
+  this.onItemClick({ arg: arg });
 };
 
 /**
  * Display a number of items in the menu (or hide the menu if there is nothing
  * to display)
  * @param items The items to show in the menu
  * @param match Matching text to highlight in the output
  */
 Menu.prototype.show = function(items, match) {
   this.items = items.filter(function(item) {
     return item.hidden === undefined || item.hidden !== true;
   }.bind(this));
 
   if (match) {
     this.items = this.items.map(function(item) {
-      return gethighlightingProxy(item, match, this.template.ownerDocument);
+      return getHighlightingProxy(item, match, this.template.ownerDocument);
     }.bind(this));
   }
 
   if (this.items.length === 0) {
     this.element.style.display = 'none';
     return;
   }
 
@@ -8222,17 +8229,17 @@ Menu.prototype.show = function(items, ma
   this.element.appendChild(options);
 
   this.element.style.display = 'block';
 };
 
 /**
  * Create a proxy around an item that highlights matching text
  */
-function gethighlightingProxy(item, match, document) {
+function getHighlightingProxy(item, match, document) {
   if (typeof Proxy === 'undefined') {
     return item;
   }
   return Proxy.create({
     get: function(rcvr, name) {
       var value = item[name];
       if (name !== 'name') {
         return value;
@@ -8284,22 +8291,22 @@ Menu.prototype.setChoiceIndex = function
  * @return true if an item was 'clicked', false otherwise
  */
 Menu.prototype.selectChoice = function() {
   var selected = this.element.querySelector('.gcli-menu-highlight .gcli-menu-name');
   if (!selected) {
     return false;
   }
 
-  var name = selected.innerHTML;
+  var name = selected.textContent;
   var arg = new Argument(name);
   arg.suffix = ' ';
-
-  var conversion = this.type.parse(arg);
-  this.onItemClick({ conversion: conversion });
+  arg.prefix = ' ';
+
+  this.onItemClick({ arg: arg });
   return true;
 };
 
 /**
  * Hide the menu
  */
 Menu.prototype.hide = function() {
   this.element.style.display = 'none';
@@ -8493,18 +8500,20 @@ SelectionTooltipField.prototype.setConve
     // at least that has a name.
     return prediction.value.description ? prediction.value : prediction;
   }, this);
   this.menu.show(items, conversion.arg.text);
   this.setMessage(conversion.message);
 };
 
 SelectionTooltipField.prototype.itemClicked = function(ev) {
-  this.onFieldChange(ev);
-  this.setMessage(ev.conversion.message);
+  var conversion = this.type.parse(ev.arg);
+
+  this.onFieldChange({ conversion: conversion });
+  this.setMessage(conversion.message);
 };
 
 SelectionTooltipField.prototype.onInputChange = function(ev) {
   this.item = ev.currentTarget.item;
   var conversion = this.getConversion();
   this.onFieldChange({ conversion: conversion });
   this.setMessage(conversion.message);
 };
@@ -10335,17 +10344,18 @@ Tooltip.prototype.selectChoice = functio
   }
   return false;
 };
 
 /**
  * Called by the onFieldChange event on the current Field
  */
 Tooltip.prototype.fieldChanged = function(ev) {
-  this.requisition.setAssignment(this.assignment, ev.conversion.arg);
+  var options = { argUpdate: true, matchPadding: true };
+  this.requisition.setAssignment(this.assignment, ev.conversion.arg, options);
 
   var isError = ev.conversion.message != null && ev.conversion.message !== '';
   this.focusManager.setError(isError);
 
   // Nasty hack, the inputter won't know about the text change yet, so it will
   // get it's calculations wrong. We need to wait until the current set of
   // changes has had a chance to propagate
   this.document.defaultView.setTimeout(function() {
--- a/browser/devtools/commandline/test/browser_cmd_jsb.js
+++ b/browser/devtools/commandline/test/browser_cmd_jsb.js
@@ -1,57 +1,73 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the jsb command works as it should
 
 const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
                  "test/browser_cmd_jsb_script.jsi";
 
+let scratchpadWin = null;
+let Scratchpad = null;
+
 function test() {
-  DeveloperToolbarTest.test("about:blank", [ /*GJT_test*/ ]);
+  DeveloperToolbarTest.test("about:blank", [ GJT_test ]);
 }
 
 function GJT_test() {
   helpers.setInput('jsb');
   helpers.check({
     input:  'jsb',
-    hints:     ' <url> [indentSize] [indentChar] [preserveNewlines] [preserveMaxNewlines] [jslintHappy] [braceStyle] [spaceBeforeConditional] [unescapeStrings]',
+    hints:     ' <url> [options]',
     markup: 'VVV',
     status: 'ERROR'
   });
+  DeveloperToolbarTest.exec({ completed: false });
 
-  gBrowser.addTabsProgressListener({
-    onProgressChange: DeveloperToolbarTest.checkCalled(function GJT_onProgressChange(aBrowser) {
-      gBrowser.removeTabsProgressListener(this);
+  Services.ww.registerNotification(function(aSubject, aTopic, aData) {
+      if (aTopic == "domwindowopened") {
+        Services.ww.unregisterNotification(arguments.callee);
 
-      let win = aBrowser._contentWindow;
-      let uri = win.document.location.href;
-      let result = win.atob(uri.replace(/.*,/, ""));
+        scratchpadWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+        scratchpadWin.addEventListener("load", function GDT_onLoad() {
+          scratchpadWin.removeEventListener("load", GDT_onLoad, false);
+          Scratchpad = scratchpadWin.Scratchpad;
 
-      result = result.replace(/[\r\n]]/g, "\n");
+          let observer = {
+            onReady: function GJT_onReady() {
+              Scratchpad.removeObserver(observer);
 
-      let correct = "function somefunc() {\n" +
-                    "    for (let n = 0; n < 500; n++) {\n" +
-                    "        if (n % 2 == 1) {\n" +
-                    "            console.log(n);\n" +
-                    "            console.log(n + 1);\n" +
-                    "        }\n" +
-                    "    }\n" +
-                    "}";
-      is(result, correct, "JS has been correctly prettified");
-    })
-  });
+              let result = Scratchpad.getText();
+              result = result.replace(/[\r\n]]*/g, "\n");
+              let correct = "function somefunc() {\n" +
+                        "  if (true) // Some comment\n" +
+                        "  doSomething();\n" +
+                        "  for (let n = 0; n < 500; n++) {\n" +
+                        "    if (n % 2 == 1) {\n" +
+                        "      console.log(n);\n" +
+                        "      console.log(n + 1);\n" +
+                        "    }\n" +
+                        "  }\n" +
+                        "}";
+              is(result, correct, "JS has been correctly prettified");
+
+              finishUp();
+            },
+          };
+          Scratchpad.addObserver(observer);
+        }, false);
+      }
+    });
 
   info("Checking beautification");
+  DeveloperToolbarTest.exec({
+    typed: "jsb " + TEST_URI,
+    completed: false
+  });
+}
 
-  helpers.setInput('jsb ' + TEST_URI);
-  /*
-  helpers.check({
-    input:  'jsb',
-    hints:     ' [options]',
-    markup: 'VVV',
-    status: 'VALID'
-  });
-  */
-
-  DeveloperToolbarTest.exec({ completed: false });
-}
+let finishUp = DeveloperToolbarTest.checkCalled(function GJT_finishUp() {
+  if (scratchpadWin) {
+    scratchpadWin.close();
+    scratchpadWin = null;
+  }
+});
--- a/browser/devtools/commandline/test/browser_cmd_jsb_script.jsi
+++ b/browser/devtools/commandline/test/browser_cmd_jsb_script.jsi
@@ -1,1 +1,2 @@
-function somefunc(){for(let n=0;n<500;n++){if(n%2==1){console.log(n);console.log(n+1);}}}
+function somefunc(){if (true) // Some comment
+doSomething();for(let n=0;n<500;n++){if(n%2==1){console.log(n);console.log(n+1);}}}
deleted file mode 100644
--- a/browser/devtools/highlighter/InsideOutBox.jsm
+++ /dev/null
@@ -1,804 +0,0 @@
-/*
- * Software License Agreement (BSD License)
- *
- * Copyright (c) 2007, Parakey Inc.
- * All rights reserved.
- * 
- * Redistribution and use of this software in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 
- * * Redistributions of source code must retain the above
- *   copyright notice, this list of conditions and the
- *   following disclaimer.
- * 
- * * Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the
- *   following disclaimer in the documentation and/or other
- *   materials provided with the distribution.
- * 
- * * Neither the name of Parakey Inc. nor the names of its
- *   contributors may be used to endorse or promote products
- *   derived from this software without specific prior
- *   written permission of Parakey Inc.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Creator:
- *  Joe Hewitt
- * Contributors
- *  John J. Barton (IBM Almaden)
- *  Jan Odvarko (Mozilla Corp.)
- *  Max Stepanov (Aptana Inc.)
- *  Rob Campbell (Mozilla Corp.)
- *  Hans Hillen (Paciello Group, Mozilla)
- *  Curtis Bartley (Mozilla Corp.)
- *  Mike Collins (IBM Almaden)
- *  Kevin Decker
- *  Mike Ratcliffe (Comartis AG)
- *  Hernan Rodríguez Colmeiro
- *  Austin Andrews
- *  Christoph Dorn
- *  Steven Roussey (AppCenter Inc, Network54)
- */
-
-///////////////////////////////////////////////////////////////////////////
-//// InsideOutBox
-
-/**
- * InsideOutBoxView is a simple interface definition for views implementing
- * InsideOutBox controls. All implementors must define these methods.
- * Implemented in InspectorUI.
- */
-
-/*
-InsideOutBoxView = {
-  //
-   * Retrieves the parent object for a given child object.
-   * @param aChild
-   *        The child node to retrieve the parent object for.
-   * @returns a DOM node | null
-   //
-  getParentObject: function(aChild) {},
-
-  //
-   * Retrieves a given child node.
-   *
-   * If both index and previousSibling are passed, the implementation
-   * may assume that previousSibling will be the return for getChildObject
-   * with index-1.
-   * @param aParent
-   *        The parent object of the child object to retrieve.
-   * @param aIndex
-   *        The index of the child object to retrieve from aParent.
-   * @param aPreviousSibling
-   *        The previous sibling of the child object to retrieve.
-   *        Supercedes aIndex.
-   * @returns a DOM object | null
-   //
-  getChildObject: function(aParent, aIndex, aPreviousSibling) {},
-
-  //
-   * Renders the HTML representation of the object. Should return an HTML
-   * object which will be displayed to the user.
-   * @param aObject
-   *        The object to create the box object for.
-   * @param aIsRoot
-   *        Is the object the root object. May not be used in all
-   *        implementations.
-   * @returns an object box | null
-   //
-  createObjectBox: function(aObject, aIsRoot) {},
-
-  //
-  * Convenience wrappers for classList API.
-  * @param aObject
-  *        DOM node to query/set.
-  * @param aClassName
-  *        String containing the class name to query/set.
-  //
-  hasClass: function(aObject, aClassName) {},
-  addClass: function(aObject, aClassName) {},
-  removeClass: function(aObject, aClassName) {}
-};
-*/
-
-/**
- * Creates a tree based on objects provided by a separate "view" object.
- *
- * Construction uses an "inside-out" algorithm, meaning that the view's job is
- * first to tell us the ancestry of each object, and secondarily its
- * descendants.
- *
- * Constructor
- * @param aView
- *        The view requiring the InsideOutBox.
- * @param aBox
- *        The box object containing the InsideOutBox. Required to add/remove
- *        children during box manipulation (toggling opened or closed).
- */
-
-var EXPORTED_SYMBOLS = ["InsideOutBox"];
-
-const Cu = Components.utils;
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
-
-function InsideOutBox(aView, aBox)
-{
-  this.view = aView;
-  this.box = aBox;
-
-  this.rootObject = null;
-
-  this.rootObjectBox = null;
-  this.selectedObjectBox = null;
-  this.highlightedObjectBox = null;
-  this.scrollIntoView = false;
-};
-
-InsideOutBox.prototype =
-{
-  /**
-   * Highlight the given object node in the tree.
-   * @param aObject
-   *        the object to highlight.
-   * @returns objectBox
-   */
-  highlight: function IOBox_highlight(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    this.highlightObjectBox(objectBox);
-    return objectBox;
-  },
-
-  /**
-   * Open the given object node in the tree.
-   * @param aObject
-   *        The object node to open.
-   * @returns objectBox
-   */
-  openObject: function IOBox_openObject(aObject)
-  {
-    let object = aObject;
-    let firstChild = this.view.getChildObject(object, 0);
-    if (firstChild)
-      object = firstChild;
-
-    return this.openToObject(object);
-  },
-
-  /**
-   * Open the tree up to the given object node.
-   * @param aObject
-   *        The object in the tree to open to.
-   * @returns objectBox
-   */
-  openToObject: function IOBox_openToObject(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    this.openObjectBox(objectBox);
-    return objectBox;
-  },
-
-  /**
-   * Select the given object node in the tree.
-   * @param aObject
-   *        The object node to select.
-   * @param makeBoxVisible
-   *        Boolean. Open the object box in the tree?
-   * @param forceOpen
-   *        Force the object box open by expanding all elements in the tree?
-   * @param scrollIntoView
-   *        Scroll the objectBox into view?
-   * @returns nsIDOMNode|null
-   *          A DOM node that represents the "object box", the element that
-   *          holds/displays the given aObject representation in the tree. If
-   *          the object cannot be selected, if it is a stale object, null is
-   *          returned.
-   */
-  select:
-  function IOBox_select(aObject, makeBoxVisible, forceOpen, scrollIntoView)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    if (!objectBox) {
-      return null;
-    }
-    this.selectObjectBox(objectBox, forceOpen);
-    if (makeBoxVisible) {
-      this.openObjectBox(objectBox);
-    }
-    if (scrollIntoView) {
-      // We want to center the label of the element, not the whole tag
-      // (which includes all of its children, and is vertically huge).
-      LayoutHelpers.scrollIntoViewIfNeeded(objectBox.firstElementChild);
-    }
-
-    return objectBox;
-  },
-
-  /**
-   * Expands/contracts the given object, depending on its state.
-   * @param aObject
-   *        The tree node to expand/contract.
-   */
-  toggleObject: function IOBox_toggleObject(aObject)
-  {
-    let box = this.createObjectBox(aObject);
-    if (!(this.view.hasClass(box, "open")))
-      this.expandObjectBox(box);
-    else
-      this.contractObjectBox(box);
-  },
-
-  /**
-   * Expand the given object in the tree.
-   * @param aObject
-   *        The tree node to expand.
-   */
-  expandObject: function IOBox_expandObject(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    if (objectBox)
-      this.expandObjectBox(objectBox);
-  },
-
-  /**
-   * Contract the given object in the tree.
-   * @param aObject
-   *        The tree node to contract.
-   */
-  contractObject: function IOBox_contractObject(aObject)
-  {
-    let objectBox = this.createObjectBox(aObject);
-    if (objectBox)
-      this.contractObjectBox(objectBox);
-  },
-
-  /**
-   * General method for iterating over an object's ancestors and performing
-   * some function.
-   * @param aObject
-   *        The object whose ancestors we wish to iterate over.
-   * @param aCallback
-   *        The function to call with the object as argument.
-   */
-
-  iterateObjectAncestors: function IOBox_iterateObjectAncesors(aObject, aCallback)
-  {
-    let object = aObject;
-    if (!(aCallback && typeof(aCallback) == "function")) {
-      this.view._log("Illegal argument in IOBox.iterateObjectAncestors");
-      return;
-    }
-    while ((object = this.getParentObjectBox(object)))
-      aCallback(object);
-  },
-
-  /**
-   * Highlight the given objectBox in the tree.
-   * @param aObjectBox
-   *        The objectBox to highlight.
-   */
-  highlightObjectBox: function IOBox_highlightObjectBox(aObjectBox)
-  {
-    let self = this;
-
-    if (!aObjectBox)
-      return;
-
-    if (this.highlightedObjectBox) {
-      this.view.removeClass(this.highlightedObjectBox, "highlighted");
-      this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
-        self.view.removeClass(box, "highlightOpen");
-      });
-    }
-
-    this.highlightedObjectBox = aObjectBox;
-
-    this.view.addClass(aObjectBox, "highlighted");
-    this.iterateObjectAncestors(this.highlightedObjectBox, function (box) {
-      self.view.addClass(box, "highlightOpen");
-    });
-
-    aObjectBox.scrollIntoView(true);
-  },
-
-  /**
-   * Select the given objectBox in the tree, forcing it to be open if necessary.
-   * @param aObjectBox
-   *        The objectBox to select.
-   * @param forceOpen
-   *        Force the box (subtree) to be open?
-   */
-  selectObjectBox: function IOBox_selectObjectBox(aObjectBox, forceOpen)
-  {
-    let isSelected = this.selectedObjectBox &&
-      aObjectBox == this.selectedObjectBox;
-
-    // aObjectBox is already selected, return
-    if (isSelected)
-      return;
-
-    if (this.selectedObjectBox)
-      this.view.removeClass(this.selectedObjectBox, "selected");
-
-    this.selectedObjectBox = aObjectBox;
-
-    if (aObjectBox) {
-      this.view.addClass(aObjectBox, "selected");
-
-      // Force it open the first time it is selected
-      if (forceOpen)
-        this.expandObjectBox(aObjectBox, true);
-    }
-  },
-
-  /**
-   * Returns the next object box in the tree for navigation purposes.
-   */
-  nextObjectBox: function IOBox_nextObjectBox(aBoxObject)
-  {
-    let candidate;
-    let boxObject = aBoxObject || this.selectedObjectBox;
-    if (!boxObject)
-      return this.rootObjectBox;
-
-    // If expanded, return the first child.
-    let isOpen = this.view.hasClass(boxObject, "open");
-    let childObjectBox = this.getChildObjectBox(boxObject);
-    if (isOpen && childObjectBox && childObjectBox.firstChild) {
-      candidate = childObjectBox.firstChild;
-    } else {
-      // Otherwise we get the next available sibling.
-      while (boxObject) {
-        if (boxObject.nextSibling) {
-          boxObject = boxObject.nextSibling;
-          break;
-        }
-        boxObject = this.getParentObjectBox(boxObject);
-      }
-      candidate = boxObject;
-    }
-
-    // If the node is not an element (comments or text nodes), we
-    // jump to the next line.
-    if (candidate &&
-        candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) {
-      return this.nextObjectBox(candidate);
-    }
-
-    return candidate;
-  },
-
-  /**
-   * Returns the next object in the tree for navigation purposes.
-   */
-  nextObject: function IOBox_nextObject()
-  {
-    let next = this.nextObjectBox();
-    return next ? next.repObject : null;
-  },
-
-  /**
-   * Returns the object that is below the selection.
-   *
-   * @param aDistance Number of lines to jump.
-   */
-   farNextObject: function IOBox_farPreviousProject(aDistance)
-   {
-     let boxObject = this.selectedObjectBox;
-     while (aDistance-- > 0) {
-       let newBoxObject = this.nextObjectBox(boxObject);
-       if (!newBoxObject) {
-         break;
-       }
-       boxObject = newBoxObject;
-     }
-     return boxObject ? boxObject.repObject : null;
-   },
-
-  /**
-   * Returns the last visible child box of an object box.
-   */
-  lastVisible: function IOBox_lastVisibleChild(aNode)
-  {
-    if (!this.view.hasClass(aNode, "open"))
-      return aNode;
-
-    let childBox = this.getChildObjectBox(aNode);
-    if (!childBox || !childBox.lastChild)
-      return aNode;
-
-    return this.lastVisible(childBox.lastChild);
-  },
-
-  /**
-   * Returns the previous object box in the tree for navigation purposes.
-   */
-  previousObjectBox: function IOBox_previousObjectBox(aBoxObject)
-  {
-    let boxObject = aBoxObject || this.selectedObjectBox;
-    if (!boxObject)
-        return this.rootObjectBox;
-
-    let candidate;
-    let sibling = boxObject.previousSibling;
-    if (sibling) {
-      candidate = this.lastVisible(sibling);
-    } else {
-      candidate = this.getParentObjectBox(boxObject);
-    }
-
-    // If the node is not an element (comments or text nodes), we
-    // jump to the previous line.
-    if (candidate &&
-        candidate.repObject.nodeType != candidate.repObject.ELEMENT_NODE) {
-      return this.previousObjectBox(candidate);
-    }
-
-    return candidate;
-  },
-
-  /**
-   * Returns the previous object in the tree for navigation purposes.
-   */
-  previousObject: function IOBox_previousObject()
-  {
-    let boxObject = this.previousObjectBox();
-    return boxObject ? boxObject.repObject : null;
-  },
-
-  /**
-   * Returns the object that is above the selection.
-   *
-   * @param aDistance Number of lines to jump.
-   */
-   farPreviousObject: function IOBox_farPreviousProject(aDistance)
-   {
-     let boxObject = this.selectedObjectBox;
-     while (aDistance-- > 0) {
-       let newBoxObject = this.previousObjectBox(boxObject);
-       if (!newBoxObject) {
-         break;
-       }
-       boxObject = newBoxObject;
-       if (boxObject === this.rootObjectBox)
-         break;
-     }
-     return boxObject ? boxObject.repObject : null;
-   },
-
-  /**
-   * Open the ancestors of the given object box.
-   * @param aObjectBox
-   *        The object box to open.
-   */
-  openObjectBox: function IOBox_openObjectBox(aObjectBox)
-  {
-    if (!aObjectBox)
-      return;
-
-    let self = this;
-    this.iterateObjectAncestors(aObjectBox, function (box) {
-      self.view.addClass(box, "open");
-      let labelBox = box.querySelector(".nodeLabelBox");
-      if (labelBox)
-        labelBox.setAttribute("aria-expanded", "true");
-   });
-  },
-
-  /**
-   * Expand the given object box.
-   * @param aObjectBox
-   *        The object box to expand.
-   */
-  expandObjectBox: function IOBox_expandObjectBox(aObjectBox)
-  {
-    let nodeChildBox = this.getChildObjectBox(aObjectBox);
-
-    // no children means nothing to expand, return
-    if (!nodeChildBox)
-      return;
-
-    if (!aObjectBox.populated) {
-      let firstChild = this.view.getChildObject(aObjectBox.repObject, 0);
-      this.populateChildBox(firstChild, nodeChildBox);
-    }
-    let labelBox = aObjectBox.querySelector(".nodeLabelBox");
-    if (labelBox)
-      labelBox.setAttribute("aria-expanded", "true");
-    this.view.addClass(aObjectBox, "open");
-  },
-
-  /**
-   * Contract the given object box.
-   * @param aObjectBox
-   *        The object box to contract.
-   */
-  contractObjectBox: function IOBox_contractObjectBox(aObjectBox)
-  {
-    this.view.removeClass(aObjectBox, "open");
-    let nodeLabel = aObjectBox.querySelector(".nodeLabel");
-    let labelBox = nodeLabel.querySelector(".nodeLabelBox");
-    if (labelBox)
-      labelBox.setAttribute("aria-expanded", "false");
-  },
-
-  /**
-   * Toggle the given object box, forcing open if requested.
-   * @param aObjectBox
-   *        The object box to toggle.
-   * @param forceOpen
-   *        Force the objectbox open?
-   */
-  toggleObjectBox: function IOBox_toggleObjectBox(aObjectBox, forceOpen)
-  {
-    let isOpen = this.view.hasClass(aObjectBox, "open");
-
-    if (!forceOpen && isOpen)
-      this.contractObjectBox(aObjectBox);
-    else if (!isOpen)
-      this.expandObjectBox(aObjectBox);
-  },
-
-  /**
-   * Creates all of the boxes for an object, its ancestors, and siblings.
-   * @param aObject
-   *        The tree node to create the object boxes for.
-   * @returns anObjectBox or null
-   */
-  createObjectBox: function IOBox_createObjectBox(aObject)
-  {
-    if (!aObject)
-      return null;
-
-    this.rootObject = this.getRootNode(aObject) || aObject;
-
-    // Get or create all of the boxes for the target and its ancestors
-    let objectBox = this.createObjectBoxes(aObject, this.rootObject);
-
-    if (!objectBox)
-      return null;
-
-    if (aObject == this.rootObject)
-      return objectBox;
-
-    return this.populateChildBox(aObject, objectBox.parentNode);
-  },
-
-  /**
-   * Creates all of the boxes for an object, its ancestors, and siblings up to
-   * a root.
-   * @param aObject
-   *        The tree's object node to create the object boxes for.
-   * @param aRootObject
-   *        The root object at which to stop building object boxes.
-   * @returns an object box or null
-   */
-  createObjectBoxes: function IOBox_createObjectBoxes(aObject, aRootObject)
-  {
-    if (!aObject)
-      return null;
-
-    if (aObject == aRootObject) {
-      if (!this.rootObjectBox || this.rootObjectBox.repObject != aRootObject) {
-        if (this.rootObjectBox) {
-          try {
-            this.box.removeChild(this.rootObjectBox);
-          } catch (exc) {
-            this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " +
-              this.box + " must not contain " + this.rootObjectBox);
-          }
-        }
-
-        this.highlightedObjectBox = null;
-        this.selectedObjectBox = null;
-        this.rootObjectBox = this.view.createObjectBox(aObject, true);
-        this.box.appendChild(this.rootObjectBox);
-      }
-      return this.rootObjectBox;
-    }
-
-    let parentNode = this.view.getParentObject(aObject);
-    let parentObjectBox = this.createObjectBoxes(parentNode, aRootObject);
-
-    if (!parentObjectBox)
-      return null;
-
-    let parentChildBox = this.getChildObjectBox(parentObjectBox);
-
-    if (!parentChildBox)
-      return null;
-
-    let childObjectBox = this.findChildObjectBox(parentChildBox, aObject);
-
-    return childObjectBox ? childObjectBox
-      : this.populateChildBox(aObject, parentChildBox);
-  },
-
-  /**
-   * Locate the object box for a given object node.
-   * @param aObject
-   *        The given object node in the tree.
-   * @returns an object box or null.
-   */
-  findObjectBox: function IOBox_findObjectBox(aObject)
-  {
-    if (!aObject)
-      return null;
-
-    if (aObject == this.rootObject)
-      return this.rootObjectBox;
-
-    let parentNode = this.view.getParentObject(aObject);
-    let parentObjectBox = this.findObjectBox(parentNode);
-    if (!parentObjectBox)
-      return null;
-
-    let parentChildBox = this.getChildObjectBox(parentObjectBox);
-    if (!parentChildBox)
-      return null;
-
-    return this.findChildObjectBox(parentChildBox, aObject);
-  },
-
-  getAncestorByClass: function IOBox_getAncestorByClass(node, className)
-  {
-    for (let parent = node; parent; parent = parent.parentNode) {
-      if (this.view.hasClass(parent, className))
-        return parent;
-    }
-
-    return null;
-  },
-
-  /**
-   * We want all children of the parent of repObject.
-   */
-  populateChildBox: function IOBox_populateChildBox(repObject, nodeChildBox)
-  {
-    if (!repObject)
-      return null;
-
-    let parentObjectBox = this.getAncestorByClass(nodeChildBox, "nodeBox");
-
-    if (parentObjectBox.populated)
-      return this.findChildObjectBox(nodeChildBox, repObject);
-
-    let lastSiblingBox = this.getChildObjectBox(nodeChildBox);
-    let siblingBox = nodeChildBox.firstChild;
-    let targetBox = null;
-    let view = this.view;
-    let targetSibling = null;
-    let parentNode = view.getParentObject(repObject);
-
-    for (let i = 0; 1; ++i) {
-      targetSibling = view.getChildObject(parentNode, i, targetSibling);
-      if (!targetSibling)
-        break;
-
-      // Check if we need to start appending, or continue to insert before
-      if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
-        lastSiblingBox = null;
-
-      if (!siblingBox || siblingBox.repObject != targetSibling) {
-        let newBox = view.createObjectBox(targetSibling);
-        if (newBox) {
-          if (lastSiblingBox)
-            nodeChildBox.insertBefore(newBox, lastSiblingBox);
-          else
-            nodeChildBox.appendChild(newBox);
-        }
-
-        siblingBox = newBox;
-      }
-
-      if (targetSibling == repObject)
-        targetBox = siblingBox;
-
-      if (siblingBox && siblingBox.repObject == targetSibling)
-        siblingBox = siblingBox.nextSibling;
-    }
-
-    if (targetBox)
-      parentObjectBox.populated = true;
-
-    return targetBox;
-  },
-
-  /**
-   * Get the parent object box of a given object box.
-   * @params aObjectBox
-   *         The object box of the parent.
-   * @returns an object box or null
-   */
-  getParentObjectBox: function IOBox_getParentObjectBox(aObjectBox)
-  {
-    let parent = aObjectBox.parentNode ? aObjectBox.parentNode.parentNode : null;
-    return parent && parent.repObject ? parent : null;
-  },
-
-  /**
-   * Get the child object box of a given object box.
-   * @param aObjectBox
-   *        The object box whose child you want.
-   * @returns an object box or null
-   */
-  getChildObjectBox: function IOBox_getChildObjectBox(aObjectBox)
-  {
-    return aObjectBox.querySelector(".nodeChildBox");
-  },
-
-  /**
-   * Find the child object box for a given repObject within the subtree
-   * rooted at aParentNodeBox.
-   * @param aParentNodeBox
-   *        root of the subtree in which to search for repObject.
-   * @param aRepObject
-   *        The object you wish to locate in the subtree.
-   * @returns an object box or null
-   */
-  findChildObjectBox: function IOBox_findChildObjectBox(aParentNodeBox, aRepObject)
-  {
-    let childBox = aParentNodeBox.firstChild;
-    while (childBox) {
-      if (childBox.repObject == aRepObject)
-        return childBox;
-      childBox = childBox.nextSibling;
-    }
-    return null; // not found
-  },
-
-  /**
-   * Determines if the given node is an ancestor of the current root.
-   * @param aNode
-   *        The node to look for within the tree.
-   * @returns boolean
-   */
-  isInExistingRoot: function IOBox_isInExistingRoot(aNode)
-  {
-    let parentNode = aNode;
-    while (parentNode && parentNode != this.rootObject) {
-      parentNode = this.view.getParentObject(parentNode);
-    }
-    return parentNode == this.rootObject;
-  },
-
-  /**
-   * Get the root node of a given node.
-   * @param aNode
-   *        The node whose root you wish to retrieve.
-   * @returns a root node or null
-   */
-  getRootNode: function IOBox_getRootNode(aNode)
-  {
-    let node = aNode;
-    let tmpNode;
-    while ((tmpNode = this.view.getParentObject(node)))
-      node = tmpNode;
-
-    return node;
-  },
-
-  /**
-   * Clean up our mess.
-   */
-  destroy: function IOBox_destroy()
-  {
-    delete this.view;
-    delete this.box;
-    delete this.rootObject;
-    delete this.rootObjectBox;
-    delete this.selectedObjectBox;
-    delete this.highlightedObjectBox;
-    delete this.scrollIntoView;
-  }
-};
--- a/browser/devtools/highlighter/Makefile.in
+++ b/browser/devtools/highlighter/Makefile.in
@@ -6,19 +6,16 @@
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
-	domplate.jsm \
-	InsideOutBox.jsm \
-	TreePanel.jsm \
 	highlighter.jsm \
 	$(NULL)
 
 EXTRA_PP_JS_MODULES = \
 	inspector.jsm \
 	$(NULL)
 
 TEST_DIRS += test
deleted file mode 100644
--- a/browser/devtools/highlighter/TreePanel.jsm
+++ /dev/null
@@ -1,844 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cu = Components.utils;
-const Ci = Components.interfaces;
-
-Cu.import("resource:///modules/domplate.jsm");
-Cu.import("resource:///modules/InsideOutBox.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource:///modules/inspector.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
-
-var EXPORTED_SYMBOLS = ["TreePanel", "DOMHelpers"];
-
-const INSPECTOR_URI = "chrome://browser/content/inspector.html";
-
-/**
- * TreePanel
- * A container for the Inspector's HTML Tree Panel widget constructor function.
- * @param aContext nsIDOMWindow (xulwindow)
- * @param aIUI global InspectorUI object
- */
-function TreePanel(aContext, aIUI) {
-  this._init(aContext, aIUI);
-};
-
-TreePanel.prototype = {
-  showTextNodesWithWhitespace: false,
-  id: "treepanel", // DO NOT LOCALIZE
-  _open: false,
-
-  /**
-   * The tree panel container element.
-   * @returns xul:panel|xul:vbox|null
-   *          xul:panel is returned when the tree panel is not docked, or
-   *          xul:vbox when when the tree panel is docked.
-   *          null is returned when no container is available.
-   */
-  get container()
-  {
-    return this.document.getElementById("inspector-tree-box");
-  },
-
-  /**
-   * Main TreePanel boot-strapping method. Initialize the TreePanel with the
-   * originating context and the InspectorUI global.
-   * @param aContext nsIDOMWindow (xulwindow)
-   * @param aIUI global InspectorUI object
-   */
-  _init: function TP__init(aContext, aIUI)
-  {
-    this.IUI = aIUI;
-    this.window = aContext;
-    this.document = this.window.document;
-    this.button =
-     this.IUI.chromeDoc.getElementById("inspector-treepanel-toolbutton");
-
-    domplateUtils.setDOM(this.window);
-
-    this.DOMHelpers = new DOMHelpers(this.window);
-
-    let isOpen = this.isOpen.bind(this);
-
-    this.editingEvents = {};
-  },
-
-  /**
-   * Initialization function for the TreePanel.
-   */
-  initializeIFrame: function TP_initializeIFrame()
-  {
-    if (!this.initializingTreePanel || this.treeLoaded) {
-      return;
-    }
-    this.treeBrowserDocument = this.treeIFrame.contentDocument;
-    this.treePanelDiv = this.treeBrowserDocument.createElement("div");
-    this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
-    this.treePanelDiv.ownerPanel = this;
-    this.ioBox = new InsideOutBox(this, this.treePanelDiv);
-    this.ioBox.createObjectBox(this.IUI.win.document.documentElement);
-    this.treeLoaded = true;
-    this._boundTreeKeyPress = this.onTreeKeyPress.bind(this);
-    this.treeIFrame.addEventListener("keypress", this._boundTreeKeyPress.bind(this), true);
-    this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
-    this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
-    this.treeIFrame.focus();
-    delete this.initializingTreePanel;
-    Services.obs.notifyObservers(null,
-      this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null);
-    if (this.pendingSelection) {
-      this.select(this.pendingSelection.node, this.pendingSelection.scroll);
-      delete this.pendingSelection;
-    }
-  },
-
-  /**
-   * Open the inspector's tree panel and initialize it.
-   */
-  open: function TP_open()
-  {
-    if (this._open) {
-      return;
-    }
-
-    this._open = true;
-
-    this.button.setAttribute("checked", true);
-    this.initializingTreePanel = true;
-
-    this.treeIFrame = this.document.getElementById("inspector-tree-iframe");
-    if (!this.treeIFrame) {
-      this.treeIFrame = this.document.createElement("iframe");
-      this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
-      this.treeIFrame.flex = 1;
-      this.treeIFrame.setAttribute("type", "content");
-      this.treeIFrame.setAttribute("context", "inspector-node-popup");
-    }
-
-    let treeBox = null;
-    treeBox = this.document.createElement("vbox");
-    treeBox.id = "inspector-tree-box";
-    treeBox.state = "open";
-    try {
-      treeBox.height =
-        Services.prefs.getIntPref("devtools.inspector.htmlHeight");
-    } catch(e) {
-      treeBox.height = 112;
-    }
-
-    treeBox.minHeight = 64;
-
-    this.splitter = this.document.createElement("splitter");
-    this.splitter.id = "inspector-tree-splitter";
-    this.splitter.className = "devtools-horizontal-splitter";
-
-    let container = this.document.getElementById("appcontent");
-    container.appendChild(this.splitter);
-    container.appendChild(treeBox);
-
-    treeBox.appendChild(this.treeIFrame);
-
-    this._boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
-    {
-      this.treeIFrame.removeEventListener("load",
-        this._boundLoadedInitializeTreePanel, true);
-      delete this._boundLoadedInitializeTreePanel;
-      this.initializeIFrame();
-    }.bind(this);
-
-    this.treeIFrame.addEventListener("load",
-      this._boundLoadedInitializeTreePanel, true);
-
-    let src = this.treeIFrame.getAttribute("src");
-    if (src != INSPECTOR_URI) {
-      this.treeIFrame.setAttribute("src", INSPECTOR_URI);
-    } else {
-      this.treeIFrame.contentWindow.location.reload();
-    }
-  },
-
-  /**
-   * Close the TreePanel.
-   */
-  close: function TP_close()
-  {
-    this._open = false;
-
-    // Stop caring about the tree iframe load if it's in progress.
-    if (this._boundLoadedInitializeTreePanel) {
-      this.treeIFrame.removeEventListener("load",
-        this._boundLoadedInitializeTreePanel, true);
-      delete this._boundLoadedInitializeTreePanel;
-    }
-
-    this.button.removeAttribute("checked");
-    let treeBox = this.container;
-    Services.prefs.setIntPref("devtools.inspector.htmlHeight", treeBox.height);
-    let treeBoxParent = treeBox.parentNode;
-    treeBoxParent.removeChild(this.splitter);
-    treeBoxParent.removeChild(treeBox);
-
-    if (this.treePanelDiv) {
-      this.treePanelDiv.ownerPanel = null;
-      let parent = this.treePanelDiv.parentNode;
-      parent.removeChild(this.treePanelDiv);
-      this.treeIFrame.removeEventListener("keypress", this._boundTreeKeyPress, true);
-      delete this.treePanelDiv;
-      delete this.treeBrowserDocument;
-    }
-
-    if (this.ioBox) {
-      this.ioBox.destroy();
-      delete this.ioBox;
-    }
-
-    this.treeLoaded = false;
-  },
-
-  /**
-   * Is the TreePanel open?
-   * @returns boolean
-   */
-  isOpen: function TP_isOpen()
-  {
-    return this._open;
-  },
-
-  /**
-   * Toggle the TreePanel.
-   */
-  toggle: function TP_toggle()
-  {
-    this.isOpen() ? this.close() : this.open();
-  },
-
-  /**
-   * Create the ObjectBox for the given object.
-   * @param object nsIDOMNode
-   * @param isRoot boolean - Is this the root object?
-   * @returns InsideOutBox
-   */
-  createObjectBox: function TP_createObjectBox(object, isRoot)
-  {
-    let tag = domplateUtils.getNodeTag(object);
-    if (tag)
-      return tag.replace({object: object}, this.treeBrowserDocument);
-  },
-
-  getParentObject: function TP_getParentObject(node)
-  {
-    return this.DOMHelpers.getParentObject(node);
-  },
-
-  getChildObject: function TP_getChildObject(node, index, previousSibling)
-  {
-    return this.DOMHelpers.getChildObject(node, index, previousSibling,
-                                        this.showTextNodesWithWhitespace);
-  },
-
-  getFirstChild: function TP_getFirstChild(node)
-  {
-    return this.DOMHelpers.getFirstChild(node);
-  },
-
-  getNextSibling: function TP_getNextSibling(node)
-  {
-    return this.DOMHelpers.getNextSibling(node);
-  },
-
-  /////////////////////////////////////////////////////////////////////
-  // Event Handling
-
-  /**
-   * Handle click events in the html tree panel.
-   * @param aEvent
-   *        The mouse event.
-   */
-  onTreeClick: function TP_onTreeClick(aEvent)
-  {
-    let node;
-    let target = aEvent.target;
-    let hitTwisty = false;
-    if (this.hasClass(target, "twisty")) {
-      node = this.getRepObject(aEvent.target.nextSibling);
-      hitTwisty = true;
-    } else {
-      node = this.getRepObject(aEvent.target);
-    }
-
-    if (node) {
-      if (hitTwisty) {
-        this.ioBox.toggleObject(node);
-      } else {
-        if (this.IUI.inspecting) {
-          this.IUI.stopInspecting(true);
-        } else {
-          this.navigate(node);
-        }
-      }
-    }
-  },
-
-  /**
-   * Handle double-click events in the html tree panel.
-   * Double-clicking an attribute name or value allows it to be edited.
-   * @param aEvent
-   *        The mouse event.
-   */
-  onTreeDblClick: function TP_onTreeDblClick(aEvent)
-  {
-    // if already editing an attribute value, double-clicking elsewhere
-    // in the tree is the same as a click, which dismisses the editor
-    if (this.editingContext)
-      this.closeEditor();
-
-    let target = aEvent.target;
-
-    if (!this.hasClass(target, "editable")) {
-      return;
-    }
-
-    let repObj = this.getRepObject(target);
-
-    if (this.hasClass(target, "nodeValue")) {
-      let attrName = target.getAttribute("data-attributeName");
-      let attrVal = target.innerHTML;
-
-      this.editAttribute(target, repObj, attrName, attrVal);
-    }
-
-    if (this.hasClass(target, "nodeName")) {
-      let attrName = target.innerHTML;
-      let attrValNode = target.nextSibling.nextSibling; // skip 2 (=)
-
-      if (attrValNode)
-        this.editAttribute(target, repObj, attrName, attrValNode.innerHTML);
-    }
-  },
-
-  navigate: function TP_navigate(node)
-  {
-    if (!node)
-      return;
-    this.ioBox.select(node, false, false, true);
-
-    if (this.IUI.highlighter.isNodeHighlightable(node)) {
-      this.IUI.select(node, true, false, "treepanel");
-      this.IUI.highlighter.highlight(node);
-    }
-  },
-
-  onTreeKeyPress: function TP_onTreeKeyPress(aEvent)
-  {
-    let handled = true;
-    switch(aEvent.keyCode) {
-      case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
-        this.ioBox.contractObjectBox(this.ioBox.selectedObjectBox);
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_RIGHT:
-        this.ioBox.expandObjectBox(this.ioBox.selectedObjectBox);
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_UP:
-        this.navigate(this.ioBox.previousObject());
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_DOWN:
-        this.navigate(this.ioBox.nextObject());
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP:
-        this.navigate(this.ioBox.farPreviousObject(10));
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN:
-        this.navigate(this.ioBox.farNextObject(10));
-        break;
-      case Ci.nsIDOMKeyEvent.DOM_VK_HOME:
-        this.navigate(this.ioBox.rootObject);
-        break;
-      default:
-        handled = false;
-    }
-    if (handled) {
-      aEvent.stopPropagation();
-      aEvent.preventDefault();
-    }
-  },
-
-  /**
-   * Starts the editor for an attribute name or value.
-   * @param aAttrObj
-   *        The DOM object representing the attribute name or value in the HTML
-   *        Tree.
-   * @param aRepObj
-   *        The original DOM (target) object being inspected/edited
-   * @param aAttrName
-   *        The name of the attribute being edited
-   * @param aAttrVal
-   *        The current value of the attribute being edited
-   */
-  editAttribute:
-  function TP_editAttribute(aAttrObj, aRepObj, aAttrName, aAttrVal)
-  {
-    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
-    let editorInput =
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-    let attrDims = aAttrObj.getBoundingClientRect();
-    // figure out actual viewable viewport dimensions (sans scrollbars)
-    let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
-    let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
-
-    // saves the editing context for use when the editor is saved/closed
-    this.editingContext = {
-      attrObj: aAttrObj,
-      repObj: aRepObj,
-      attrName: aAttrName,
-      attrValue: aAttrVal
-    };
-
-    // highlight attribute-value node in tree while editing
-    this.addClass(aAttrObj, "editingAttributeValue");
-
-    // show the editor
-    this.addClass(editor, "editing");
-
-    // offset the editor below the attribute-value node being edited
-    let editorVerticalOffset = 2;
-
-    // keep the editor comfortably within the bounds of the viewport
-    let editorViewportBoundary = 5;
-
-    // outer editor is sized based on the <input> box inside it
-    editorInput.style.width = Math.min(attrDims.width, viewportWidth -
-                                editorViewportBoundary) + "px";
-    editorInput.style.height = Math.min(attrDims.height, viewportHeight -
-                                editorViewportBoundary) + "px";
-    let editorDims = editor.getBoundingClientRect();
-
-    // calculate position for the editor according to the attribute node
-    let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
-                    // center the editor against the attribute value
-                    ((editorDims.width - attrDims.width) / 2);
-    let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
-                    attrDims.height + editorVerticalOffset;
-
-    // but, make sure the editor stays within the visible viewport
-    editorLeft = Math.max(0, Math.min(
-                                      (this.treeIFrame.contentWindow.scrollX +
-                                          viewportWidth - editorDims.width),
-                                      editorLeft)
-                          );
-    editorTop = Math.max(0, Math.min(
-                                      (this.treeIFrame.contentWindow.scrollY +
-                                          viewportHeight - editorDims.height),
-                                      editorTop)
-                          );
-
-    // position the editor
-    editor.style.left = editorLeft + "px";
-    editor.style.top = editorTop + "px";
-
-    // set and select the text
-    if (this.hasClass(aAttrObj, "nodeValue")) {
-      editorInput.value = aAttrVal;
-      editorInput.select();
-    } else {
-      editorInput.value = aAttrName;
-      editorInput.select();
-    }
-
-    // listen for editor specific events
-    this.bindEditorEvent(editor, "click", function(aEvent) {
-      aEvent.stopPropagation();
-    });
-    this.bindEditorEvent(editor, "dblclick", function(aEvent) {
-      aEvent.stopPropagation();
-    });
-    this.bindEditorEvent(editor, "keypress",
-                          this.handleEditorKeypress.bind(this));
-
-    // event notification
-    Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED,
-                                  null);
-  },
-
-  /**
-   * Handle binding an event handler for the editor.
-   * (saves the callback for easier unbinding later)
-   * @param aEditor
-   *        The DOM object for the editor
-   * @param aEventName
-   *        The name of the event to listen for
-   * @param aEventCallback
-   *        The callback to bind to the event (and also to save for later
-   *          unbinding)
-   */
-  bindEditorEvent:
-  function TP_bindEditorEvent(aEditor, aEventName, aEventCallback)
-  {
-    this.editingEvents[aEventName] = aEventCallback;
-    aEditor.addEventListener(aEventName, aEventCallback, false);
-  },
-
-  /**
-   * Handle unbinding an event handler from the editor.
-   * (unbinds the previously bound and saved callback)
-   * @param aEditor
-   *        The DOM object for the editor
-   * @param aEventName
-   *        The name of the event being listened for
-   */
-  unbindEditorEvent: function TP_unbindEditorEvent(aEditor, aEventName)
-  {
-    aEditor.removeEventListener(aEventName, this.editingEvents[aEventName],
-                                  false);
-    this.editingEvents[aEventName] = null;
-  },
-
-  /**
-   * Handle keypress events in the editor.
-   * @param aEvent
-   *        The keyboard event.
-   */
-  handleEditorKeypress: function TP_handleEditorKeypress(aEvent)
-  {
-    if (aEvent.which == this.window.KeyEvent.DOM_VK_RETURN) {
-      this.saveEditor();
-      aEvent.preventDefault();
-      aEvent.stopPropagation();
-    } else if (aEvent.keyCode == this.window.KeyEvent.DOM_VK_ESCAPE) {
-      this.closeEditor();
-      aEvent.preventDefault();
-      aEvent.stopPropagation();
-    }
-  },
-
-  /**
-   * Close the editor and cleanup.
-   */
-  closeEditor: function TP_closeEditor()
-  {
-    if (!this.treeBrowserDocument) // already closed, bug 706092
-      return;
-
-    let editor = this.treeBrowserDocument.getElementById("attribute-editor");
-
-    let editorInput =
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-
-    // remove highlight from attribute-value node in tree
-    this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
-
-    // hide editor
-    this.removeClass(editor, "editing");
-
-    // stop listening for editor specific events
-    this.unbindEditorEvent(editor, "click");
-    this.unbindEditorEvent(editor, "dblclick");
-    this.unbindEditorEvent(editor, "keypress");
-
-    // clean up after the editor
-    editorInput.value = "";
-    editorInput.blur();
-    this.editingContext = null;
-    this.editingEvents = {};
-
-    // event notification
-    Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
-                                  null);
-  },
-
-  /**
-   * Commit the edits made in the editor, then close it.
-   */
-  saveEditor: function TP_saveEditor()
-  {
-    let editorInput =
-      this.treeBrowserDocument.getElementById("attribute-editor-input");
-    let dirty = false;
-
-    if (this.hasClass(this.editingContext.attrObj, "nodeValue")) {
-      // set the new attribute value on the original target DOM element
-      this.editingContext.repObj.setAttribute(this.editingContext.attrName,
-                                                editorInput.value);
-
-      // update the HTML tree attribute value
-      this.editingContext.attrObj.innerHTML = editorInput.value;
-      dirty = true;
-    }
-
-    if (this.hasClass(this.editingContext.attrObj, "nodeName")) {
-      // remove the original attribute from the original target DOM element
-      this.editingContext.repObj.removeAttribute(this.editingContext.attrName);
-
-      // set the new attribute value on the original target DOM element
-      this.editingContext.repObj.setAttribute(editorInput.value,
-                                              this.editingContext.attrValue);
-
-      // update the HTML tree attribute value
-      this.editingContext.attrObj.innerHTML = editorInput.value;
-      dirty = true;
-    }
-
-    this.IUI.isDirty = dirty;
-    this.IUI.nodeChanged("treepanel");
-
-    // event notification
-    Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
-                                  null);
-
-    this.closeEditor();
-  },
-
-  /**
-   * Simple tree select method.
-   * @param aNode the DOM node in the content document to select.
-   * @param aScroll boolean scroll to the visible node?
-   */
-  select: function TP_select(aNode, aScroll, aFrom)
-  {
-    if (this.ioBox) {
-      this.ioBox.select(aNode, true, aFrom != "treepanel", aScroll);
-    } else {
-      this.pendingSelection = { node: aNode, scroll: aScroll };
-    }
-  },
-
-  ///////////////////////////////////////////////////////////////////////////
-  //// Utility functions
-
-  /**
-   * Does the given object have a class attribute?
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   * @returns boolean
-   */
-  hasClass: function TP_hasClass(aNode, aClass)
-  {
-    if (!(aNode instanceof this.window.Element))
-      return false;
-    return aNode.classList.contains(aClass);
-  },
-
-  /**
-   * Add the class name to the given object.
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   */
-  addClass: function TP_addClass(aNode, aClass)
-  {
-    if (aNode instanceof this.window.Element)
-      aNode.classList.add(aClass);
-  },
-
-  /**
-   * Remove the class name from the given object
-   * @param aNode
-   *        the DOM node.
-   * @param aClass
-   *        The class string.
-   */
-  removeClass: function TP_removeClass(aNode, aClass)
-  {
-    if (aNode instanceof this.window.Element)
-      aNode.classList.remove(aClass);
-  },
-
-  /**
-   * Get the "repObject" from the HTML panel's domplate-constructed DOM node.
-   * In this system, a "repObject" is the Object being Represented by the box
-   * object. It is the "real" object that we're building our facade around.
-   *
-   * @param element
-   *        The element in the HTML panel the user clicked.
-   * @returns either a real node or null
-   */
-  getRepObject: function TP_getRepObject(element)
-  {
-    let target = null;
-    for (let child = element; child; child = child.parentNode) {
-      if (this.hasClass(child, "repTarget"))
-        target = child;
-
-      if (child.repObject) {
-        if (!target && this.hasClass(child.repObject, "repIgnore"))
-          break;
-        else
-          return child.repObject;
-      }
-    }
-    return null;
-  },
-
-  /**
-   * Remove a node box from the tree view.
-   * @param aElement
-   *        The DOM node to remove from the HTML IOBox.
-   */
-  deleteChildBox: function TP_deleteChildBox(aElement)
-  {
-    let childBox = this.ioBox.findObjectBox(aElement);
-    if (!childBox) {
-      return;
-    }
-    childBox.parentNode.removeChild(childBox);
-  },
-
-  /**
-   * Destructor function. Cleanup.
-   */
-  destroy: function TP_destroy()
-  {
-    if (this.isOpen()) {
-      this.close();
-    }
-
-    domplateUtils.setDOM(null);
-
-    if (this.DOMHelpers) {
-      this.DOMHelpers.destroy();
-      delete this.DOMHelpers;
-    }
-
-    if (this.treePanelDiv) {
-      this.treePanelDiv.ownerPanel = null;
-      let parent = this.treePanelDiv.parentNode;
-      parent.removeChild(this.treePanelDiv);
-      delete this.treePanelDiv;
-      delete this.treeBrowserDocument;
-    }
-
-    if (this.treeIFrame) {
-      this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false);
-      this.treeIFrame.removeEventListener("click", this.onTreeClick, false);
-      let parent = this.treeIFrame.parentNode;
-      parent.removeChild(this.treeIFrame);
-      delete this.treeIFrame;
-    }
-  }
-};
-
-
-/**
- * DOMHelpers
- * Makes DOM traversal easier. Goes through iframes.
- *
- * @constructor
- * @param nsIDOMWindow aWindow
- *        The content window, owning the document to traverse.
- */
-function DOMHelpers(aWindow) {
-  this.window = aWindow;
-};
-
-DOMHelpers.prototype = {
-  getParentObject: function Helpers_getParentObject(node)
-  {
-    let parentNode = node ? node.parentNode : null;
-
-    if (!parentNode) {
-      // Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
-      // and Notation. top level windows have no parentNode
-      if (node && node == this.window.Node.DOCUMENT_NODE) {
-        // document type
-        if (node.defaultView) {
-          let embeddingFrame = node.defaultView.frameElement;
-          if (embeddingFrame)
-            return embeddingFrame.parentNode;
-        }
-      }
-      // a Document object without a parentNode or window
-      return null;  // top level has no parent
-    }
-
-    if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
-      if (parentNode.defaultView) {
-        return parentNode.defaultView.frameElement;
-      }
-      // parent is document element, but no window at defaultView.
-      return null;
-    }
-
-    if (!parentNode.localName)
-      return null;
-
-    return parentNode;
-  },
-
-  getChildObject: function Helpers_getChildObject(node, index, previousSibling,
-                                                showTextNodesWithWhitespace)
-  {
-    if (!node)
-      return null;
-
-    if (node.contentDocument) {
-      // then the node is a frame
-      if (index == 0) {
-        return node.contentDocument.documentElement;  // the node's HTMLElement
-      }
-      return null;
-    }
-
-    if (node instanceof this.window.GetSVGDocument) {
-      let svgDocument = node.getSVGDocument();
-      if (svgDocument) {
-        // then the node is a frame
-        if (index == 0) {
-          return svgDocument.documentElement;  // the node's SVGElement
-        }
-        return null;
-      }
-    }
-
-    let child = null;
-    if (previousSibling)  // then we are walking
-      child = this.getNextSibling(previousSibling);
-    else
-      child = this.getFirstChild(node);
-
-    if (showTextNodesWithWhitespace)
-      return child;
-
-    for (; child; child = this.getNextSibling(child)) {
-      if (!this.isWhitespaceText(child))
-        return child;
-    }
-
-    return null;  // we have no children worth showing.
-  },
-
-  getFirstChild: function Helpers_getFirstChild(node)
-  {
-    let SHOW_ALL = Components.interfaces.nsIDOMNodeFilter.SHOW_ALL;
-    this.treeWalker = node.ownerDocument.createTreeWalker(node,
-      SHOW_ALL, null, false);
-    return this.treeWalker.firstChild();
-  },
-
-  getNextSibling: function Helpers_getNextSibling(node)
-  {
-    let next = this.treeWalker.nextSibling();
-
-    if (!next)
-      delete this.treeWalker;
-
-    return next;
-  },
-
-  isWhitespaceText: function Helpers_isWhitespaceText(node)
-  {
-    return node.nodeType == this.window.Node.TEXT_NODE &&
-                            !/[^\s]/.exec(node.nodeValue);
-  },
-
-  destroy: function Helpers_destroy()
-  {
-    delete this.window;
-    delete this.treeWalker;
-  }
-};
deleted file mode 100644
--- a/browser/devtools/highlighter/domplate.jsm
+++ /dev/null
@@ -1,1891 +0,0 @@
-/*
- * Software License Agreement (BSD License)
- *
- * Copyright (c) 2007, Parakey Inc.
- * All rights reserved.
- *
- * Redistribution and use of this software in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above
- *   copyright notice, this list of conditions and the
- *   following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- *   copyright notice, this list of conditions and the
- *   following disclaimer in the documentation and/or other
- *   materials provided with the distribution.
- *
- * * Neither the name of Parakey Inc. nor the names of its
- *   contributors may be used to endorse or promote products
- *   derived from this software without specific prior
- *   written permission of Parakey Inc.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Creator:
- *  Joe Hewitt
- * Contributors:
- *  John J. Barton (IBM Almaden)
- *  Jan Odvarko (Mozilla Corp.)
- *  Max Stepanov (Aptana Inc.)
- *  Rob Campbell (Mozilla Corp.)
- *  Hans Hillen (Paciello Group, Mozilla)
- *  Curtis Bartley (Mozilla Corp.)
- *  Mike Collins (IBM Almaden)
- *  Kevin Decker
- *  Mike Ratcliffe (Comartis AG)
- *  Hernan Rodríguez Colmeiro
- *  Austin Andrews
- *  Christoph Dorn
- *  Steven Roussey (AppCenter Inc, Network54)
- * Firefox Port Contributors:
- *  Rob Campbell
- *  Julian Viereck
- *  Mihai Sucan
- */
-
-var EXPORTED_SYMBOLS = ["domplate", "HTMLTemplates", "domplateUtils"];
-
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-const invisibleTags = {
-  "head": true,
-  "base": true,
-  "basefont": true,
-  "isindex": true,
-  "link": true,
-  "meta": true,
-  "script": true,
-  "style": true,
-  "title": true,
-};
-
-// End tags for void elements are forbidden
-// http://wiki.whatwg.org/wiki/HTML_vs._XHTML
-const selfClosingTags = {
-  "meta": 1,
-  "link": 1,
-  "area": 1,
-  "base": 1,
-  "col": 1,
-  "input": 1,
-  "img": 1,
-  "br": 1,
-  "hr": 1,
-  "param": 1,
-  "embed": 1
-};
-
-const reNotWhitespace = /[^\s]/;
-const showTextNodesWithWhitespace = false;
-
-var DOM = {};
-var domplateUtils = {};
-
-/**
- * Utility function to allow outside caller to set a global scope within
- * domplate's DOM object. Specifically for access to DOM constants and classes.
- * @param aGlobal
- *        The global object whose scope we wish to capture.
- */
-domplateUtils.setDOM = function(aGlobal)
-{
-  DOM = aGlobal;
-  if (!aGlobal) {
-    womb = null;
-  }
-};
-
-/**
- * main domplate constructor function.
- */
-
-let domplate = function()
-{
-  let lastSubject;
-  for (let i = 0; i < arguments.length; ++i)
-    lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
-
-  for (let name in lastSubject) {
-    let val = lastSubject[name];
-    if (isTag(val))
-      val.tag.subject = lastSubject;
-  }
-
-  return lastSubject;
-};
-
-var womb = null;
-
-///////////////////////////////////////////////////////////////////////////
-//// Base functions
-
-function DomplateTag(tagName)
-{
-  this.tagName = tagName;
-}
-
-function DomplateEmbed()
-{
-}
-
-function DomplateLoop()
-{
-}
-
-///////////////////////////////////////////////////////////////////////////
-//// Definitions
-
-domplate.context = function(context, fn)
-{
-  let lastContext = domplate.lastContext;
-  domplate.topContext = context;
-  fn.apply(context);
-  domplate.topContext = lastContext;
-};
-
-domplate.TAG = function()
-{
-  let embed = new DomplateEmbed();
-  return embed.merge(arguments);
-};
-
-domplate.FOR = function()
-{
-  let loop = new DomplateLoop();
-  return loop.merge(arguments);
-};
-
-DomplateTag.prototype =
-{
-  /**
-   * Initializer for DOM templates. Called to create new Functions objects
-   * like TR, TD, OBJLINK, etc. See defineTag
-   * @param args keyword argments for the template, the {} brace stuff after
-   *        the tag name, eg TR({...}, TD(...
-   * @param oldTag a nested tag, eg the TD tag in TR({...}, TD(...
-   */
-  merge: function(args, oldTag)
-  {
-    if (oldTag)
-      this.tagName = oldTag.tagName;
-
-    this.context = oldTag ? oldTag.context : null;  // normally null on construction
-    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) : [];
-
-    let attrs = args.length ? args[0] : null;
-    let hasAttrs = typeof(attrs) == "object" && !isTag(attrs);
-
-    // Do not clear children, they can be copied from the oldTag.
-    //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);
-  },
-
-  /**
-   * Parse node attributes.
-   * @param args
-   *        Object of arguments to process.
-   */
-  parseAttrs: function(args)
-  {
-    for (let name in args) {
-      let val = parseValue(args[name]);
-      readPartNames(val, this.vars);
-
-      if (name.indexOf("on") == 0) {
-        let eventName = name.substr(2);
-        if (!this.listeners)
-          this.listeners = [];
-        this.listeners.push(eventName, val);
-      } else if (name[0] == "_") {
-        let propName = name.substr(1);
-        if (!this.props)
-          this.props = {};
-        this.props[propName] = val;
-      } else if (name[0] == "$") {
-        let 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()
-  {
-    if (this.renderMarkup)
-      return;
-
-    this.compileMarkup();
-    this.compileDOM();
-  },
-
-  compileMarkup: function()
-  {
-    this.markupArgs = [];
-    let topBlock = [], topOuts = [], blocks = [],
-      info = {args: this.markupArgs, argIndex: 0};
-
-    this.generateMarkup(topBlock, topOuts, blocks, info);
-    this.addCode(topBlock, topOuts, blocks);
-
-    let fnBlock = ['(function (__code__, __context__, __in__, __out__'];
-    for (let i = 0; i < info.argIndex; ++i)
-      fnBlock.push(', s', i);
-    fnBlock.push(') {\n');
-
-    if (this.subject)
-      fnBlock.push('with (this) {\n');
-    if (this.context)
-      fnBlock.push('with (__context__) {\n');
-    fnBlock.push('with (__in__) {\n');
-
-    fnBlock.push.apply(fnBlock, blocks);
-
-    if (this.subject)
-      fnBlock.push('}\n');
-    if (this.context)
-      fnBlock.push('}\n');
-
-    fnBlock.push('}})\n');
-
-    function __link__(tag, code, outputs, args)
-    {
-      tag.tag.compile();
-
-      let tagOutputs = [];
-      let markupArgs = [code, tag.tag.context, args, tagOutputs];
-      markupArgs.push.apply(markupArgs, tag.tag.markupArgs);
-      tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs);
-
-      outputs.push(tag);
-      outputs.push(tagOutputs);
-    }
-
-    function __escape__(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);
-    }
-
-    function __loop__(iter, outputs, fn)
-    {
-      let iterOuts = [];
-      outputs.push(iterOuts);
-
-      if (iter instanceof Array)
-        iter = new ArrayIterator(iter);
-
-      try {
-        while (1) {
-          let value = iter.next();
-          let itemOuts = [0, 0];
-          iterOuts.push(itemOuts);
-          fn.apply(this, [value, itemOuts]);
-        }
-      } catch (exc) {
-        if (exc != StopIteration)
-          throw exc;
-      }
-    }
-
-    let js = fnBlock.join("");
-    this.renderMarkup = eval(js);
-  },
-
-  getVarNames: function(args)
-  {
-    if (this.vars)
-      args.push.apply(args, this.vars);
-
-    for (let i = 0; i < this.children.length; ++i) {
-      let child = this.children[i];
-      if (isTag(child))
-        child.tag.getVarNames(args);
-      else if (child instanceof Parts) {
-        for (let i = 0; i < child.parts.length; ++i) {
-          if (child.parts[i] instanceof Variable) {
-            let name = child.parts[i].name;
-            let names = name.split(".");
-            args.push(names[0]);
-          }
-        }
-      }
-    }
-  },
-
-  generateMarkup: function(topBlock, topOuts, blocks, info)
-  {
-    topBlock.push(',"<', this.tagName, '"');
-
-    for (let name in this.attrs) {
-      if (name != "class") {
-        let val = this.attrs[name];
-        topBlock.push(', " ', name, '=\\""');
-        addParts(val, ',', topBlock, info, true);
-        topBlock.push(', "\\""');
-      }
-    }
-
-    if (this.listeners) {
-      for (let i = 0; i < this.listeners.length; i += 2)
-        readPartNames(this.listeners[i+1], topOuts);
-    }
-
-    if (this.props) {
-      for (let name in this.props)
-        readPartNames(this.props[name], topOuts);
-    }
-
-    if (this.attrs.hasOwnProperty("class") || this.classes) {
-      topBlock.push(', " class=\\""');
-      if (this.attrs.hasOwnProperty("class"))
-        addParts(this.attrs["class"], ',', topBlock, info, true);
-      topBlock.push(', " "');
-      for (let name in this.classes) {
-        topBlock.push(', (');
-        addParts(this.classes[name], '', topBlock, info);
-        topBlock.push(' ? "', name, '" + " " : "")');
-      }
-      topBlock.push(', "\\""');
-    }
-    topBlock.push(',">"');
-
-    this.generateChildMarkup(topBlock, topOuts, blocks, info);
-    topBlock.push(',"</', this.tagName, '>"');
-  },
-
-  generateChildMarkup: function(topBlock, topOuts, blocks, info)
-  {
-    for (let i = 0; i < this.children.length; ++i) {
-      let child = this.children[i];
-      if (isTag(child))
-        child.tag.generateMarkup(topBlock, topOuts, blocks, info);
-      else
-        addParts(child, ',', topBlock, info, true);
-    }
-  },
-
-  addCode: function(topBlock, topOuts, blocks)
-  {
-    if (topBlock.length)
-      blocks.push('__code__.push(""', topBlock.join(""), ');\n');
-    if (topOuts.length)
-      blocks.push('__out__.push(', topOuts.join(","), ');\n');
-    topBlock.splice(0, topBlock.length);
-    topOuts.splice(0, topOuts.length);
-  },
-
-  addLocals: function(blocks)
-  {
-    let varNames = [];
-    this.getVarNames(varNames);
-
-    let map = {};
-    for (let i = 0; i < varNames.length; ++i) {
-      let name = varNames[i];
-      if ( map.hasOwnProperty(name) )
-        continue;
-
-      map[name] = 1;
-      let names = name.split(".");
-      blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';\n');
-    }
-  },
-
-  compileDOM: function()
-  {
-    let path = [];
-    let blocks = [];
-    this.domArgs = [];
-    path.embedIndex = 0;
-    path.loopIndex = 0;
-    path.staticIndex = 0;
-    path.renderIndex = 0;
-    let nodeCount = this.generateDOM(path, blocks, this.domArgs);
-
-    let fnBlock = ['(function (root, context, o'];
-
-    for (let i = 0; i < path.staticIndex; ++i)
-      fnBlock.push(', ', 's'+i);
-
-    for (let i = 0; i < path.renderIndex; ++i)
-      fnBlock.push(', ', 'd'+i);
-
-    fnBlock.push(') {\n');
-    for (let i = 0; i < path.loopIndex; ++i)
-      fnBlock.push('var l', i, ' = 0;\n');
-    for (let i = 0; i < path.embedIndex; ++i)
-      fnBlock.push('var e', i, ' = 0;\n');
-
-    if (this.subject)
-      fnBlock.push('with (this) {\n');
-    if (this.context)
-      fnBlock.push('with (context) {\n');
-
-    fnBlock.push(blocks.join(""));
-
-    if (this.subject)
-      fnBlock.push('}\n');
-    if (this.context)
-      fnBlock.push('}\n');
-
-    fnBlock.push('return ', nodeCount, ';\n');
-    fnBlock.push('})\n');
-
-    function __bind__(object, fn)
-    {
-      return function(event) { return fn.apply(object, [event]); }
-    }
-
-    function __link__(node, tag, args)
-    {
-      if (!tag || !tag.tag)
-        return;
-
-      tag.tag.compile();
-
-      let domArgs = [node, tag.tag.context, 0];
-      domArgs.push.apply(domArgs, tag.tag.domArgs);
-      domArgs.push.apply(domArgs, args);
-
-      return tag.tag.renderDOM.apply(tag.tag.subject, domArgs);
-    }
-
-    function __loop__(iter, fn)
-    {
-      let nodeCount = 0;
-      for (let i = 0; i < iter.length; ++i) {
-        iter[i][0] = i;
-        iter[i][1] = nodeCount;
-        nodeCount += fn.apply(this, iter[i]);
-      }
-      return nodeCount;
-    }
-
-    function __path__(parent, offset)
-    {
-      let root = parent;
-
-      for (let i = 2; i < arguments.length; ++i) {
-        let index = arguments[i];
-        if (i == 3)
-          index += offset;
-
-        if (index == -1)
-          parent = parent.parentNode;
-        else
-          parent = parent.childNodes[index];
-      }
-
-      return parent;
-    }
-    let js = fnBlock.join("");
-    // Exceptions on this line are often in the eval
-    this.renderDOM = eval(js);
-  },
-
-  generateDOM: function(path, blocks, args)
-  {
-    if (this.listeners || this.props)
-      this.generateNodePath(path, blocks);
-
-    if (this.listeners) {
-      for (let i = 0; i < this.listeners.length; i += 2) {
-        let val = this.listeners[i+1];
-        let arg = generateArg(val, path, args);
-        blocks.push('node.addEventListener("', this.listeners[i],
-          '", __bind__(this, ', arg, '), false);\n');
-      }
-    }
-
-    if (this.props) {
-      for (let name in this.props) {
-        let val = this.props[name];
-        let arg = generateArg(val, path, args);
-        blocks.push('node.', name, ' = ', arg, ';\n');
-      }
-    }
-
-    this.generateChildDOM(path, blocks, args);
-    return 1;
-  },
-
-  generateNodePath: function(path, blocks)
-  {
-    blocks.push("var node = __path__(root, o");
-    for (let i = 0; i < path.length; ++i)
-      blocks.push(",", path[i]);
-    blocks.push(");\n");
-  },
-
-  generateChildDOM: function(path, blocks, args)
-  {
-    path.push(0);
-    for (let i = 0; i < this.children.length; ++i) {
-      let 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();
-  },
-
-  /*
-   * We are just hiding from javascript.options.strict. For some reasons it's
-   * ok if we return undefined here.
-   * @return null or undefined or possibly a context.
-   */
-  getContext: function()
-  {
-    return this.context;
-  }
-};
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-DomplateEmbed.prototype = copyObject(DomplateTag.prototype,
-{
-  merge: function(args, oldTag)
-  {
-    this.value = oldTag ? oldTag.value : parseValue(args[0]);
-    this.attrs = oldTag ? oldTag.attrs : {};
-    this.vars = oldTag ? copyArray(oldTag.vars) : [];
-
-    let attrs = args[1];
-    for (let name in attrs) {
-      let val = parseValue(attrs[name]);
-      this.attrs[name] = val;
-      readPartNames(val, this.vars);
-    }
-
-    return creator(this, DomplateEmbed);
-  },
-
-  getVarNames: function(names)
-  {
-    if (this.value instanceof Parts)
-      names.push(this.value.parts[0].name);
-
-    if (this.vars)
-      names.push.apply(names, this.vars);
-  },
-
-  generateMarkup: function(topBlock, topOuts, blocks, info)
-  {
-    this.addCode(topBlock, topOuts, blocks);
-
-    blocks.push('__link__(');
-    addParts(this.value, '', blocks, info);
-    blocks.push(', __code__, __out__, {\n');
-
-    let lastName = null;
-    for (let name in this.attrs) {
-      if (lastName)
-        blocks.push(',');
-      lastName = name;
-
-      let val = this.attrs[name];
-      blocks.push('"', name, '":');
-      addParts(val, '', blocks, info);
-    }
-
-    blocks.push('});\n');
-  },
-
-  generateDOM: function(path, blocks, args)
-  {
-    let embedName = 'e' + path.embedIndex++;
-
-    this.generateNodePath(path, blocks);
-
-    let valueName = 'd' + path.renderIndex++;
-    let argsName = 'd' + path.renderIndex++;
-    blocks.push(embedName + ' = __link__(node, ', valueName, ', ',
-      argsName, ');\n');
-
-    return embedName;
-  }
-});
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-DomplateLoop.prototype = copyObject(DomplateTag.prototype,
-{
-  merge: function(args, oldTag)
-  {
-    this.isLoop = true;
-    this.varName = oldTag ? oldTag.varName : args[0];
-    this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
-    this.vars = [];
-
-    this.children = oldTag ? copyArray(oldTag.children) : [];
-
-    let offset = Math.min(args.length, 2);
-    parseChildren(args, offset, this.vars, this.children);
-
-    return creator(this, DomplateLoop);
-  },
-
-  getVarNames: function(names)
-  {
-    if (this.iter instanceof Parts)
-      names.push(this.iter.parts[0].name);
-
-    DomplateTag.prototype.getVarNames.apply(this, [names]);
-  },
-
-  generateMarkup: function(topBlock, topOuts, blocks, info)
-  {
-    this.addCode(topBlock, topOuts, blocks);
-
-    let iterName;
-    if (this.iter instanceof Parts) {
-      let part = this.iter.parts[0];
-      iterName = part.name;
-
-      if (part.format) {
-        for (let 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__) {\n');
-    this.generateChildMarkup(topBlock, topOuts, blocks, info);
-    this.addCode(topBlock, topOuts, blocks);
-    blocks.push('}]);\n');
-  },
-
-  generateDOM: function(path, blocks, args)
-  {
-    let iterName = 'd' + path.renderIndex++;
-    let counterName = 'i' + path.loopIndex;
-    let loopName = 'l' + path.loopIndex++;
-
-    if (!path.length)
-      path.push(-1, 0);
-
-    let preIndex = path.renderIndex;
-    path.renderIndex = 0;
-
-    let nodeCount = 0;
-
-    let subBlocks = [];
-    let basePath = path[path.length-1];
-    for (let i = 0; i < this.children.length; ++i) {
-      path[path.length - 1] = basePath + '+' + loopName + '+' + nodeCount;
-
-      let 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 (let i = 0; i < path.renderIndex; ++i)
-      blocks.push(',d' + i);
-    blocks.push(') {\n');
-    blocks.push(subBlocks.join(""));
-    blocks.push('return ', nodeCount, ';\n');
-    blocks.push('}]);\n');
-
-    path.renderIndex = preIndex;
-
-    return loopName;
-  }
-});
-
-///////////////////////////////////////////////////////////////////////////
-
-function Variable(name, format)
-{
-  this.name = name;
-  this.format = format;
-}
-
-function Parts(parts)
-{
-  this.parts = parts;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-function parseParts(str)
-{
-  let re = /\$([_A-Za-z][_A-Za-z0-9.|]*)/g;
-  let index = 0;
-  let parts = [];
-
-  let m;
-  while (m = re.exec(str)) {
-    let pre = str.substr(index, (re.lastIndex-m[0].length)-index);
-    if (pre)
-      parts.push(pre);
-
-    let expr = m[1].split("|");
-    parts.push(new Variable(expr[0], expr.slice(1)));
-    index = re.lastIndex;
-  }
-
-  if (!index)
-    return str;
-
-  let post = str.substr(index);
-  if (post)
-    parts.push(post);
-
-  return new Parts(parts);
-}
-
-function parseValue(val)
-{
-  return typeof(val) == 'string' ? parseParts(val) : val;
-}
-
-function parseChildren(args, offset, vars, children)
-{
-  for (let i = offset; i < args.length; ++i) {
-    let val = parseValue(args[i]);
-    children.push(val);
-    readPartNames(val, vars);
-  }
-}
-
-function readPartNames(val, vars)
-{
-  if (val instanceof Parts) {
-    for (let i = 0; i < val.parts.length; ++i) {
-      let part = val.parts[i];
-      if (part instanceof Variable)
-        vars.push(part.name);
-    }
-  }
-}
-
-function generateArg(val, path, args)
-{
-  if (val instanceof Parts) {
-    let vals = [];
-    for (let i = 0; i < val.parts.length; ++i) {
-      let part = val.parts[i];
-      if (part instanceof Variable) {
-        let varName = 'd' + path.renderIndex++;
-        if (part.format) {
-          for (let 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)
-{
-  let vals = [];
-  if (val instanceof Parts) {
-    for (let i = 0; i < val.parts.length; ++i) {
-      let part = val.parts[i];
-      if (part instanceof Variable) {
-        let partName = part.name;
-        if (part.format) {
-          for (let 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 + '"');
-
-  let parts = vals.join(delim);
-  if (parts)
-    block.push(delim, parts);
-}
-
-function isTag(obj)
-{
-  return (typeof(obj) == "function" || obj instanceof Function) && !!obj.tag;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//// creator
-
-function creator(tag, cons)
-{
-  let fn = new Function(
-    "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;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//// Utility functions
-
-function arrayInsert(array, index, other)
-{
-  for (let i = 0; i < other.length; ++i)
-    array.splice(i+index, 0, other[i]);
-
-  return array;
-}
-
-function cloneArray(array, fn)
-{
-  let newArray = [];
-
-  if (fn)
-    for (let i = 0; i < array.length; ++i)
-      newArray.push(fn(array[i]));
-  else
-    for (let i = 0; i < array.length; ++i)
-      newArray.push(array[i]);
-
-  return newArray;
-}
-
-// fn, thisObject, args => thisObject.fn(args, arguments);
-function bind()
-{
-  let args = cloneArray(arguments), fn = args.shift(), object = args.shift();
-  return function bind()
-  {
-    return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments));
-  }
-}
-
-function copyArray(oldArray)
-{
-  let array = [];
-  if (oldArray)
-    for (let i = 0; i < oldArray.length; ++i)
-      array.push(oldArray[i]);
-  return array;
-}
-
-function copyObject(l, r)
-{
-  let m = {};
-  extend(m, l);
-  extend(m, r);
-  return m;
-}
-
-function escapeNewLines(value)
-{
-  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
-}
-
-function extend(l, r)
-{
-  for (let n in r)
-    l[n] = r[n];
-}
-
-function cropString(text, limit, alterText)
-{
-  if (!alterText)
-    alterText = "..."; //…
-
-  text = text + "";
-
-  if (!limit)
-    limit = 88; // todo
-  var halfLimit = (limit / 2);
-  halfLimit -= 2; // adjustment for alterText's increase in size
-
-  if (text.length > limit)
-    return text.substr(0, halfLimit) + alterText +
-      text.substr(text.length - halfLimit);
-  else
-    return text;
-}
-
-function cropMultipleLines(text, limit)
-{
-  return escapeNewLines(this.cropString(text, limit));
-}
-
-function isVisible(elt)
-{
-  if (elt.localName) {
-    return elt.offsetWidth > 0 || elt.offsetHeight > 0 ||
-      elt.localName.toLowerCase() in invisibleTags;
-  } else {
-    return elt.offsetWidth > 0 || elt.offsetHeight > 0;
-  }
-    // || isElementSVG(elt) || isElementMathML(elt);
-}
-
-// Local Helpers
-
-function isElementXHTML(node)
-{
-  return node.nodeName == node.nodeName.toLowerCase();
-}
-
-function isContainerElement(element)
-{
-  let tag = element.localName.toLowerCase();
-  switch (tag) {
-    case "script":
-    case "style":
-    case "iframe":
-    case "frame":
-    case "tabbrowser":
-    case "browser":
-      return true;
-    case "link":
-      return element.getAttribute("rel") == "stylesheet";
-    case "embed":
-      return element.getSVGDocument();
-  }
-  return false;
-}
-
-domplateUtils.isWhitespace = function isWhitespace(text)
-{
-  return !reNotWhitespace.exec(text);
-};
-
-domplateUtils.isWhitespaceText = function isWhitespaceText(node)
-{
-  if (node instanceof DOM.HTMLAppletElement)
-    return false;
-  return node.nodeType == DOM.Node.TEXT_NODE && this.isWhitespace(node.nodeValue);
-}
-
-function isSelfClosing(element)
-{
-  //if (isElementSVG(element) || isElementMathML(element))
-  //    return true;
-  var tag = element.localName.toLowerCase();
-  return (selfClosingTags.hasOwnProperty(tag));
-};
-
-function isEmptyElement(element)
-{
-  if (showTextNodesWithWhitespace) {
-    return !element.firstChild && isSelfClosing(element);
-  } else {
-    for (let child = element.firstChild; child; child = child.nextSibling) {
-      if (!domplateUtils.isWhitespaceText(child))
-        return false;
-    }
-  }
-  return isSelfClosing(element);
-}
-
-function getEmptyElementTag(node)
-{
-  let isXhtml= isElementXHTML(node);
-  if (isXhtml)
-    return HTMLTemplates.XEmptyElement.tag;
-  else
-    return HTMLTemplates.EmptyElement.tag;
-}
-
-/**
- * Determines if the given node has any children which are elements.
- *
- * @param {Element} element Element to test.
- * @return true if immediate children of type Element exist, false otherwise
- */
-function hasNoElementChildren(element)
-{
-  if (element.childElementCount != 0)
-    return false;
-
-  return true;
-}
-
-domplateUtils.getNodeTag = function getNodeTag(node, expandAll)
-{
-  if (node instanceof DOM.Element) {
-    if (node instanceof DOM.HTMLHtmlElement && node.ownerDocument
-        && node.ownerDocument.doctype)
-      return HTMLTemplates.HTMLHtmlElement.tag;
-    else if (node instanceof DOM.HTMLAppletElement)
-      return getEmptyElementTag(node);
-    else if (isContainerElement(node))
-      return HTMLTemplates.Element.tag;
-    else if (isEmptyElement(node))
-      return getEmptyElementTag(node);
-    else if (hasNoElementChildren(node))
-      return HTMLTemplates.TextElement.tag;
-    else
-      return HTMLTemplates.Element.tag;
-  }
-  else if (node instanceof DOM.Text)
-    return HTMLTemplates.TextNode.tag;
-  else if (node instanceof DOM.CDATASection)
-    return HTMLTemplates.CDATANode.tag;
-  else if (node instanceof DOM.Comment)
-    return HTMLTemplates.CommentNode.tag;
-  else if (node instanceof DOM.SourceText)
-    return HTMLTemplates.SourceText.tag;
-  else
-    return HTMLTemplates.Nada.tag;
-}
-
-function getNodeBoxTag(nodeBox)
-{
-  let re = /([^\s]+)NodeBox/;
-  let m = re.exec(nodeBox.className);
-  if (!m)
-    return null;
-
-  let nodeBoxType = m[1];
-  if (nodeBoxType == "container")
-    return HTMLTemplates.Element.tag;
-  else if (nodeBoxType == "text")
-    return HTMLTemplates.TextElement.tag;
-  else if (nodeBoxType == "empty")
-    return HTMLTemplates.EmptyElement.tag;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//// ArrayIterator
-
-function ArrayIterator(array)
-{
-  let index = -1;
-
-  this.next = function()
-  {
-    if (++index >= array.length)
-      throw StopIteration;
-
-    return array[index];
-  };
-}
-
-function StopIteration() {}
-
-domplate.$break = function()
-{
-  throw StopIteration;
-};
-
-///////////////////////////////////////////////////////////////////////////
-//// Renderer
-
-var Renderer =
-{
-  renderHTML: function(args, outputs, self)
-  {
-    let code = [];
-    let markupArgs = [code, this.tag.getContext(), args, outputs];
-    markupArgs.push.apply(markupArgs, this.tag.markupArgs);
-    this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs);
-    return code.join("");
-  },
-
-  insertRows: function(args, before, self)
-  {
-    if (!args)
-      args = {};
-
-    this.tag.compile();
-
-    let outputs = [];
-    let html = this.renderHTML(args, outputs, self);
-
-    let doc = before.ownerDocument;
-    let table = doc.createElement("table");
-    table.innerHTML = html;
-
-    let tbody = table.firstChild;
-    let parent = before.localName.toLowerCase() == "tr" ? before.parentNode : before;
-    let after = before.localName.toLowerCase() == "tr" ? before.nextSibling : null;
-
-    let firstRow = tbody.firstChild, lastRow;
-    while (tbody.firstChild) {
-      lastRow = tbody.firstChild;
-      if (after)
-        parent.insertBefore(lastRow, after);
-      else
-        parent.appendChild(lastRow);
-    }
-
-    // To save the next poor soul:
-    // In order to properly apply properties and event handlers on elements
-    // constructed by a FOR tag, the tag needs to be able to iterate up and
-    // down the tree, meaning if FOR is the root element as is the case with
-    // many insertRows calls, it will need to iterator over portions of the
-    // new parent.
-    //
-    // To achieve this end, __path__ defines the -1 operator which allows
-    // parent traversal. When combined with the offset that we calculate
-    // below we are able to iterate over the elements.
-    //
-    // This fails when applied to a non-loop element as non-loop elements
-    // Do not generate to proper path to bounce up and down the tree.
-    let offset = 0;
-    if (this.tag.isLoop) {
-      let node = firstRow.parentNode.firstChild;
-      for (; node && node != firstRow; node = node.nextSibling)
-        ++offset;
-    }
-
-    // strict warning: this.tag.context undefined
-    let domArgs = [firstRow, this.tag.getContext(), 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];
-  },
-
-  insertBefore: function(args, before, self)
-  {
-    return this.insertNode(args, before.ownerDocument,
-      function(frag) {
-        before.parentNode.insertBefore(frag, before);
-      }, self);
-  },
-
-  insertAfter: function(args, after, self)
-  {
-    return this.insertNode(args, after.ownerDocument,
-      function(frag) {
-        after.parentNode.insertBefore(frag, after.nextSibling);
-      }, self);
-  },
-
-  insertNode: function(args, doc, inserter, self)
-  {
-    if (!args)
-      args = {};
-
-    this.tag.compile();
-
-    let outputs = [];
-    let html = this.renderHTML(args, outputs, self);
-
-    let range = doc.createRange();
-    range.selectNode(doc.body);
-    let frag = range.createContextualFragment(html);
-
-    let root = frag.firstChild;
-    root = inserter(frag) || root;
-
-    let 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;
-  },
-
-  replace: function(args, parent, self)
-  {
-    if (!args)
-      args = {};
-
-    this.tag.compile();
-
-    let outputs = [];
-    let html = this.renderHTML(args, outputs, self);
-
-    let root;
-    if (parent.nodeType == DOM.Node.ELEMENT_NODE) {
-      parent.innerHTML = html;
-      root = parent.firstChild;
-    } else {
-      if (!parent || parent.nodeType != DOM.Node.DOCUMENT_NODE)
-        return;
-
-      if (!womb || womb.ownerDocument != parent)
-        womb = parent.createElement("div");
-
-      womb.innerHTML = html;
-
-      root = womb.firstChild;
-    }
-
-    let 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;
-  },
-
-  append: function(args, parent, self)
-  {
-    if (!args)
-      args = {};
-
-    this.tag.compile();
-
-    let outputs = [];
-    let html = this.renderHTML(args, outputs, self);
-
-    if (!womb || womb.ownerDocument != parent.ownerDocument)
-      womb = parent.ownerDocument.createElement("div");
-    womb.innerHTML = html;
-
-    let root = womb.firstChild;
-    while (womb.firstChild)
-      parent.appendChild(womb.firstChild);
-
-    let 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;
-  }
-};
-
-///////////////////////////////////////////////////////////////////////////
-//// defineTags macro
-
-/**
- * Create default tags for a list of tag names.
- * @param Arguments
- *        list of string arguments
- */
-
-function defineTags()
-{
-  for (let i = 0; i < arguments.length; ++i) {
-    let tagName = arguments[i];
-    let fn = new Function("var newTag = new DomplateTag('" + tagName +
-      "'); return newTag.merge(arguments);");
-
-    let fnName = tagName.toUpperCase();
-    domplate[fnName] = fn;
-  }
-}
-
-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", "b", "span", "strong", "table",
-  "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul", "iframe",
-  "code"
-);
-
-///////////////////////////////////////////////////////////////////////////
-//// HTMLTemplates
-
-let HTMLTemplates = {
-  showTextNodesWithWhitespace: false
-};
-
-let BaseTemplates = {
-  showTextNodesWithWhitespace: false
-};
-
-///////////////////////////////////////////////////////////////////////////
-//// HTMLTemplates.Reps
-
-BaseTemplates.OBJECTLINK = domplate.A({
-  "class": "objectLink objectLink-$className a11yFocus",
-  _repObject: "$object"
-});
-
-BaseTemplates.Rep = domplate(
-{
-  className: "",
-  inspectable: true,
-
-  supportsObject: function(object, type)
-  {
-    return false;
-  },
-
-  inspectObject: function(object, context)
-  {
-    // Firebug.chrome.select(object);  // todo
-  },
-
-  browseObject: function(object, context)
-  {
-  },
-
-  persistObject: function(object, context)
-  {
-  },
-
-  getRealObject: function(object, context)
-  {
-    return object;
-  },
-
-  /**
-   * Return a sensible string title for the given object, removing any wrapper
-   * information from it.
-   * @param aObject
-   *        The object to get the title of.
-   * @returns string
-   */
-
-  getTitle: function(aObject)
-  {
-    // e.g., [object XPCWrappedNative [object foo]]
-    let label = safeToString(aObject);
-
-    const re =/\[object ([^\]]*)/;
-    let objectMatch = re.exec(label);
-    let secondObjectMatch = null;
-    if (objectMatch) {
-      // e.g., XPCWrappedNative [object foo
-      secondObjectMatch = re.exec(objectMatch[1]);
-    }
-
-    if (secondObjectMatch)
-      return secondObjectMatch[1];  // eg foo
-    else
-      return objectMatch ? objectMatch[1] : label;
-  },
-
-  getTooltip: function(object)
-  {
-    return null;
-  },
-
-  /**
-   * Called by chrome.onContextMenu to build the context menu when the
-   * underlying object has this rep.
-   * See also Panel for a similar function also called by onContextMenu.
-   * Extensions may monkey patch and chain off this call.
-   * @param object: the 'realObject', a model value, eg a DOM property
-   * @param target: the HTML element clicked on.
-   * @param context: the context, probably FirebugContext
-   * @returns an array of menu items.
-   */
-  getContextMenuItems: function(object, target, context)
-  {
-    return [];
-  },
-
-  /////////////////////////////////////////////////////////////////////////
-  // Convenience for domplates
-
-  STR: function(name)
-  {
-    return name; // todo getproperty?
-  },
-
-  cropString: function(text)
-  {
-    return cropString(text);
-  },
-
-  cropMultipleLines: function(text, limit)
-  {
-    return cropMultipleLines(text, limit);
-  },
-
-  toLowerCase: function(text)
-  {
-    return text ? text.toLowerCase() : text;
-  },
-
-  plural: function(n)
-  {
-    return n == 1 ? "" : "s";
-  }
-});
-
-BaseTemplates.Element = domplate(BaseTemplates.Rep,
-{
-  tag:
-    BaseTemplates.OBJECTLINK(
-      "&lt;",
-      domplate.SPAN({"class": "nodeTag"},
-        "$object.localName|toLowerCase"),
-      domplate.FOR("attr", "$object|attrIterator",
-        "&nbsp;$attr.localName=&quot;",
-        domplate.SPAN({"class": "nodeValue"},
-          "$attr.nodeValue"),
-        "&quot;"
-      ),
-      "&gt;"
-    ),
-
-  shortTag:
-    BaseTemplates.OBJECTLINK(
-      domplate.SPAN({"class": "$object|getVisible"},
-        domplate.SPAN({"class": "selectorTag"},
-          "$object|getSelectorTag"),
-        domplate.SPAN({"class": "selectorId"},
-          "$object|getSelectorId"),
-        domplate.SPAN({"class": "selectorClass"},
-          "$object|getSelectorClass"),
-        domplate.SPAN({"class": "selectorValue"},
-          "$object|getValue")
-      )
-    ),
-
-  getVisible: function(elt)
-  {
-    return isVisible(elt) ? "" : "selectorHidden";
-  },
-
-  getSelectorTag: function(elt)
-  {
-    return elt.localName.toLowerCase();
-  },
-
-  getSelectorId: function(elt)
-  {
-    return elt.id ? ("#" + elt.id) : "";
-  },
-
-  getSelectorClass: function(elt)
-  {
-    return elt.getAttribute("class")
-      ? ("." + elt.getAttribute("class").split(" ")[0])
-      : "";
-  },
-
-  getValue: function(elt)
-  { // todo getFileName
-    let value;
-/*
-    if (elt instanceof HTMLImageElement)
-      value = getFileName(elt.getAttribute("src"));
-    else if (elt instanceof HTMLAnchorElement)
-      value = getFileName(elt.getAttribute("href"));
-    else if (elt instanceof HTMLInputElement)
-      value = elt.getAttribute("value");
-    else if (elt instanceof HTMLFormElement)
-      value = getFileName(elt.getAttribute("action"));
-    else if (elt instanceof HTMLScriptElement)
-      value = getFileName(elt.getAttribute("src"));
-
-    return value ? " " + cropMultipleLines(value, 20) : ""; */
-    // trying a simplified version from above commented section
-    // todo
-    if (elt instanceof DOM.HTMLImageElement)
-      value = elt.getAttribute("src");
-    else if (elt instanceof DOM.HTMLAnchorElement)
-      value = elt.getAttribute("href");
-    else if (elt instanceof DOM.HTMLInputElement)
-      value = elt.getAttribute("value");
-    else if (elt instanceof DOM.HTMLFormElement)
-      value = elt.getAttribute("action");
-    else if (elt instanceof DOM.HTMLScriptElement)
-      value = elt.getAttribute("src");
-
-    return value ? " " + cropMultipleLines(value, 20) : "";
-  },
-
-  attrIterator: function(elt)
-  {
-    let attrs = [];
-    let idAttr, classAttr;
-    if (elt.attributes) {
-      for (let i = 0; i < elt.attributes.length; ++i) {
-        var attr = elt.attributes[i];
-        if (attr.localName.indexOf("-moz-math") != -1)
-          continue;
-        else if (attr.localName == "id")
-          idAttr = attr;
-        else if (attr.localName == "class")
-          classAttr = attr;
-        else
-          attrs.push(attr);
-      }
-    }
-    if (classAttr)
-      attrs.unshift(classAttr);
-    if (idAttr)
-      attrs.unshift(idAttr);
-    return attrs;
-  },
-
-  shortAttrIterator: function(elt)
-  {
-    let attrs = [];
-    if (elt.attributes) {
-      for (let i = 0; i < elt.attributes.length; ++i) {
-        let attr = elt.attributes[i];
-          if (attr.localName == "id" || attr.localName == "class")
-            attrs.push(attr);
-      }
-    }
-
-    return attrs;
-  },
-
-  getHidden: function(elt)
-  {
-    return isVisible(elt) ? "" : "nodeHidden";
-  },
-
-/* getXPath: function(elt)
-  {
-    return getElementTreeXPath(elt); // todo
-  }, */
-
-  getNodeTextGroups: function(element)
-  {
-    let text =  element.textContent;
-    return [{str: text, 'class': '', extra: ''}];
-  },
-
-  className: "element",
-
-  supportsObject: function(object, type)
-  {
-    return object instanceof DOM.Element;
-  },
-
-  browseObject: function(elt, context)
-  {
-    let tag = elt.localName.toLowerCase();
-    return true;
-  },
-});
-
-
-///////////////////////////////////////////////////////////////////////////
-//// HTMLTemplates.tags
-
-BaseTemplates.AttrTag =
-  domplate.SPAN({"class": "nodeAttr editGroup"},
-    "&nbsp;",
-    domplate.SPAN({"class": "nodeName editable"}, "$attr.nodeName"),
-    "=&quot;",
-    domplate.SPAN({"class": "nodeValue editable", "data-attributeName": "$attr.nodeName"}, "$attr.nodeValue"),
-    "&quot;");
-
-BaseTemplates.TextTag =
-  domplate.SPAN({"class": "nodeText editable"},
-    domplate.FOR("chr", "$object|getNodeTextGroups",
-      domplate.SPAN({"class": "$chr.class $chr.extra"},
-        "$chr.str")));
-
-///////////////////////////////////////////////////////////////////////////
-//// HTMLTemplates
-
-
-
-HTMLTemplates.CompleteElement = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class":
-        "nodeBox open $object|getHidden repIgnore",
-        _repObject: "$object", role : 'presentation'},
-      domplate.DIV({"class": "nodeLabel", role: "presentation"},
-        domplate.SPAN({"class": "nodeLabelBox repTarget repTarget",
-          role : 'treeitem', 'aria-expanded' : 'false'},
-          "&lt;",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
-          domplate.SPAN({"class": "nodeBracket"}, "&gt;")
-        )
-      ),
-      domplate.DIV({"class": "nodeChildBox", role :"group"},
-        domplate.FOR("child", "$object|childIterator",
-          domplate.TAG("$child|getNodeTag", {object: "$child"})
-        )
-      ),
-      domplate.DIV({"class": "nodeCloseLabel", role:"presentation"},
-        "&lt;/",
-        domplate.SPAN({"class": "nodeTag"},
-          "$object.nodeName|toLowerCase"),
-        "&gt;"
-      )
-    ),
-
-  getNodeTag: function(node)
-  {
-    return domplateUtils.getNodeTag(node, true);
-  },
-
-  childIterator: function(node)
-  {
-    if (node.contentDocument)
-      return [node.contentDocument.documentElement];
-
-    if (this.showTextNodesWithWhitespace)
-      return cloneArray(node.childNodes);
-    else {
-      let nodes = [];
-      for (let child = node.firstChild; child; child = child.nextSibling) {
-        if (child.nodeType != DOM.Node.TEXT_NODE || !domplateUtils.isWhitespaceText(child))
-          nodes.push(child);
-      }
-      return nodes;
-    }
-  }
-});
-
-HTMLTemplates.SoloElement = domplate(HTMLTemplates.CompleteElement,
-{
-  tag:
-    domplate.DIV({"class": "soloElement",
-      onmousedown: "$onMouseDown"},
-      HTMLTemplates.CompleteElement.tag),
-
-  onMouseDown: function(event)
-  {
-    for (let child = event.target; child; child = child.parentNode) {
-      if (child.repObject) { // todo
-          // let panel = Firebug.getElementPanel(child);
-          // Firebug.chrome.select(child.repObject);
-          break;
-      }
-    }
-  }
-});
-
-HTMLTemplates.Element = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class": "nodeBox containerNodeBox $object|getHidden repIgnore",
-      _repObject: "$object", role: "presentation"},
-      domplate.DIV({"class": "nodeLabel", role: "presentation"},
-        domplate.IMG({"class": "twisty", role: "presentation"}),
-        domplate.SPAN({"class": "nodeLabelBox repTarget",
-          role: 'treeitem', 'aria-expanded': 'false'},
-          "&lt;",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
-          domplate.SPAN({"class": "nodeBracket editable insertBefore"},
-            "&gt;")
-        )
-      ),
-      domplate.DIV({"class": "nodeChildBox", role: "group"}), /* nodeChildBox is special signal in insideOutBox */
-      domplate.DIV({"class": "nodeCloseLabel", role: "presentation"},
-        domplate.SPAN({"class": "nodeCloseLabelBox repTarget"},
-          "&lt;/",
-          domplate.SPAN({"class": "nodeTag"}, "$object.nodeName|toLowerCase"),
-          "&gt;"
-        )
-      )
-    )
-});
-
-HTMLTemplates.HTMLHtmlElement = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class":
-        "nodeBox htmlNodeBox containerNodeBox $object|getHidden repIgnore",
-        _repObject: "$object", role: "presentation"},
-      domplate.DIV({"class": "docType"},
-        "$object|getDocType"),
-      domplate.DIV({"class": "nodeLabel", role: "presentation"},
-        domplate.IMG({"class": "twisty", role: "presentation"}),
-        domplate.SPAN({"class": "nodeLabelBox repTarget",
-            role: 'treeitem', 'aria-expanded' : 'false'},
-          "&lt;",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
-          domplate.SPAN({"class":
-            "nodeBracket editable insertBefore"}, "&gt;")
-        )
-      ), /* nodeChildBox is special signal in insideOutBox */
-      domplate.DIV({"class": "nodeChildBox", role: "group"}),
-      domplate.DIV({"class": "nodeCloseLabel", role:  "presentation"},
-        domplate.SPAN({"class": "nodeCloseLabelBox repTarget"},
-          "&lt;/",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          "&gt;"
-        )
-      )
-    ),
-
-  getDocType: function(obj)
-  {
-    let doctype = obj.ownerDocument.doctype;
-    return '<!DOCTYPE ' + doctype.name + (doctype.publicId ? ' PUBLIC "' +
-      doctype.publicId + '"': '') + (doctype.systemId ? ' "' +
-      doctype.systemId + '"' : '') + '>';
-  }
-});
-
-HTMLTemplates.TextElement = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class":
-        "nodeBox textNodeBox $object|getHidden repIgnore",
-        _repObject: "$object", role: 'presentation'},
-      domplate.DIV({"class": "nodeLabel", role: "presentation"},
-        domplate.SPAN({"class": "nodeLabelBox repTarget",
-            role: 'treeitem'},
-          "&lt;",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
-          domplate.SPAN({"class":
-            "nodeBracket editable insertBefore"}, "&gt;"),
-          BaseTemplates.TextTag,
-          "&lt;/",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          "&gt;"
-        )
-      )
-    )
-});
-
-HTMLTemplates.EmptyElement = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class":
-        "nodeBox emptyNodeBox $object|getHidden repIgnore",
-        _repObject: "$object", role: 'presentation'},
-      domplate.DIV({"class": "nodeLabel", role: "presentation"},
-        domplate.SPAN({"class": "nodeLabelBox repTarget",
-            role: 'treeitem'},
-          "&lt;",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
-          domplate.SPAN({"class":
-            "nodeBracket editable insertBefore"}, "&gt;")
-        )
-      )
-    )
-});
-
-HTMLTemplates.XEmptyElement = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class":
-        "nodeBox emptyNodeBox $object|getHidden repIgnore",
-        _repObject: "$object", role: 'presentation'},
-      domplate.DIV({"class": "nodeLabel", role: "presentation"},
-        domplate.SPAN({"class": "nodeLabelBox repTarget",
-            role : 'treeitem'},
-          "&lt;",
-          domplate.SPAN({"class": "nodeTag"},
-            "$object.nodeName|toLowerCase"),
-          domplate.FOR("attr", "$object|attrIterator", BaseTemplates.AttrTag),
-          domplate.SPAN({"class":
-            "nodeBracket editable insertBefore"}, "/&gt;")
-        )
-      )
-    )
-});
-
-HTMLTemplates.AttrNode = domplate(BaseTemplates.Element,
-{
-  tag: BaseTemplates.AttrTag
-});
-
-HTMLTemplates.TextNode = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class": "nodeBox", _repObject: "$object",
-      role: 'presentation'}, BaseTemplates.TextTag)
-});
-
-HTMLTemplates.CDATANode = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class": "nodeBox", _repObject: "$object",
-      role: 'presentation'},
-        "&lt;![CDATA[",
-        domplate.SPAN({"class": "nodeText nodeCDATA editable"},
-          "$object.nodeValue"),
-        "]]&gt;")
-});
-
-HTMLTemplates.CommentNode = domplate(BaseTemplates.Element,
-{
-  tag:
-    domplate.DIV({"class": "nodeBox nodeComment",
-        _repObject: "$object", role : 'presentation'},
-      "&lt;!--",
-      domplate.SPAN({"class": "nodeComment editable"},
-        "$object.nodeValue"),
-      "--&gt;")
-});
-
-HTMLTemplates.Nada = domplate(BaseTemplates.Rep,
-{
-  tag: domplate.SPAN(""),
-  className: "nada"
-});
-
deleted file mode 100644
--- a/browser/devtools/highlighter/inspector.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
-  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
-  <link rel="stylesheet" href="chrome://browser/skin/devtools/htmlpanel.css" type="text/css"/>
-</head>
-<body role="application">
-  <div id="attribute-editor">
-    <input id="attribute-editor-input" />
-  </div>
-</body>
-</html>
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -8,22 +8,22 @@ const Cc = Components.classes;
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 var EXPORTED_SYMBOLS = ["InspectorUI"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/TreePanel.jsm");
 Cu.import("resource:///modules/devtools/MarkupView.jsm");
 Cu.import("resource:///modules/highlighter.jsm");
 Cu.import("resource:///modules/devtools/LayoutView.jsm");
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource:///modules/devtools/EventEmitter.jsm");
+Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
 
 // Inspector notifications dispatched through the nsIObserverService.
 const INSPECTOR_NOTIFICATIONS = {
   // Fires once the Inspector completes the initialization and opens up on
   // screen.
   OPENED: "inspector-opened",
 
   // Fires once the Inspector is closed.
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -1,18 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 browser.jar:
-    content/browser/inspector.html                (highlighter/inspector.html)
     content/browser/devtools/markup-view.xhtml    (markupview/markup-view.xhtml)
     content/browser/devtools/markup-view.css      (markupview/markup-view.css)
     content/browser/NetworkPanel.xhtml            (webconsole/NetworkPanel.xhtml)
-    content/browser/devtools/HUDService-content.js (webconsole/HUDService-content.js)
     content/browser/devtools/webconsole.js        (webconsole/webconsole.js)
 *   content/browser/devtools/webconsole.xul       (webconsole/webconsole.xul)
 *   content/browser/scratchpad.xul                (scratchpad/scratchpad.xul)
     content/browser/scratchpad.js                 (scratchpad/scratchpad.js)
     content/browser/splitview.css                 (shared/splitview.css)
     content/browser/styleeditor.xul               (styleeditor/styleeditor.xul)
     content/browser/styleeditor.css               (styleeditor/styleeditor.css)
     content/browser/devtools/csshtmltree.xul      (styleinspector/csshtmltree.xul)
--- a/browser/devtools/layoutview/LayoutView.jsm
+++ b/browser/devtools/layoutview/LayoutView.jsm
@@ -124,16 +124,18 @@ LayoutView.prototype = {
    */
   destroy: function LV_destroy() {
     this.inspector.off("select", this.onSelect);
     this.inspector.off("unlocked", this.onUnlock);
     this.browser.removeEventListener("MozAfterPaint", this.update, true);
     this.iframe.removeEventListener("keypress", this.bound_handleKeypress, true);
     this.inspector.chromeWindow.removeEventListener("message", this.onMessage, true);
     this.close();
+    this.sizeHeadingLabel = null;
+    this.sizeLabel = null;
     this.iframe = null;
     this.view.parentNode.removeChild(this.view);
   },
 
   /**
    * Build the Layout container:
    *
    * <vbox id="inspector-layoutview-container">
@@ -154,16 +156,20 @@ LayoutView.prototype = {
 
   /**
    * Called when the iframe is loaded.
    */
   onDocumentReady: function LV_onDocumentReady() {
     this.documentReady = true;
     this.doc = this.iframe.contentDocument;
 
+    // Save reference to the labels displaying size of the node.
+    this.sizeLabel = this.doc.querySelector(".size > span");
+    this.sizeHeadingLabel = this.doc.getElementById("element-size");
+
     // We can't do that earlier because open() and close() need to do stuff
     // inside the iframe.
 
     if (this.inspector.locked)
       this.onSelect();
     else
       this.onUnlock();
 
@@ -294,53 +300,58 @@ LayoutView.prototype = {
 
     // First, we update the first part of the layout view, with
     // the size of the element.
 
     let clientRect = node.getBoundingClientRect();
     let width = Math.round(clientRect.width);
     let height = Math.round(clientRect.height);
 
-    let elt = this.doc.querySelector("#element-size");
     let newLabel = width + "x" + height;
-    if (elt.textContent != newLabel) {
-      elt.textContent = newLabel;
+    if (this.sizeHeadingLabel.textContent != newLabel) {
+      this.sizeHeadingLabel.textContent = newLabel;
     }
 
     // If the view is closed, no need to do anything more.
     if (!this.isOpen) return;
 
     // We compute and update the values of margins & co.
     let style = this.browser.contentWindow.getComputedStyle(node);;
 
     for (let i in this.map) {
-      let selector = this.map[i].selector;
       let property = this.map[i].property;
       this.map[i].value = parseInt(style.getPropertyValue(property));
     }
 
     let margins = this.processMargins(node);
     if ("top" in margins) this.map.marginTop.value = "auto";
     if ("right" in margins) this.map.marginRight.value = "auto";
     if ("bottom" in margins) this.map.marginBottom.value = "auto";
     if ("left" in margins) this.map.marginLeft.value = "auto";
 
     for (let i in this.map) {
       let selector = this.map[i].selector;
       let span = this.doc.querySelector(selector);
+      if (span.textContent.length > 0 &&
+          span.textContent == this.map[i].value) {
+        continue;
+      }
       span.textContent = this.map[i].value;
     }
 
     width -= this.map.borderLeft.value + this.map.borderRight.value +
              this.map.paddingLeft.value + this.map.paddingRight.value;
 
     height -= this.map.borderTop.value + this.map.borderBottom.value +
               this.map.paddingTop.value + this.map.paddingBottom.value;
 
-    this.doc.querySelector(".size > span").textContent = width + "x" + height;
+    let newValue = width + "x" + height;
+    if (this.sizeLabel.textContent != newValue) {
+      this.sizeLabel.textContent = newValue;
+    }
   },
 
   /**
    * Find margins declared 'auto'
    */
   processMargins: function LV_processMargins(node) {
     let margins = {};
 
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/FloatingScrollbars.jsm");
+Cu.import("resource:///modules/devtools/EventEmitter.jsm");
 
 var EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
 
 const MIN_WIDTH = 50;
 const MIN_HEIGHT = 50;
 
 const MAX_WIDTH = 10000;
 const MAX_HEIGHT = 10000;
@@ -62,16 +63,23 @@ let ResponsiveUIManager = {
           aTab.__responsiveUI.close();
         }
         break;
       case "resize toggle":
           this.toggle(aWindow, aTab);
       default:
     }
   },
+
+  get events() {
+    if (!this._eventEmitter) {
+      this._eventEmitter = new EventEmitter();
+    }
+    return this._eventEmitter;
+  },
 }
 
 let presets = [
   // Phones
   {key: "320x480", width: 320, height: 480},    // iPhone, B2G, with <meta viewport>
   {key: "360x640", width: 360, height: 640},    // Android 4, phones, with <meta viewport>
 
   // Tablets
@@ -157,16 +165,17 @@ function ResponsiveUI(aWindow, aTab)
 
   try {
     if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
       this.rotate();
     }
   } catch(e) {}
 
   switchToFloatingScrollbars(this.tab);
+  ResponsiveUIManager.events.emit("on", this.tab, this);
 }
 
 ResponsiveUI.prototype = {
   _transitionsEnabled: true,
   get transitionsEnabled() this._transitionsEnabled,
   set transitionsEnabled(aValue) {
     this._transitionsEnabled = aValue;
     if (aValue && !this._resizing && this.stack.hasAttribute("responsivemode")) {
@@ -210,16 +219,17 @@ ResponsiveUI.prototype = {
     this.stack.removeChild(this.resizer);
     this.stack.removeChild(this.resizeBar);
 
     // Unset the responsive mode.
     this.container.removeAttribute("responsivemode");
     this.stack.removeAttribute("responsivemode");
 
     delete this.tab.__responsiveUI;
+    ResponsiveUIManager.events.emit("off", this.tab, this);
   },
 
   /**
    * Handle keypressed.
    *
    * @param aEvent
    */
   onKeypress: function RUI_onKeypress(aEvent) {
--- a/browser/devtools/responsivedesign/test/Makefile.in
+++ b/browser/devtools/responsivedesign/test/Makefile.in
@@ -40,20 +40,28 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
-		browser_responsiveui.js \
 		browser_responsiveruleview.js \
 		browser_responsive_cmd.js \
 		browser_responsivecomputedview.js \
-		browser_responsiveuiaddcustompreset.js \
 		head.js \
 		helpers.js \
 		$(NULL)
 
+# Disabled on Mac for frequent intermittent failures
+ifneq ($(OS_ARCH), Darwin)
+_BROWSER_FILES += \
+		browser_responsiveui.js \
+		browser_responsiveuiaddcustompreset.js \
+	$(NULL)
+else
+$(warning browser_responsiveui.js is disabled on OS X for intermittent failures. Bug 798772) \
+$(warning browser_responsiveuiaddcustompreset.js is disabled on OS X for intermittent failures. Bugs 798775, 798777)
+endif
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/responsivedesign/test/browser_responsiveui.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveui.js
@@ -1,28 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   let instance, widthBeforeClose, heightBeforeClose;
+  let events = ResponsiveUI.ResponsiveUIManager.events;
 
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     waitForFocus(startTest, content);
   }, true);
 
   content.location = "data:text/html,mop";
 
   function startTest() {
     document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
+    events.once("on", function() {executeSoon(onUIOpen)});
     synthesizeKeyFromKeyTag("key_responsiveUI");
-    executeSoon(onUIOpen);
   }
 
   function onUIOpen() {
     // Is it open?
     let container = gBrowser.getBrowserContainer();
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     // Menus are correctly updated?
@@ -113,38 +114,37 @@ function test() {
     is(content.innerHeight, initialWidth, "The height is now the width.");
     let [width, height] = extractSizeFromString(instance.menulist.firstChild.firstChild.getAttribute("label"));
     is(width, initialHeight, "Label updated (width).");
     is(height, initialWidth, "Label updated (height).");
 
     widthBeforeClose = content.innerWidth;
     heightBeforeClose = content.innerHeight;
 
+    events.once("off", function() {executeSoon(restart)});
     EventUtils.synthesizeKey("VK_ESCAPE", {});
-
-    executeSoon(restart);
   }
 
   function restart() {
+    events.once("on", function() {executeSoon(onUIOpen2)});
     synthesizeKeyFromKeyTag("key_responsiveUI");
-    executeSoon(onUIOpen2);
   }
 
   function onUIOpen2() {
     let container = gBrowser.getBrowserContainer();
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     // Menus are correctly updated?
     is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "true", "menus checked");
 
     is(content.innerWidth, widthBeforeClose, "width restored.");
     is(content.innerHeight, heightBeforeClose, "height restored.");
 
+    events.once("off", function() {executeSoon(finishUp)});
     EventUtils.synthesizeKey("VK_ESCAPE", {});
-    executeSoon(finishUp);
   }
 
   function finishUp() {
 
     // Menus are correctly updated?
     is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
 
     delete instance;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/DOMHelpers.jsm
@@ -0,0 +1,124 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const EXPORTED_SYMBOLS = ["DOMHelpers"];
+
+/**
+ * DOMHelpers
+ * Makes DOM traversal easier. Goes through iframes.
+ *
+ * @constructor
+ * @param nsIDOMWindow aWindow
+ *        The content window, owning the document to traverse.
+ */
+function DOMHelpers(aWindow) {
+  this.window = aWindow;
+};
+
+DOMHelpers.prototype = {
+  getParentObject: function Helpers_getParentObject(node)
+  {
+    let parentNode = node ? node.parentNode : null;
+
+    if (!parentNode) {
+      // Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
+      // and Notation. top level windows have no parentNode
+      if (node && node == this.window.Node.DOCUMENT_NODE) {
+        // document type
+        if (node.defaultView) {
+          let embeddingFrame = node.defaultView.frameElement;
+          if (embeddingFrame)
+            return embeddingFrame.parentNode;
+        }
+      }
+      // a Document object without a parentNode or window
+      return null;  // top level has no parent
+    }
+
+    if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
+      if (parentNode.defaultView) {
+        return parentNode.defaultView.frameElement;
+      }
+      // parent is document element, but no window at defaultView.
+      return null;
+    }
+
+    if (!parentNode.localName)
+      return null;
+
+    return parentNode;
+  },
+
+  getChildObject: function Helpers_getChildObject(node, index, previousSibling,
+                                                showTextNodesWithWhitespace)
+  {
+    if (!node)
+      return null;
+
+    if (node.contentDocument) {
+      // then the node is a frame
+      if (index == 0) {
+        return node.contentDocument.documentElement;  // the node's HTMLElement
+      }
+      return null;
+    }
+
+    if (node instanceof this.window.GetSVGDocument) {
+      let svgDocument = node.getSVGDocument();
+      if (svgDocument) {
+        // then the node is a frame
+        if (index == 0) {
+          return svgDocument.documentElement;  // the node's SVGElement
+        }
+        return null;
+      }
+    }
+
+    let child = null;
+    if (previousSibling)  // then we are walking
+      child = this.getNextSibling(previousSibling);
+    else
+      child = this.getFirstChild(node);
+
+    if (showTextNodesWithWhitespace)
+      return child;
+
+    for (; child; child = this.getNextSibling(child)) {
+      if (!this.isWhitespaceText(child))
+        return child;
+    }
+
+    return null;  // we have no children worth showing.
+  },
+
+  getFirstChild: function Helpers_getFirstChild(node)
+  {
+    let SHOW_ALL = Components.interfaces.nsIDOMNodeFilter.SHOW_ALL;
+    this.treeWalker = node.ownerDocument.createTreeWalker(node,
+      SHOW_ALL, null, false);
+    return this.treeWalker.firstChild();
+  },
+
+  getNextSibling: function Helpers_getNextSibling(node)
+  {
+    let next = this.treeWalker.nextSibling();
+
+    if (!next)
+      delete this.treeWalker;
+
+    return next;
+  },
+
+  isWhitespaceText: function Helpers_isWhitespaceText(node)
+  {
+    return node.nodeType == this.window.Node.TEXT_NODE &&
+                            !/[^\s]/.exec(node.nodeValue);
+  },
+
+  destroy: function Helpers_destroy()
+  {
+    delete this.window;
+    delete this.treeWalker;
+  }
+};
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -2,35 +2,36 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EXPORTED_SYMBOLS = [ "DeveloperToolbar" ];
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
-
-const WEBCONSOLE_CONTENT_SCRIPT_URL =
-  "chrome://browser/content/devtools/HUDService-content.js";
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource:///modules/devtools/Commands.jsm");
 
 const Node = Components.interfaces.nsIDOMNode;
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "gcli",
                                   "resource:///modules/devtools/gcli.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
                                   "resource:///modules/devtools/CmdCmd.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "PageErrorListener",
+                                  "resource://gre/modules/devtools/WebConsoleUtils.jsm");
+
 /**
  * Due to a number of panel bugs we need a way to check if we are running on
  * Linux. See the comments for TooltipPanel and OutputPanel for further details.
  *
  * When bug 780102 is fixed all isLinux checks can be removed and we can revert
  * to using panels.
  */
 XPCOMUtils.defineLazyGetter(this, "isLinux", function () {
@@ -51,17 +52,18 @@ function DeveloperToolbar(aChromeWindow,
 
   this._element = aToolbarElement;
   this._element.hidden = true;
   this._doc = this._element.ownerDocument;
 
   this._lastState = NOTIFICATIONS.HIDE;
   this._pendingShowCallback = undefined;
   this._pendingHide = false;
-  this._errorsCount = {};
+  this._errorsCount = Object.create(null);
+  this._errorListeners = Object.create(null);
   this._webConsoleButton = this._doc
                            .getElementById("developer-toolbar-webconsole");
 
   try {
     CmdCommands.refreshAutoCommands(aChromeWindow);
   }
   catch (ex) {
     console.error(ex);
@@ -83,19 +85,16 @@ const NOTIFICATIONS = {
 };
 
 /**
  * Attach notification constants to the object prototype so tests etc can
  * use them without needing to import anything
  */
 DeveloperToolbar.prototype.NOTIFICATIONS = NOTIFICATIONS;
 
-DeveloperToolbar.prototype._contentMessageListeners =
-  ["WebConsole:CachedMessages", "WebConsole:PageError"];
-
 /**
  * Is the toolbar open?
  */
 Object.defineProperty(DeveloperToolbar.prototype, 'visible', {
   get: function DT_visible() {
     return !this._element.hidden;
   },
   enumerable: true
@@ -280,31 +279,28 @@ DeveloperToolbar.prototype._onload = fun
 DeveloperToolbar.prototype._initErrorsCount = function DT__initErrorsCount(aTab)
 {
   let tabId = aTab.linkedPanel;
   if (tabId in this._errorsCount) {
     this._updateErrorsCount();
     return;
   }
 
-  let messageManager = aTab.linkedBrowser.messageManager;
-  messageManager.loadFrameScript(WEBCONSOLE_CONTENT_SCRIPT_URL, true);
+  let window = aTab.linkedBrowser.contentWindow;
+  let listener = new PageErrorListener(window, {
+    onPageError: this._onPageError.bind(this, tabId),
+  });
+  listener.init();
 
+  this._errorListeners[tabId] = listener;
   this._errorsCount[tabId] = 0;
 
-  this._contentMessageListeners.forEach(function(aName) {
-    messageManager.addMessageListener(aName, this);
-  }, this);
+  let messages = listener.getCachedMessages();
+  messages.forEach(this._onPageError.bind(this, tabId));
 
-  let message = {
-    features: ["PageError"],
-    cachedMessages: ["PageError"],
-  };
-
-  this.sendMessageToTab(aTab, "WebConsole:Init", message);
   this._updateErrorsCount();
 };
 
 /**
  * Stop the listeners needed for tracking the number of errors for a given
  * tab.
  *
  * @private
@@ -314,24 +310,20 @@ DeveloperToolbar.prototype._initErrorsCo
 DeveloperToolbar.prototype._stopErrorsCount = function DT__stopErrorsCount(aTab)
 {
   let tabId = aTab.linkedPanel;
   if (!(tabId in this._errorsCount)) {
     this._updateErrorsCount();
     return;
   }
 
-  this.sendMessageToTab(aTab, "WebConsole:Destroy", {});
+  this._errorListeners[tabId].destroy();
+  delete this._errorListeners[tabId];
+  delete this._errorsCount[tabId];
 
-  let messageManager = aTab.linkedBrowser.messageManager;
-  this._contentMessageListeners.forEach(function(aName) {
-    messageManager.removeMessageListener(aName, this);
-  }, this);
-
-  delete this._errorsCount[tabId];
   this._updateErrorsCount();
 };
 
 /**
  * Hide the developer toolbar.
  */
 DeveloperToolbar.prototype.hide = function DT_hide()
 {
@@ -429,83 +421,36 @@ DeveloperToolbar.prototype.handleEvent =
     this._stopErrorsCount(aEvent.target);
   }
   else if (aEvent.type == "beforeunload") {
     this._onPageBeforeUnload(aEvent);
   }
 };
 
 /**
- * The handler of messages received from the nsIMessageManager.
- *
- * @param object aMessage the message received from the content process.
- */
-DeveloperToolbar.prototype.receiveMessage = function DT_receiveMessage(aMessage)
-{
-  if (!aMessage.json || !(aMessage.json.hudId in this._errorsCount)) {
-    return;
-  }
-
-  let tabId = aMessage.json.hudId;
-  let errors = this._errorsCount[tabId];
-
-  switch (aMessage.name) {
-    case "WebConsole:PageError":
-      this._onPageError(tabId, aMessage.json.pageError);
-      break;
-    case "WebConsole:CachedMessages":
-      aMessage.json.messages.forEach(this._onPageError.bind(this, tabId));
-      break;
-  }
-
-  if (errors != this._errorsCount[tabId]) {
-    this._updateErrorsCount(tabId);
-  }
-};
-
-/**
- * Send a message to the content process using the nsIMessageManager of the
- * given tab.
- *
- * @param nsIDOMNode aTab the tab you want to send a message to.
- * @param string aName the name of the message you want to send.
- * @param object aMessage the message to send.
- */
-DeveloperToolbar.prototype.sendMessageToTab =
-function DT_sendMessageToTab(aTab, aName, aMessage)
-{
-  let tabId = aTab.linkedPanel;
-  aMessage.hudId = tabId;
-  if (!("id" in aMessage)) {
-    aMessage.id = "DevToolbar-" + this.sequenceId;
-  }
-
-  aTab.linkedBrowser.messageManager.sendAsyncMessage(aName, aMessage);
-};
-
-/**
- * Process a "WebConsole:PageError" message received from the given tab. This
- * method counts the JavaScript exceptions received.
+ * Count a page error received for the currently selected tab. This
+ * method counts the JavaScript exceptions received and CSS errors/warnings.
  *
  * @private
  * @param string aTabId the ID of the tab from where the page error comes.
- * @param object aPageError the page error object received from the content
- * process.
+ * @param object aPageError the page error object received from the
+ * PageErrorListener.
  */
 DeveloperToolbar.prototype._onPageError =
 function DT__onPageError(aTabId, aPageError)
 {
   if (aPageError.category == "CSS Parser" ||
       aPageError.category == "CSS Loader" ||
       (aPageError.flags & aPageError.warningFlag) ||
       (aPageError.flags & aPageError.strictFlag)) {
     return; // just a CSS or JS warning
   }
 
   this._errorsCount[aTabId]++;
+  this._updateErrorsCount(aTabId);
 };
 
 /**
  * The |beforeunload| event handler. This function resets the errors count when
  * a different page starts loading.
  *
  * @private
  * @param nsIDOMEvent aEvent the beforeunload DOM event.
@@ -633,17 +578,16 @@ function OutputPanel(aChromeDoc, aInput,
     this._panel.setAttribute("height", "1px");
   }
 
   this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
 
   this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
   this._frame.id = "gcli-output-frame";
   this._frame.setAttribute("src", "chrome://browser/content/devtools/commandlineoutput.xhtml");
-  this._frame.setAttribute("flex", "1");
   this._panel.appendChild(this._frame);
 
   this.displayedOutput = undefined;
 
   this._onload = this._onload.bind(this);
   this._frame.addEventListener("load", this._onload, true);
 
   this.loaded = false;
@@ -670,61 +614,116 @@ OutputPanel.prototype._onload = function
   this.loaded = true;
   if (this._loadCallback) {
     this._loadCallback();
     delete this._loadCallback;
   }
 };
 
 /**
+ * Determine the scrollbar width in the current document.
+ *
+ * @private
+ */
+Object.defineProperty(OutputPanel.prototype, 'scrollbarWidth', {
+  get: function() {
+    if (this.__scrollbarWidth) {
+      return this.__scrollbarWidth;
+    }
+
+    let hbox = this.document.createElementNS(XUL_NS, "hbox");
+    hbox.setAttribute("style", "height: 0%; overflow: hidden");
+
+    let scrollbar = this.document.createElementNS(XUL_NS, "scrollbar");
+    scrollbar.setAttribute("orient", "vertical");
+    hbox.appendChild(scrollbar);
+
+    this.document.documentElement.appendChild(hbox);
+    this.__scrollbarWidth = scrollbar.clientWidth;
+    this.document.documentElement.removeChild(hbox);
+
+    return this.__scrollbarWidth;
+  },
+  enumerable: true
+});
+
+/**
  * Prevent the popup from hiding if it is not permitted via this.canHide.
  */
 OutputPanel.prototype._onpopuphiding = function OP_onpopuphiding(aEvent)
 {
   // TODO: When we switch back from tooltip to panel we can remove this hack:
   // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
   if (isLinux && !this.canHide) {
     aEvent.preventDefault();
   }
 };
 
 /**
  * Display the OutputPanel.
  */
 OutputPanel.prototype.show = function OP_show()
 {
-  // This is nasty, but displaying the panel causes it to re-flow, which can
-  // change the size it should be, so we need to resize the iframe after the
-  // panel has displayed
-  this._panel.ownerDocument.defaultView.setTimeout(function() {
-    this._resize();
-  }.bind(this), 0);
-
   if (isLinux) {
     this.canHide = false;
   }
 
+  // We need to reset the iframe size in order for future size calculations to
+  // be correct
+  this._frame.style.minHeight = this._frame.style.maxHeight = 0;
+  this._frame.style.minWidth = 0;
+
   this._panel.openPopup(this._input, "before_start", 0, 0, false, false, null);
   this._resize();
 
   this._input.focus();
 };
 
 /**
  * Internal helper to set the height of the output panel to fit the available
  * content;
  */
 OutputPanel.prototype._resize = function CLP_resize()
 {
   if (this._panel == null || this.document == null || !this._panel.state == "closed") {
     return
   }
 
-  this._frame.height = this.document.body.scrollHeight;
-  this._frame.width = this._input.clientWidth + 2;
+  // Set max panel width to match any content with a max of the width of the
+  // browser window.
+  let maxWidth = this._panel.ownerDocument.documentElement.clientWidth;
+  let width = Math.min(maxWidth, this.document.documentElement.scrollWidth);
+
+  // Add scrollbar width to content size in case a scrollbar is needed.
+  width += this.scrollbarWidth;
+
+  // Set the width of the iframe.
+  this._frame.style.minWidth = width + "px";
+
+  // browserAdjustment is used to correct the panel height according to the
+  // browsers borders etc.
+  const browserAdjustment = 15;
+
+  // Set max panel height to match any content with a max of the height of the
+  // browser window.
+  let maxHeight =
+    this._panel.ownerDocument.documentElement.clientHeight - browserAdjustment;
+  let height = Math.min(maxHeight, this.document.documentElement.scrollHeight);
+
+  // Set the height of the iframe. Setting iframe.height does not work.
+  this._frame.style.minHeight = this._frame.style.maxHeight = height + "px";
+
+  // Set the height and width of the panel to match the iframe.
+  this._panel.sizeTo(width, height);
+
+  // Move the panel to the correct position in the case that it has been
+  // positioned incorrectly.
+  let screenX = this._input.boxObject.screenX;
+  let screenY = this._toolbar.boxObject.screenY;
+  this._panel.moveTo(screenX, screenY - height);
 };
 
 /**
  * Called by GCLI when a command is executed.
  */
 OutputPanel.prototype._outputChanged = function OP_outputChanged(aEvent)
 {
   if (aEvent.output.hidden) {
--- a/browser/devtools/shared/Jsbeautify.jsm
+++ b/browser/devtools/shared/Jsbeautify.jsm
@@ -1,1317 +1,1303 @@
-/* Any copyright is dedicated to the Public Domain.
-* http://creativecommons.org/publicdomain/zero/1.0/ */
-
+/*jslint onevar: false, plusplus: false */
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
 /*
-JS Beautifier written by Einar Lielmanis, <einar@jsbeautifier.org>
-    http://jsbeautifier.org/
+
+ JS Beautifier
+---------------
+
 
-Originally converted to javascript by Vital, <vital76@gmail.com>
-"End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
+  Written by Einar Lielmanis, <einar@jsbeautifier.org>
+      http://jsbeautifier.org/
 
-You are free to use this in any way you want, in case you find this useful or
-working for you.
+  Originally converted to javascript by Vital, <vital76@gmail.com>
+  "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
 
-Usage:
-js_beautify(js_source_text);
-js_beautify(js_source_text, options);
+  You are free to use this in any way you want, in case you find this useful or working for you.
+
+  Usage:
+    js_beautify(js_source_text);
+    js_beautify(js_source_text, options);
 
-The options are:
-indent_size (default 4)          — indentation size.
-indent_char (default space)      — character to indent with.
-preserve_newlines (default true) — whether existing line breaks should be
-                                   preserved.
-max_preserve_newlines (default unlimited) - maximum number of line breaks to be
-                                            preserved in one chunk.
-jslint_happy (default false)     — if true, then jslint-stricter mode is
-                                   enforced.
+  The options are:
+    indent_size (default 4)          - indentation size,
+    indent_char (default space)      - character to indent with,
+    preserve_newlines (default true) - whether existing line breaks should be preserved,
+    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
+
+    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.
 
-    jslint_happy   !jslint_happy
-    ---------------------------------
-     function ()    function()
+            jslint_happy   !jslint_happy
+            ---------------------------------
+             function ()      function()
 
-brace_style (default "collapse") - "collapse" | "expand" | "end-expand" |
-                                   "expand-strict"
-    put braces on the same line as control statements (default), or put braces
-    on own line (Allman / ANSI style), or just put end braces on own line.
+    brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "expand-strict"
+            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
 
-    expand-strict: put brace on own line even in such cases:
+            expand-strict: put brace on own line even in such cases:
 
-      var a =
-      {
-        a: 5,
-        b: 6
-      }
-    This mode may break your scripts - e.g "return { a: 1 }" will be broken into
-    two lines, so beware.
+                var a =
+                {
+                    a: 5,
+                    b: 6
+                }
+            This mode may break your scripts - e.g "return { a: 1 }" will be broken into two lines, so beware.
 
-space_before_conditional (default true) - should the space before conditional
-statement be added, "if(true)" vs "if (true)",
+    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
 
-unescape_strings (default false) - should printable characters in
-strings encoded in \xNN notation be unescaped, "example" vs
-"\x65\x78\x61\x6d\x70\x6c\x65"
+    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
+
+    e.g
 
-e.g
+    js_beautify(js_source_text, {
+      'indent_size': 1,
+      'indent_char': '\t'
+    });
 
-js_beautify(js_source_text, {
-  'indent_size': 1,
-  'indent_char': '\t'
-});
+
 */
 
-"use strict";
+let EXPORTED_SYMBOLS = ["js_beautify"];
 
-var EXPORTED_SYMBOLS = [ "js_beautify" ];
+function js_beautify(js_source_text, options) {
 
-function js_beautify(js_source_text, options={}) {
-  let input, output, token_text, last_type, last_text, last_last_text,
-    last_word, flags, flag_store, indent_string, whitespace, wordchar,
-    punct, parser_pos, line_starters, digits, prefix, token_type,
-    do_block_just_closed, wanted_newline, just_added_newline, n_newlines,
-    opt_brace_style, preindent_string = '';
+    var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string;
+    var whitespace, wordchar, punct, parser_pos, line_starters, digits;
+    var prefix, token_type, do_block_just_closed;
+    var wanted_newline, just_added_newline, n_newlines;
+    var preindent_string = '';
+
 
-  // compatibility
-  if (options.space_after_anon_function !== undefined &&
-      options.jslint_happy === undefined) {
-    options.jslint_happy = options.space_after_anon_function;
-  }
-  if (options.braces_on_own_line !== undefined) {
-    opt_brace_style = options.braces_on_own_line ? "expand" : "collapse";
-  }
-  opt_brace_style = options.brace_style ? options.brace_style :
-                    (opt_brace_style ? opt_brace_style : "collapse");
+    // Some interpreters have unexpected results with foo = baz || bar;
+    options = options ? options : {};
+
+    var opt_brace_style;
+
+    // compatibility
+    if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) {
+        options.jslint_happy = options.space_after_anon_function;
+    }
+    if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
+        opt_brace_style = options.braces_on_own_line ? "expand" : "collapse";
+    }
+    opt_brace_style = options.brace_style ? options.brace_style : (opt_brace_style ? opt_brace_style : "collapse");
 
 
-  let opt_indent_size = options.indent_size ? options.indent_size : 4;
-  let opt_indent_char = options.indent_char ? options.indent_char : ' ';
-  let opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ?
-    true : options.preserve_newlines;
-  let opt_max_preserve_newlines =
-    typeof options.max_preserve_newlines === 'undefined' ?
-    false : options.max_preserve_newlines;
-  let opt_jslint_happy = options.jslint_happy === 'undefined' ?
-    false : options.jslint_happy;
-  let opt_keep_array_indentation =
-    typeof options.keep_array_indentation === 'undefined' ?
-    false : options.keep_array_indentation;
-  let opt_space_before_conditional =
-    typeof options.space_before_conditional === 'undefined' ?
-    true : options.space_before_conditional;
-  let opt_indent_case = typeof options.indent_case === 'undefined' ?
-    false : options.indent_case;
-  let opt_unescape_strings = typeof options.unescape_strings === 'undefined' ?
-    false : options.unescape_strings;
+    var opt_indent_size = options.indent_size ? options.indent_size : 4;
+    var opt_indent_char = options.indent_char ? options.indent_char : ' ';
+    var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines;
+    var opt_max_preserve_newlines = typeof options.max_preserve_newlines === 'undefined' ? false : options.max_preserve_newlines;
+    var opt_jslint_happy = options.jslint_happy === 'undefined' ? false : options.jslint_happy;
+    var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? false : options.keep_array_indentation;
+    var opt_space_before_conditional = typeof options.space_before_conditional === 'undefined' ? true : options.space_before_conditional;
+    var opt_indent_case = typeof options.indent_case === 'undefined' ? false : options.indent_case;
+    var opt_unescape_strings = typeof options.unescape_strings === 'undefined' ? false : options.unescape_strings;
 
-  just_added_newline = false;
+    just_added_newline = false;
 
-  // cache the source's length.
-  let input_length = js_source_text.length;
+    // cache the source's length.
+    var input_length = js_source_text.length;
 
-  function trim_output(eat_newlines) {
-    eat_newlines = typeof eat_newlines === 'undefined' ? false : eat_newlines;
-    while (output.length && (output[output.length - 1] === ' ' ||
-           output[output.length - 1] === indent_string ||
-           output[output.length - 1] === preindent_string ||
-           (eat_newlines && (output[output.length - 1] === '\n' ||
-           output[output.length - 1] === '\r')))) {
-      output.pop();
+    function trim_output(eat_newlines) {
+        eat_newlines = typeof eat_newlines === 'undefined' ? false : eat_newlines;
+        while (output.length && (output[output.length - 1] === ' '
+            || output[output.length - 1] === indent_string
+            || output[output.length - 1] === preindent_string
+            || (eat_newlines && (output[output.length - 1] === '\n' || output[output.length - 1] === '\r')))) {
+            output.pop();
+        }
     }
-  }
-
-  function trim(s) {
-    return s.replace(/^\s\s*|\s\s*$/, '');
-  }
 
-  function split_newlines(s)
-  {
-    return s.split(/\x0d\x0a|\x0a/);
-  }
+    function trim(s) {
+        return s.replace(/^\s\s*|\s\s*$/, '');
+    }
 
-  function force_newline()
-  {
-    let old_keep_array_indentation = opt_keep_array_indentation;
-    opt_keep_array_indentation = false;
-    print_newline();
-    opt_keep_array_indentation = old_keep_array_indentation;
-  }
+    // we could use just string.split, but
+    // IE doesn't like returning empty strings
+    function split_newlines(s) {
+        return s.split(/\x0d\x0a|\x0a/);
+    }
 
-  function print_newline(ignore_repeated) {
-
-    flags.eat_next_space = false;
-    if (opt_keep_array_indentation && is_array(flags.mode)) {
-      return;
+    function force_newline() {
+        var old_keep_array_indentation = opt_keep_array_indentation;
+        opt_keep_array_indentation = false;
+        print_newline();
+        opt_keep_array_indentation = old_keep_array_indentation;
     }
 
-    ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;
+    function print_newline(ignore_repeated) {
+
+        flags.eat_next_space = false;
+        if (opt_keep_array_indentation && is_array(flags.mode)) {
+            return;
+        }
+
+        ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;
+
+        flags.if_line = false;
+        trim_output();
+
+        if (!output.length) {
+            return; // no newline on start of file
+        }
 
-    flags.if_line = false;
-    trim_output();
+        if (output[output.length - 1] !== "\n" || !ignore_repeated) {
+            just_added_newline = true;
+            output.push("\n");
+        }
+        if (preindent_string) {
+            output.push(preindent_string);
+        }
+        for (var i = 0; i < flags.indentation_level; i += 1) {
+            output.push(indent_string);
+        }
+        if (flags.var_line && flags.var_line_reindented) {
+            output.push(indent_string); // skip space-stuffing, if indenting with a tab
+        }
+        if (flags.case_body) {
+            output.push(indent_string);
+        }
+    }
+
+
+
+    function print_single_space() {
 
-    if (!output.length) {
-      return; // no newline on start of file
+        if (last_type === 'TK_COMMENT') {
+            return print_newline();
+        }
+        if (flags.eat_next_space) {
+            flags.eat_next_space = false;
+            return;
+        }
+        var last_output = ' ';
+        if (output.length) {
+            last_output = output[output.length - 1];
+        }
+        if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space
+            output.push(' ');
+        }
+    }
+
+
+    function print_token() {
+        just_added_newline = false;
+        flags.eat_next_space = false;
+        output.push(token_text);
     }
 
-    if (output[output.length - 1] !== "\n" || !ignore_repeated) {
-      just_added_newline = true;
-      output.push("\n");
-    }
-    if (preindent_string) {
-      output.push(preindent_string);
-    }
-    for (let i = 0; i < flags.indentation_level; i += 1) {
-      output.push(indent_string);
-    }
-    if (flags.var_line && flags.var_line_reindented) {
-      output.push(indent_string); // skip space-stuffing, if indenting with a tab
-    }
-    if (flags.case_body) {
-      output.push(indent_string);
-    }
-  }
-
-  function print_single_space() {
-
-    if (last_type === 'TK_COMMENT') {
-      return print_newline();
+    function indent() {
+        flags.indentation_level += 1;
     }
-    if (flags.eat_next_space) {
-      flags.eat_next_space = false;
-      return;
-    }
-    let last_output = ' ';
-    if (output.length) {
-      last_output = output[output.length - 1];
-    }
-    if (last_output !== ' ' && last_output !== '\n' &&
-        last_output !== indent_string) { // prevent occassional duplicate space
-      output.push(' ');
-    }
-  }
-
-
-  function print_token() {
-    just_added_newline = false;
-    flags.eat_next_space = false;
-    output.push(token_text);
-  }
-
-  function indent() {
-    flags.indentation_level += 1;
-  }
 
 
-  function remove_indent() {
-    if (output.length && output[output.length - 1] === indent_string) {
-      output.pop();
-    }
-  }
-
-  function set_mode(mode) {
-    if (flags) {
-      flag_store.push(flags);
+    function remove_indent() {
+        if (output.length && output[output.length - 1] === indent_string) {
+            output.pop();
+        }
     }
-    flags = {
-      previous_mode: flags ? flags.mode : 'BLOCK',
-      mode: mode,
-      var_line: false,
-      var_line_tainted: false,
-      var_line_reindented: false,
-      in_html_comment: false,
-      if_line: false,
-      in_case_statement: false, // switch(..){ INSIDE HERE }
-      in_case: false, // we're on the exact line with "case 0:"
-      case_body: false, // the indented case-action block
-      eat_next_space: false,
-      indentation_baseline: -1,
-      indentation_level: (flags ? flags.indentation_level +
-                         (flags.case_body ? 1 : 0) +
-                         ((flags.var_line && flags.var_line_reindented) ? 1 : 0) : 0),
-      ternary_depth: 0
-    };
-  }
 
-  function is_array(mode) {
-    return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]';
-  }
+    function set_mode(mode) {
+        if (flags) {
+            flag_store.push(flags);
+        }
+        flags = {
+            previous_mode: flags ? flags.mode : 'BLOCK',
+            mode: mode,
+            var_line: false,
+            var_line_tainted: false,
+            var_line_reindented: false,
+            in_html_comment: false,
+            if_line: false,
+            in_case_statement: false, // switch(..){ INSIDE HERE }
+            in_case: false, // we're on the exact line with "case 0:"
+            case_body: false, // the indented case-action block
+            eat_next_space: false,
+            indentation_baseline: -1,
+            indentation_level: (flags ? flags.indentation_level + (flags.case_body ? 1 : 0) + ((flags.var_line && flags.var_line_reindented) ? 1 : 0) : 0),
+            ternary_depth: 0
+        };
+    }
 
-  function is_expression(mode) {
-    return in_array(mode, ['[EXPRESSION]', '(EXPRESSION)',
-           '(FOR-EXPRESSION)', '(COND-EXPRESSION)']);
-  }
+    function is_array(mode) {
+        return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]';
+    }
+
+    function is_expression(mode) {
+        return in_array(mode, ['[EXPRESSION]', '(EXPRESSION)', '(FOR-EXPRESSION)', '(COND-EXPRESSION)']);
+    }
 
-  function restore_mode() {
-    do_block_just_closed = flags.mode === 'DO_BLOCK';
-    if (flag_store.length > 0) {
-      let mode = flags.mode;
-      flags = flag_store.pop();
-      flags.previous_mode = mode;
+    function restore_mode() {
+        do_block_just_closed = flags.mode === 'DO_BLOCK';
+        if (flag_store.length > 0) {
+            var mode = flags.mode;
+            flags = flag_store.pop();
+            flags.previous_mode = mode;
+        }
     }
-  }
 
-  function all_lines_start_with(lines, c) {
-    for (let line of lines) {
-      line = trim(line);
-      if (line.charAt(0) !== c) {
-        return f