From 356a7f436d48689e67885ff4548d3da12a8a3d5c Mon Sep 17 00:00:00 2001
From: Shane Tomlinson <stomlinson@mozilla.com>
Date: Tue, 31 Jan 2012 15:41:46 +0000
Subject: [PATCH] Split the state machine into state, state_machine, history,
 and command.

* Pull out core state machine code from the original state_machine.js.
* Update code and tests for use in new state machine.
* Add new state_machine core.
* Add history.
* Add command.
* Update tests.
* Rename resources/state_machine to resources/state

This should simplify a lot of these "cancel email verification" problems.
---
 .../static/dialog/controllers/authenticate.js |   1 +
 resources/static/dialog/controllers/dialog.js |   2 +-
 .../resources/{state_machine.js => state.js}  | 110 ++++--------------
 resources/static/shared/command.js            |  30 +++++
 resources/static/shared/history.js            |  47 ++++++++
 resources/static/shared/state_machine.js      |  73 ++++++++++++
 .../test/cases/controllers/authenticate.js    |   2 +-
 .../resources/{state_machine.js => state.js}  |  18 ++-
 resources/static/test/cases/shared/class.js   |   2 +
 resources/static/test/cases/shared/command.js |  48 ++++++++
 resources/static/test/cases/shared/history.js |  62 ++++++++++
 .../static/test/cases/shared/state_machine.js |  96 +++++++++++++++
 resources/views/dialog_layout.ejs             |   5 +-
 resources/views/test.ejs                      |  12 +-
 scripts/compress.sh                           |   3 +-
 15 files changed, 408 insertions(+), 103 deletions(-)
 rename resources/static/dialog/resources/{state_machine.js => state.js} (74%)
 create mode 100644 resources/static/shared/command.js
 create mode 100644 resources/static/shared/history.js
 create mode 100644 resources/static/shared/state_machine.js
 rename resources/static/test/cases/resources/{state_machine.js => state.js} (96%)
 create mode 100644 resources/static/test/cases/shared/command.js
 create mode 100644 resources/static/test/cases/shared/history.js
 create mode 100644 resources/static/test/cases/shared/state_machine.js

