Merge m-c to inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 25 Aug 2015 16:54:11 -0400
changeset 259328 c47ccde36664172dff25f8b444ffd7a6b150cee3
parent 259327 3a5257ac933dce09eb38c8f3615aa14ce996f1ee (current diff)
parent 259302 c46370eea81a9860ae77d1f0c7776c24e816138e (diff)
child 259329 7c5cc8cfca19afaeb4af08c669a78219edea72c4
push id29277
push userryanvm@gmail.com
push dateWed, 26 Aug 2015 18:32:23 +0000
treeherdermozilla-central@fea87cbeaa6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound. a=merge
browser/components/loop/test/shared/vendor/sinon-1.15.0.js
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -442,25 +442,25 @@ let gFxAccounts = {
       break;
     case "migrate-signup":
     case "migrate-verify":
       // The migration flow calls for the menu item to open sync prefs rather
       // than requesting migration start immediately.
       this.openPreferences();
       break;
     default:
-      this.openAccountsPage(null, { entrypoint: "menupanel" });
+      this.openPreferences();
       break;
     }
 
     PanelUI.hide();
   },
 
   openPreferences: function () {
-    openPreferences("paneSync");
+    openPreferences("paneSync", { urlParams: { entrypoint: "menupanel" } });
   },
 
   openAccountsPage: function (action, urlParams={}) {
     // An entrypoint param is used for server-side metrics.  If the current tab
     // is UITour, assume that it initiated the call to this method and override
     // the entrypoint accordingly.
     if (UITour.tourBrowsersByWindow.get(window) &&
         UITour.tourBrowsersByWindow.get(window).has(gBrowser.selectedBrowser)) {
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -266,30 +266,22 @@ let gSyncUI = {
    *        Indicates the entrypoint from where this method was called.
    */
 
   openSetup: function SUI_openSetup(wizardType, entryPoint = "syncbutton") {
     let xps = Components.classes["@mozilla.org/weave/service;1"]
                                 .getService(Components.interfaces.nsISupports)
                                 .wrappedJSObject;
     if (xps.fxAccountsEnabled) {
-      fxAccounts.getSignedInUser().then(userData => {
-        if (userData) {
-          this.openPrefs();
-        } else {
-          // If the user is also in an uitour, set the entrypoint to `uitour`
-          if (UITour.tourBrowsersByWindow.get(window) &&
-              UITour.tourBrowsersByWindow.get(window).has(gBrowser.selectedBrowser)) {
-            entryPoint = "uitour";
-          }
-          switchToTabHavingURI("about:accounts?entrypoint=" + entryPoint, true, {
-            replaceQueryString: true
-          });
-        }
-      });
+      // If the user is also in an uitour, set the entrypoint to `uitour`
+      if (UITour.tourBrowsersByWindow.get(window) &&
+          UITour.tourBrowsersByWindow.get(window).has(gBrowser.selectedBrowser)) {
+        entryPoint = "uitour";
+      }
+      this.openPrefs(entryPoint);
     } else {
       let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
       if (win)
         win.focus();
       else {
         window.openDialog("chrome://browser/content/sync/setup.xul",
                           "weaveSetup", "centerscreen,chrome,resizable=no",
                           wizardType);
@@ -304,18 +296,18 @@ let gSyncUI = {
     let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
     if (win)
       win.focus();
     else
       window.openDialog("chrome://browser/content/sync/addDevice.xul",
                         "syncAddDevice", "centerscreen,chrome,resizable=no");
   },
 
-  openPrefs: function SUI_openPrefs() {
-    openPreferences("paneSync");
+  openPrefs: function (entryPoint) {
+    openPreferences("paneSync", { urlParams: { entrypoint: entryPoint } });
   },
 
   openSignInAgainPage: function (entryPoint = "syncbutton") {
     gFxAccounts.openSignInAgainPage(entryPoint);
   },
 
   // Helpers
   _updateLastSyncTime: function SUI__updateLastSyncTime() {
--- a/browser/base/content/test/general/browser_fullscreen-window-open.js
+++ b/browser/base/content/test/general/browser_fullscreen-window-open.js
@@ -327,17 +327,17 @@ WindowListener.prototype = {
   callback_onSuccess: null,
   callBack_onFinalize: null,
 
   onOpenWindow: function(aXULWindow) {
     Services.wm.removeListener(this);
 
     let domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindow);
-    domwindow.addEventListener("load", function onLoad(aEvent) {
+    let onLoad = aEvent => {
       is(domwindow.document.location.href, this.test_url,
         "Opened Window is expected: "+ this.test_title);
       if (this.callback_onSuccess) {
         this.callback_onSuccess();
       }
 
       domwindow.removeEventListener("load", onLoad, true);
 
@@ -347,15 +347,16 @@ WindowListener.prototype = {
           domwindow.close();
           executeSoon(this.callBack_onFinalize);
         }.bind(this), 3000);
       }
       else {
         domwindow.close();
         executeSoon(this.callBack_onFinalize);
       }
-    }.bind(this), true);
+    };
+    domwindow.addEventListener("load", onLoad, true);
   },
   onCloseWindow: function(aXULWindow) {},
   onWindowTitleChange: function(aXULWindow, aNewTitle) {},
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowMediatorListener,
                                          Ci.nsISupports]),
 };
--- a/browser/base/content/test/general/browser_fxaccounts.js
+++ b/browser/base/content/test/general/browser_fxaccounts.js
@@ -79,19 +79,19 @@ add_task(function* test_nouser() {
   // Check the world - the FxA footer area is visible as it is offering a signin.
   Assert.ok(isFooterVisible())
 
   Assert.equal(panelUILabel.getAttribute("label"), panelUIStatus.getAttribute("defaultlabel"));
   Assert.ok(!panelUIStatus.hasAttribute("tooltiptext"), "no tooltip when signed out");
   Assert.ok(!panelUIFooter.hasAttribute("fxastatus"), "no fxsstatus when signed out");
   Assert.ok(!panelUIFooter.hasAttribute("fxaprofileimage"), "no fxaprofileimage when signed out");
 
-  let promiseOpen = promiseTabOpen("about:accounts?entrypoint=menupanel");
+  let promisePreferencesOpened = promiseObserver("test:browser_fxaccounts:openPreferences");
   panelUIStatus.click();
-  yield promiseOpen;
+  yield promisePreferencesOpened;
 });
 
 /*
 XXX - Bug 1191162 - need a better hawk mock story or this will leak in debug builds.
 
 add_task(function* test_unverifiedUser() {
   let promiseUpdateDone = promiseObserver("test:browser_fxaccounts:updateAppMenuItem");
   yield setSignedInUser(false); // this will fire the observer that does the update.
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -527,17 +527,27 @@ function openPreferences(paneID, extraAr
   // This function is duplicated from preferences.js.
   function internalPrefCategoryNameToFriendlyName(aName) {
     return (aName || "").replace(/^pane./, function(toReplace) { return toReplace[4].toLowerCase(); });
   }
 
   if (getBoolPref("browser.preferences.inContent")) {
     let win = Services.wm.getMostRecentWindow("navigator:browser");
     let friendlyCategoryName = internalPrefCategoryNameToFriendlyName(paneID);
-    let preferencesURL = "about:preferences" +
+    let params;
+    if (extraArgs && extraArgs["urlParams"]) {
+      params = new URLSearchParams();
+      let urlParams = extraArgs["urlParams"];
+      for (let name in urlParams) {
+        if (urlParams[name] !== undefined) {
+          params.set(name, urlParams[name]);
+        }
+      }
+    }
+    let preferencesURL = "about:preferences" + (params ? "?" + params : "") +
                          (friendlyCategoryName ? "#" + friendlyCategoryName : "");
     let newLoad = true;
     let browser = null;
     if (!win) {
       const Cc = Components.classes;
       const Ci = Components.interfaces;
       let windowArguments = Cc["@mozilla.org/supports-array;1"]
                               .createInstance(Ci.nsISupportsArray);
--- a/browser/components/customizableui/test/browser_967000_button_sync.js
+++ b/browser/components/customizableui/test/browser_967000_button_sync.js
@@ -40,18 +40,18 @@ function openAboutAccountsFromMenuPanel(
     deferred.resolve();
   }
   gBrowser.selectedBrowser.addEventListener("load", handler, true);
 
   syncButton.click();
   yield deferred.promise;
   newTab = gBrowser.selectedTab;
 
-  is(gBrowser.currentURI.spec, "about:accounts?entrypoint=" + entryPoint,
-    "Firefox Sync page opened with `menupanel` entrypoint");
+  is(gBrowser.currentURI.spec, "about:preferences?entrypoint=" + entryPoint + "#sync",
+    "Firefox Sync preference page opened with `menupanel` entrypoint");
   ok(!isPanelUIOpen(), "The panel closed");
 
   if(isPanelUIOpen()) {
     let panelHidePromise = promisePanelHidden(window);
     PanelUI.hide();
     yield panelHidePromise;
   }
 }
--- a/browser/components/loop/.eslintrc
+++ b/browser/components/loop/.eslintrc
@@ -31,16 +31,17 @@
   },
   "rules": {
     // turn off all kinds of stuff that we actually do want, because
     // right now, we're bootstrapping the linting infrastructure.  We'll
     // want to audit these rules, and start turning them on and fixing the
     // problems they find, one at a time.
 
     // Eslint built-in rules are documented at <http://eslint.org/docs/rules/>
+    "block-spacing": [2, "always"],
     "callback-return": 0,         // TBD
     "camelcase": 0,               // TODO: set to 2
     "comma-spacing": 2,
     "computed-property-spacing": [2, "never"],
     "consistent-return": 0,       // TODO: set to 2
     "curly": [2, "all"],
     "dot-location": [2, "property"],
     "eol-last": 2,
--- a/browser/components/loop/.eslintrc-gecko
+++ b/browser/components/loop/.eslintrc-gecko
@@ -57,12 +57,13 @@
   },
   "rules": {
     "arrow-parens": 0,         // TBD
     "arrow-spacing": 2,
     "eqeqeq": 0,               // TBD
     "generator-star-spacing": [2, "after"],
     // We should fix the errors and enable this (set to 2)
     "no-var": 0,
+    "prefer-arrow-callback": 0,// TODO: Set to 2.
     "require-yield": 0,        // TODO: Set to 2.
     "strict": [2, "global"]
   }
 }
--- a/browser/components/loop/content/js/otconfig.js
+++ b/browser/components/loop/content/js/otconfig.js
@@ -2,8 +2,11 @@
  * 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/. */
 
 window.OTProperties = {
   cdnURL: "loop/"
 };
 window.OTProperties.assetURL = window.OTProperties.cdnURL + "sdk-content/";
 window.OTProperties.configURL = window.OTProperties.assetURL + "js/dynamic_config.min.js";
+
+// We don't use the SDK's CSS. This will prevent spurious 404 errors.
+window.OTProperties.cssURL = "about:blank";
--- a/browser/components/loop/content/shared/css/common.css
+++ b/browser/components/loop/content/shared/css/common.css
@@ -581,15 +581,18 @@ html[dir="rtl"] .context-wrapper > .cont
 .context-wrapper > .context-info > .context-url {
   display: block;
   color: #00a9dc;
   font-weight: 700;
   clear: both;
 }
 
 .clicks-allowed.context-wrapper:hover {
-  background-color: #dbf7ff;
+  border: 2px solid #5cccee;
+  /* Due to the increased border width, reduce the padding accordingly so that
+     the text doesn't move. */
+  padding: calc(.8em - 1px);
 }
 
 /* Only underline the url, not the associated text */
 .clicks-allowed.context-wrapper:hover > .context-info > .context-url {
   text-decoration: underline;
 }
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -878,17 +878,17 @@ body[platform="win"] .share-service-drop
   background-image: url("../img/icons-16x16.svg#add-active");
 }
 
 .context-url-view-wrapper {
   /* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p,
      0.5rem for padding of .text-chat-entry > p */
   padding: calc(18px - 1px - 0.5rem);
   margin-bottom: 0.5em;
-  background-color: #E8F6FE;
+  background-color: #dbf7ff;
 }
 
 .room-context {
   background: rgba(0,0,0,.8);
   border-top: 2px solid #444;
   border-bottom: 2px solid #444;
   padding: .5rem;
   position: absolute;
@@ -1605,23 +1605,23 @@ html[dir="rtl"] .text-chat-entry.receive
   margin-right: 0;
   margin-left: -10px;
 }
 
 .text-chat-entry.special.room-name {
   color: black;
   font-weight: bold;
   text-align: start;
-  background-color: #E8F6FE;
+  background-color: #dbf7ff;
   margin-bottom: 0;
   margin-right: 0;
 }
 
 .text-chat-entry.special.room-name p {
-  background: #E8F6FE;
+  background: #dbf7ff;
   max-width: 100%;
   /* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p,
    0.5rem for padding of .text-chat-entry > p */
   padding: calc(18px - 1px - 0.5rem);
   padding-bottom: 0px;
 }
 
 .text-chat-entry.special > p {
--- a/browser/components/loop/content/shared/js/otSdkDriver.js
+++ b/browser/components/loop/content/shared/js/otSdkDriver.js
@@ -267,24 +267,27 @@ loop.OTSdkDriver = (function() {
     disconnectSession: function() {
       this.endScreenShare();
 
       this.dispatcher.dispatch(new sharedActions.DataChannelsAvailable({
         available: false
       }));
 
       if (this.session) {
-        this.session.off("sessionDisconnected streamCreated streamDestroyed connectionCreated connectionDestroyed streamPropertyChanged");
+        this.session.off("sessionDisconnected streamCreated streamDestroyed " +
+                         "connectionCreated connectionDestroyed " +
+                         "streamPropertyChanged signal:readyForDataChannel");
         this.session.disconnect();
         delete this.session;
 
         this._notifyMetricsEvent("Session.connectionDestroyed", "local");
       }
       if (this.publisher) {
-        this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated");
+        this.publisher.off("accessAllowed accessDenied accessDialogOpened " +
+                           "streamCreated streamDestroyed");
         this.publisher.destroy();
         delete this.publisher;
       }
 
       this._noteConnectionLengthIfNeeded(this._getTwoWayMediaStartTime(), performance.now());
 
       // Also, tidy these variables ready for next time.
       delete this._sessionConnected;
--- a/browser/components/loop/content/shared/js/textChatView.js
+++ b/browser/components/loop/content/shared/js/textChatView.js
@@ -71,18 +71,17 @@ loop.shared.views.chat = (function(mozL1
       );
     }
   });
 
   var TextChatRoomName = React.createClass({displayName: "TextChatRoomName",
     mixins: [React.addons.PureRenderMixin],
 
     propTypes: {
-      message: React.PropTypes.string.isRequired,
-      useDesktopPaths: React.PropTypes.bool.isRequired
+      message: React.PropTypes.string.isRequired
     },
 
     render: function() {
       return (
         React.createElement("div", {className: "text-chat-entry special room-name"}, 
           React.createElement("p", null, mozL10n.get("rooms_welcome_title", {conversationName: this.props.message}))
         )
       );
@@ -175,18 +174,17 @@ loop.shared.views.chat = (function(mozL1
             
               this.props.messageList.map(function(entry, i) {
                 if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
                   switch (entry.contentType) {
                     case CHAT_CONTENT_TYPES.ROOM_NAME:
                       return (
                         React.createElement(TextChatRoomName, {
                           key: i, 
-                          message: entry.message, 
-                          useDesktopPaths: this.props.useDesktopPaths})
+                          message: entry.message})
                       );
                     case CHAT_CONTENT_TYPES.CONTEXT:
                       return (
                         React.createElement("div", {className: "context-url-view-wrapper", key: i}, 
                           React.createElement(sharedViews.ContextUrlView, {
                             allowClick: true, 
                             description: entry.message, 
                             dispatcher: this.props.dispatcher, 
--- a/browser/components/loop/content/shared/js/textChatView.jsx
+++ b/browser/components/loop/content/shared/js/textChatView.jsx
@@ -71,18 +71,17 @@ loop.shared.views.chat = (function(mozL1
       );
     }
   });
 
   var TextChatRoomName = React.createClass({
     mixins: [React.addons.PureRenderMixin],
 
     propTypes: {
-      message: React.PropTypes.string.isRequired,
-      useDesktopPaths: React.PropTypes.bool.isRequired
+      message: React.PropTypes.string.isRequired
     },
 
     render: function() {
       return (
         <div className="text-chat-entry special room-name">
           <p>{mozL10n.get("rooms_welcome_title", {conversationName: this.props.message})}</p>
         </div>
       );
@@ -175,18 +174,17 @@ loop.shared.views.chat = (function(mozL1
             {
               this.props.messageList.map(function(entry, i) {
                 if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
                   switch (entry.contentType) {
                     case CHAT_CONTENT_TYPES.ROOM_NAME:
                       return (
                         <TextChatRoomName
                           key={i}
-                          message={entry.message}
-                          useDesktopPaths={this.props.useDesktopPaths} />
+                          message={entry.message} />
                       );
                     case CHAT_CONTENT_TYPES.CONTEXT:
                       return (
                         <div className="context-url-view-wrapper" key={i}>
                           <sharedViews.ContextUrlView
                             allowClick={true}
                             description={entry.message}
                             dispatcher={this.props.dispatcher}
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -1150,17 +1150,17 @@ loop.shared.views = (function(_, mozL10n
                 isLoading: this.props.isScreenShareLoading, 
                 mediaType: "screen-share", 
                 posterUrl: this.props.screenSharePosterUrl, 
                 srcVideoObject: this.props.screenShareVideoObject})
             ), 
             React.createElement(loop.shared.views.chat.TextChatView, {
               dispatcher: this.props.dispatcher, 
               showRoomName: this.props.showContextRoomName, 
-              useDesktopPaths: false}), 
+              useDesktopPaths: this.props.useDesktopPaths}), 
              this.state.localMediaAboslutelyPositioned ?
               null : this.renderLocalVideo()
           )
         )
       );
     }
   });
 
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -1150,17 +1150,17 @@ loop.shared.views = (function(_, mozL10n
                 isLoading={this.props.isScreenShareLoading}
                 mediaType="screen-share"
                 posterUrl={this.props.screenSharePosterUrl}
                 srcVideoObject={this.props.screenShareVideoObject} />
             </div>
             <loop.shared.views.chat.TextChatView
               dispatcher={this.props.dispatcher}
               showRoomName={this.props.showContextRoomName}
-              useDesktopPaths={false} />
+              useDesktopPaths={this.props.useDesktopPaths} />
             { this.state.localMediaAboslutelyPositioned ?
               null : this.renderLocalVideo() }
           </div>
         </div>
       );
     }
   });
 
--- a/browser/components/loop/modules/MozLoopService.jsm
+++ b/browser/components/loop/modules/MozLoopService.jsm
@@ -893,17 +893,17 @@ let MozLoopServiceInternal = {
       // in about:blank and then get lost.
       // Sadly we can't use chatbox.promiseChatLoaded() as promise chaining
       // involves event loop spins, which means it might be too late.
       // Have we already done it?
       if (chatbox.contentWindow.navigator.mozLoop) {
         return;
       }
 
-      chatbox.addEventListener("DOMContentLoaded", function loaded(event) {
+      let loaded = event => {
         if (event.target != chatbox.contentDocument) {
           return;
         }
         chatbox.removeEventListener("DOMContentLoaded", loaded, true);
 
         let chatbar = chatbox.parentNode;
         let window = chatbox.contentWindow;
 
@@ -982,17 +982,18 @@ let MozLoopServiceInternal = {
             }
           }
         };
 
         let pc_static = new window.mozRTCPeerConnectionStatic();
         pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange);
 
         UITour.notify("Loop:ChatWindowOpened");
-      }.bind(this), true);
+      };
+      chatbox.addEventListener("DOMContentLoaded", loaded, true);
     };
 
     let chatboxInstance = Chat.open(null, origin, "", url, undefined, undefined,
                                     callback);
     if (!chatboxInstance) {
       return null;
     // It's common for unit tests to overload Chat.open.
     } else if (chatboxInstance.setAttribute) {
--- a/browser/components/loop/standalone/content/index.html
+++ b/browser/components/loop/standalone/content/index.html
@@ -111,16 +111,19 @@
         window.MutationObserver = myMutationObserver;
       }
 
       window.OTProperties = {
         cdnURL: "shared/libs/"
       };
       window.OTProperties.assetURL = window.OTProperties.cdnURL + "sdk-content/";
       window.OTProperties.configURL = window.OTProperties.assetURL + "js/dynamic_config.min.js";
+
+      // We don't use the SDK's CSS. This will prevent spurious 404 errors.
+      window.OTProperties.cssURL = "about:blank";
     </script>
     <script type="text/javascript" src="js/multiplexGum.js"></script>
     <script type="text/javascript" src="shared/libs/sdk.js"></script>
     <script>
       // multiplexGum needs evaluation before sdk.js, but TBPlugin is not
       // defined until after sdk.js has been evaluated. This updates the
       // navigator object to reference TBPlugin if it was defined by sdk.js.
       if (!navigator.originalGum) {
--- a/browser/components/loop/standalone/package.json
+++ b/browser/components/loop/standalone/package.json
@@ -7,17 +7,17 @@
     "url": "git@github.com:mozilla/loop-client.git"
   },
   "engines": {
     "node": "0.10.x",
     "npm": "1.3.x"
   },
   "dependencies": {},
   "devDependencies": {
-    "eslint": "1.0.x",
+    "eslint": "1.2.x",
     "eslint-plugin-react": "3.2.x",
     "express": "4.x"
   },
   "scripts": {
     "test": "make test",
     "start": "make runserver"
   },
   "license": "MPL-2.0"
--- a/browser/components/loop/test/desktop-local/index.html
+++ b/browser/components/loop/test/desktop-local/index.html
@@ -32,17 +32,17 @@
   <script src="../../content/libs/l10n.js"></script>
   <script src="../../content/shared/libs/react-0.12.2.js"></script>
   <script src="../../content/shared/libs/lodash-3.9.3.js"></script>
   <script src="../../content/shared/libs/backbone-1.2.1.js"></script>
 
   <!-- test dependencies -->
   <script src="../shared/vendor/mocha-2.2.5.js"></script>
   <script src="../shared/vendor/chai-3.0.0.js"></script>
-  <script src="../shared/vendor/sinon-1.15.0.js"></script>
+  <script src="../shared/vendor/sinon-1.16.1.js"></script>
   <script>
     /*global chai,mocha */
     chai.config.includeStack = true;
     mocha.setup({ui: 'bdd', timeout: 10000});
   </script>
 
   <!-- App scripts -->
   <script src="../../content/shared/js/utils.js"></script>
--- a/browser/components/loop/test/shared/index.html
+++ b/browser/components/loop/test/shared/index.html
@@ -33,17 +33,17 @@
   <script src="../../content/shared/libs/lodash-3.9.3.js"></script>
   <script src="../../content/shared/libs/backbone-1.2.1.js"></script>
   <script src="../../standalone/content/libs/l10n-gaia-02ca67948fe8.js"></script>
 
   <!-- test dependencies -->
   <script src="vendor/mocha-2.2.5.js"></script>
   <script src="vendor/chai-3.0.0.js"></script>
   <script src="vendor/chai-as-promised-5.1.0.js"></script>
-  <script src="vendor/sinon-1.15.0.js"></script>
+  <script src="vendor/sinon-1.16.1.js"></script>
   <script>
     /*global chai, mocha */
     chai.config.includeStack = true;
     mocha.setup({ui: 'bdd', timeout: 10000});
   </script>
 
   <!-- App scripts -->
   <script src="../../content/shared/js/utils.js"></script>
--- a/browser/components/loop/test/shared/otSdkDriver_test.js
+++ b/browser/components/loop/test/shared/otSdkDriver_test.js
@@ -408,16 +408,76 @@ describe("loop.OTSdkDriver", function ()
     it("should disconnect the session", function() {
       driver.session = session;
 
       driver.disconnectSession();
 
       sinon.assert.calledOnce(session.disconnect);
     });
 
+    it("should unsubscribe to all the publisher events that were subscribed to in #setupStreamElements", function() {
+      var subscribedEvents = [];
+
+      // First find out which events were subscribed to.
+      sandbox.stub(publisher, "on", function(eventName) {
+        subscribedEvents.push(eventName);
+      });
+
+      driver.setupStreamElements(new sharedActions.SetupStreamElements({
+        publisherConfig: publisherConfig
+      }));
+
+      // Now disconnect, checking for any unexpected unsubscribes, or any missed
+      // unsubscribes.
+      sandbox.stub(publisher, "off", function(eventNames) {
+        var events = eventNames.split(" ");
+
+        events.forEach(function(eventName) {
+          var index = subscribedEvents.indexOf(eventName);
+
+          expect(index).not.eql(-1);
+
+          subscribedEvents.splice(index, 1);
+        });
+      });
+
+      driver.disconnectSession();
+
+      expect(subscribedEvents).eql([]);
+    });
+
+    it("should unsubscribe to all the subscriber events that were subscribed to in #connectSession", function() {
+      var subscribedEvents = [];
+
+      // First find out which events were subscribed to.
+      sandbox.stub(session, "on", function(eventName) {
+        subscribedEvents.push(eventName);
+      });
+
+      driver.connectSession(sessionData);
+
+      // Now disconnect, checking for any unexpected unsubscribes, or any missed
+      // unsubscribes.
+      sandbox.stub(session, "off", function(eventNames) {
+        var events = eventNames.split(" ");
+
+        events.forEach(function(eventName) {
+          var index = subscribedEvents.indexOf(eventName);
+
+          expect(index).not.eql(-1);
+
+          subscribedEvents.splice(index, 1);
+        });
+      });
+
+      driver.disconnectSession();
+
+      expect(subscribedEvents).eql([]);
+    });
+
     it("should dispatch a DataChannelsAvailable action with available = false", function() {
       driver.disconnectSession();
 
       sinon.assert.calledOnce(dispatcher.dispatch);
       sinon.assert.calledWithExactly(dispatcher.dispatch,
         new sharedActions.DataChannelsAvailable({
           available: false
         }));
rename from browser/components/loop/test/shared/vendor/sinon-1.15.0.js
rename to browser/components/loop/test/shared/vendor/sinon-1.16.1.js
--- a/browser/components/loop/test/shared/vendor/sinon-1.15.0.js
+++ b/browser/components/loop/test/shared/vendor/sinon-1.16.1.js
@@ -1,10 +1,10 @@
 /**
- * Sinon.JS 1.15.0, 2015/05/30
+ * Sinon.JS 1.16.1, 2015/08/20
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
  *
  * (The BSD License)
  * 
  * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no
  * All rights reserved.
@@ -666,461 +666,521 @@
             return ascii(this, object, processed, indent);
         }
     };
 
     return Formatio.prototype;
 });
 !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
 (function (global){
-/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
-/*global global*/
+/*global global, window*/
 /**
  * @author Christian Johansen (christian@cjohansen.no) and contributors
  * @license BSD
  *
  * Copyright (c) 2010-2014 Christian Johansen
  */
 
-// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
-// browsers, a number.
-// see https://github.com/cjohansen/Sinon.JS/pull/436
-var timeoutResult = setTimeout(function() {}, 0);
-var addTimerReturnsObject = typeof timeoutResult === "object";
-clearTimeout(timeoutResult);
-
-var NativeDate = Date;
-var id = 1;
-
-/**
- * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
- * number of milliseconds. This is used to support human-readable strings passed
- * to clock.tick()
- */
-function parseTime(str) {
-    if (!str) {
-        return 0;
-    }
-
-    var strings = str.split(":");
-    var l = strings.length, i = l;
-    var ms = 0, parsed;
-
-    if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
-        throw new Error("tick only understands numbers and 'h:m:s'");
-    }
-
-    while (i--) {
-        parsed = parseInt(strings[i], 10);
-
-        if (parsed >= 60) {
-            throw new Error("Invalid time " + str);
+(function (global) {
+    
+    // Make properties writable in IE, as per
+    // http://www.adequatelygood.com/Replacing-setTimeout-Globally.html
+    // JSLint being anal
+    var glbl = global;
+
+    global.setTimeout = glbl.setTimeout;
+    global.clearTimeout = glbl.clearTimeout;
+    global.setImmediate = glbl.setImmediate;
+    global.clearImmediate = glbl.clearImmediate;
+    global.setInterval = glbl.setInterval;
+    global.clearInterval = glbl.clearInterval;
+    global.Date = glbl.Date;
+
+    // node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
+    // browsers, a number.
+    // see https://github.com/cjohansen/Sinon.JS/pull/436
+
+    var NOOP = function () { return undefined; };
+    var timeoutResult = setTimeout(NOOP, 0);
+    var addTimerReturnsObject = typeof timeoutResult === "object";
+    clearTimeout(timeoutResult);
+
+    var NativeDate = Date;
+    var uniqueTimerId = 1;
+
+    /**
+     * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
+     * number of milliseconds. This is used to support human-readable strings passed
+     * to clock.tick()
+     */
+    function parseTime(str) {
+        if (!str) {
+            return 0;
+        }
+
+        var strings = str.split(":");
+        var l = strings.length, i = l;
+        var ms = 0, parsed;
+
+        if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
+            throw new Error("tick only understands numbers and 'h:m:s'");
         }
 
-        ms += parsed * Math.pow(60, (l - i - 1));
-    }
-
-    return ms * 1000;
-}
-
-/**
- * Used to grok the `now` parameter to createClock.
- */
-function getEpoch(epoch) {
-    if (!epoch) { return 0; }
-    if (typeof epoch.getTime === "function") { return epoch.getTime(); }
-    if (typeof epoch === "number") { return epoch; }
-    throw new TypeError("now should be milliseconds since UNIX epoch");
-}
-
-function inRange(from, to, timer) {
-    return timer && timer.callAt >= from && timer.callAt <= to;
-}
-
-function mirrorDateProperties(target, source) {
-    if (source.now) {
-        target.now = function now() {
-            return target.clock.now;
-        };
-    } else {
-        delete target.now;
-    }
-
-    if (source.toSource) {
-        target.toSource = function toSource() {
-            return source.toSource();
-        };
-    } else {
-        delete target.toSource;
-    }
-
-    target.toString = function toString() {
-        return source.toString();
-    };
-
-    target.prototype = source.prototype;
-    target.parse = source.parse;
-    target.UTC = source.UTC;
-    target.prototype.toUTCString = source.prototype.toUTCString;
-
-    for (var prop in source) {
-        if (source.hasOwnProperty(prop)) {
-            target[prop] = source[prop];
+        while (i--) {
+            parsed = parseInt(strings[i], 10);
+
+            if (parsed >= 60) {
+                throw new Error("Invalid time " + str);
+            }
+
+            ms += parsed * Math.pow(60, (l - i - 1));
+        }
+
+        return ms * 1000;
+    }
+
+    /**
+     * Used to grok the `now` parameter to createClock.
+     */
+    function getEpoch(epoch) {
+        if (!epoch) { return 0; }
+        if (typeof epoch.getTime === "function") { return epoch.getTime(); }
+        if (typeof epoch === "number") { return epoch; }
+        throw new TypeError("now should be milliseconds since UNIX epoch");
+    }
+
+    function inRange(from, to, timer) {
+        return timer && timer.callAt >= from && timer.callAt <= to;
+    }
+
+    function mirrorDateProperties(target, source) {
+        var prop;
+        for (prop in source) {
+            if (source.hasOwnProperty(prop)) {
+                target[prop] = source[prop];
+            }
+        }
+
+        // set special now implementation
+        if (source.now) {
+            target.now = function now() {
+                return target.clock.now;
+            };
+        } else {
+            delete target.now;
+        }
+
+        // set special toSource implementation
+        if (source.toSource) {
+            target.toSource = function toSource() {
+                return source.toSource();
+            };
+        } else {
+            delete target.toSource;
         }
-    }
-
-    return target;
-}
-
-function createDate() {
-    function ClockDate(year, month, date, hour, minute, second, ms) {
-        // Defensive and verbose to avoid potential harm in passing
-        // explicit undefined when user does not pass argument
-        switch (arguments.length) {
-        case 0:
-            return new NativeDate(ClockDate.clock.now);
-        case 1:
-            return new NativeDate(year);
-        case 2:
-            return new NativeDate(year, month);
-        case 3:
-            return new NativeDate(year, month, date);
-        case 4:
-            return new NativeDate(year, month, date, hour);
-        case 5:
-            return new NativeDate(year, month, date, hour, minute);
-        case 6:
-            return new NativeDate(year, month, date, hour, minute, second);
-        default:
-            return new NativeDate(year, month, date, hour, minute, second, ms);
+
+        // set special toString implementation
+        target.toString = function toString() {
+            return source.toString();
+        };
+
+        target.prototype = source.prototype;
+        target.parse = source.parse;
+        target.UTC = source.UTC;
+        target.prototype.toUTCString = source.prototype.toUTCString;
+
+        return target;
+    }
+
+    function createDate() {
+        function ClockDate(year, month, date, hour, minute, second, ms) {
+            // Defensive and verbose to avoid potential harm in passing
+            // explicit undefined when user does not pass argument
+            switch (arguments.length) {
+            case 0:
+                return new NativeDate(ClockDate.clock.now);
+            case 1:
+                return new NativeDate(year);
+            case 2:
+                return new NativeDate(year, month);
+            case 3:
+                return new NativeDate(year, month, date);
+            case 4:
+                return new NativeDate(year, month, date, hour);
+            case 5:
+                return new NativeDate(year, month, date, hour, minute);
+            case 6:
+                return new NativeDate(year, month, date, hour, minute, second);
+            default:
+                return new NativeDate(year, month, date, hour, minute, second, ms);
+            }
         }
-    }
-
-    return mirrorDateProperties(ClockDate, NativeDate);
-}
-
-function addTimer(clock, timer) {
-    if (typeof timer.func === "undefined") {
-        throw new Error("Callback must be provided to timer calls");
-    }
-
-    if (!clock.timers) {
-        clock.timers = {};
-    }
-
-    timer.id = id++;
-    timer.createdAt = clock.now;
-    timer.callAt = clock.now + (timer.delay || 0);
-
-    clock.timers[timer.id] = timer;
-
-    if (addTimerReturnsObject) {
-        return {
-            id: timer.id,
-            ref: function() {},
-            unref: function() {}
-        };
-    }
-    else {
+
+        return mirrorDateProperties(ClockDate, NativeDate);
+    }
+
+    function addTimer(clock, timer) {
+        if (timer.func === undefined) {
+            throw new Error("Callback must be provided to timer calls");
+        }
+
+        if (!clock.timers) {
+            clock.timers = {};
+        }
+
+        timer.id = uniqueTimerId++;
+        timer.createdAt = clock.now;
+        timer.callAt = clock.now + (timer.delay || (clock.duringTick ? 1 : 0));
+
+        clock.timers[timer.id] = timer;
+
+        if (addTimerReturnsObject) {
+            return {
+                id: timer.id,
+                ref: NOOP,
+                unref: NOOP
+            };
+        }
+
         return timer.id;
     }
-}
-
-function firstTimerInRange(clock, from, to) {
-    var timers = clock.timers, timer = null;
-
-    for (var id in timers) {
-        if (!inRange(from, to, timers[id])) {
-            continue;
+
+
+    function compareTimers(a, b) {
+        // Sort first by absolute timing
+        if (a.callAt < b.callAt) {
+            return -1;
+        }
+        if (a.callAt > b.callAt) {
+            return 1;
+        }
+
+        // Sort next by immediate, immediate timers take precedence
+        if (a.immediate && !b.immediate) {
+            return -1;
         }
-
-        if (!timer || ~compareTimers(timer, timers[id])) {
-            timer = timers[id];
+        if (!a.immediate && b.immediate) {
+            return 1;
+        }
+
+        // Sort next by creation time, earlier-created timers take precedence
+        if (a.createdAt < b.createdAt) {
+            return -1;
+        }
+        if (a.createdAt > b.createdAt) {
+            return 1;
+        }
+
+        // Sort next by id, lower-id timers take precedence
+        if (a.id < b.id) {
+            return -1;
+        }
+        if (a.id > b.id) {
+            return 1;
         }
-    }
-
-    return timer;
-}
-
-function compareTimers(a, b) {
-    // Sort first by absolute timing
-    if (a.callAt < b.callAt) {
-        return -1;
-    }
-    if (a.callAt > b.callAt) {
-        return 1;
-    }
-
-    // Sort next by immediate, immediate timers take precedence
-    if (a.immediate && !b.immediate) {
-        return -1;
-    }
-    if (!a.immediate && b.immediate) {
-        return 1;
-    }
-
-    // Sort next by creation time, earlier-created timers take precedence
-    if (a.createdAt < b.createdAt) {
-        return -1;
-    }
-    if (a.createdAt > b.createdAt) {
-        return 1;
-    }
-
-    // Sort next by id, lower-id timers take precedence
-    if (a.id < b.id) {
-        return -1;
-    }
-    if (a.id > b.id) {
-        return 1;
-    }
-
-    // As timer ids are unique, no fallback `0` is necessary
-}
-
-function callTimer(clock, timer) {
-    if (typeof timer.interval == "number") {
-        clock.timers[timer.id].callAt += timer.interval;
-    } else {
-        delete clock.timers[timer.id];
-    }
-
-    try {
-        if (typeof timer.func == "function") {
-            timer.func.apply(null, timer.args);
+
+        // As timer ids are unique, no fallback `0` is necessary
+    }
+
+    function firstTimerInRange(clock, from, to) {
+        var timers = clock.timers,
+            timer = null,
+            id,
+            isInRange;
+
+        for (id in timers) {
+            if (timers.hasOwnProperty(id)) {
+                isInRange = inRange(from, to, timers[id]);
+
+                if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) {
+                    timer = timers[id];
+                }
+            }
+        }
+
+        return timer;
+    }
+
+    function callTimer(clock, timer) {
+        var exception;
+
+        if (typeof timer.interval === "number") {
+            clock.timers[timer.id].callAt += timer.interval;
         } else {
-            eval(timer.func);
+            delete clock.timers[timer.id];
         }
-    } catch (e) {
-        var exception = e;
-    }
-
-    if (!clock.timers[timer.id]) {
+
+        try {
+            if (typeof timer.func === "function") {
+                timer.func.apply(null, timer.args);
+            } else {
+                eval(timer.func);
+            }
+        } catch (e) {
+            exception = e;
+        }
+
+        if (!clock.timers[timer.id]) {
+            if (exception) {
+                throw exception;
+            }
+            return;
+        }
+
         if (exception) {
             throw exception;
         }
-        return;
-    }
-
-    if (exception) {
-        throw exception;
-    }
-}
-
-function uninstall(clock, target) {
-    var method;
-
-    for (var i = 0, l = clock.methods.length; i < l; i++) {
-        method = clock.methods[i];
-
-        if (target[method].hadOwnProperty) {
-            target[method] = clock["_" + method];
-        } else {
-            try {
-                delete target[method];
-            } catch (e) {}
+    }
+
+    function uninstall(clock, target) {
+        var method,
+            i,
+            l;
+
+        for (i = 0, l = clock.methods.length; i < l; i++) {
+            method = clock.methods[i];
+
+            if (target[method].hadOwnProperty) {
+                target[method] = clock["_" + method];
+            } else {
+                try {
+                    delete target[method];
+                } catch (ignore) {}
+            }
         }
-    }
-
-    // Prevent multiple executions which will completely remove these props
-    clock.methods = [];
-}
-
-function hijackMethod(target, method, clock) {
-    clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
-    clock["_" + method] = target[method];
-
-    if (method == "Date") {
-        var date = mirrorDateProperties(clock[method], target[method]);
-        target[method] = date;
-    } else {
-        target[method] = function () {
-            return clock[method].apply(clock, arguments);
-        };
-
-        for (var prop in clock[method]) {
-            if (clock[method].hasOwnProperty(prop)) {
-                target[method][prop] = clock[method][prop];
+
+        // Prevent multiple executions which will completely remove these props
+        clock.methods = [];
+    }
+
+    function hijackMethod(target, method, clock) {
+        var prop;
+
+        clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
+        clock["_" + method] = target[method];
+
+        if (method === "Date") {
+            var date = mirrorDateProperties(clock[method], target[method]);
+            target[method] = date;
+        } else {
+            target[method] = function () {
+                return clock[method].apply(clock, arguments);
+            };
+
+            for (prop in clock[method]) {
+                if (clock[method].hasOwnProperty(prop)) {
+                    target[method][prop] = clock[method][prop];
+                }
+            }
+        }
+
+        target[method].clock = clock;
+    }
+
+    var timers = {
+        setTimeout: setTimeout,
+        clearTimeout: clearTimeout,
+        setImmediate: global.setImmediate,
+        clearImmediate: global.clearImmediate,
+        setInterval: setInterval,
+        clearInterval: clearInterval,
+        Date: Date
+    };
+
+    var keys = Object.keys || function (obj) {
+        var ks = [],
+            key;
+
+        for (key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                ks.push(key);
             }
         }
-    }
-
-    target[method].clock = clock;
-}
-
-var timers = {
-    setTimeout: setTimeout,
-    clearTimeout: clearTimeout,
-    setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
-    clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined),
-    setInterval: setInterval,
-    clearInterval: clearInterval,
-    Date: Date
-};
-
-var keys = Object.keys || function (obj) {
-    var ks = [];
-    for (var key in obj) {
-        ks.push(key);
-    }
-    return ks;
-};
-
-exports.timers = timers;
-
-var createClock = exports.createClock = function (now) {
-    var clock = {
-        now: getEpoch(now),
-        timeouts: {},
-        Date: createDate()
-    };
-
-    clock.Date.clock = clock;
-
-    clock.setTimeout = function setTimeout(func, timeout) {
-        return addTimer(clock, {
-            func: func,
-            args: Array.prototype.slice.call(arguments, 2),
-            delay: timeout
-        });
-    };
-
-    clock.clearTimeout = function clearTimeout(timerId) {
-        if (!timerId) {
-            // null appears to be allowed in most browsers, and appears to be
-            // relied upon by some libraries, like Bootstrap carousel
-            return;
-        }
-        if (!clock.timers) {
-            clock.timers = [];
-        }
-        // in Node, timerId is an object with .ref()/.unref(), and
-        // its .id field is the actual timer id.
-        if (typeof timerId === "object") {
-            timerId = timerId.id
-        }
-        if (timerId in clock.timers) {
-            delete clock.timers[timerId];
-        }
-    };
-
-    clock.setInterval = function setInterval(func, timeout) {
-        return addTimer(clock, {
-            func: func,
-            args: Array.prototype.slice.call(arguments, 2),
-            delay: timeout,
-            interval: timeout
-        });
-    };
-
-    clock.clearInterval = function clearInterval(timerId) {
-        clock.clearTimeout(timerId);
+
+        return ks;
     };
 
-    clock.setImmediate = function setImmediate(func) {
-        return addTimer(clock, {
-            func: func,
-            args: Array.prototype.slice.call(arguments, 1),
-            immediate: true
-        });
-    };
-
-    clock.clearImmediate = function clearImmediate(timerId) {
-        clock.clearTimeout(timerId);
-    };
-
-    clock.tick = function tick(ms) {
-        ms = typeof ms == "number" ? ms : parseTime(ms);
-        var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now;
-        var timer = firstTimerInRange(clock, tickFrom, tickTo);
-
-        var firstException;
-        while (timer && tickFrom <= tickTo) {
-            if (clock.timers[timer.id]) {
-                tickFrom = clock.now = timer.callAt;
-                try {
-                    callTimer(clock, timer);
-                } catch (e) {
-                    firstException = firstException || e;
-                }
+    exports.timers = timers;
+
+    function createClock(now) {
+        var clock = {
+            now: getEpoch(now),
+            timeouts: {},
+            Date: createDate()
+        };
+
+        clock.Date.clock = clock;
+
+        clock.setTimeout = function setTimeout(func, timeout) {
+            return addTimer(clock, {
+                func: func,
+                args: Array.prototype.slice.call(arguments, 2),
+                delay: timeout
+            });
+        };
+
+        clock.clearTimeout = function clearTimeout(timerId) {
+            if (!timerId) {
+                // null appears to be allowed in most browsers, and appears to be
+                // relied upon by some libraries, like Bootstrap carousel
+                return;
+            }
+
+            if (!clock.timers) {
+                clock.timers = [];
+            }
+
+            // in Node, timerId is an object with .ref()/.unref(), and
+            // its .id field is the actual timer id.
+            if (typeof timerId === "object") {
+                timerId = timerId.id;
+            }
+
+            if (clock.timers.hasOwnProperty(timerId)) {
+                delete clock.timers[timerId];
             }
-
-            timer = firstTimerInRange(clock, previous, tickTo);
-            previous = tickFrom;
-        }
-
-        clock.now = tickTo;
-
-        if (firstException) {
-            throw firstException;
+        };
+
+        clock.setInterval = function setInterval(func, timeout) {
+            return addTimer(clock, {
+                func: func,
+                args: Array.prototype.slice.call(arguments, 2),
+                delay: timeout,
+                interval: timeout
+            });
+        };
+
+        clock.clearInterval = function clearInterval(timerId) {
+            clock.clearTimeout(timerId);
+        };
+
+        clock.setImmediate = function setImmediate(func) {
+            return addTimer(clock, {
+                func: func,
+                args: Array.prototype.slice.call(arguments, 1),
+                immediate: true
+            });
+        };
+
+        clock.clearImmediate = function clearImmediate(timerId) {
+            clock.clearTimeout(timerId);
+        };
+
+        clock.tick = function tick(ms) {
+            ms = typeof ms === "number" ? ms : parseTime(ms);
+            var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now;
+            var timer = firstTimerInRange(clock, tickFrom, tickTo);
+
+            clock.duringTick = true;
+
+            var firstException;
+            while (timer && tickFrom <= tickTo) {
+                if (clock.timers[timer.id]) {
+                    tickFrom = clock.now = timer.callAt;
+                    try {
+                        callTimer(clock, timer);
+                    } catch (e) {
+                        firstException = firstException || e;
+                    }
+                }
+
+                timer = firstTimerInRange(clock, previous, tickTo);
+                previous = tickFrom;
+            }
+
+            clock.duringTick = false;
+            clock.now = tickTo;
+
+            if (firstException) {
+                throw firstException;
+            }
+
+            return clock.now;
+        };
+
+        clock.reset = function reset() {
+            clock.timers = {};
+        };
+
+        return clock;
+    }
+    exports.createClock = createClock;
+
+    function detectKnownFailSituation(methods) {
+        if (methods.indexOf("Date") < 0) { return; }
+
+        if (methods.indexOf("setTimeout") < 0) {
+            throw new Error("Native setTimeout will not work when Date is faked");
         }
 
-        return clock.now;
-    };
-
-    clock.reset = function reset() {
-        clock.timers = {};
+        if (methods.indexOf("setImmediate") < 0) {
+            throw new Error("Native setImmediate will not work when Date is faked");
+        }
+    }
+
+    exports.install = function install(target, now, toFake) {
+        var i,
+            l;
+
+        if (typeof target === "number") {
+            toFake = now;
+            now = target;
+            target = null;
+        }
+
+        if (!target) {
+            target = global;
+        }
+
+        var clock = createClock(now);
+
+        clock.uninstall = function () {
+            uninstall(clock, target);
+        };
+
+        clock.methods = toFake || [];
+
+        if (clock.methods.length === 0) {
+            clock.methods = keys(timers);
+        }
+
+        detectKnownFailSituation(clock.methods);
+
+        for (i = 0, l = clock.methods.length; i < l; i++) {
+            hijackMethod(target, clock.methods[i], clock);
+        }
+
+        return clock;
     };
 
-    return clock;
-};
-
-exports.install = function install(target, now, toFake) {
-    if (typeof target === "number") {
-        toFake = now;
-        now = target;
-        target = null;
-    }
-
-    if (!target) {
-        target = global;
-    }
-
-    var clock = createClock(now);
-
-    clock.uninstall = function () {
-        uninstall(clock, target);
-    };
-
-    clock.methods = toFake || [];
-
-    if (clock.methods.length === 0) {
-        clock.methods = keys(timers);
-    }
-
-    for (var i = 0, l = clock.methods.length; i < l; i++) {
-        hijackMethod(target, clock.methods[i], clock);
-    }
-
-    return clock;
-};
+}(global || this));
 
 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
 },{}]},{},[1])(1)
 });
   })();
   var define;
 /**
  * Sinon core utilities. For internal use only.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
 var sinon = (function () {
 "use strict";
-
-    var sinon;
+ // eslint-disable-line no-unused-vars
+    
+    var sinonModule;
     var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
-        sinon = module.exports = require("./sinon/util/core");
+        sinonModule = module.exports = require("./sinon/util/core");
         require("./sinon/extend");
         require("./sinon/typeOf");
         require("./sinon/times_in_words");
         require("./sinon/spy");
         require("./sinon/call");
         require("./sinon/behavior");
         require("./sinon/stub");
         require("./sinon/mock");
@@ -1133,46 +1193,46 @@ var sinon = (function () {
         require("./sinon/format");
         require("./sinon/log_error");
     }
 
     if (isAMD) {
         define(loadDependencies);
     } else if (isNode) {
         loadDependencies(require, module.exports, module);
-        sinon = module.exports;
+        sinonModule = module.exports;
     } else {
-        sinon = {};
-    }
-
-    return sinon;
+        sinonModule = {};
+    }
+
+    return sinonModule;
 }());
 
 /**
  * @depend ../../sinon.js
  */
 /**
  * Sinon core utilities. For internal use only.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon) {
-    var div = typeof document != "undefined" && document.createElement("div");
+(function (sinonGlobal) {
+    
+    var div = typeof document !== "undefined" && document.createElement("div");
     var hasOwn = Object.prototype.hasOwnProperty;
 
     function isDOMNode(obj) {
         var success = false;
 
         try {
             obj.appendChild(div);
-            success = div.parentNode == obj;
+            success = div.parentNode === obj;
         } catch (e) {
             return false;
         } finally {
             try {
                 obj.removeChild(div);
             } catch (e) {
                 // Remove failed, not much we can do about that
             }
@@ -1209,49 +1269,50 @@ var sinon = (function () {
     var hasES5Support = "keys" in Object;
 
     function makeApi(sinon) {
         sinon.wrapMethod = function wrapMethod(object, property, method) {
             if (!object) {
                 throw new TypeError("Should wrap property of object");
             }
 
-            if (typeof method != "function" && typeof method != "object") {
+            if (typeof method !== "function" && typeof method !== "object") {
                 throw new TypeError("Method wrapper should be a function or a property descriptor");
             }
 
             function checkWrappedMethod(wrappedMethod) {
+                var error;
+
                 if (!isFunction(wrappedMethod)) {
                     error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
                                         property + " as function");
                 } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
                     error = new TypeError("Attempted to wrap " + property + " which is already wrapped");
                 } else if (wrappedMethod.calledBefore) {
-                    var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
+                    var verb = wrappedMethod.returns ? "stubbed" : "spied on";
                     error = new TypeError("Attempted to wrap " + property + " which is already " + verb);
                 }
 
                 if (error) {
                     if (wrappedMethod && wrappedMethod.stackTrace) {
                         error.stack += "\n--------------\n" + wrappedMethod.stackTrace;
                     }
                     throw error;
                 }
             }
 
-            var error, wrappedMethod;
+            var error, wrappedMethod, i;
 
             // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem
             // when using hasOwn.call on objects from other frames.
             var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property);
 
             if (hasES5Support) {
-                var methodDesc = (typeof method == "function") ? {value: method} : method,
-                    wrappedMethodDesc = sinon.getPropertyDescriptor(object, property),
-                    i;
+                var methodDesc = (typeof method === "function") ? {value: method} : method;
+                var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property);
 
                 if (!wrappedMethodDesc) {
                     error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
                                         property + " as function");
                 } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) {
                     error = new TypeError("Attempted to wrap " + property + " which is already wrapped");
                 }
                 if (error) {
@@ -1288,17 +1349,17 @@ var sinon = (function () {
             method.restore = function () {
                 // For prototype properties try to reset by delete first.
                 // If this fails (ex: localStorage on mobile safari) then force a reset
                 // via direct assignment.
                 if (!owned) {
                     // In some cases `delete` may throw an error
                     try {
                         delete object[property];
-                    } catch (e) {}
+                    } catch (e) {} // eslint-disable-line no-empty
                     // For native code functions `delete` fails without throwing an error
                     // on Chrome < 43, PhantomJS, etc.
                 } else if (hasES5Support) {
                     Object.defineProperty(object, property, wrappedMethodDesc);
                 }
 
                 // Use strict equality comparison to check failures then force a reset
                 // via direct assignment.
@@ -1322,22 +1383,18 @@ var sinon = (function () {
             return new F();
         };
 
         sinon.deepEqual = function deepEqual(a, b) {
             if (sinon.match && sinon.match.isMatcher(a)) {
                 return a.test(b);
             }
 
-            if (typeof a != "object" || typeof b != "object") {
-                if (isReallyNaN(a) && isReallyNaN(b)) {
-                    return true;
-                } else {
-                    return a === b;
-                }
+            if (typeof a !== "object" || typeof b !== "object") {
+                return isReallyNaN(a) && isReallyNaN(b) || a === b;
             }
 
             if (isElement(a) || isElement(b)) {
                 return a === b;
             }
 
             if (a === b) {
                 return true;
@@ -1348,47 +1405,53 @@ var sinon = (function () {
             }
 
             if (a instanceof RegExp && b instanceof RegExp) {
                 return (a.source === b.source) && (a.global === b.global) &&
                     (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline);
             }
 
             var aString = Object.prototype.toString.call(a);
-            if (aString != Object.prototype.toString.call(b)) {
+            if (aString !== Object.prototype.toString.call(b)) {
                 return false;
             }
 
-            if (aString == "[object Date]") {
+            if (aString === "[object Date]") {
                 return a.valueOf() === b.valueOf();
             }
 
-            var prop, aLength = 0, bLength = 0;
-
-            if (aString == "[object Array]" && a.length !== b.length) {
+            var prop;
+            var aLength = 0;
+            var bLength = 0;
+
+            if (aString === "[object Array]" && a.length !== b.length) {
                 return false;
             }
 
             for (prop in a) {
-                aLength += 1;
-
-                if (!(prop in b)) {
-                    return false;
-                }
-
-                if (!deepEqual(a[prop], b[prop])) {
-                    return false;
+                if (a.hasOwnProperty(prop)) {
+                    aLength += 1;
+
+                    if (!(prop in b)) {
+                        return false;
+                    }
+
+                    if (!deepEqual(a[prop], b[prop])) {
+                        return false;
+                    }
                 }
             }
 
             for (prop in b) {
-                bLength += 1;
+                if (b.hasOwnProperty(prop)) {
+                    bLength += 1;
+                }
             }
 
-            return aLength == bLength;
+            return aLength === bLength;
         };
 
         sinon.functionName = function functionName(func) {
             var name = func.displayName || func.name;
 
             // Use function decomposition as a last resort to get function
             // name. Does not rely on function decomposition to work - if it
             // doesn't debugging will be slightly less informative
@@ -1398,17 +1461,19 @@ var sinon = (function () {
                 name = matches && matches[1];
             }
 
             return name;
         };
 
         sinon.functionToString = function toString() {
             if (this.getCall && this.callCount) {
-                var thisValue, prop, i = this.callCount;
+                var thisValue,
+                    prop;
+                var i = this.callCount;
 
                 while (i--) {
                     thisValue = this.getCall(i).thisValue;
 
                     for (prop in thisValue) {
                         if (thisValue[prop] === this) {
                             return prop;
                         }
@@ -1431,22 +1496,24 @@ var sinon = (function () {
                     keys.push(key);
                 }
             }
 
             return keys;
         };
 
         sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) {
-            var proto = object, descriptor;
+            var proto = object;
+            var descriptor;
+
             while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) {
                 proto = Object.getPrototypeOf(proto);
             }
             return descriptor;
-        }
+        };
 
         sinon.getConfig = function (custom) {
             var config = {};
             custom = custom || {};
             var defaults = sinon.defaultConfig;
 
             for (var prop in defaults) {
                 if (defaults.hasOwnProperty(prop)) {
@@ -1461,19 +1528,19 @@ var sinon = (function () {
             injectIntoThis: true,
             injectInto: null,
             properties: ["spy", "stub", "mock", "clock", "server", "requests"],
             useFakeTimers: true,
             useFakeServer: true
         };
 
         sinon.timesInWords = function timesInWords(count) {
-            return count == 1 && "once" ||
-                count == 2 && "twice" ||
-                count == 3 && "thrice" ||
+            return count === 1 && "once" ||
+                count === 2 && "twice" ||
+                count === 3 && "thrice" ||
                 (count || 0) + " times";
         };
 
         sinon.calledInOrder = function (spies) {
             for (var i = 1, l = spies.length; i < l; i++) {
                 if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) {
                     return false;
                 }
@@ -1511,39 +1578,45 @@ var sinon = (function () {
             } else if (isRestorable(object)) {
                 object.restore();
             }
         };
 
         return sinon;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports) {
         makeApi(exports);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
-        loadDependencies(require, module.exports);
-    } else if (!sinon) {
+        return;
+    }
+
+    if (isNode) {
+        loadDependencies(require, module.exports, module);
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
 
         // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
         var hasDontEnumBug = (function () {
             var obj = {
                 constructor: function () {
                     return "0";
                 },
@@ -1567,38 +1640,40 @@ var sinon = (function () {
                 },
                 hasOwnProperty: function () {
                     return "7";
                 },
                 length: function () {
                     return "8";
                 },
                 unique: function () {
-                    return "9"
+                    return "9";
                 }
             };
 
             var result = [];
             for (var prop in obj) {
-                result.push(obj[prop]());
+                if (obj.hasOwnProperty(prop)) {
+                    result.push(obj[prop]());
+                }
             }
             return result.join("") !== "0123456789";
         })();
 
         /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will
          *         override properties in previous sources.
          *
          * target - The Object to extend
          * sources - Objects to copy properties from.
          *
          * Returns the extended target
          */
         function extend(target /*, sources */) {
-            var sources = Array.prototype.slice.call(arguments, 1),
-                source, i, prop;
+            var sources = Array.prototype.slice.call(arguments, 1);
+            var source, i, prop;
 
             for (i = 0; i < sources.length; i++) {
                 source = sources[i];
 
                 for (prop in source) {
                     if (source.hasOwnProperty(prop)) {
                         target[prop] = source[prop];
                     }
@@ -1607,46 +1682,52 @@ var sinon = (function () {
                 // Make sure we copy (own) toString method even when in JScript with DontEnum bug
                 // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
                 if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) {
                     target.toString = source.toString;
                 }
             }
 
             return target;
-        };
+        }
 
         sinon.extend = extend;
         return sinon.extend;
     }
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         module.exports = makeApi(sinon);
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
 
         function timesInWords(count) {
             switch (count) {
                 case 1:
                     return "once";
                 case 2:
                     return "twice";
@@ -1657,100 +1738,109 @@ var sinon = (function () {
             }
         }
 
         sinon.timesInWords = timesInWords;
         return sinon.timesInWords;
     }
 
     function loadDependencies(require, exports, module) {
-        var sinon = require("./util/core");
-        module.exports = makeApi(sinon);
-    }
-
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+        var core = require("./util/core");
+        module.exports = makeApi(core);
+    }
+
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  */
 /**
  * Format functions
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2014 Christian Johansen
  */
-
-(function (sinon, formatio) {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
         function typeOf(value) {
             if (value === null) {
                 return "null";
             } else if (value === undefined) {
                 return "undefined";
             }
             var string = Object.prototype.toString.call(value);
             return string.substring(8, string.length - 1).toLowerCase();
-        };
+        }
 
         sinon.typeOf = typeOf;
         return sinon.typeOf;
     }
 
     function loadDependencies(require, exports, module) {
-        var sinon = require("./util/core");
-        module.exports = makeApi(sinon);
-    }
-
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+        var core = require("./util/core");
+        module.exports = makeApi(core);
+    }
+
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
     }
 }(
-    (typeof sinon == "object" && sinon || null),
-    (typeof formatio == "object" && formatio)
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
 ));
 
 /**
  * @depend util/core.js
  * @depend typeOf.js
  */
 /*jslint eqeqeq: false, onevar: false, plusplus: false*/
 /*global module, require, sinon*/
 /**
  * Match functions
  *
  * @author Maximilian Antoni (mail@maxantoni.de)
  * @license BSD
  *
  * Copyright (c) 2012 Maximilian Antoni
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
         function assertType(value, type, name) {
             var actual = sinon.typeOf(value);
             if (actual !== type) {
                 throw new TypeError("Expected type of " + name + " to be " +
                     type + ", but was " + actual);
             }
         }
@@ -1768,63 +1858,33 @@ var sinon = (function () {
         function matchObject(expectation, actual) {
             if (actual === null || actual === undefined) {
                 return false;
             }
             for (var key in expectation) {
                 if (expectation.hasOwnProperty(key)) {
                     var exp = expectation[key];
                     var act = actual[key];
-                    if (match.isMatcher(exp)) {
+                    if (isMatcher(exp)) {
                         if (!exp.test(act)) {
                             return false;
                         }
                     } else if (sinon.typeOf(exp) === "object") {
                         if (!matchObject(exp, act)) {
                             return false;
                         }
                     } else if (!sinon.deepEqual(exp, act)) {
                         return false;
                     }
                 }
             }
             return true;
         }
 
-        matcher.or = function (m2) {
-            if (!arguments.length) {
-                throw new TypeError("Matcher expected");
-            } else if (!isMatcher(m2)) {
-                m2 = match(m2);
-            }
-            var m1 = this;
-            var or = sinon.create(matcher);
-            or.test = function (actual) {
-                return m1.test(actual) || m2.test(actual);
-            };
-            or.message = m1.message + ".or(" + m2.message + ")";
-            return or;
-        };
-
-        matcher.and = function (m2) {
-            if (!arguments.length) {
-                throw new TypeError("Matcher expected");
-            } else if (!isMatcher(m2)) {
-                m2 = match(m2);
-            }
-            var m1 = this;
-            var and = sinon.create(matcher);
-            and.test = function (actual) {
-                return m1.test(actual) && m2.test(actual);
-            };
-            and.message = m1.message + ".and(" + m2.message + ")";
-            return and;
-        };
-
-        var match = function (expectation, message) {
+        function match(expectation, message) {
             var m = sinon.create(matcher);
             var type = sinon.typeOf(expectation);
             switch (type) {
             case "object":
                 if (typeof expectation.test === "function") {
                     m.test = function (actual) {
                         return expectation.test(actual) === true;
                     };
@@ -1839,17 +1899,18 @@ var sinon = (function () {
                 }
                 m.test = function (actual) {
                     return matchObject(expectation, actual);
                 };
                 m.message = "match(" + str.join(", ") + ")";
                 break;
             case "number":
                 m.test = function (actual) {
-                    return expectation == actual;
+                    // we need type coercion here
+                    return expectation == actual; // eslint-disable-line eqeqeq
                 };
                 break;
             case "string":
                 m.test = function (actual) {
                     if (typeof actual !== "string") {
                         return false;
                     }
                     return actual.indexOf(expectation) !== -1;
@@ -1876,16 +1937,46 @@ var sinon = (function () {
                 m.test = function (actual) {
                     return sinon.deepEqual(expectation, actual);
                 };
             }
             if (!m.message) {
                 m.message = "match(" + expectation + ")";
             }
             return m;
+        }
+
+        matcher.or = function (m2) {
+            if (!arguments.length) {
+                throw new TypeError("Matcher expected");
+            } else if (!isMatcher(m2)) {
+                m2 = match(m2);
+            }
+            var m1 = this;
+            var or = sinon.create(matcher);
+            or.test = function (actual) {
+                return m1.test(actual) || m2.test(actual);
+            };
+            or.message = m1.message + ".or(" + m2.message + ")";
+            return or;
+        };
+
+        matcher.and = function (m2) {
+            if (!arguments.length) {
+                throw new TypeError("Matcher expected");
+            } else if (!isMatcher(m2)) {
+                m2 = match(m2);
+            }
+            var m1 = this;
+            var and = sinon.create(matcher);
+            and.test = function (actual) {
+                return m1.test(actual) && m2.test(actual);
+            };
+            and.message = m1.message + ".and(" + m2.message + ")";
+            return and;
         };
 
         match.isMatcher = isMatcher;
 
         match.any = match(function () {
             return true;
         }, "any");
 
@@ -1959,122 +2050,134 @@ var sinon = (function () {
         match.array = match.typeOf("array");
         match.regexp = match.typeOf("regexp");
         match.date = match.typeOf("date");
 
         sinon.match = match;
         return match;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         require("./typeOf");
         module.exports = makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  */
 /**
  * Format functions
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2014 Christian Johansen
  */
-
-(function (sinon, formatio) {
+(function (sinonGlobal, formatio) {
+    
     function makeApi(sinon) {
         function valueFormatter(value) {
             return "" + value;
         }
 
         function getFormatioFormatter() {
             var formatter = formatio.configure({
                     quoteStrings: false,
                     limitChildrenCount: 250
                 });
 
             function format() {
                 return formatter.ascii.apply(formatter, arguments);
-            };
+            }
 
             return format;
         }
 
-        function getNodeFormatter(value) {
-            function format(value) {
-                return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
-            };
-
+        function getNodeFormatter() {
             try {
                 var util = require("util");
             } catch (e) {
                 /* Node, but no util module - would be very old, but better safe than sorry */
             }
 
+            function format(v) {
+                var isObjectWithNativeToString = typeof v === "object" && v.toString === Object.prototype.toString;
+                return isObjectWithNativeToString ? util.inspect(v) : v;
+            }
+
             return util ? format : valueFormatter;
         }
 
-        var isNode = typeof module !== "undefined" && module.exports && typeof require == "function",
-            formatter;
+        var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
+        var formatter;
 
         if (isNode) {
             try {
                 formatio = require("formatio");
-            } catch (e) {}
+            }
+            catch (e) {} // eslint-disable-line no-empty
         }
 
         if (formatio) {
-            formatter = getFormatioFormatter()
+            formatter = getFormatioFormatter();
         } else if (isNode) {
             formatter = getNodeFormatter();
         } else {
             formatter = valueFormatter;
         }
 
         sinon.format = formatter;
         return sinon.format;
     }
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         module.exports = makeApi(sinon);
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
     }
 }(
-    (typeof sinon == "object" && sinon || null),
-    (typeof formatio == "object" && formatio)
+    typeof sinon === "object" && sinon, // eslint-disable-line no-undef
+    typeof formatio === "object" && formatio // eslint-disable-line no-undef
 ));
 
 /**
   * @depend util/core.js
   * @depend match.js
   * @depend format.js
   */
 /**
@@ -2082,29 +2185,29 @@ var sinon = (function () {
   *
   * @author Christian Johansen (christian@cjohansen.no)
   * @author Maximilian Antoni (mail@maxantoni.de)
   * @license BSD
   *
   * Copyright (c) 2010-2013 Christian Johansen
   * Copyright (c) 2013 Maximilian Antoni
   */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
+    var slice = Array.prototype.slice;
+
     function makeApi(sinon) {
         function throwYieldError(proxy, text, args) {
             var msg = sinon.functionName(proxy) + text;
             if (args.length) {
                 msg += " Received [" + slice.call(args).join(", ") + "]";
             }
             throw new Error(msg);
         }
 
-        var slice = Array.prototype.slice;
-
         var callProto = {
             calledOn: function calledOn(thisValue) {
                 if (sinon.match && sinon.match.isMatcher(thisValue)) {
                     return thisValue.test(this.thisValue);
                 }
                 return this.thisValue === thisValue;
             },
 
@@ -2133,17 +2236,17 @@ var sinon = (function () {
                     if (!sinon.match || !sinon.match(expectation).test(actual)) {
                         return false;
                     }
                 }
                 return true;
             },
 
             calledWithExactly: function calledWithExactly() {
-                return arguments.length == this.args.length &&
+                return arguments.length === this.args.length &&
                     this.calledWith.apply(this, arguments);
             },
 
             notCalledWith: function notCalledWith() {
                 return !this.calledWith.apply(this, arguments);
             },
 
             notCalledWithMatch: function notCalledWithMatch() {
@@ -2186,17 +2289,17 @@ var sinon = (function () {
                 this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
             },
 
             callArgOnWith: function (pos, thisValue) {
                 var args = slice.call(arguments, 2);
                 this.args[pos].apply(thisValue, args);
             },
 
-            yield: function () {
+            "yield": function () {
                 this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
             },
 
             yieldOn: function (thisValue) {
                 var args = this.args;
                 for (var i = 0, l = args.length; i < l; ++i) {
                     if (typeof args[i] === "function") {
                         args[i].apply(thisValue, slice.call(arguments, 1));
@@ -2217,163 +2320,177 @@ var sinon = (function () {
                         args[i][prop].apply(thisValue, slice.call(arguments, 2));
                         return;
                     }
                 }
                 throwYieldError(this.proxy, " cannot yield to '" + prop +
                     "' since no callback was passed.", args);
             },
 
+            getStackFrames: function () {
+                // Omit the error message and the two top stack frames in sinon itself:
+                return this.stack && this.stack.split("\n").slice(3);
+            },
+
             toString: function () {
                 var callStr = this.proxy.toString() + "(";
                 var args = [];
 
                 for (var i = 0, l = this.args.length; i < l; ++i) {
                     args.push(sinon.format(this.args[i]));
                 }
 
                 callStr = callStr + args.join(", ") + ")";
 
-                if (typeof this.returnValue != "undefined") {
+                if (typeof this.returnValue !== "undefined") {
                     callStr += " => " + sinon.format(this.returnValue);
                 }
 
                 if (this.exception) {
                     callStr += " !" + this.exception.name;
 
                     if (this.exception.message) {
                         callStr += "(" + this.exception.message + ")";
                     }
                 }
+                if (this.stack) {
+                    callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at ");
+
+                }
 
                 return callStr;
             }
         };
 
         callProto.invokeCallback = callProto.yield;
 
-        function createSpyCall(spy, thisValue, args, returnValue, exception, id) {
+        function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) {
             if (typeof id !== "number") {
                 throw new TypeError("Call id is not a number");
             }
             var proxyCall = sinon.create(callProto);
             proxyCall.proxy = spy;
             proxyCall.thisValue = thisValue;
             proxyCall.args = args;
             proxyCall.returnValue = returnValue;
             proxyCall.exception = exception;
             proxyCall.callId = id;
+            proxyCall.stack = stack;
 
             return proxyCall;
         }
         createSpyCall.toString = callProto.toString; // used by mocks
 
         sinon.spyCall = createSpyCall;
         return createSpyCall;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         require("./match");
         require("./format");
         module.exports = makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
   * @depend times_in_words.js
   * @depend util/core.js
   * @depend extend.js
   * @depend call.js
   * @depend format.js
   */
 /**
   * Spy functions
   *
   * @author Christian Johansen (christian@cjohansen.no)
   * @license BSD
   *
   * Copyright (c) 2010-2013 Christian Johansen
   */
-
-(function (sinon) {
-
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
         var push = Array.prototype.push;
         var slice = Array.prototype.slice;
         var callId = 0;
 
         function spy(object, property, types) {
-            if (!property && typeof object == "function") {
+            if (!property && typeof object === "function") {
                 return spy.create(object);
             }
 
             if (!object && !property) {
                 return spy.create(function () { });
             }
 
             if (types) {
                 var methodDesc = sinon.getPropertyDescriptor(object, property);
                 for (var i = 0; i < types.length; i++) {
                     methodDesc[types[i]] = spy.create(methodDesc[types[i]]);
                 }
                 return sinon.wrapMethod(object, property, methodDesc);
-            } else {
-                var method = object[property];
-                return sinon.wrapMethod(object, property, spy.create(method));
             }
+
+            return sinon.wrapMethod(object, property, spy.create(object[property]));
         }
 
         function matchingFake(fakes, args, strict) {
             if (!fakes) {
-                return;
+                return undefined;
             }
 
             for (var i = 0, l = fakes.length; i < l; i++) {
                 if (fakes[i].matches(args, strict)) {
                     return fakes[i];
                 }
             }
         }
 
         function incrementCallCount() {
             this.called = true;
             this.callCount += 1;
             this.notCalled = false;
-            this.calledOnce = this.callCount == 1;
-            this.calledTwice = this.callCount == 2;
-            this.calledThrice = this.callCount == 3;
+            this.calledOnce = this.callCount === 1;
+            this.calledTwice = this.callCount === 2;
+            this.calledThrice = this.callCount === 3;
         }
 
         function createCallProperties() {
             this.firstCall = this.getCall(0);
             this.secondCall = this.getCall(1);
             this.thirdCall = this.getCall(2);
             this.lastCall = this.getCall(this.callCount - 1);
         }
 
         var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
         function createProxy(func, proxyLength) {
             // Retain the function length:
             var p;
             if (proxyLength) {
-                eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) +
+                eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) + // eslint-disable-line no-eval
                     ") { return p.invoke(func, this, slice.call(arguments)); });");
             } else {
                 p = function proxy() {
                     return p.invoke(func, this, slice.call(arguments));
                 };
             }
             p.isSinonProxy = true;
             return p;
@@ -2401,29 +2518,30 @@ var sinon = (function () {
                 this.secondCall = null;
                 this.thirdCall = null;
                 this.lastCall = null;
                 this.args = [];
                 this.returnValues = [];
                 this.thisValues = [];
                 this.exceptions = [];
                 this.callIds = [];
+                this.stacks = [];
                 if (this.fakes) {
                     for (var i = 0; i < this.fakes.length; i++) {
                         this.fakes[i].reset();
                     }
                 }
 
                 return this;
             },
 
             create: function create(func, spyLength) {
                 var name;
 
-                if (typeof func != "function") {
+                if (typeof func !== "function") {
                     func = function () { };
                 } else {
                     name = sinon.functionName(func);
                 }
 
                 if (!spyLength) {
                     spyLength = func.length;
                 }
@@ -2472,16 +2590,17 @@ var sinon = (function () {
                 } catch (e) {
                     exception = e;
                 } finally {
                     delete this.invoking;
                 }
 
                 push.call(this.exceptions, exception);
                 push.call(this.returnValues, returnValue);
+                push.call(this.stacks, new Error().stack);
 
                 // Make return value and exception available in the calls:
                 createCallProperties.call(this);
 
                 if (exception !== undefined) {
                     throw exception;
                 }
 
@@ -2495,17 +2614,17 @@ var sinon = (function () {
 
             getCall: function getCall(i) {
                 if (i < 0 || i >= this.callCount) {
                     return null;
                 }
 
                 return sinon.spyCall(this, this.thisValues[i], this.args[i],
                                         this.returnValues[i], this.exceptions[i],
-                                        this.callIds[i]);
+                                        this.callIds[i], this.stacks[i]);
             },
 
             getCalls: function () {
                 var calls = [];
                 var i;
 
                 for (i = 0; i < this.callCount; i++) {
                     calls.push(this.getCall(i));
@@ -2572,30 +2691,30 @@ var sinon = (function () {
                 return fake;
             },
 
             matches: function (args, strict) {
                 var margs = this.matchingAguments;
 
                 if (margs.length <= args.length &&
                     sinon.deepEqual(margs, args.slice(0, margs.length))) {
-                    return !strict || margs.length == args.length;
+                    return !strict || margs.length === args.length;
                 }
             },
 
             printf: function (format) {
-                var spy = this;
+                var spyInstance = this;
                 var args = slice.call(arguments, 1);
                 var formatter;
 
                 return (format || "").replace(/%(.)/g, function (match, specifyer) {
                     formatter = spyApi.formatters[specifyer];
 
-                    if (typeof formatter == "function") {
-                        return formatter.call(null, spy, args);
+                    if (typeof formatter === "function") {
+                        return formatter.call(null, spyInstance, args);
                     } else if (!isNaN(parseInt(specifyer, 10))) {
                         return sinon.format(args[specifyer - 1]);
                     }
 
                     return "%" + specifyer;
                 });
             }
         };
@@ -2669,49 +2788,49 @@ var sinon = (function () {
                 "' since it was not yet invoked.");
         });
         delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
             throw new Error(this.toString() + " cannot yield to '" + property +
                 "' since it was not yet invoked.");
         });
 
         spyApi.formatters = {
-            c: function (spy) {
-                return sinon.timesInWords(spy.callCount);
+            c: function (spyInstance) {
+                return sinon.timesInWords(spyInstance.callCount);
             },
 
-            n: function (spy) {
-                return spy.toString();
+            n: function (spyInstance) {
+                return spyInstance.toString();
             },
 
-            C: function (spy) {
+            C: function (spyInstance) {
                 var calls = [];
 
-                for (var i = 0, l = spy.callCount; i < l; ++i) {
-                    var stringifiedCall = "    " + spy.getCall(i).toString();
+                for (var i = 0, l = spyInstance.callCount; i < l; ++i) {
+                    var stringifiedCall = "    " + spyInstance.getCall(i).toString();
                     if (/\n/.test(calls[i - 1])) {
                         stringifiedCall = "\n" + stringifiedCall;
                     }
                     push.call(calls, stringifiedCall);
                 }
 
                 return calls.length > 0 ? "\n" + calls.join("\n") : "";
             },
 
-            t: function (spy) {
+            t: function (spyInstance) {
                 var objects = [];
 
-                for (var i = 0, l = spy.callCount; i < l; ++i) {
-                    push.call(objects, sinon.format(spy.thisValues[i]));
+                for (var i = 0, l = spyInstance.callCount; i < l; ++i) {
+                    push.call(objects, sinon.format(spyInstance.thisValues[i]));
                 }
 
                 return objects.join(", ");
             },
 
-            "*": function (spy, args) {
+            "*": function (spyInstance, args) {
                 var formatted = [];
 
                 for (var i = 0, l = args.length; i < l; ++i) {
                     push.call(formatted, sinon.format(args[i]));
                 }
 
                 return formatted.join(", ");
             }
@@ -2720,73 +2839,81 @@ var sinon = (function () {
         sinon.extend(spy, spyApi);
 
         spy.spyCall = sinon.spyCall;
         sinon.spy = spy;
 
         return spy;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
-        var sinon = require("./util/core");
+        var core = require("./util/core");
         require("./call");
         require("./extend");
         require("./times_in_words");
         require("./format");
-        module.exports = makeApi(sinon);
+        module.exports = makeApi(core);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  * @depend extend.js
  */
 /**
  * Stub behavior
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @author Tim Fischbach (mail@timfischbach.de)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     var slice = Array.prototype.slice;
     var join = Array.prototype.join;
     var useLeftMostCallback = -1;
     var useRightMostCallback = -2;
 
     var nextTick = (function () {
         if (typeof process === "object" && typeof process.nextTick === "function") {
             return process.nextTick;
-        } else if (typeof setImmediate === "function") {
+        }
+
+        if (typeof setImmediate === "function") {
             return setImmediate;
-        } else {
-            return function (callback) {
-                setTimeout(callback, 0);
-            };
         }
+
+        return function (callback) {
+            setTimeout(callback, 0);
+        };
     })();
 
     function throwsException(error, message) {
-        if (typeof error == "string") {
+        if (typeof error === "string") {
             this.exception = new Error(message || "");
             this.exception.name = error;
         } else if (!error) {
             this.exception = new Error("Error");
         } else {
             this.exception = error;
         }
 
@@ -2808,22 +2935,22 @@ var sinon = (function () {
 
         if (callArgAt === useRightMostCallback) {
             argumentList = slice.call(args).reverse();
         }
 
         var callArgProp = behavior.callArgProp;
 
         for (var i = 0, l = argumentList.length; i < l; ++i) {
-            if (!callArgProp && typeof argumentList[i] == "function") {
+            if (!callArgProp && typeof argumentList[i] === "function") {
                 return argumentList[i];
             }
 
             if (callArgProp && argumentList[i] &&
-                typeof argumentList[i][callArgProp] == "function") {
+                typeof argumentList[i][callArgProp] === "function") {
                 return argumentList[i][callArgProp];
             }
         }
 
         return null;
     }
 
     function makeApi(sinon) {
@@ -2846,20 +2973,20 @@ var sinon = (function () {
 
                 return msg;
             }
 
             return "argument at index " + behavior.callArgAt + " is not a function: " + func;
         }
 
         function callCallback(behavior, args) {
-            if (typeof behavior.callArgAt == "number") {
+            if (typeof behavior.callArgAt === "number") {
                 var func = getCallback(behavior, args);
 
-                if (typeof func != "function") {
+                if (typeof func !== "function") {
                     throw new TypeError(getCallbackError(behavior, func, args));
                 }
 
                 if (behavior.callbackAsync) {
                     nextTick(function () {
                         func.apply(behavior.callbackContext, behavior.callbackArguments);
                     });
                 } else {
@@ -2873,29 +3000,29 @@ var sinon = (function () {
                 var behavior = sinon.extend({}, sinon.behavior);
                 delete behavior.create;
                 behavior.stub = stub;
 
                 return behavior;
             },
 
             isPresent: function isPresent() {
-                return (typeof this.callArgAt == "number" ||
+                return (typeof this.callArgAt === "number" ||
                         this.exception ||
-                        typeof this.returnArgAt == "number" ||
+                        typeof this.returnArgAt === "number" ||
                         this.returnThis ||
                         this.returnValueDefined);
             },
 
             invoke: function invoke(context, args) {
                 callCallback(this, args);
 
                 if (this.exception) {
                     throw this.exception;
-                } else if (typeof this.returnArgAt == "number") {
+                } else if (typeof this.returnArgAt === "number") {
                     return args[this.returnArgAt];
                 } else if (this.returnThis) {
                     return context;
                 }
 
                 return this.returnValue;
             },
 
@@ -2911,70 +3038,73 @@ var sinon = (function () {
                 return this.stub.onSecondCall();
             },
 
             onThirdCall: function onThirdCall() {
                 return this.stub.onThirdCall();
             },
 
             withArgs: function withArgs(/* arguments */) {
-                throw new Error("Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" is not supported. " +
-                                "Use \"stub.withArgs(...).onCall(...)\" to define sequential behavior for calls with certain arguments.");
+                throw new Error(
+                    "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " +
+                    "is not supported. Use \"stub.withArgs(...).onCall(...)\" " +
+                    "to define sequential behavior for calls with certain arguments."
+                );
             },
 
             callsArg: function callsArg(pos) {
-                if (typeof pos != "number") {
+                if (typeof pos !== "number") {
                     throw new TypeError("argument index is not number");
                 }
 
                 this.callArgAt = pos;
                 this.callbackArguments = [];
                 this.callbackContext = undefined;
                 this.callArgProp = undefined;
                 this.callbackAsync = false;
 
                 return this;
             },
 
             callsArgOn: function callsArgOn(pos, context) {
-                if (typeof pos != "number") {
+                if (typeof pos !== "number") {
                     throw new TypeError("argument index is not number");
                 }
-                if (typeof context != "object") {
+                if (typeof context !== "object") {
                     throw new TypeError("argument context is not an object");
                 }
 
                 this.callArgAt = pos;
                 this.callbackArguments = [];
                 this.callbackContext = context;
                 this.callArgProp = undefined;
                 this.callbackAsync = false;
 
                 return this;
             },
 
             callsArgWith: function callsArgWith(pos) {
-                if (typeof pos != "number") {
+                if (typeof pos !== "number") {
                     throw new TypeError("argument index is not number");
                 }
 
                 this.callArgAt = pos;
                 this.callbackArguments = slice.call(arguments, 1);
                 this.callbackContext = undefined;
                 this.callArgProp = undefined;
                 this.callbackAsync = false;
 
                 return this;
             },
 
             callsArgOnWith: function callsArgWith(pos, context) {
-                if (typeof pos != "number") {
+                if (typeof pos !== "number") {
                     throw new TypeError("argument index is not number");
                 }
-                if (typeof context != "object") {
+                if (typeof context !== "object") {
                     throw new TypeError("argument context is not an object");
                 }
 
                 this.callArgAt = pos;
                 this.callbackArguments = slice.call(arguments, 2);
                 this.callbackContext = context;
                 this.callArgProp = undefined;
                 this.callbackAsync = false;
@@ -2998,17 +3128,17 @@ var sinon = (function () {
                 this.callbackContext = undefined;
                 this.callArgProp = undefined;
                 this.callbackAsync = false;
 
                 return this;
             },
 
             yieldsOn: function (context) {
-                if (typeof context != "object") {
+                if (typeof context !== "object") {
                     throw new TypeError("argument context is not an object");
                 }
 
                 this.callArgAt = useLeftMostCallback;
                 this.callbackArguments = slice.call(arguments, 1);
                 this.callbackContext = context;
                 this.callArgProp = undefined;
                 this.callbackAsync = false;
@@ -3022,17 +3152,17 @@ var sinon = (function () {
                 this.callbackContext = undefined;
                 this.callArgProp = prop;
                 this.callbackAsync = false;
 
                 return this;
             },
 
             yieldsToOn: function (prop, context) {
-                if (typeof context != "object") {
+                if (typeof context !== "object") {
                     throw new TypeError("argument context is not an object");
                 }
 
                 this.callArgAt = useLeftMostCallback;
                 this.callbackArguments = slice.call(arguments, 2);
                 this.callbackContext = context;
                 this.callArgProp = prop;
                 this.callbackAsync = false;
@@ -3041,150 +3171,163 @@ var sinon = (function () {
             },
 
             throws: throwsException,
             throwsException: throwsException,
 
             returns: function returns(value) {
                 this.returnValue = value;
                 this.returnValueDefined = true;
+                this.exception = undefined;
 
                 return this;
             },
 
             returnsArg: function returnsArg(pos) {
-                if (typeof pos != "number") {
+                if (typeof pos !== "number") {
                     throw new TypeError("argument index is not number");
                 }
 
                 this.returnArgAt = pos;
 
                 return this;
             },
 
             returnsThis: function returnsThis() {
                 this.returnThis = true;
 
                 return this;
             }
         };
 
+        function createAsyncVersion(syncFnName) {
+            return function () {
+                var result = this[syncFnName].apply(this, arguments);
+                this.callbackAsync = true;
+                return result;
+            };
+        }
+
         // create asynchronous versions of callsArg* and yields* methods
         for (var method in proto) {
             // need to avoid creating anotherasync versions of the newly added async methods
-            if (proto.hasOwnProperty(method) &&
-                method.match(/^(callsArg|yields)/) &&
-                !method.match(/Async/)) {
-                proto[method + "Async"] = (function (syncFnName) {
-                    return function () {
-                        var result = this[syncFnName].apply(this, arguments);
-                        this.callbackAsync = true;
-                        return result;
-                    };
-                })(method);
+            if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) {
+                proto[method + "Async"] = createAsyncVersion(method);
             }
         }
 
         sinon.behavior = proto;
         return proto;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         require("./extend");
         module.exports = makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  * @depend extend.js
  * @depend spy.js
  * @depend behavior.js
  */
 /**
  * Stub functions
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
         function stub(object, property, func) {
-            if (!!func && typeof func != "function" && typeof func != "object") {
+            if (!!func && typeof func !== "function" && typeof func !== "object") {
                 throw new TypeError("Custom stub should be a function or a property descriptor");
             }
 
-            var wrapper;
+            var wrapper,
+                prop;
 
             if (func) {
-                if (typeof func == "function") {
+                if (typeof func === "function") {
                     wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func;
                 } else {
                     wrapper = func;
                     if (sinon.spy && sinon.spy.create) {
                         var types = sinon.objectKeys(wrapper);
                         for (var i = 0; i < types.length; i++) {
                             wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]);
                         }
                     }
                 }
             } else {
                 var stubLength = 0;
-                if (typeof object == "object" && typeof object[property] == "function") {
+                if (typeof object === "object" && typeof object[property] === "function") {
                     stubLength = object[property].length;
                 }
                 wrapper = stub.create(stubLength);
             }
 
             if (!object && typeof property === "undefined") {
                 return sinon.stub.create();
             }
 
-            if (typeof property === "undefined" && typeof object == "object") {
-                for (var prop in object) {
+            if (typeof property === "undefined" && typeof object === "object") {
+                for (prop in object) {
                     if (typeof sinon.getPropertyDescriptor(object, prop).value === "function") {
                         stub(object, prop);
                     }
                 }
 
                 return object;
             }
 
             return sinon.wrapMethod(object, property, wrapper);
         }
 
-        function getDefaultBehavior(stub) {
-            return stub.defaultBehavior || getParentBehaviour(stub) || sinon.behavior.create(stub);
+
+        /*eslint-disable no-use-before-define*/
+        function getParentBehaviour(stubInstance) {
+            return (stubInstance.parent && getCurrentBehavior(stubInstance.parent));
         }
 
-        function getParentBehaviour(stub) {
-            return (stub.parent && getCurrentBehavior(stub.parent));
+        function getDefaultBehavior(stubInstance) {
+            return stubInstance.defaultBehavior ||
+                    getParentBehaviour(stubInstance) ||
+                    sinon.behavior.create(stubInstance);
         }
 
-        function getCurrentBehavior(stub) {
-            var behavior = stub.behaviors[stub.callCount - 1];
-            return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stub);
+        function getCurrentBehavior(stubInstance) {
+            var behavior = stubInstance.behaviors[stubInstance.callCount - 1];
+            return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stubInstance);
         }
+        /*eslint-enable no-use-before-define*/
 
         var uuid = 0;
 
         var proto = {
             create: function create(stubLength) {
                 var functionStub = function () {
                     return getCurrentBehavior(functionStub).invoke(this, arguments);
                 };
@@ -3238,59 +3381,67 @@ var sinon = (function () {
                 return this.onCall(1);
             },
 
             onThirdCall: function onThirdCall() {
                 return this.onCall(2);
             }
         };
 
+        function createBehavior(behaviorMethod) {
+            return function () {
+                this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this);
+                this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);
+                return this;
+            };
+        }
+
         for (var method in sinon.behavior) {
             if (sinon.behavior.hasOwnProperty(method) &&
                 !proto.hasOwnProperty(method) &&
-                method != "create" &&
-                method != "withArgs" &&
-                method != "invoke") {
-                proto[method] = (function (behaviorMethod) {
-                    return function () {
-                        this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this);
-                        this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);
-                        return this;
-                    };
-                }(method));
+                method !== "create" &&
+                method !== "withArgs" &&
+                method !== "invoke") {
+                proto[method] = createBehavior(method);
             }
         }
 
         sinon.extend(stub, proto);
         sinon.stub = stub;
 
         return stub;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
-        var sinon = require("./util/core");
+        var core = require("./util/core");
         require("./behavior");
         require("./spy");
         require("./extend");
-        module.exports = makeApi(sinon);
+        module.exports = makeApi(core);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend times_in_words.js
  * @depend util/core.js
  * @depend call.js
  * @depend extend.js
  * @depend match.js
  * @depend spy.js
@@ -3300,26 +3451,26 @@ var sinon = (function () {
 /**
  * Mock functions.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
         var push = [].push;
         var match = sinon.match;
 
         function mock(object) {
-            if (typeof console !== undefined && console.warn) {
-                console.warn("mock will be removed from Sinon.JS v2.0");
-            }
+            // if (typeof console !== undefined && console.warn) {
+            //     console.warn("mock will be removed from Sinon.JS v2.0");
+            // }
 
             if (!object) {
                 return sinon.expectation.create("Anonymous mock");
             }
 
             return mock.create(object);
         }
 
@@ -3328,16 +3479,29 @@ var sinon = (function () {
                 return;
             }
 
             for (var i = 0, l = collection.length; i < l; i += 1) {
                 callback(collection[i]);
             }
         }
 
+        function arrayEquals(arr1, arr2, compareLength) {
+            if (compareLength && (arr1.length !== arr2.length)) {
+                return false;
+            }
+
+            for (var i = 0, l = arr1.length; i < l; i++) {
+                if (!sinon.deepEqual(arr1[i], arr2[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
         sinon.extend(mock, {
             create: function create(object) {
                 if (!object) {
                     throw new TypeError("object is null");
                 }
 
                 var mockObject = sinon.extend({}, mock);
                 mockObject.object = object;
@@ -3372,25 +3536,26 @@ var sinon = (function () {
 
                 return expectation;
             },
 
             restore: function restore() {
                 var object = this.object;
 
                 each(this.proxies, function (proxy) {
-                    if (typeof object[proxy].restore == "function") {
+                    if (typeof object[proxy].restore === "function") {
                         object[proxy].restore();
                     }
                 });
             },
 
             verify: function verify() {
                 var expectations = this.expectations || {};
-                var messages = [], met = [];
+                var messages = [];
+                var met = [];
 
                 each(this.proxies, function (proxy) {
                     each(expectations[proxy], function (expectation) {
                         if (!expectation.met()) {
                             push.call(messages, expectation.toString());
                         } else {
                             push.call(met, expectation.toString());
                         }
@@ -3404,101 +3569,112 @@ var sinon = (function () {
                 } else if (met.length > 0) {
                     sinon.expectation.pass(messages.concat(met).join("\n"));
                 }
 
                 return true;
             },
 
             invokeMethod: function invokeMethod(method, thisValue, args) {
-                var expectations = this.expectations && this.expectations[method];
-                var length = expectations && expectations.length || 0, i;
-
-                for (i = 0; i < length; i += 1) {
-                    if (!expectations[i].met() &&
-                        expectations[i].allowsCall(thisValue, args)) {
-                        return expectations[i].apply(thisValue, args);
+                var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : [];
+                var expectationsWithMatchingArgs = [];
+                var currentArgs = args || [];
+                var i, available;
+
+                for (i = 0; i < expectations.length; i += 1) {
+                    var expectedArgs = expectations[i].expectedArguments || [];
+                    if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) {
+                        expectationsWithMatchingArgs.push(expectations[i]);
                     }
                 }
 
-                var messages = [], available, exhausted = 0;
-
-                for (i = 0; i < length; i += 1) {
-                    if (expectations[i].allowsCall(thisValue, args)) {
-                        available = available || expectations[i];
+                for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) {
+                    if (!expectationsWithMatchingArgs[i].met() &&
+                        expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) {
+                        return expectationsWithMatchingArgs[i].apply(thisValue, args);
+                    }
+                }
+
+                var messages = [];
+                var exhausted = 0;
+
+                for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) {
+                    if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) {
+                        available = available || expectationsWithMatchingArgs[i];
                     } else {
                         exhausted += 1;
                     }
+                }
+
+                if (available && exhausted === 0) {
+                    return available.apply(thisValue, args);
+                }
+
+                for (i = 0; i < expectations.length; i += 1) {
                     push.call(messages, "    " + expectations[i].toString());
                 }
 
-                if (exhausted === 0) {
-                    return available.apply(thisValue, args);
-                }
-
                 messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({
                     proxy: method,
                     args: args
                 }));
 
                 sinon.expectation.fail(messages.join("\n"));
             }
         });
 
         var times = sinon.timesInWords;
         var slice = Array.prototype.slice;
 
         function callCountInWords(callCount) {
-            if (callCount == 0) {
+            if (callCount === 0) {
                 return "never called";
-            } else {
-                return "called " + times(callCount);
             }
+
+            return "called " + times(callCount);
         }
 
         function expectedCallCountInWords(expectation) {
             var min = expectation.minCalls;
             var max = expectation.maxCalls;
 
-            if (typeof min == "number" && typeof max == "number") {
+            if (typeof min === "number" && typeof max === "number") {
                 var str = times(min);
 
-                if (min != max) {
+                if (min !== max) {
                     str = "at least " + str + " and at most " + times(max);
                 }
 
                 return str;
             }
 
-            if (typeof min == "number") {
+            if (typeof min === "number") {
                 return "at least " + times(min);
             }
 
             return "at most " + times(max);
         }
 
         function receivedMinCalls(expectation) {
-            var hasMinLimit = typeof expectation.minCalls == "number";
+            var hasMinLimit = typeof expectation.minCalls === "number";
             return !hasMinLimit || expectation.callCount >= expectation.minCalls;
         }
 
         function receivedMaxCalls(expectation) {
-            if (typeof expectation.maxCalls != "number") {
+            if (typeof expectation.maxCalls !== "number") {
                 return false;
             }
 
-            return expectation.callCount == expectation.maxCalls;
+            return expectation.callCount === expectation.maxCalls;
         }
 
         function verifyMatcher(possibleMatcher, arg) {
-            if (match && match.isMatcher(possibleMatcher)) {
-                return possibleMatcher.test(arg);
-            } else {
-                return true;
-            }
+            var isMatcher = match && match.isMatcher(possibleMatcher);
+
+            return isMatcher && possibleMatcher.test(arg) || true;
         }
 
         sinon.expectation = {
             minCalls: 1,
             maxCalls: 1,
 
             create: function create(methodName) {
                 var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);
@@ -3510,32 +3686,32 @@ var sinon = (function () {
 
             invoke: function invoke(func, thisValue, args) {
                 this.verifyCallAllowed(thisValue, args);
 
                 return sinon.spy.invoke.apply(this, arguments);
             },
 
             atLeast: function atLeast(num) {
-                if (typeof num != "number") {
+                if (typeof num !== "number") {
                     throw new TypeError("'" + num + "' is not number");
                 }
 
                 if (!this.limitsSet) {
                     this.maxCalls = null;
                     this.limitsSet = true;
                 }
 
                 this.minCalls = num;
 
                 return this;
             },
 
             atMost: function atMost(num) {
-                if (typeof num != "number") {
+                if (typeof num !== "number") {
                     throw new TypeError("'" + num + "' is not number");
                 }
 
                 if (!this.limitsSet) {
                     this.minCalls = null;
                     this.limitsSet = true;
                 }
 
@@ -3556,17 +3732,17 @@ var sinon = (function () {
                 return this.exactly(2);
             },
 
             thrice: function thrice() {
                 return this.exactly(3);
             },
 
             exactly: function exactly(num) {
-                if (typeof num != "number") {
+                if (typeof num !== "number") {
                     throw new TypeError("'" + num + "' is not a number");
                 }
 
                 this.atLeast(num);
                 return this.atMost(num);
             },
 
             met: function met() {
@@ -3594,17 +3770,17 @@ var sinon = (function () {
                 }
 
                 if (args.length < this.expectedArguments.length) {
                     sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) +
                         "), expected " + sinon.format(this.expectedArguments));
                 }
 
                 if (this.expectsExactArgCount &&
-                    args.length != this.expectedArguments.length) {
+                    args.length !== this.expectedArguments.length) {
                     sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) +
                         "), expected " + sinon.format(this.expectedArguments));
                 }
 
                 for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
 
                     if (!verifyMatcher(this.expectedArguments[i], args[i])) {
                         sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
@@ -3633,17 +3809,17 @@ var sinon = (function () {
 
                 args = args || [];
 
                 if (args.length < this.expectedArguments.length) {
                     return false;
                 }
 
                 if (this.expectsExactArgCount &&
-                    args.length != this.expectedArguments.length) {
+                    args.length !== this.expectedArguments.length) {
                     return false;
                 }
 
                 for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
                     if (!verifyMatcher(this.expectedArguments[i], args[i])) {
                         return false;
                     }
 
@@ -3715,17 +3891,17 @@ var sinon = (function () {
                 throw exception;
             }
         };
 
         sinon.mock = mock;
         return mock;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         require("./times_in_words");
         require("./call");
         require("./extend");
         require("./match");
@@ -3733,57 +3909,63 @@ var sinon = (function () {
         require("./stub");
         require("./format");
 
         module.exports = makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  * @depend spy.js
  * @depend stub.js
  * @depend mock.js
  */
 /**
  * Collections of stubs, spies and mocks.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     var push = [].push;
     var hasOwnProperty = Object.prototype.hasOwnProperty;
 
     function getFakes(fakeCollection) {
         if (!fakeCollection.fakes) {
             fakeCollection.fakes = [];
         }
 
         return fakeCollection.fakes;
     }
 
     function each(fakeCollection, method) {
         var fakes = getFakes(fakeCollection);
 
         for (var i = 0, l = fakes.length; i < l; i += 1) {
-            if (typeof fakes[i][method] == "function") {
+            if (typeof fakes[i][method] === "function") {
                 fakes[i][method]();
             }
         }
     }
 
     function compact(fakeCollection) {
         var fakes = getFakes(fakeCollection);
         var i = 0;
@@ -3831,31 +4013,31 @@ var sinon = (function () {
             spy: function spy() {
                 return this.add(sinon.spy.apply(sinon, arguments));
             },
 
             stub: function stub(object, property, value) {
                 if (property) {
                     var original = object[property];
 
-                    if (typeof original != "function") {
+                    if (typeof original !== "function") {
                         if (!hasOwnProperty.call(object, property)) {
                             throw new TypeError("Cannot stub non-existent own property " + property);
                         }
 
                         object[property] = value;
 
                         return this.add({
                             restore: function () {
                                 object[property] = original;
                             }
                         });
                     }
                 }
-                if (!property && !!object && typeof object == "object") {
+                if (!property && !!object && typeof object === "object") {
                     var stubbedObj = sinon.stub.apply(sinon, arguments);
 
                     for (var prop in stubbedObj) {
                         if (typeof stubbedObj[prop] === "function") {
                             this.add(stubbedObj[prop]);
                         }
                     }
 
@@ -3887,39 +4069,43 @@ var sinon = (function () {
                 return obj;
             }
         };
 
         sinon.collection = collection;
         return collection;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         require("./mock");
         require("./spy");
         require("./stub");
         module.exports = makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/*global lolex */
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * Fake timer API
  * setTimeout
  * setInterval
  * clearTimeout
  * clearInterval
  * tick
@@ -3928,92 +4114,90 @@ var sinon = (function () {
  *
  * Inspired by jsUnitMockTimeOut from JsUnit
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-if (typeof sinon == "undefined") {
-    var sinon = {};
-}
-
-(function (global) {
-    function makeApi(sinon, lol) {
+(function () {
+    
+    function makeApi(s, lol) {
+        /*global lolex */
         var llx = typeof lolex !== "undefined" ? lolex : lol;
 
-        sinon.useFakeTimers = function () {
-            var now, methods = Array.prototype.slice.call(arguments);
+        s.useFakeTimers = function () {
+            var now;
+            var methods = Array.prototype.slice.call(arguments);
 
             if (typeof methods[0] === "string") {
                 now = 0;
             } else {
                 now = methods.shift();
             }
 
             var clock = llx.install(now || 0, methods);
             clock.restore = clock.uninstall;
             return clock;
         };
 
-        sinon.clock = {
+        s.clock = {
             create: function (now) {
                 return llx.createClock(now);
             }
         };
 
-        sinon.timers = {
+        s.timers = {
             setTimeout: setTimeout,
             clearTimeout: clearTimeout,
             setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
             clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined),
             setInterval: setInterval,
             clearInterval: clearInterval,
             Date: Date
         };
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, epxorts, module, lolex) {
-        var sinon = require("./core");
-        makeApi(sinon, lolex);
-        module.exports = sinon;
+        var core = require("./core");
+        makeApi(core, lolex);
+        module.exports = core;
     }
 
     if (isAMD) {
         define(loadDependencies);
     } else if (isNode) {
         loadDependencies(require, module.exports, module, require("lolex"));
     } else {
-        makeApi(sinon);
-    }
-}(typeof global != "undefined" && typeof global !== "function" ? global : this));
+        makeApi(sinon); // eslint-disable-line no-undef
+    }
+}());
 
 /**
  * Minimal Event interface implementation
  *
  * Original implementation by Sven Fuchs: https://gist.github.com/995028
  * Modifications and tests by Christian Johansen.
  *
  * @author Sven Fuchs (svenfuchs@artweb-design.de)
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2011 Sven Fuchs, Christian Johansen
  */
-
-if (typeof sinon == "undefined") {
+if (typeof sinon === "undefined") {
     this.sinon = {};
 }
 
 (function () {
+    
     var push = [].push;
 
     function makeApi(sinon) {
         sinon.Event = function Event(type, bubbles, cancelable, target) {
             this.initEvent(type, bubbles, cancelable, target);
         };
 
         sinon.Event.prototype = {
@@ -4035,91 +4219,91 @@ if (typeof sinon == "undefined") {
             this.initEvent(type, false, false, target);
             this.loaded = progressEventRaw.loaded || null;
             this.total = progressEventRaw.total || null;
             this.lengthComputable = !!progressEventRaw.total;
         };
 
         sinon.ProgressEvent.prototype = new sinon.Event();
 
-        sinon.ProgressEvent.prototype.constructor =  sinon.ProgressEvent;
+        sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent;
 
         sinon.CustomEvent = function CustomEvent(type, customData, target) {
             this.initEvent(type, false, false, target);
             this.detail = customData.detail || null;
         };
 
         sinon.CustomEvent.prototype = new sinon.Event();
 
-        sinon.CustomEvent.prototype.constructor =  sinon.CustomEvent;
+        sinon.CustomEvent.prototype.constructor = sinon.CustomEvent;
 
         sinon.EventTarget = {
             addEventListener: function addEventListener(event, listener) {
                 this.eventListeners = this.eventListeners || {};
                 this.eventListeners[event] = this.eventListeners[event] || [];
                 push.call(this.eventListeners[event], listener);
             },
 
             removeEventListener: function removeEventListener(event, listener) {
                 var listeners = this.eventListeners && this.eventListeners[event] || [];
 
                 for (var i = 0, l = listeners.length; i < l; ++i) {
-                    if (listeners[i] == listener) {
+                    if (listeners[i] === listener) {
                         return listeners.splice(i, 1);
                     }
                 }
             },
 
             dispatchEvent: function dispatchEvent(event) {
                 var type = event.type;
                 var listeners = this.eventListeners && this.eventListeners[type] || [];
 
                 for (var i = 0; i < listeners.length; i++) {
-                    if (typeof listeners[i] == "function") {
+                    if (typeof listeners[i] === "function") {
                         listeners[i].call(this, event);
                     } else {
                         listeners[i].handleEvent(event);
                     }
                 }
 
                 return !!event.defaultPrevented;
             }
         };
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require) {
         var sinon = require("./core");
         makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
     } else if (isNode) {
         loadDependencies(require);
     } else {
-        makeApi(sinon);
+        makeApi(sinon); // eslint-disable-line no-undef
     }
 }());
 
 /**
  * @depend util/core.js
  */
 /**
  * Logs errors
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2014 Christian Johansen
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     // cache a reference to setTimeout, so that our reference won't be stubbed out
     // when using fake timers and errors will still get logged
     // https://github.com/cjohansen/Sinon.JS/issues/381
     var realSetTimeout = setTimeout;
 
     function makeApi(sinon) {
 
         function log() {}
@@ -4132,106 +4316,112 @@ if (typeof sinon == "undefined") {
             if (err.stack) {
                 sinon.log(err.stack);
             }
 
             logError.setTimeout(function () {
                 err.message = msg + err.message;
                 throw err;
             }, 0);
-        };
+        }
 
         // wrap realSetTimeout with something we can stub in tests
         logError.setTimeout = function (func, timeout) {
             realSetTimeout(func, timeout);
-        }
+        };
 
         var exports = {};
         exports.log = sinon.log = log;
         exports.logError = sinon.logError = logError;
 
         return exports;
     }
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         module.exports = makeApi(sinon);
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend core.js
  * @depend ../extend.js
  * @depend event.js
  * @depend ../log_error.js
  */
 /**
  * Fake XDomainRequest object
  */
-
-if (typeof sinon == "undefined") {
+if (typeof sinon === "undefined") {
     this.sinon = {};
 }
 
 // wrapper for global
 (function (global) {
+    
     var xdr = { XDomainRequest: global.XDomainRequest };
     xdr.GlobalXDomainRequest = global.XDomainRequest;
-    xdr.supportsXDR = typeof xdr.GlobalXDomainRequest != "undefined";
-    xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest :  false;
+    xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined";
+    xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false;
 
     function makeApi(sinon) {
         sinon.xdr = xdr;
 
         function FakeXDomainRequest() {
             this.readyState = FakeXDomainRequest.UNSENT;
             this.requestBody = null;
             this.requestHeaders = {};
             this.status = 0;
             this.timeout = null;
 
-            if (typeof FakeXDomainRequest.onCreate == "function") {
+            if (typeof FakeXDomainRequest.onCreate === "function") {
                 FakeXDomainRequest.onCreate(this);
             }
         }
 
-        function verifyState(xdr) {
-            if (xdr.readyState !== FakeXDomainRequest.OPENED) {
+        function verifyState(x) {
+            if (x.readyState !== FakeXDomainRequest.OPENED) {
                 throw new Error("INVALID_STATE_ERR");
             }
 
-            if (xdr.sendFlag) {
+            if (x.sendFlag) {
                 throw new Error("INVALID_STATE_ERR");
             }
         }
 
-        function verifyRequestSent(xdr) {
-            if (xdr.readyState == FakeXDomainRequest.UNSENT) {
+        function verifyRequestSent(x) {
+            if (x.readyState === FakeXDomainRequest.UNSENT) {
                 throw new Error("Request not sent");
             }
-            if (xdr.readyState == FakeXDomainRequest.DONE) {
+            if (x.readyState === FakeXDomainRequest.DONE) {
                 throw new Error("Request done");
             }
         }
 
         function verifyResponseBodyType(body) {
-            if (typeof body != "string") {
+            if (typeof body !== "string") {
                 var error = new Error("Attempted to respond to fake XDomainRequest with " +
                                     body + ", which is not a string.");
                 error.name = "InvalidBodyException";
                 throw error;
             }
         }
 
         sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, {
@@ -4256,28 +4446,28 @@ if (typeof sinon == "undefined") {
                 case FakeXDomainRequest.LOADING:
                     if (this.sendFlag) {
                         //raise the progress event
                         eventName = "onprogress";
                     }
                     break;
                 case FakeXDomainRequest.DONE:
                     if (this.isTimeout) {
-                        eventName = "ontimeout"
+                        eventName = "ontimeout";
                     } else if (this.errorFlag || (this.status < 200 || this.status > 299)) {
                         eventName = "onerror";
                     } else {
-                        eventName = "onload"
+                        eventName = "onload";
                     }
                     break;
                 }
 
                 // raising event (if defined)
                 if (eventName) {
-                    if (typeof this[eventName] == "function") {
+                    if (typeof this[eventName] === "function") {
                         try {
                             this[eventName]();
                         } catch (e) {
                             sinon.logError("Fake XHR " + eventName + " handler", e);
                         }
                     }
                 }
             },
@@ -4289,17 +4479,17 @@ if (typeof sinon == "undefined") {
                     this.requestBody = data;
                 }
                 this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
 
                 this.errorFlag = false;
                 this.sendFlag = true;
                 this.readyStateChange(FakeXDomainRequest.OPENED);
 
-                if (typeof this.onSend == "function") {
+                if (typeof this.onSend === "function") {
                     this.onSend(this);
                 }
             },
 
             abort: function abort() {
                 this.aborted = true;
                 this.responseText = null;
                 this.errorFlag = true;
@@ -4326,17 +4516,17 @@ if (typeof sinon == "undefined") {
 
                 this.readyStateChange(FakeXDomainRequest.DONE);
             },
 
             respond: function respond(status, contentType, body) {
                 // content-type ignored, since XDomainRequest does not carry this
                 // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease
                 // test integration across browsers
-                this.status = typeof status == "number" ? status : 200;
+                this.status = typeof status === "number" ? status : 200;
                 this.setResponseBody(body || "");
             },
 
             simulatetimeout: function simulatetimeout() {
                 this.status = 0;
                 this.isTimeout = true;
                 // Access to this should actually throw an error
                 this.responseText = undefined;
@@ -4367,34 +4557,34 @@ if (typeof sinon == "undefined") {
                 global.XDomainRequest = sinon.FakeXDomainRequest;
             }
             return sinon.FakeXDomainRequest;
         };
 
         sinon.FakeXDomainRequest = FakeXDomainRequest;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./core");
         require("../extend");
         require("./event");
         require("../log_error");
         makeApi(sinon);
         module.exports = sinon;
     }
 
     if (isAMD) {
         define(loadDependencies);
     } else if (isNode) {
         loadDependencies(require, module.exports, module);
     } else {
-        makeApi(sinon);
+        makeApi(sinon); // eslint-disable-line no-undef
     }
 })(typeof global !== "undefined" ? global : self);
 
 /**
  * @depend core.js
  * @depend ../extend.js
  * @depend event.js
  * @depend ../log_error.js
@@ -4402,34 +4592,45 @@ if (typeof sinon == "undefined") {
 /**
  * Fake XMLHttpRequest object
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (global) {
+(function (sinonGlobal, global) {
+    
+    function getWorkingXHR(globalScope) {
+        var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined";
+        if (supportsXHR) {
+            return globalScope.XMLHttpRequest;
+        }
+
+        var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined";
+        if (supportsActiveX) {
+            return function () {
+                return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0");
+            };
+        }
+
+        return false;
+    }
 
     var supportsProgress = typeof ProgressEvent !== "undefined";
     var supportsCustomEvent = typeof CustomEvent !== "undefined";
     var supportsFormData = typeof FormData !== "undefined";
     var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest };
     sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
     sinonXhr.GlobalActiveXObject = global.ActiveXObject;
-    sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject != "undefined";
-    sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest != "undefined";
-    sinonXhr.workingXHR = sinonXhr.supportsXHR ? sinonXhr.GlobalXMLHttpRequest : sinonXhr.supportsActiveX
-                                     ? function () {
-                                        return new sinonXhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0")
-                                    } : false;
+    sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined";
+    sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined";
+    sinonXhr.workingXHR = getWorkingXHR(global);
     sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest());
 
-    /*jsl:ignore*/
     var unsafeHeaders = {
         "Accept-Charset": true,
         "Accept-Encoding": true,
         Connection: true,
         "Content-Length": true,
         Cookie: true,
         Cookie2: true,
         "Content-Transfer-Encoding": true,
@@ -4440,101 +4641,106 @@ if (typeof sinon == "undefined") {
         Referer: true,
         TE: true,
         Trailer: true,
         "Transfer-Encoding": true,
         Upgrade: true,
         "User-Agent": true,
         Via: true
     };
-    /*jsl:end*/
-
-    function FakeXMLHttpRequest() {
-        this.readyState = FakeXMLHttpRequest.UNSENT;
-        this.requestHeaders = {};
-        this.requestBody = null;
-        this.status = 0;
-        this.statusText = "";
-        this.upload = new UploadProgress();
-        if (sinonXhr.supportsCORS) {
-            this.withCredentials = false;
-        }
-
-        var xhr = this;
-        var events = ["loadstart", "load", "abort", "loadend"];
-
-        function addEventListener(eventName) {
-            xhr.addEventListener(eventName, function (event) {
-                var listener = xhr["on" + eventName];
-
-                if (listener && typeof listener == "function") {
-                    listener.call(this, event);
-                }
-            });
-        }
-
-        for (var i = events.length - 1; i >= 0; i--) {
-            addEventListener(events[i]);
-        }
-
-        if (typeof FakeXMLHttpRequest.onCreate == "function") {
-            FakeXMLHttpRequest.onCreate(this);
-        }
-    }
 
     // An upload object is created for each
     // FakeXMLHttpRequest and allows upload
     // events to be simulated using uploadProgress
     // and uploadError.
     function UploadProgress() {
         this.eventListeners = {
             progress: [],
             load: [],
             abort: [],
             error: []
-        }
+        };
     }
 
     UploadProgress.prototype.addEventListener = function addEventListener(event, listener) {
         this.eventListeners[event].push(listener);
     };
 
     UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) {
         var listeners = this.eventListeners[event] || [];
 
         for (var i = 0, l = listeners.length; i < l; ++i) {
-            if (listeners[i] == listener) {
+            if (listeners[i] === listener) {
                 return listeners.splice(i, 1);
             }
         }
     };
 
     UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) {
         var listeners = this.eventListeners[event.type] || [];
 
         for (var i = 0, listener; (listener = listeners[i]) != null; i++) {
             listener(event);
         }
     };
 
+    // Note that for FakeXMLHttpRequest to work pre ES5
+    // we lose some of the alignment with the spec.
+    // To ensure as close a match as possible,
+    // set responseType before calling open, send or respond;
+    function FakeXMLHttpRequest() {
+        this.readyState = FakeXMLHttpRequest.UNSENT;
+        this.requestHeaders = {};
+        this.requestBody = null;
+        this.status = 0;
+        this.statusText = "";
+        this.upload = new UploadProgress();
+        this.responseType = "";
+        this.response = "";
+        if (sinonXhr.supportsCORS) {
+            this.withCredentials = false;
+        }
+
+        var xhr = this;
+        var events = ["loadstart", "load", "abort", "loadend"];
+
+        function addEventListener(eventName) {
+            xhr.addEventListener(eventName, function (event) {
+                var listener = xhr["on" + eventName];
+
+                if (listener && typeof listener === "function") {
+                    listener.call(this, event);
+                }
+            });
+        }
+
+        for (var i = events.length - 1; i >= 0; i--) {
+            addEventListener(events[i]);
+        }
+
+        if (typeof FakeXMLHttpRequest.onCreate === "function") {
+            FakeXMLHttpRequest.onCreate(this);
+        }
+    }
+
     function verifyState(xhr) {
         if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
             throw new Error("INVALID_STATE_ERR");
         }
 
         if (xhr.sendFlag) {
             throw new Error("INVALID_STATE_ERR");
         }
     }
 
     function getHeader(headers, header) {
         header = header.toLowerCase();
 
         for (var h in headers) {
-            if (h.toLowerCase() == header) {
+            if (h.toLowerCase() === header) {
                 return h;
             }
         }
 
         return null;
     }
 
     // filtering to enable a white-list version of Sinon FakeXhr,
@@ -4565,21 +4771,22 @@ if (typeof sinon == "undefined") {
         case 3: return obj[method](args[0], args[1], args[2]);
         case 4: return obj[method](args[0], args[1], args[2], args[3]);
         case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]);
         }
     };
 
     FakeXMLHttpRequest.filters = [];
     FakeXMLHttpRequest.addFilter = function addFilter(fn) {
-        this.filters.push(fn)
+        this.filters.push(fn);
     };
     var IE6Re = /MSIE 6/;
     FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) {
-        var xhr = new sinonXhr.workingXHR();
+        var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap
+
         each([
             "open",
             "setRequestHeader",
             "send",
             "abort",
             "getResponseHeader",
             "getAllResponseHeaders",
             "addEventListener",
@@ -4589,17 +4796,17 @@ if (typeof sinon == "undefined") {
             fakeXhr[method] = function () {
                 return apply(xhr, method, arguments);
             };
         });
 
         var copyAttrs = function (args) {
             each(args, function (attr) {
                 try {
-                    fakeXhr[attr] = xhr[attr]
+                    fakeXhr[attr] = xhr[attr];
                 } catch (e) {
                     if (!IE6Re.test(navigator.userAgent)) {
                         throw e;
                     }
                 }
             });
         };
 
@@ -4617,64 +4824,67 @@ if (typeof sinon == "undefined") {
             if (fakeXhr.onreadystatechange) {
                 fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr });
             }
         };
 
         if (xhr.addEventListener) {
             for (var event in fakeXhr.eventListeners) {
                 if (fakeXhr.eventListeners.hasOwnProperty(event)) {
+
+                    /*eslint-disable no-loop-func*/
                     each(fakeXhr.eventListeners[event], function (handler) {
                         xhr.addEventListener(event, handler);
                     });
+                    /*eslint-enable no-loop-func*/
                 }
             }
             xhr.addEventListener("readystatechange", stateChange);
         } else {
             xhr.onreadystatechange = stateChange;
         }
         apply(xhr, "open", xhrArgs);
     };
     FakeXMLHttpRequest.useFilters = false;
 
     function verifyRequestOpened(xhr) {
-        if (xhr.readyState != FakeXMLHttpRequest.OPENED) {
+        if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
             throw new Error("INVALID_STATE_ERR - " + xhr.readyState);
         }
     }
 
     function verifyRequestSent(xhr) {
-        if (xhr.readyState == FakeXMLHttpRequest.DONE) {
+        if (xhr.readyState === FakeXMLHttpRequest.DONE) {
             throw new Error("Request done");
         }
     }
 
     function verifyHeadersReceived(xhr) {
-        if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
+        if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) {
             throw new Error("No headers received");
         }
     }
 
     function verifyResponseBodyType(body) {
-        if (typeof body != "string") {
+        if (typeof body !== "string") {
             var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
                                  body + ", which is not a string.");
             error.name = "InvalidBodyException";
             throw error;
         }
     }
 
     FakeXMLHttpRequest.parseXML = function parseXML(text) {
         var xmlDoc;
 
-        if (typeof DOMParser != "undefined") {
+        if (typeof DOMParser !== "undefined") {
             var parser = new DOMParser();
             xmlDoc = parser.parseFromString(text, "text/xml");
         } else {
-            xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+            xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
             xmlDoc.async = "false";
             xmlDoc.loadXML(text);
         }
 
         return xmlDoc;
     };
 
     FakeXMLHttpRequest.statusCodes = {
@@ -4726,42 +4936,45 @@ if (typeof sinon == "undefined") {
         sinon.xhr = sinonXhr;
 
         sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {
             async: true,
 
             open: function open(method, url, async, username, password) {
                 this.method = method;
                 this.url = url;
-                this.async = typeof async == "boolean" ? async : true;
+                this.async = typeof async === "boolean" ? async : true;
                 this.username = username;
                 this.password = password;
                 this.responseText = null;
+                this.response = this.responseType === "json" ? null : "";
                 this.responseXML = null;
                 this.requestHeaders = {};
                 this.sendFlag = false;
 
                 if (FakeXMLHttpRequest.useFilters === true) {
                     var xhrArgs = arguments;
                     var defake = some(FakeXMLHttpRequest.filters, function (filter) {
-                        return filter.apply(this, xhrArgs)
+                        return filter.apply(this, xhrArgs);
                     });
                     if (defake) {
                         return FakeXMLHttpRequest.defake(this, arguments);
                     }
                 }
                 this.readyStateChange(FakeXMLHttpRequest.OPENED);
             },
 
             readyStateChange: function readyStateChange(state) {
                 this.readyState = state;
 
-                if (typeof this.onreadystatechange == "function") {
+                var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this);
+
+                if (typeof this.onreadystatechange === "function") {
                     try {
-                        this.onreadystatechange();
+                        this.onreadystatechange(readyStateChangeEvent);
                     } catch (e) {
                         sinon.logError("Fake XHR onreadystatechange handler", e);
                     }
                 }
 
                 switch (this.readyState) {
                     case FakeXMLHttpRequest.DONE:
                         if (supportsProgress) {
@@ -4769,17 +4982,17 @@ if (typeof sinon == "undefined") {
                             this.dispatchEvent(new sinon.ProgressEvent("progress", {loaded: 100, total: 100}));
                         }
                         this.upload.dispatchEvent(new sinon.Event("load", false, false, this));
                         this.dispatchEvent(new sinon.Event("load", false, false, this));
                         this.dispatchEvent(new sinon.Event("loadend", false, false, this));
                         break;
                 }
 
-                this.dispatchEvent(new sinon.Event("readystatechange"));
+                this.dispatchEvent(readyStateChangeEvent);
             },
 
             setRequestHeader: function setRequestHeader(header, value) {
                 verifyState(this);
 
                 if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
                     throw new Error("Refused to set unsafe header \"" + header + "\"");
                 }
@@ -4822,28 +5035,30 @@ if (typeof sinon == "undefined") {
                         this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
                     }
 
                     this.requestBody = data;
                 }
 
                 this.errorFlag = false;
                 this.sendFlag = this.async;
+                this.response = this.responseType === "json" ? null : "";
                 this.readyStateChange(FakeXMLHttpRequest.OPENED);
 
-                if (typeof this.onSend == "function") {
+                if (typeof this.onSend === "function") {
                     this.onSend(this);
                 }
 
                 this.dispatchEvent(new sinon.Event("loadstart", false, false, this));
             },
 
             abort: function abort() {
                 this.aborted = true;
                 this.responseText = null;
+                this.response = this.responseType === "json" ? null : "";
                 this.errorFlag = true;
                 this.requestHeaders = {};
                 this.responseHeaders = {};
 
                 if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) {
                     this.readyStateChange(FakeXMLHttpRequest.DONE);
                     this.sendFlag = false;
                 }
@@ -4914,21 +5129,22 @@ if (typeof sinon == "undefined") {
                     (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
                     try {
                         this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
                     } catch (e) {
                         // Unable to parse XML - no biggie
                     }
                 }
 
+                this.response = this.responseType === "json" ? JSON.parse(this.responseText) : this.responseText;
                 this.readyStateChange(FakeXMLHttpRequest.DONE);
             },
 
             respond: function respond(status, headers, body) {
-                this.status = typeof status == "number" ? status : 200;
+                this.status = typeof status === "number" ? status : 200;
                 this.statusText = FakeXMLHttpRequest.statusCodes[this.status];
                 this.setResponseHeaders(headers || {});
                 this.setResponseBody(body || "");
             },
 
             uploadProgress: function uploadProgress(progressEventRaw) {
                 if (supportsProgress) {
                     this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw));
@@ -4973,54 +5189,60 @@ if (typeof sinon == "undefined") {
                 }
             };
             if (sinonXhr.supportsXHR) {
                 global.XMLHttpRequest = FakeXMLHttpRequest;
             }
 
             if (sinonXhr.supportsActiveX) {
                 global.ActiveXObject = function ActiveXObject(objId) {
-                    if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
+                    if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
 
                         return new FakeXMLHttpRequest();
                     }
 
                     return new sinonXhr.GlobalActiveXObject(objId);
                 };
             }
 
             return FakeXMLHttpRequest;
         };
 
         sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./core");
         require("../extend");
         require("./event");
         require("../log_error");
         makeApi(sinon);
         module.exports = sinon;
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (typeof sinon === "undefined") {
         return;
-    } else {
-        makeApi(sinon);
-    }
-
-})(typeof global !== "undefined" ? global : self);
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon, // eslint-disable-line no-undef
+    typeof global !== "undefined" ? global : self
+));
 
 /**
  * @depend fake_xdomain_request.js
  * @depend fake_xml_http_request.js
  * @depend ../format.js
  * @depend ../log_error.js
  */
 /**
@@ -5029,95 +5251,101 @@ if (typeof sinon == "undefined") {
  * both synchronously and asynchronously. To respond synchronuously, canned
  * answers have to be provided upfront.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-if (typeof sinon == "undefined") {
-    var sinon = {};
-}
-
 (function () {
+    
     var push = [].push;
-    function F() {}
-
-    function create(proto) {
-        F.prototype = proto;
-        return new F();
-    }
 
     function responseArray(handler) {
         var response = handler;
 
-        if (Object.prototype.toString.call(handler) != "[object Array]") {
+        if (Object.prototype.toString.call(handler) !== "[object Array]") {
             response = [200, {}, handler];
         }
 
-        if (typeof response[2] != "string") {
+        if (typeof response[2] !== "string") {
             throw new TypeError("Fake server response body should be string, but was " +
                                 typeof response[2]);
         }
 
         return response;
     }
 
     var wloc = typeof window !== "undefined" ? window.location : {};
     var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
 
     function matchOne(response, reqMethod, reqUrl) {
         var rmeth = response.method;
-        var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
+        var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase();
         var url = response.url;
-        var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
+        var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl));
 
         return matchMethod && matchUrl;
     }
 
     function match(response, request) {
         var requestUrl = request.url;
 
         if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
             requestUrl = requestUrl.replace(rCurrLoc, "");
         }
 
         if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
-            if (typeof response.response == "function") {
+            if (typeof response.response === "function") {
                 var ru = response.url;
-                var args = [request].concat(ru && typeof ru.exec == "function" ? ru.exec(requestUrl).slice(1) : []);
+                var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []);
                 return response.response.apply(response, args);
             }
 
             return true;
         }
 
         return false;
     }
 
     function makeApi(sinon) {
         sinon.fakeServer = {
-            create: function () {
-                var server = create(this);
+            create: function (config) {
+                var server = sinon.create(this);
+                server.configure(config);
                 if (!sinon.xhr.supportsCORS) {
                     this.xhr = sinon.useFakeXDomainRequest();
                 } else {
                     this.xhr = sinon.useFakeXMLHttpRequest();
                 }
                 server.requests = [];
 
                 this.xhr.onCreate = function (xhrObj) {
                     server.addRequest(xhrObj);
                 };
 
                 return server;
             },
-
+            configure: function (config) {
+                var whitelist = {
+                    "autoRespond": true,
+                    "autoRespondAfter": true,
+                    "respondImmediately": true,
+                    "fakeHTTPMethods": true
+                };
+                var setting;
+
+                config = config || {};
+                for (setting in config) {
+                    if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) {
+                        this[setting] = config[setting];
+                    }
+                }
+            },
             addRequest: function addRequest(xhrObj) {
                 var server = this;
                 push.call(this.requests, xhrObj);
 
                 xhrObj.onSend = function () {
                     server.handleRequest(this);
 
                     if (server.respondImmediately) {
@@ -5131,17 +5359,17 @@ if (typeof sinon == "undefined") {
                         server.responding = true;
                     }
                 };
             },
 
             getHTTPMethod: function getHTTPMethod(request) {
                 if (this.fakeHTTPMethods && /post/i.test(request.method)) {
                     var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
-                    return !!matches ? matches[1] : request.method;
+                    return matches ? matches[1] : request.method;
                 }
 
                 return request.method;
             },
 
             handleRequest: function handleRequest(xhr) {
                 if (xhr.async) {
                     if (!this.queue) {
@@ -5152,61 +5380,60 @@ if (typeof sinon == "undefined") {
                 } else {
                     this.processRequest(xhr);
                 }
             },
 
             log: function log(response, request) {
                 var str;
 
-                str =  "Request:\n"  + sinon.format(request)  + "\n\n";
+                str = "Request:\n" + sinon.format(request) + "\n\n";
                 str += "Response:\n" + sinon.format(response) + "\n\n";
 
                 sinon.log(str);
             },
 
             respondWith: function respondWith(method, url, body) {
-                if (arguments.length == 1 && typeof method != "function") {
+                if (arguments.length === 1 && typeof method !== "function") {
                     this.response = responseArray(method);
                     return;
                 }
 
                 if (!this.responses) {
                     this.responses = [];
                 }
 
-                if (arguments.length == 1) {
+                if (arguments.length === 1) {
                     body = method;
                     url = method = null;
                 }
 
-                if (arguments.length == 2) {
+                if (arguments.length === 2) {
                     body = url;
                     url = method;
                     method = null;
                 }
 
                 push.call(this.responses, {
                     method: method,
                     url: url,
-                    response: typeof body == "function" ? body : responseArray(body)
+                    response: typeof body === "function" ? body : responseArray(body)
                 });
             },
 
             respond: function respond() {
                 if (arguments.length > 0) {
                     this.respondWith.apply(this, arguments);
                 }
 
                 var queue = this.queue || [];
                 var requests = queue.splice(0, queue.length);
-                var request;
-
-                while (request = requests.shift()) {
-                    this.processRequest(request);
+
+                for (var i = 0; i < requests.length; i++) {
+                    this.processRequest(requests[i]);
                 }
             },
 
             processRequest: function processRequest(request) {
                 try {
                     if (request.aborted) {
                         return;
                     }
@@ -5217,50 +5444,50 @@ if (typeof sinon == "undefined") {
                         for (var l = this.responses.length, i = l - 1; i >= 0; i--) {
                             if (match.call(this, this.responses[i], request)) {
                                 response = this.responses[i].response;
                                 break;
                             }
                         }
                     }
 
-                    if (request.readyState != 4) {
+                    if (request.readyState !== 4) {
                         this.log(response, request);
 
                         request.respond(response[0], response[1], response[2]);
                     }
                 } catch (e) {
                     sinon.logError("Fake server request processing", e);
                 }
             },
 
             restore: function restore() {
                 return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
             }
         };
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./core");
         require("./fake_xdomain_request");
         require("./fake_xml_http_request");
         require("../format");
         makeApi(sinon);
         module.exports = sinon;
     }
 
     if (isAMD) {
         define(loadDependencies);
     } else if (isNode) {
         loadDependencies(require, module.exports, module);
     } else {
-        makeApi(sinon);
+        makeApi(sinon); // eslint-disable-line no-undef
     }
 }());
 
 /**
  * @depend fake_server.js
  * @depend fake_timers.js
  */
 /**
@@ -5272,27 +5499,27 @@ if (typeof sinon == "undefined") {
  * in any environment where the ajax implementation depends on setInterval or
  * setTimeout.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
 (function () {
+    
     function makeApi(sinon) {
         function Server() {}
         Server.prototype = sinon.fakeServer;
 
         sinon.fakeServerWithClock = new Server();
 
         sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {
             if (xhr.async) {
-                if (typeof setTimeout.clock == "object") {
+                if (typeof setTimeout.clock === "object") {
                     this.clock = setTimeout.clock;
                 } else {
                     this.clock = sinon.useFakeTimers();
                     this.resetClock = true;
                 }
 
                 if (!this.longestTimeout) {
                     var clockSetTimeout = this.clock.setTimeout;
@@ -5336,32 +5563,32 @@ if (typeof sinon == "undefined") {
             if (this.clock) {
                 this.clock.restore();
             }
 
             return sinon.fakeServer.restore.apply(this, arguments);
         };
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require) {
         var sinon = require("./core");
         require("./fake_server");
         require("./fake_timers");
         makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
     } else if (isNode) {
         loadDependencies(require);
     } else {
-        makeApi(sinon);
+        makeApi(sinon); // eslint-disable-line no-undef
     }
 }());
 
 /**
  * @depend util/core.js
  * @depend extend.js
  * @depend collection.js
  * @depend util/fake_timers.js
@@ -5371,18 +5598,18 @@ if (typeof sinon == "undefined") {
  * Manages fake collections as well as fake utilities such as Sinon's
  * timers and fake XHR implementation in one convenient object.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function () {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
         var push = [].push;
 
         function exposeValue(sandbox, config, key, value) {
             if (!value) {
                 return;
             }
 
@@ -5393,25 +5620,25 @@ if (typeof sinon == "undefined") {
                 push.call(sandbox.args, value);
             }
         }
 
         function prepareSandboxFromConfig(config) {
             var sandbox = sinon.create(sinon.sandbox);
 
             if (config.useFakeServer) {
-                if (typeof config.useFakeServer == "object") {
+                if (typeof config.useFakeServer === "object") {
                     sandbox.serverPrototype = config.useFakeServer;
                 }
 
                 sandbox.useFakeServer();
             }
 
             if (config.useFakeTimers) {
-                if (typeof config.useFakeTimers == "object") {
+                if (typeof config.useFakeTimers === "object") {
                     sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
                 } else {
                     sandbox.useFakeTimers();
                 }
             }
 
             return sandbox;
         }
@@ -5471,22 +5698,24 @@ if (typeof sinon == "undefined") {
                 if (!config) {
                     return sinon.create(sinon.sandbox);
                 }
 
                 var sandbox = prepareSandboxFromConfig(config);
                 sandbox.args = sandbox.args || [];
                 sandbox.injectedKeys = [];
                 sandbox.injectInto = config.injectInto;
-                var prop, value, exposed = sandbox.inject({});
+                var prop,
+                    value;
+                var exposed = sandbox.inject({});
 
                 if (config.properties) {
                     for (var i = 0, l = config.properties.length; i < l; i++) {
                         prop = config.properties[i];
-                        value = exposed[prop] || prop == "sandbox" && sandbox;
+                        value = exposed[prop] || prop === "sandbox" && sandbox;
                         exposeValue(sandbox, config, prop, value);
                     }
                 } else {
                     exposeValue(sandbox, config, "sandbox", value);
                 }
 
                 return sandbox;
             },
@@ -5494,103 +5723,109 @@ if (typeof sinon == "undefined") {
             match: sinon.match
         });
 
         sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
 
         return sinon.sandbox;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         require("./extend");
         require("./util/fake_server_with_clock");
         require("./util/fake_timers");
         require("./collection");
         module.exports = makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}());
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend util/core.js
  * @depend sandbox.js
  */
 /**
  * Test function, sandboxes fakes
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     function makeApi(sinon) {
         var slice = Array.prototype.slice;
 
         function test(callback) {
             var type = typeof callback;
 
-            if (type != "function") {
+            if (type !== "function") {
                 throw new TypeError("sinon.test needs to wrap a test function, got " + type);
             }
 
             function sinonSandboxedTest() {
                 var config = sinon.getConfig(sinon.config);
                 config.injectInto = config.injectIntoThis && this || config.injectInto;
                 var sandbox = sinon.sandbox.create(config);
                 var args = slice.call(arguments);
                 var oldDone = args.length && args[args.length - 1];
                 var exception, result;
 
-                if (typeof oldDone == "function") {
-                    args[args.length - 1] = function sinonDone(result) {
-                        if (result) {
+                if (typeof oldDone === "function") {
+                    args[args.length - 1] = function sinonDone(res) {
+                        if (res) {
                             sandbox.restore();
                             throw exception;
                         } else {
                             sandbox.verifyAndRestore();
                         }
-                        oldDone(result);
+                        oldDone(res);
                     };
                 }
 
                 try {
                     result = callback.apply(this, args.concat(sandbox.args));
                 } catch (e) {
                     exception = e;
                 }
 
-                if (typeof oldDone != "function") {
+                if (typeof oldDone !== "function") {
                     if (typeof exception !== "undefined") {
                         sandbox.restore();
                         throw exception;
                     } else {
                         sandbox.verifyAndRestore();
                     }
                 }
 
                 return result;
             }
 
             if (callback.length) {
-                return function sinonAsyncSandboxedTest(callback) {
+                return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars
                     return sinonSandboxedTest.apply(this, arguments);
                 };
             }
 
             return sinonSandboxedTest;
         }
 
         test.config = {
@@ -5600,48 +5835,48 @@ if (typeof sinon == "undefined") {
             useFakeTimers: true,
             useFakeServer: true
         };
 
         sinon.test = test;
         return test;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
-        var sinon = require("./util/core");
+        var core = require("./util/core");
         require("./sandbox");
-        module.exports = makeApi(sinon);
+        module.exports = makeApi(core);
     }
 
     if (isAMD) {
         define(loadDependencies);
     } else if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (sinon) {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    } else if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(typeof sinon === "object" && sinon || null)); // eslint-disable-line no-undef
 
 /**
  * @depend util/core.js
  * @depend test.js
  */
 /**
  * Test case, sandboxes all test functions
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon) {
+(function (sinonGlobal) {
+    
     function createTest(property, setUp, tearDown) {
         return function () {
             if (setUp) {
                 setUp.apply(this, arguments);
             }
 
             var exception, result;
 
@@ -5660,31 +5895,34 @@ if (typeof sinon == "undefined") {
             }
 
             return result;
         };
     }
 
     function makeApi(sinon) {
         function testCase(tests, prefix) {
-            if (!tests || typeof tests != "object") {
+            if (!tests || typeof tests !== "object") {
                 throw new TypeError("sinon.testCase needs an object with test functions");
             }
 
             prefix = prefix || "test";
             var rPrefix = new RegExp("^" + prefix);
-            var methods = {}, testName, property, method;
+            var methods = {};
             var setUp = tests.setUp;
             var tearDown = tests.tearDown;
+            var testName,
+                property,
+                method;
 
             for (testName in tests) {
                 if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) {
                     property = tests[testName];
 
-                    if (typeof property == "function" && rPrefix.test(testName)) {
+                    if (typeof property === "function" && rPrefix.test(testName)) {
                         method = property;
 
                         if (setUp || tearDown) {
                             method = createTest(property, setUp, tearDown);
                         }
 
                         methods[testName] = sinon.test(method);
                     } else {
@@ -5695,52 +5933,58 @@ if (typeof sinon == "undefined") {
 
             return methods;
         }
 
         sinon.testCase = testCase;
         return testCase;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
-        var sinon = require("./util/core");
+        var core = require("./util/core");
         require("./test");
-        module.exports = makeApi(sinon);
+        module.exports = makeApi(core);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-}(typeof sinon == "object" && sinon || null));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon // eslint-disable-line no-undef
+));
 
 /**
  * @depend times_in_words.js
  * @depend util/core.js
  * @depend match.js
  * @depend format.js
  */
 /**
  * Assertions matching the test spy retrieval interface.
  *
  * @author Christian Johansen (christian@cjohansen.no)
  * @license BSD
  *
  * Copyright (c) 2010-2013 Christian Johansen
  */
-
-(function (sinon, global) {
+(function (sinonGlobal, global) {
+    
     var slice = Array.prototype.slice;
 
     function makeApi(sinon) {
         var assert;
 
         function verifyIsStub() {
             var method;
 
@@ -5749,50 +5993,50 @@ if (typeof sinon == "undefined") {
 
                 if (!method) {
                     assert.fail("fake is not a spy");
                 }
 
                 if (method.proxy && method.proxy.isSinonProxy) {
                     verifyIsStub(method.proxy);
                 } else {
-                    if (typeof method != "function") {
+                    if (typeof method !== "function") {
                         assert.fail(method + " is not a function");
                     }
 
-                    if (typeof method.getCall != "function") {
+                    if (typeof method.getCall !== "function") {
                         assert.fail(method + " is not stubbed");
                     }
                 }
 
             }
         }
 
         function failAssertion(object, msg) {
             object = object || global;
             var failMethod = object.fail || assert.fail;
             failMethod.call(object, msg);
         }
 
         function mirrorPropAsAssertion(name, method, message) {
-            if (arguments.length == 2) {
+            if (arguments.length === 2) {
                 message = method;
                 method = name;
             }
 
             assert[name] = function (fake) {
                 verifyIsStub(fake);
 
                 var args = slice.call(arguments, 1);
                 var failed = false;
 
-                if (typeof method == "function") {
+                if (typeof method === "function") {
                     failed = !method(fake);
                 } else {
-                    failed = typeof fake[method] == "function" ?
+                    failed = typeof fake[method] === "function" ?
                         !fake[method].apply(fake, args) : !fake[method];
                 }
 
                 if (failed) {
                     failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args)));
                 } else {
                     assert.pass(name);
                 }
@@ -5809,21 +6053,22 @@ if (typeof sinon == "undefined") {
 
             fail: function fail(message) {
                 var error = new Error(message);
                 error.name = this.failException || assert.failException;
 
                 throw error;
             },
 
-            pass: function pass(assertion) {},
+            pass: function pass() {},
 
             callOrder: function assertCallOrder() {
                 verifyIsStub.apply(null, arguments);
-                var expected = "", actual = "";
+                var expected = "";
+                var actual = "";
 
                 if (!sinon.calledInOrder(arguments)) {
                     try {
                         expected = [].join.call(arguments, ", ");
                         var calls = slice.call(arguments);
                         var i = calls.length;
                         while (i) {
                             if (!calls[--i].called) {
@@ -5840,67 +6085,71 @@ if (typeof sinon == "undefined") {
                 } else {
                     assert.pass("callOrder");
                 }
             },
 
             callCount: function assertCallCount(method, count) {
                 verifyIsStub(method);
 
-                if (method.callCount != count) {
+                if (method.callCount !== count) {
                     var msg = "expected %n to be called " + sinon.timesInWords(count) +
                         " but was called %c%C";
                     failAssertion(this, method.printf(msg));
                 } else {
                     assert.pass("callCount");
                 }
             },
 
             expose: function expose(target, options) {
                 if (!target) {
                     throw new TypeError("target is null or undefined");
                 }
 
                 var o = options || {};
-                var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix;
-                var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
+                var prefix = typeof o.prefix === "undefined" && "assert" || o.prefix;
+                var includeFail = typeof o.includeFail === "undefined" || !!o.includeFail;
 
                 for (var method in this) {
-                    if (method != "expose" && (includeFail || !/^(fail)/.test(method))) {
+                    if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) {
                         target[exposedName(prefix, method)] = this[method];
                     }
                 }
 
                 return target;
             },
 
             match: function match(actual, expectation) {
                 var matcher = sinon.match(expectation);
                 if (matcher.test(actual)) {
                     assert.pass("match");
                 } else {
                     var formatted = [
                         "expected value to match",
                         "    expected = " + sinon.format(expectation),
                         "    actual = " + sinon.format(actual)
-                    ]
+                    ];
+
                     failAssertion(this, formatted.join("\n"));
                 }
             }
         };
 
         mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
         mirrorPropAsAssertion("notCalled", function (spy) {
             return !spy.called;
         }, "expected %n to not have been called but was called %c%C");
         mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
         mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
         mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
         mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
-        mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t");
+        mirrorPropAsAssertion(
+            "alwaysCalledOn",
+            "expected %n to always be called with %1 as this but was called with %t"
+        );
         mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
         mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
         mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C");
         mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C");
         mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C");
         mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C");
         mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C");
         mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C");
@@ -5908,32 +6157,38 @@ if (typeof sinon == "undefined") {
         mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
         mirrorPropAsAssertion("threw", "%n did not throw exception%C");
         mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
 
         sinon.assert = assert;
         return assert;
     }
 
-    var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+    var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
     var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
 
     function loadDependencies(require, exports, module) {
         var sinon = require("./util/core");
         require("./match");
         require("./format");
         module.exports = makeApi(sinon);
     }
 
     if (isAMD) {
         define(loadDependencies);
-    } else if (isNode) {
+        return;
+    }
+
+    if (isNode) {
         loadDependencies(require, module.exports, module);
-    } else if (!sinon) {
         return;
-    } else {
-        makeApi(sinon);
-    }
-
-}(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global));
+    }
+
+    if (sinonGlobal) {
+        makeApi(sinonGlobal);
+    }
+}(
+    typeof sinon === "object" && sinon, // eslint-disable-line no-undef
+    typeof global !== "undefined" ? global : self
+));
 
   return sinon;
 }));
--- a/browser/components/loop/test/standalone/index.html
+++ b/browser/components/loop/test/standalone/index.html
@@ -33,17 +33,17 @@
   <script src="../../content/shared/libs/react-0.12.2.js"></script>
   <script src="../../content/shared/libs/jquery-2.1.4.js"></script>
   <script src="../../content/shared/libs/lodash-3.9.3.js"></script>
   <script src="../../content/shared/libs/backbone-1.2.1.js"></script>
   <script src="../../standalone/content/libs/l10n-gaia-02ca67948fe8.js"></script>
   <!-- test dependencies -->
   <script src="../shared/vendor/mocha-2.2.5.js"></script>
   <script src="../shared/vendor/chai-3.0.0.js"></script>
-  <script src="../shared/vendor/sinon-1.15.0.js"></script>
+  <script src="../shared/vendor/sinon-1.16.1.js"></script>
   <script src="../shared/sdk_mock.js"></script>
   <script>
     chai.config.includeStack = true;
     mocha.setup({ui: 'bdd', timeout: 10000});
   </script>
   <!-- App scripts -->
   <script src="../../content/shared/js/utils.js"></script>
   <script src="../../content/shared/js/models.js"></script>
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/components/loop/test/xpcshell/head.js
@@ -66,17 +66,17 @@ function setupFakeFxAUserProfile() {
   MozLoopServiceInternal.fxAOAuthProfile = { email: "fake@invalid.com" };
 
   do_register_cleanup(function() {
     MozLoopServiceInternal.fxAOAuthTokenData = null;
     MozLoopServiceInternal.fxAOAuthProfile = null;
   });
 }
 
-function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
+function waitForCondition(aConditionFn, aMaxTries = 50, aCheckInterval = 100) {
   function tryAgain() {
     function tryNow() {
       tries++;
       if (aConditionFn()) {
         deferred.resolve();
       } else if (tries < aMaxTries) {
         tryAgain();
       } else {
--- a/browser/components/loop/test/xpcshell/test_looppush_initialize.js
+++ b/browser/components/loop/test/xpcshell/test_looppush_initialize.js
@@ -156,38 +156,37 @@ add_test(function test_ping_websocket() 
   );
 });
 
 add_test(function test_retry_pushurl() {
   MozLoopPushHandler.shutdown();
   loopServer.registerPathHandler("/push-server-config", (request, response) => {
     // The PushHandler should retry the request for the push-server-config for
     // each of these cases without throwing an error.
-    let n = 0;
     switch (++pushServerRequestCount) {
-    case ++n:
+    case 1:
       // Non-200 response
       response.setStatusLine(null, 500, "Retry");
       response.processAsync();
       response.finish();
       break;
-    case ++n:
+    case 2:
       // missing parameter
       response.setStatusLine(null, 200, "OK");
       response.write(JSON.stringify({pushServerURI: null}));
       response.processAsync();
       response.finish();
       break;
-    case ++n:
+    case 3:
       // json parse error
       response.setStatusLine(null, 200, "OK");
       response.processAsync();
       response.finish();
       break;
-    case ++n:
+    case 4:
       response.setStatusLine(null, 200, "OK");
       response.write(JSON.stringify({pushServerURI: kServerPushUrl}));
       response.processAsync();
       response.finish();
 
       run_next_test();
       break;
     }
--- a/browser/components/migration/IEProfileMigrator.js
+++ b/browser/components/migration/IEProfileMigrator.js
@@ -287,17 +287,17 @@ Cookies.prototype = {
       aCallback(success);
     }).apply(this);
     cookiesGenerator.next();
   },
 
   _readCookieFile: function C__readCookieFile(aFile, aCallback) {
     let fileReader = Cc["@mozilla.org/files/filereader;1"].
                      createInstance(Ci.nsIDOMFileReader);
-    fileReader.addEventListener("loadend", (function onLoadEnd() {
+    let onLoadEnd = () => {
       fileReader.removeEventListener("loadend", onLoadEnd, false);
 
       if (fileReader.readyState != fileReader.DONE) {
         Cu.reportError("Could not read cookie contents: " + fileReader.error);
         aCallback(false);
         return;
       }
 
@@ -305,17 +305,18 @@ Cookies.prototype = {
       try {
         this._parseCookieBuffer(fileReader.result);
       } catch (ex) {
         Components.utils.reportError("Unable to migrate cookie: " + ex);
         success = false;
       } finally {
         aCallback(success);
       }
-    }).bind(this), false);
+    };
+    fileReader.addEventListener("loadend", onLoadEnd, false);
     fileReader.readAsText(new File(aFile));
   },
 
   /**
    * Parses a cookie file buffer and returns an array of the contained cookies.
    *
    * The cookie file format is a newline-separated-values with a "*" used as
    * delimeter between multiple records.
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -15,17 +15,19 @@ support-files =
 [browser_0_library_left_pane_migration.js]
 [browser_410196_paste_into_tags.js]
 [browser_416459_cut.js]
 [browser_423515.js]
 [browser_425884.js]
 [browser_435851_copy_query.js]
 [browser_475045.js]
 [browser_555547.js]
+[browser_bookmarkProperties_addFolderDefaultButton.js]
 [browser_bookmarkProperties_addKeywordForThisSearch.js]
+[browser_bookmarkProperties_readOnlyRoot.js]
 [browser_bookmarklet_windowOpen.js]
 support-files =
   pageopeningwindow.html
 [browser_bookmarksProperties.js]
 [browser_drag_bookmarks_on_toolbar.js]
 skip-if = e10s # Bug ?????? - test fails - "Number of dragged items should be the same. - Got 0, expected 1"
 [browser_forgetthissite_single.js]
 [browser_history_sidebar_search.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_addFolderDefaultButton.js
@@ -0,0 +1,37 @@
+"use strict"
+
+add_task(function* () {
+  info("Bug 475529 - Add is the default button for the new folder dialog.");
+
+  yield withSidebarTree("bookmarks", function* (tree) {
+    let itemId = PlacesUIUtils.leftPaneQueries["UnfiledBookmarks"];
+    tree.selectItems([itemId]);
+    ok(tree.controller.isCommandEnabled("placesCmd_new:folder"),
+       "'placesCmd_new:folder' on current selected node is enabled");
+
+    yield withBookmarksDialog(
+      false,
+      function openDialog() {
+        tree.controller.doCommand("placesCmd_new:folder");
+      },
+      function* test(dialogWin) {
+        let promiseTitleChangeNotification = promiseBookmarksNotification(
+          "onItemChanged", (itemId, prop, isAnno, val) => prop == "title" && val =="n");
+
+        fillBookmarkTextField("editBMPanel_namePicker", "n", dialogWin, false);
+
+        // Confirm and close the dialog.
+        EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
+        yield promiseTitleChangeNotification;
+
+        let bookmark = yield PlacesUtils.bookmarks.fetch({
+          parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+          index: PlacesUtils.bookmarks.DEFAULT_INDEX
+        });
+
+        is(bookmark.title, "n", "folder name has been edited");
+        yield PlacesUtils.bookmarks.remove(bookmark);
+      }
+    );
+  });
+});
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_addKeywordForThisSearch.js
@@ -5,17 +5,17 @@ const TEST_URL = "http://mochi.test:8888
 add_task(function* () {
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_URL,
   }, function* (browser) {
     // We must wait for the context menu code to build metadata.
     yield openContextMenuForContentSelector(browser, 'form > input[name="search"]');
 
-    yield withBookmarksDialog(AddKeywordForSearchField, function* (dialogWin) {
+    yield withBookmarksDialog(true, AddKeywordForSearchField, function* (dialogWin) {
       let acceptBtn = dialogWin.document.documentElement.getButton("accept");
       ok(acceptBtn.disabled, "Accept button is disabled");
 
       let promiseKeywordNotification = promiseBookmarksNotification(
         "onItemChanged", (itemId, prop, isAnno, val) => prop == "keyword" && val =="kw");
 
       fillBookmarkTextField("editBMPanel_keywordField", "kw", dialogWin);
 
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_readOnlyRoot.js
@@ -0,0 +1,42 @@
+"use strict"
+
+add_task(function* () {
+  info("Bug 479348 - Properties on a root should be read-only.");
+
+  yield withSidebarTree("bookmarks", function* (tree) {
+    let itemId = PlacesUIUtils.leftPaneQueries["UnfiledBookmarks"];
+    tree.selectItems([itemId]);
+    ok(tree.controller.isCommandEnabled("placesCmd_show:info"),
+       "'placesCmd_show:info' on current selected node is enabled");
+
+    yield withBookmarksDialog(
+      true,
+      function openDialog() {
+        tree.controller.doCommand("placesCmd_show:info");
+      },
+      function* test(dialogWin) {
+        // Check that the dialog is read-only.
+        ok(dialogWin.gEditItemOverlay.readOnly, "Dialog is read-only");
+        // Check that accept button is disabled
+        let acceptButton = dialogWin.document.documentElement.getButton("accept");
+        ok(acceptButton.disabled, "Accept button is disabled");
+
+        // Check that name picker is read only
+        let namepicker = dialogWin.document.getElementById("editBMPanel_namePicker");
+        ok(namepicker.readOnly, "Name field is read-only");
+        is(namepicker.value,
+           PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
+           "Node title is correct");
+        // Blur the field and ensure root's name has not been changed.
+        namepicker.blur();
+        is(namepicker.value,
+           PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
+           "Root title is correct");
+        // Check the shortcut's title.
+        let bookmark = yield PlacesUtils.bookmarks.fetch(tree.selectedNode.bookmarkGuid);
+        is(bookmark.title, null,
+           "Shortcut title is null");
+      }
+    );
+  });
+});
--- a/browser/components/places/tests/browser/browser_bookmarksProperties.js
+++ b/browser/components/places/tests/browser/browser_bookmarksProperties.js
@@ -48,110 +48,16 @@ function add_bookmark(aURI) {
   return bId;
 }
 
 // Each test is an obj w/ a desc property and run method.
 var gTests = [];
 var gCurrentTest = null;
 
 //------------------------------------------------------------------------------
-// TEST SKELETON: use this template to add new tests.
-/*
-gTests.push({
-  desc: "Bug Description",
-  sidebar: SIDEBAR_BOOKMARKS_ID, // See SIDEBAR_ constants above.
-  action: ACTION_EDIT, // See ACTION_ constants above.
-  itemType: null, // See TYPE_ constants above, required for ACTION_ADD, only for Bookmarks sidebar.
-  historyView: SIDEBAR_HISTORY_BYLASTVISITED_VIEW, // See constants above, only for History sidebar.
-  window: null, // Will contain handle of dialog window
-
-  setup: function(aCallback) {
-    // Setup everything needed for this test, runs before everything else.
-    aCallback();
-  },
-
-  selectNode: function(tree) {
-    // Select the node to edit or to add to, runs when sidebar tree is visible.
-  },
-
-  run: function() {
-    // Actual test, runs when dialog is open.
-  },
-
-  finish: function() {
-    // Close window, toggle sidebar and goto next test.
-    this.window.document.documentElement.cancelDialog();
-    SidebarUI.hide();
-    runNextTest();
-  },
-
-  cleanup: function() {
-    // Undo everything added during setup, runs after dialog has been closed.
-  }
-});
-*/
-
-//------------------------------------------------------------------------------
-// Bug 479348 - Properties on a root should be read-only
-
-gTests.push({
-  desc: "Bug 479348 - Properties on a root should be read-only",
-  sidebar: SIDEBAR_BOOKMARKS_ID,
-  action: ACTION_EDIT,
-  itemType: null,
-  window: null,
-
-  setup: function(aCallback) {
-    // Nothing to do.
-    aCallback();
-  },
-
-  selectNode: function(tree) {
-    // Select Unfiled Bookmarks root.
-    var itemId = PlacesUIUtils.leftPaneQueries["UnfiledBookmarks"];
-    tree.selectItems([itemId]);
-    this.selectedNode = tree.selectedNode;
-  },
-
-  run: function() {
-    // Check that the dialog is read-only.
-    ok(this.window.gEditItemOverlay.readOnly, "Dialog is read-only");
-    // Check that accept button is disabled
-    var acceptButton = this.window.document.documentElement.getButton("accept");
-    ok(acceptButton.disabled, "Accept button is disabled");
-
-    // Check that name picker is read only
-    var namepicker = this.window.document.getElementById("editBMPanel_namePicker");
-    ok(namepicker.readOnly, "Name field is disabled");
-    is(namepicker.value,
-       PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
-       "Node title is correct");
-    // Blur the field and ensure root's name has not been changed.
-    this.window.gEditItemOverlay._namePicker.blur();
-    is(namepicker.value,
-       PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
-       "Root title is correct");
-    // Check the shortcut's title.
-    is(PlacesUtils.bookmarks.getItemTitle(this.selectedNode.itemId), null,
-       "Shortcut title is null");
-    this.finish();
-  },
-
-  finish: function() {
-    this.window.document.documentElement.cancelDialog();
-    SidebarUI.hide();
-    runNextTest();
-  },
-
-  cleanup: function() {
-    // Nothing to do.
-  }
-});
-
-//------------------------------------------------------------------------------
 // Bug 462662 - Pressing Enter to select tag from autocomplete closes bookmarks properties dialog
 gTests.push({
   desc: "Bug 462662 - Pressing Enter to select tag from autocomplete closes bookmarks properties dialog",
   sidebar: SIDEBAR_BOOKMARKS_ID,
   action: ACTION_EDIT,
   itemType: null,
   window: null,
   _itemId: null,
@@ -242,75 +148,16 @@ gTests.push({
     is(tags[0], "testTag", "Tag on node has not changed");
 
     // Cleanup.
     PlacesUtils.tagging.untagURI(PlacesUtils._uri(TEST_URL), ["testTag"]);
     PlacesUtils.bookmarks.removeItem(this._itemId);
   }
 });
 
-
-//------------------------------------------------------------------------------
-// Bug 475529 -  Add button in new folder dialog not default anymore
-gTests.push({
-  desc: "Bug 475529 - Add button in new folder dialog not default anymore",
-  sidebar: SIDEBAR_BOOKMARKS_ID,
-  action: ACTION_ADD,
-  itemType: TYPE_FOLDER,
-  window: null,
-  _itemId: null,
-
-  setup: function(aCallback) {
-    // Nothing to do.
-    aCallback();
-  },
-
-  selectNode: function(tree) {
-    // Select Unfiled Bookmarks root.
-    var itemId = PlacesUIUtils.leftPaneQueries["UnfiledBookmarks"];
-    tree.selectItems([itemId]);
-    this.selectedNode = tree.selectedNode;
-  },
-
-  run: function() {
-    this._itemId = this.window.gEditItemOverlay._paneInfo.itemId;
-    // Change folder name
-    var namePicker = this.window.document.getElementById("editBMPanel_namePicker");
-    var self = this;
-
-    this.window.addEventListener("unload", function(event) {
-      self.window.removeEventListener("unload", arguments.callee, false);
-      executeSoon(function () {
-        self.finish();
-      });
-    }, false);
-
-    info("About to focus the namePicker field");
-    namePicker.focus();
-    namePicker.select();
-    EventUtils.synthesizeKey("n", {}, this.window);
-    EventUtils.synthesizeKey("VK_RETURN", {}, this.window);
-  },
-
-  finish: function() {
-    // Window is already closed.
-    SidebarUI.hide();
-    runNextTest();
-  },
-
-  cleanup: function() {
-    // Check that folder name has been changed.
-    is(PlacesUtils.bookmarks.getItemTitle(this._itemId), "n",
-       "Folder name has been edited");
-
-    // Cleanup.
-    PlacesUtils.bookmarks.removeItem(this._itemId);
-  }
-});
-
 //------------------------------------------------------------------------------
 // Bug 476020 - Pressing Esc while having the tag autocomplete open closes the bookmarks panel
 
 gTests.push({
   desc: "Bug 476020 - Pressing Esc while having the tag autocomplete open closes the bookmarks panel",
   sidebar: SIDEBAR_BOOKMARKS_ID,
   action: ACTION_EDIT,
   itemType: null,
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -1,12 +1,8 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
   "resource://testing-common/PlacesTestUtils.jsm");
 
 // We need to cache this before test runs...
@@ -280,37 +276,42 @@ function isToolbarVisible(aToolbar) {
   let hidingValue = aToolbar.getAttribute(hidingAttribute).toLowerCase();
   // Check for both collapsed="true" and collapsed="collapsed"
   return hidingValue !== "true" && hidingValue !== hidingAttribute;
 }
 
 /**
  * Executes a task after opening the bookmarks dialog, then cancels the dialog.
  *
+ * @param autoCancel
+ *        whether to automatically cancel the dialog at the end of the task
  * @param openFn
  *        generator function causing the dialog to open
  * @param task
  *        the task to execute once the dialog is open
  */
-let withBookmarksDialog = Task.async(function* (openFn, taskFn) {
+let withBookmarksDialog = Task.async(function* (autoCancel, openFn, taskFn) {
+  let closed = false;
   let dialogPromise = new Promise(resolve => {
     Services.ww.registerNotification(function winObserver(subject, topic, data) {
-      if (topic != "domwindowopened")
-        return;
-      let win = subject.QueryInterface(Ci.nsIDOMWindow);
-      win.addEventListener("load", function load() {
-        win.removeEventListener("load", load);
-        ok(win.location.href.startsWith("chrome://browser/content/places/bookmarkProperties"),
-           "The bookmark properties dialog is ready");
+      if (topic == "domwindowopened") {
+        let win = subject.QueryInterface(Ci.nsIDOMWindow);
+        win.addEventListener("load", function load() {
+          win.removeEventListener("load", load);
+          ok(win.location.href.startsWith("chrome://browser/content/places/bookmarkProperties"),
+             "The bookmark properties dialog is ready");
+          // This is needed for the overlay.
+          waitForFocus(() => {
+            resolve(win);
+          }, win);
+        });
+      } else if (topic == "domwindowclosed") {
         Services.ww.unregisterNotification(winObserver);
-        // This is needed for the overlay.
-        waitForFocus(() => {
-          resolve(win);
-        }, win);
-      });
+        closed = true;
+      }
     });
   });
 
   info("withBookmarksDialog: opening the dialog");
   // The dialog might be modal and could block our events loop, so executeSoon.
   executeSoon(openFn);
 
   info("withBookmarksDialog: waiting for the dialog");
@@ -318,18 +319,23 @@ let withBookmarksDialog = Task.async(fun
 
   // Ensure overlay is loaded
   ok(dialogWin.gEditItemOverlay.initialized, "EditItemOverlay is initialized");
 
   info("withBookmarksDialog: executing the task");
   try {
     yield taskFn(dialogWin);
   } finally {
-    info("withBookmarksDialog: canceling the dialog");
-    dialogWin.document.documentElement.cancelDialog();
+    if (!closed) {
+      if (!autoCancel) {
+        ok(false, "The test should have closed the dialog!");
+      }
+      info("withBookmarksDialog: canceling the dialog");
+      dialogWin.document.documentElement.cancelDialog();
+    }
   }
 });
 
 /**
  * Opens the contextual menu on the element pointed by the given selector.
  *
  * @param selector
  *        Valid selector syntax
@@ -396,18 +402,58 @@ let waitForCondition = Task.async(functi
  * Fills a bookmarks dialog text field ensuring to cause expected edit events.
  *
  * @param id
  *        id of the text field
  * @param text
  *        text to fill in
  * @param win
  *        dialog window
+ * @param [optional] blur
+ *        whether to blur at the end.
  */
-function fillBookmarkTextField(id, text, win) {
+function fillBookmarkTextField(id, text, win, blur = true) {
   let elt = win.document.getElementById(id);
   elt.focus();
   elt.select();
   for (let c of text.split("")) {
     EventUtils.synthesizeKey(c, {}, win);
   }
-  elt.blur();
+  if (blur)
+    elt.blur();
 }
+
+/**
+ * Executes a task after opening the bookmarks or history sidebar. Takes care
+ * of closing the sidebar once done.
+ *
+ * @param type
+ *        either "bookmarks" or "history".
+ * @param taskFn
+ *        The task to execute once the sidebar is ready. Will get the Places
+ *        tree view as input.
+ */
+let withSidebarTree = Task.async(function* (type, taskFn) {
+  let sidebar = document.getElementById("sidebar");
+  info("withSidebarTree: waiting sidebar load");
+  let sidebarLoadedPromise = new Promise(resolve => {
+    sidebar.addEventListener("load", function load() {
+      sidebar.removeEventListener("load", load, true);
+      resolve();
+    }, true);
+  });
+  let sidebarId = type == "bookmarks" ? "viewBookmarksSidebar"
+                                      : "viewHistorySidebar";
+  SidebarUI.show(sidebarId);
+  yield sidebarLoadedPromise;
+
+  let treeId = type == "bookmarks" ? "bookmarks-view"
+                                   : "historyTree";
+  let tree = sidebar.contentDocument.getElementById(treeId);
+
+  // Need to executeSoon since the tree is initialized on sidebar load.
+  info("withSidebarTree: executing the task");
+  try {
+    yield taskFn(tree);
+  } finally {
+    SidebarUI.hide();
+  }
+});
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -560,34 +560,51 @@ let gSyncPane = {
 
   resetPass: function () {
     if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED)
       gSyncUtils.resetPassword();
     else
       gSyncUtils.resetPassphrase();
   },
 
+  _getEntryPoint: function () {
+    let params = new URLSearchParams(document.URL.split("#")[0].split("?")[1] || "");
+    return params.get("entrypoint") || "preferences";
+  },
+
+  _openAboutAccounts: function(action) {
+    let entryPoint = this._getEntryPoint();
+    let params = new URLSearchParams();
+    if (action) {
+      params.set("action", action);
+    }
+    params.set("entrypoint", entryPoint);
+
+    this.openContentInBrowser("about:accounts?" + params, {
+      replaceQueryString: true
+    });
+
+  },
+
   /**
    * Invoke the Sync setup wizard.
    *
    * @param wizardType
    *        Indicates type of wizard to launch:
    *          null    -- regular set up wizard
    *          "pair"  -- pair a device first
    *          "reset" -- reset sync
    */
   openSetup: function (wizardType) {
     let service = Components.classes["@mozilla.org/weave/service;1"]
                   .getService(Components.interfaces.nsISupports)
                   .wrappedJSObject;
 
     if (service.fxAccountsEnabled) {
-      this.openContentInBrowser("about:accounts?entrypoint=preferences", {
-        replaceQueryString: true
-      });
+      this._openAboutAccounts();
     } else {
       let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
       if (win)
         win.focus();
       else {
         window.openDialog("chrome://browser/content/sync/setup.xul",
                           "weaveSetup", "centerscreen,chrome,resizable=no",
                           wizardType);
@@ -613,44 +630,38 @@ let gSyncPane = {
   },
 
   openToS: function(aEvent) {
     aEvent.stopPropagation();
     gSyncUtils.openToS();
   },
 
   signUp: function() {
-    this.openContentInBrowser("about:accounts?action=signup&entrypoint=preferences", {
-      replaceQueryString: true
-    });
+    this._openAboutAccounts("signup");
   },
 
   signIn: function() {
-    this.openContentInBrowser("about:accounts?action=signin&entrypoint=preferences", {
-      replaceQueryString: true
-    });
+    this._openAboutAccounts("signin");
   },
 
   reSignIn: function() {
-    this.openContentInBrowser("about:accounts?action=reauth&entrypoint=preferences", {
-      replaceQueryString: true
-    });
+    this._openAboutAccounts("reauth");
   },
 
   openChangeProfileImage: function() {
-    fxAccounts.promiseAccountsChangeProfileURI("preferences", "avatar")
+    fxAccounts.promiseAccountsChangeProfileURI(this._getEntryPoint(), "avatar")
       .then(url => {
         this.openContentInBrowser(url, {
           replaceQueryString: true
         });
       });
   },
 
   manageFirefoxAccount: function() {
-    fxAccounts.promiseAccountsManageURI("preferences")
+    fxAccounts.promiseAccountsManageURI(this._getEntryPoint())
       .then(url => {
         this.openContentInBrowser(url, {
           replaceQueryString: true
         });
       });
   },
 
   verifyFirefoxAccount: function() {
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -1,13 +1,12 @@
 <?xml version="1.0"?>
-# -*- Mode: HTML -*-
-# 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/.
+<!-- 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 bindings [
 <!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd" >
 %searchBarDTD;
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 ]>
 
@@ -487,23 +486,20 @@
             if (aEvent.button == 2)
               return;
             where = whereToOpenLink(aEvent, false, true);
           }
           else {
             var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab");
             if (((aEvent instanceof KeyboardEvent) && aEvent.altKey) ^ newTabPref)
               where = "tab";
-            if ((aEvent instanceof MouseEvent) && (aEvent.button == 1 ||
-#ifdef XP_MACOSX
-                                                   aEvent.metaKey))
-#else
-                                                   aEvent.ctrlKey))
-#endif
+            if ((aEvent instanceof MouseEvent) &&
+                (aEvent.button == 1 || aEvent.getModifierState("Accel"))) {
               where = "tab-background";
+            }
           }
 
           let selection = this.telemetrySearchDetails;
           this.doSearch(textValue, where, aEngine);
 
           if (!selection || (selection.index == -1)) {
             let source = "unknown";
             let type = "unknown";
@@ -866,16 +862,21 @@
         element.setAttribute("accesskey", akey);
         element.setAttribute("cmd", "cmd_togglesuggest");
         element.setAttribute("type", "checkbox");
         element.setAttribute("checked", this._suggestEnabled);
         element.setAttribute("autocheck", "false");
         this._suggestMenuItem = element;
         cxmenu.appendChild(element);
 
+        this.addEventListener("keypress", aEvent => {
+          if (navigator.platform.startsWith("Mac") && aEvent.keyCode == KeyEvent.VK_F4)
+            this.openSearch()
+        }, true);
+
         this.controllers.appendController(this.searchbarController);
         document.getBindingParent(this)._textboxInitialized = true;
 
         // Add observer for suggest preference
         var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                             .getService(Components.interfaces.nsIPrefBranch);
         prefs.addObserver("browser.search.suggest.enabled", this, false);
       ]]></constructor>
@@ -1158,21 +1159,17 @@
 
           let list = document.getAnonymousElementByAttribute(popup, "anonid",
                                                              "search-panel-one-offs");
           if (!list) // remove this check when removing the old search UI.
             return;
 
           // accel + up/down changes the default engine and shouldn't affect
           // the selection on the one-off buttons.
-#ifdef XP_MACOSX
-          if (aEvent.metaKey)
-#else
-          if (aEvent.ctrlKey)
-#endif
+          if (aEvent.getModifierState("Accel"))
             return;
 
           let stopEvent = false;
 
           // Alt + up/down is very similar to (shift +) tab but differs in that
           // it loops through the list, whereas tab will move the focus out.
           if (aEvent.altKey &&
               (aEvent.keyCode == KeyEvent.DOM_VK_DOWN ||
@@ -1246,22 +1243,16 @@
       <handler event="keypress" keycode="VK_DOWN" modifiers="alt"
                phase="capturing"
                action="return this.openSearch();"/>
 
       <handler event="keypress" keycode="VK_UP" modifiers="alt"
                phase="capturing"
                action="return this.openSearch();"/>
 
-#ifndef XP_MACOSX
-      <handler event="keypress" keycode="VK_F4"
-               phase="capturing"
-               action="return this.openSearch();"/>
-#endif
-
       <handler event="dragover">
       <![CDATA[
         var types = event.dataTransfer.types;
         if (types.contains("text/plain") || types.contains("text/x-moz-text-internal"))
           event.preventDefault();
       ]]>
       </handler>
 
--- a/browser/components/search/jar.mn
+++ b/browser/components/search/jar.mn
@@ -1,9 +1,9 @@
 # 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/search/search.xml                           (content/search.xml)
+        content/browser/search/search.xml                           (content/search.xml)
         content/browser/search/searchbarBindings.css                (content/searchbarBindings.css)
         content/browser/search/engineManager.xul                    (content/engineManager.xul)
         content/browser/search/engineManager.js                     (content/engineManager.js)
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -433,18 +433,19 @@
 
 /* Background tab separators.
    Also show separators beside the selected tab when dragging it. */
 #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
 .tabbrowser-tab:not([visuallyselected]):not([afterselected-visible]):not([afterhovered]):not([first-visible-tab]):not(:hover)::before,
 #tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
   width: 1px;
   -moz-margin-start: -1px;
-  margin-top: calc(var(--tab-separator-margin) + 1px);
-  margin-bottom: var(--tab-separator-margin);
+  padding-top: calc(var(--tab-separator-margin) + 1px);
+  padding-bottom: var(--tab-separator-margin);
+  background-clip: content-box;
   background-color: currentColor;
   opacity: var(--tab-separator-opacity);
   content: "";
   display: -moz-box;
 }
 
 /* New tab button */
 
--- a/dom/bluetooth/BluetoothHfpManagerBase.h
+++ b/dom/bluetooth/BluetoothHfpManagerBase.h
@@ -19,13 +19,14 @@ public:
    */
   virtual bool IsScoConnected() = 0;
 
   virtual bool IsNrecEnabled() = 0;
 };
 
 #define BT_DECL_HFP_MGR_BASE                  \
   BT_DECL_PROFILE_MGR_BASE                    \
-  virtual bool IsScoConnected() override;
+  virtual bool IsScoConnected() override;     \
+  virtual bool IsNrecEnabled() override;
 
 END_BLUETOOTH_NAMESPACE
 
 #endif  //#ifndef mozilla_dom_bluetooth_bluetoothhfpmanagerbase_h__
--- a/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp-fallback/BluetoothHfpManager.cpp
@@ -100,16 +100,22 @@ BluetoothHfpManager::OnUpdateSdpRecords(
  * BluetoothHfpManagerBase function
  */
 bool
 BluetoothHfpManager::IsScoConnected()
 {
   return false;
 }
 
+bool
+BluetoothHfpManager::IsNrecEnabled()
+{
+  return false;
+}
+
 /**
  * Non-inherited functions
  */
 // static
 BluetoothHfpManager*
 BluetoothHfpManager::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -200,17 +206,17 @@ BluetoothHfpManager::ConnectSco()
    */
   return false;
 }
 
 void
 BluetoothHfpManager::HandleBackendError()
 {
   /**
-   * TODO: 
+   * TODO:
    *   Reset connection state and audio state to DISCONNECTED to handle backend
    *   error. The state change triggers UI status bar update as ordinary
    *   bluetooth turn-off sequence.
    */
 }
 
 bool
 BluetoothHfpManager::DisconnectSco()
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
@@ -87,17 +87,16 @@ public:
   }
 
   static BluetoothHfpManager* Get();
   static void InitHfpInterface(BluetoothProfileResultHandler* aRes);
   static void DeinitHfpInterface(BluetoothProfileResultHandler* aRes);
 
   bool ConnectSco();
   bool DisconnectSco();
-  bool IsNrecEnabled();
 
   /**
    * @param aSend A boolean indicates whether we need to notify headset or not
    */
   void HandleCallStateChanged(uint32_t aCallIndex, uint16_t aCallState,
                               const nsAString& aError, const nsAString& aNumber,
                               const bool aIsOutgoing, const bool aIsConference,
                               bool aSend);
--- a/dom/bluetooth/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.h
@@ -98,17 +98,16 @@ public:
    * connecting HSP socket rather than HFP socket.
    *
    * @param  aRunnable Indicate a BluetoothReplyRunnable to execute this
    *                   function. The default value is nullpter
    * @return <code>true</code> if SCO established successfully
    */
   bool ConnectSco(BluetoothReplyRunnable* aRunnable = nullptr);
   bool DisconnectSco();
-  bool IsNrecEnabled();
   bool ListenSco();
 
 #ifdef MOZ_B2G_RIL
   /**
    * @param aSend A boolean indicates whether we need to notify headset or not
    */
   void HandleCallStateChanged(uint32_t aCallIndex, uint16_t aCallState,
                               const nsAString& aError, const nsAString& aNumber,
--- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
+++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp
@@ -452,17 +452,17 @@ nsPerformanceStatsService::nsPerformance
 #else
   : mProcessId(getpid())
 #endif
   , mProcessStayed(0)
   , mProcessMoved(0)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
-    mozilla::unused << obs->AddObserver(this, "profile-before-shutdown", false);
+    mozilla::unused << obs->AddObserver(this, "profile-before-change", false);
   }
 }
 
 nsPerformanceStatsService::~nsPerformanceStatsService()
 {
 }
 
 //[implicit_jscontext] attribute bool isMonitoringCPOW;
@@ -525,17 +525,17 @@ NS_IMETHODIMP nsPerformanceStatsService:
 
 
 /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
 NS_IMETHODIMP nsPerformanceStatsService::Observe(nsISupports *, const char *, const char16_t *)
 {
   // Upload telemetry
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
-    mozilla::unused << obs->RemoveObserver(this, "profile-before-shutdown");
+    mozilla::unused << obs->RemoveObserver(this, "profile-before-change");
   }
 
   if (mProcessStayed + mProcessMoved == 0) {
     // Nothing to report.
     return NS_OK;
   }
   const uint32_t proportion = ( 100 * mProcessStayed ) / ( mProcessStayed + mProcessMoved );
   mozilla::Telemetry::Accumulate("PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED", proportion);