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