diff --git a/resources/static/dialog/controllers/authenticate.js b/resources/static/dialog/controllers/authenticate.js
index fd0d86163..6b459877e 100644
--- a/resources/static/dialog/controllers/authenticate.js
+++ b/resources/static/dialog/controllers/authenticate.js
@@ -129,6 +129,7 @@ BrowserID.Modules.Authenticate = (function() {
     start: function(options) {
       options = options || {};
 
+      addressInfo = null;
       lastEmail = options.email || "";
 
       var self=this;
diff --git a/resources/static/dialog/controllers/dialog.js b/resources/static/dialog/controllers/dialog.js
index 3358a1d24..d7acf4f3c 100644
--- a/resources/static/dialog/controllers/dialog.js
+++ b/resources/static/dialog/controllers/dialog.js
@@ -36,7 +36,7 @@ BrowserID.Modules.Dialog = (function() {
 
   function startStateMachine(controller) {
     // start this directly because it should always be running.
-    var machine = BrowserID.StateMachine.create();
+    var machine = BrowserID.State.create();
     machine.start({
       controller: controller
     });
diff --git a/resources/static/dialog/resources/state_machine.js b/resources/static/dialog/resources/state.js
similarity index 74%
rename from resources/static/dialog/resources/state_machine.js
rename to resources/static/dialog/resources/state.js
index 4ab1318a4..d7fc6d59a 100644
--- a/resources/static/dialog/resources/state_machine.js
+++ b/resources/static/dialog/resources/state.js
@@ -3,91 +3,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-(function() {
+BrowserID.State = (function() {
   var bid = BrowserID,
       storage = bid.Storage,
       mediator = bid.Mediator,
-      user = bid.User,
       publish = mediator.publish.bind(mediator),
-      subscriptions = [],
-      stateStack = [],
+      user = bid.User,
       controller,
       moduleManager = bid.module,
-      errors = bid.Errors,
       addPrimaryUser = false,
       email,
       requiredEmail;
 
-  function subscribe(message, cb) {
-    subscriptions.push(mediator.subscribe(message, cb));
-  }
-
-  function unsubscribeAll() {
-    while(subscription = subscriptions.pop()) {
-      mediator.unsubscribe(subscription);
-    }
-  }
-
-  function gotoState(push, funcName) {
-    var args = [].slice.call(arguments, 1);
-
-    if (typeof push === "boolean") {
-      // Must take the push param off to get to funcName and then the remaining
-      // arguments.
-      args = [].slice.call(args, 1);
-    }
-    else {
-      funcName = push;
-      push = true;
-    }
-
-    if (push) {
-      pushState(funcName, args);
-    }
-
-    controller[funcName].apply(controller, args);
-  }
-
-  function pushState(funcName, args) {
-    // Remember the state and the information for the state in case we have to
-    // go back to it.
-    stateStack.push({
-      funcName: funcName,
-      args: args || []
-    });
-  }
-
-  // Used for when the current state is being cancelled and the user wishes to
-  // go to the previous state.
-  function popState(info) {
-    // Skip the first state, it is where the user is at now.
-    stateStack.pop();
-    var state = stateStack[stateStack.length - 1];
-
-    if (state) {
-      state.args[0] = state.args[0] || {};
-      _.extend(state.args[0], info);
-      controller[state.funcName].apply(controller, state.args);
-    }
-  }
-
-  function getCurrentState() {
-    return stateStack[stateStack.length - 1];
-  }
-
   function startStateMachine() {
     var self = this,
-        startState = gotoState.bind(self),
-        cancelState = popState.bind(self),
-        currentState = getCurrentState.bind(self);
-
-    function updateCurrentStateInfo(info) {
-      if(info) {
-        var args = currentState().args;
-        var stateInfo = args[0] = args[0] || {};
-        _.extend(stateInfo, info);
-      }
-    }
+        subscribe = self.subscribe.bind(self),
+        startState = function(save, msg, options) {
+          if(typeof save !== "boolean") {
+            options = msg;
+            msg = save;
+            save = true;
+          }
+
+          var func = controller[msg].bind(controller);
+          self.gotoState(save, func, options);
+        }
+        cancelState = self.popState.bind(self);
 
     subscribe("offline", function(msg, info) {
       startState("doOffline");
@@ -152,11 +93,11 @@
       addPrimaryUser = !!info.add;
       email = info.email;
 
-      updateCurrentStateInfo(info);
+      //updateCurrentStateInfo(info);
 
       var idInfo = storage.getEmail(email);
       if(idInfo && idInfo.cert) {
-        mediator.publish("primary_user_ready", info);
+        publish("primary_user_ready", info);
       }
       else {
         // We don't want to put the provisioning step on the stack, instead when
@@ -251,13 +192,12 @@
     });
 
     subscribe("authenticated", function(msg, info) {
-      mediator.publish("pick_email");
+      publish("pick_email");
     });
 
     subscribe("forgot_password", function(msg, info) {
       // forgot password initiates the forgotten password flow.
-      updateCurrentStateInfo(info);
-      startState("doForgotPassword", info);
+      startState(false, "doForgotPassword", info);
     });
 
     subscribe("reset_password", function(msg, info) {
@@ -294,11 +234,7 @@
 
   }
 
-  var StateMachine = BrowserID.Class({
-    init: function() {
-      // empty
-    },
-
+  var State = BrowserID.StateMachine.extend({
     start: function(options) {
       options = options || {};
 
@@ -307,15 +243,11 @@
         throw "start: controller must be specified";
       }
 
+      State.sc.start.call(this, options);
       startStateMachine.call(this);
-    },
-
-    stop: function() {
-      unsubscribeAll();
     }
   });
 
-
-  bid.StateMachine = StateMachine;
+  return State;
 }());
 
diff --git a/resources/static/shared/command.js b/resources/static/shared/command.js
new file mode 100644
index 000000000..b1857294e
--- /dev/null
+++ b/resources/static/shared/command.js
@@ -0,0 +1,30 @@
+/*globals BrowserID: true */
+/* 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/. */
+BrowserID.Command = (function() {
+  "use strict";
+
+  var bid = BrowserID;
+
+  var Command = bid.Class({
+    init: function(options) {
+      this.run_options = options.run_options || {};
+      if(!options.callback) {
+        throw "callback required";
+      }
+      this.callback = options.callback;
+    },
+
+    run: function() {
+      this.callback(this.run_options);
+    },
+
+    extendRunOptions: function(options) {
+      _.extend(this.run_options, options);
+    }
+  });
+
+  return Command;
+}());
+
diff --git a/resources/static/shared/history.js b/resources/static/shared/history.js
new file mode 100644
index 000000000..6cbaa68fe
--- /dev/null
+++ b/resources/static/shared/history.js
@@ -0,0 +1,47 @@
+/*globals BrowserID: true */
+/* 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/. */
+BrowserID.History = (function() {
+  "use strict";
+
+  var bid = BrowserID,
+      Command = bid.Command;
+
+  var History = bid.Class({
+    init: function() {
+      this.history = [];
+    },
+
+    destroy: function() {
+      this.history = null;
+    },
+
+    createState: function(callback, options) {
+      this.current = Command.create({
+        callback: callback,
+        run_options: options
+      });
+      return this.current;
+    },
+
+    getCurrent: function() {
+      return this.current;
+    },
+
+    saveState: function() {
+      this.history.push(this.current);
+    },
+
+    getTop: function() {
+      return this.history[this.history.length - 1];
+    },
+
+    popState: function() {
+      this.current = this.history.pop();
+      return this.current;
+    }
+  });
+
+  return History;
+}());
diff --git a/resources/static/shared/state_machine.js b/resources/static/shared/state_machine.js
new file mode 100644
index 000000000..6ed6ded26
--- /dev/null
+++ b/resources/static/shared/state_machine.js
@@ -0,0 +1,73 @@
+/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*globals BrowserID: true, _:true */
+/* 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/. */
+
+BrowserID.StateMachine = (function() {
+  "use strict";
+
+  var bid = BrowserID,
+      History = bid.History,
+      history,
+      mediator = bid.Mediator,
+      subscriptions = [],
+      saveLast;
+
+  var StateMachine = bid.Class({
+    init: function() {},
+
+    start: function(options) {
+      options = options || {};
+      history = options.history || History.create();
+    },
+
+    stop: function() {
+      var subscription;
+
+      while(subscription = subscriptions.pop()) {
+        mediator.unsubscribe(subscription);
+      }
+    },
+
+    destroy: function() {
+      this.stop();
+    },
+
+    subscribe: function(message, callback) {
+      subscriptions.push(mediator.subscribe(message, function(msg, info, rehydrate) {
+        if(rehydrate) {
+          var cmd = history.getCurrent();
+          if(cmd) cmd.extendRunOptions(rehydrate);
+        }
+        callback(msg, info);
+      }));
+    },
+
+    gotoState: function(save, callback, options) {
+      if (typeof save !== "boolean") {
+        options = callback;
+        callback = save;
+        save = true;
+      }
+
+      if(saveLast) {
+        history.saveState();
+      }
+
+      saveLast = save;
+
+      var cmd = history.createState(callback, options);
+      cmd.run();
+    },
+
+    popState: function() {
+      var cmd = history.popState();
+      if(cmd) {
+        cmd.run();
+      }
+    }
+  });
+
+  return StateMachine;
+}());
diff --git a/resources/static/test/cases/controllers/authenticate.js b/resources/static/test/cases/controllers/authenticate.js
index 5808bec5e..d23169fb3 100644
--- a/resources/static/test/cases/controllers/authenticate.js
+++ b/resources/static/test/cases/controllers/authenticate.js
@@ -129,7 +129,7 @@
     testAuthenticated();
   });
 
-  asyncTest("forgotPassword triggers forgot_password message", function() {
+  asyncTest("forgotPassword - trigger forgot_password message", function() {
     $("#email").val("registered@testuser.com");
 
     register("forgot_password", function(msg, info) {
diff --git a/resources/static/test/cases/resources/state_machine.js b/resources/static/test/cases/resources/state.js
similarity index 96%
rename from resources/static/test/cases/resources/state_machine.js
rename to resources/static/test/cases/resources/state.js
index 42c7dec92..01cb58ad6 100644
--- a/resources/static/test/cases/resources/state_machine.js
+++ b/resources/static/test/cases/resources/state.js
@@ -8,6 +8,7 @@
 
   var bid = BrowserID,
       mediator = bid.Mediator,
+      State = bid.State,
       machine,
       actions,
       storage = bid.Storage,
@@ -31,12 +32,12 @@
   }
 
   function createMachine() {
-    machine = bid.StateMachine.create();
+    machine = bid.State.create();
     actions = new ActionsMock();
     machine.start({controller: actions});
   }
 
-  module("resources/state_machine", {
+  module("resources/state", {
     setup: function() {
       testHelpers.setup();
       createMachine();
@@ -54,10 +55,15 @@
   });
 
   test("attempt to create a state machine without a controller", function() {
-    raises(function() {
-      var badmachine = bid.StateMachine.create();
+    var error;
+    try {
+      var badmachine = State.create();
       badmachine.start();
-    }, "start: controller must be specified", "creating a state machine without a controller fails");
+    }
+    catch(e) {
+      error = e;
+    }
+    equal(error, "start: controller must be specified", "creating a state machine without a controller fails");
   });
 
   test("offline does offline", function() {
@@ -152,7 +158,7 @@
     // screens back.  Do do this, we are simulating the steps necessary to get
     // to the reset_password flow.
     mediator.publish("authenticate");
-    mediator.publish("forgot_password", { email: "testuser@testuser.com" });
+    mediator.publish("forgot_password", undefined, { email: "testuser@testuser.com" });
     mediator.publish("reset_password");
     actions.info.doAuthenticate = {};
     mediator.publish("cancel_state");
diff --git a/resources/static/test/cases/shared/class.js b/resources/static/test/cases/shared/class.js
index 5a771a536..bf9306ac6 100644
--- a/resources/static/test/cases/shared/class.js
+++ b/resources/static/test/cases/shared/class.js
@@ -4,6 +4,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 (function() {
+  "use strict";
+
   module("shared/class", {
   });
 
diff --git a/resources/static/test/cases/shared/command.js b/resources/static/test/cases/shared/command.js
new file mode 100644
index 000000000..0fd003bb0
--- /dev/null
+++ b/resources/static/test/cases/shared/command.js
@@ -0,0 +1,48 @@
+/*jshint browsers:true, forin: true, laxbreak: true */
+/*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: true */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+(function() {
+  "use strict";
+
+  var bid = BrowserID,
+      testHelpers = bid.TestHelpers,
+      Command = bid.Command;
+
+  module("shared/command", {
+    setup: function() {
+      testHelpers.setup();
+    },
+
+    teardown: function() {
+      testHelpers.teardown();
+    }
+  });
+
+  asyncTest("run - run_options passed to callback", function() {
+    var cmd = Command.create({
+      callback: function(options) {
+        equal(options.item, "value", "correct options sent");
+        start();
+      },
+      run_options: {
+        item: "value"
+      }
+    });
+
+    cmd.run();
+  });
+
+  asyncTest("extendRunOptions, run - run_options extended, passed to callback", function() {
+    var cmd = Command.create({
+      callback: function(options) {
+        equal(options.item, "value", "correct options sent");
+        start();
+      }
+    });
+
+    cmd.extendRunOptions({ item: "value" });
+    cmd.run();
+  });
+}());
diff --git a/resources/static/test/cases/shared/history.js b/resources/static/test/cases/shared/history.js
new file mode 100644
index 000000000..7d29e4d33
--- /dev/null
+++ b/resources/static/test/cases/shared/history.js
@@ -0,0 +1,62 @@
+/*jshint browsers:true, forin: true, laxbreak: true */
+/*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: true */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+(function() {
+  "use strict";
+
+  var bid = BrowserID,
+      testHelpers = bid.TestHelpers,
+      Command = bid.Command,
+      History = bid.History,
+      history;
+
+  module("shared/history", {
+    setup: function() {
+      testHelpers.setup();
+      history = History.create();
+    },
+
+    teardown: function() {
+      testHelpers.teardown();
+      history.destroy();
+    }
+  });
+
+  test("createState, getCurrent - create a state, get the command", function() {
+    var cmd = history.createState(function() {});
+
+    var current = history.getCurrent();
+
+    strictEqual(cmd, current, "createState returns same item as getCurrent");
+  });
+
+  test("createState, saveState - save current state to the stack", function() {
+    var cmd = history.createState(function() {});
+
+    history.saveState();
+    var topState = history.getTop();
+
+    strictEqual(cmd, topState, "createState returns same item as getCurrent");
+  });
+
+  asyncTest("popState - remove item from stack, make it current", function() {
+      var cmd1 = history.createState(function() {
+        ok(true, "correct item run");
+        start();
+      });
+
+      history.saveState();
+
+      var cmd2 = history.createState(function() {
+        ok(false, "incorrect item run");
+        start();
+      });
+
+      history.popState();
+      var current = history.getCurrent();
+      current.run();
+  });
+
+}());
diff --git a/resources/static/test/cases/shared/state_machine.js b/resources/static/test/cases/shared/state_machine.js
new file mode 100644
index 000000000..38ebfe5a8
--- /dev/null
+++ b/resources/static/test/cases/shared/state_machine.js
@@ -0,0 +1,96 @@
+/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*globals BrowserID: true, _:true */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+(function() {
+  "use strict";
+
+  var bid = BrowserID,
+      testHelpers = bid.TestHelpers,
+      StateMachine = bid.StateMachine,
+      stateMachine,
+      mediator = bid.Mediator;
+
+  module("shared/state_machine", {
+    setup: function() {
+      testHelpers.setup();
+
+      stateMachine = StateMachine.create();
+      stateMachine.start();
+    },
+    teardown: function() {
+      stateMachine.destroy();
+      testHelpers.teardown();
+    }
+  });
+
+  asyncTest("gotoState with implied save - call callback, state not saved until next gotoState", function() {
+    var called;
+    stateMachine.gotoState(function(options) {
+      ok(true, "callback called");
+      start();
+    });
+  });
+
+  asyncTest("multiple gotoState, popState - takes state off stack and runs it", function() {
+    var active;
+    stateMachine.gotoState(function(options) {
+      if(active) {
+        ok(true, "callback called");
+        start();
+      }
+    });
+    stateMachine.gotoState(function() {});
+
+    active = true;
+    stateMachine.popState();
+  });
+
+  asyncTest("gotoState with explicit save=false - do not save to stack", function() {
+    var active;
+    stateMachine.gotoState(function(options) {
+      if(active) {
+        ok(true, "callback called");
+        start();
+      }
+    });
+    // This will not go on the stack when the next state is added.
+    stateMachine.gotoState(false, function() {});
+
+    // This will never go on the stack.
+    stateMachine.gotoState(function() {});
+
+    active = true;
+    stateMachine.popState();
+  });
+
+  asyncTest("subscribe/publish - subscribe to the mediator, 3rd parameter to publish passed to handler's info", function() {
+    // set up state that will simulate a state being started.
+    var active = false;
+    stateMachine.gotoState(function(info) {
+      if(active) {
+        equal(info.item, "value", "correct info passed to handler");
+        start();
+      }
+    });
+
+    stateMachine.subscribe("message", function(msg, info) {
+      equal(info.item2, "value2", "correct info passed to message");
+
+      // Start a new state;
+      stateMachine.gotoState(function(){
+        active = true;
+        // this should cause the first state to run with the updated info.
+        stateMachine.popState();
+      }, info);
+
+    });
+
+    // simulate a message from inside the previous state that signals a new
+    // state starting.
+    mediator.publish("message", { item2: "value2" }, { item: "value" });
+  });
+
+
+}());
diff --git a/resources/views/dialog_layout.ejs b/resources/views/dialog_layout.ejs
index a6be27e5e..3d779ae93 100644
--- a/resources/views/dialog_layout.ejs
+++ b/resources/views/dialog_layout.ejs
@@ -86,6 +86,9 @@
           <script src="/shared/network.js"></script>
           <script src="/shared/provisioning.js"></script>
           <script src="/shared/user.js"></script>
+          <script src="/shared/command.js"></script>
+          <script src="/shared/history.js"></script>
+          <script src="/shared/state_machine.js"></script>
 
           <script src="/shared/modules/page_module.js"></script>
           <script src="/shared/modules/xhr_delay.js"></script>
@@ -95,7 +98,7 @@
 
           <script src="/dialog/resources/internal_api.js"></script>
           <script src="/dialog/resources/helpers.js"></script>
-          <script src="/dialog/resources/state_machine.js"></script>
+          <script src="/dialog/resources/state.js"></script>
 
           <script src="/dialog/controllers/actions.js"></script>
           <script src="/dialog/controllers/dialog.js"></script>
diff --git a/resources/views/test.ejs b/resources/views/test.ejs
index 2f59bbc53..5907eba36 100644
--- a/resources/views/test.ejs
+++ b/resources/views/test.ejs
@@ -69,7 +69,7 @@
     <script src="/i18n/en_US/client.json"></script>
     <script src="/shared/gettext.js"></script>
     <script src="/lib/vepbundle.js"></script>
-    <script src="http://testmob.org/scripts/reporter.js"></script>
+    <!--script src="http://testmob.org/scripts/reporter.js"></script-->
     <script src="/shared/browserid.js"></script>
     <script src="/lib/dom-jquery.js"></script>
     <script src="/lib/hub.js"></script>
@@ -99,6 +99,9 @@
     <script src="/shared/network.js"></script>
     <script src="/shared/provisioning.js"></script>
     <script src="/shared/user.js"></script>
+    <script src="/shared/command.js"></script>
+    <script src="/shared/history.js"></script>
+    <script src="/shared/state_machine.js"></script>
 
     <script src="/shared/modules/page_module.js"></script>
     <script src="/shared/modules/xhr_delay.js"></script>
@@ -108,7 +111,7 @@
 
     <script src="/dialog/resources/internal_api.js"></script>
     <script src="/dialog/resources/helpers.js"></script>
-    <script src="/dialog/resources/state_machine.js"></script>
+    <script src="/dialog/resources/state.js"></script>
 
     <script src="/dialog/controllers/actions.js"></script>
     <script src="/dialog/controllers/pick_email.js"></script>
@@ -146,6 +149,9 @@
     <script src="cases/shared/xhr.js"></script>
     <script src="cases/shared/network.js"></script>
     <script src="cases/shared/user.js"></script>
+    <script src="cases/shared/command.js"></script>
+    <script src="cases/shared/history.js"></script>
+    <script src="cases/shared/state_machine.js"></script>
 
     <script src="cases/shared/modules/page_module.js"></script>
     <script src="cases/shared/modules/xhr_delay.js"></script>
@@ -164,7 +170,7 @@
 
     <script src="cases/resources/internal_api.js"></script>
     <script src="cases/resources/helpers.js"></script>
-    <script src="cases/resources/state_machine.js"></script>
+    <script src="cases/resources/state.js"></script>
 
     <script src="cases/controllers/actions.js"></script>
     <script src="cases/controllers/pick_email.js"></script>
diff --git a/scripts/compress.sh b/scripts/compress.sh
index 45b644df8..eacb37f92 100755
--- a/scripts/compress.sh
+++ b/scripts/compress.sh
@@ -63,11 +63,10 @@ echo "generating for the following locales:"
 echo $locales
 
 for locale in $locales; do
-    mkdir -p $BUILD_PATH/$locale
     mkdir -p $BUILD_PATH/../i18n/$locale
     # Touch as the trigger locale doesn't really exist
     touch $BUILD_PATH/../i18n/${locale}/client.json
-    cat lib/jquery-1.7.1.min.js lib/winchan.js lib/underscore-min.js lib/vepbundle.js lib/ejs.js shared/javascript-extensions.js i18n/${locale}/client.json shared/gettext.js shared/browserid.js lib/hub.js lib/dom-jquery.js lib/module.js lib/jschannel.js $BUILD_PATH/templates.js shared/renderer.js shared/class.js shared/mediator.js shared/tooltip.js shared/validation.js shared/helpers.js shared/screens.js shared/browser-support.js shared/wait-messages.js shared/error-messages.js shared/error-display.js shared/storage.js shared/xhr.js shared/network.js shared/provisioning.js shared/user.js shared/modules/page_module.js shared/modules/xhr_delay.js shared/modules/xhr_disable_form.js shared/modules/code_check.js shared/modules/cookie_check.js dialog/resources/internal_api.js dialog/resources/helpers.js dialog/resources/state_machine.js dialog/controllers/actions.js dialog/controllers/dialog.js dialog/controllers/authenticate.js dialog/controllers/forgot_password.js dialog/controllers/check_registration.js dialog/controllers/pick_email.js dialog/controllers/add_email.js dialog/controllers/required_email.js dialog/controllers/verify_primary_user.js dialog/controllers/provision_primary_user.js dialog/controllers/primary_user_provisioned.js dialog/controllers/email_chosen.js dialog/start.js > $BUILD_PATH/$locale/dialog.uncompressed.js
+    cat lib/jquery-1.7.1.min.js lib/winchan.js lib/underscore-min.js lib/vepbundle.js lib/ejs.js shared/javascript-extensions.js i18n/${locale}/client.json shared/gettext.js shared/browserid.js lib/hub.js lib/dom-jquery.js lib/module.js lib/jschannel.js $BUILD_PATH/templates.js shared/renderer.js shared/class.js shared/mediator.js shared/tooltip.js shared/validation.js shared/helpers.js shared/screens.js shared/browser-support.js shared/wait-messages.js shared/error-messages.js shared/error-display.js shared/storage.js shared/xhr.js shared/network.js shared/provisioning.js shared/user.js shared/command.js shared/history.js shared/state_machine.js shared/modules/page_module.js shared/modules/xhr_delay.js shared/modules/xhr_disable_form.js shared/modules/code_check.js shared/modules/cookie_check.js dialog/resources/internal_api.js dialog/resources/helpers.js dialog/resources/state.js dialog/controllers/actions.js dialog/controllers/dialog.js dialog/controllers/authenticate.js dialog/controllers/forgot_password.js dialog/controllers/check_registration.js dialog/controllers/pick_email.js dialog/controllers/add_email.js dialog/controllers/required_email.js dialog/controllers/verify_primary_user.js dialog/controllers/provision_primary_user.js dialog/controllers/primary_user_provisioned.js dialog/controllers/email_chosen.js dialog/start.js > $BUILD_PATH/$locale/dialog.uncompressed.js
 done
 
 # produce the dialog css
-- 
GitLab