From f999881008dff44a0292482092cdd2a8b6509d88 Mon Sep 17 00:00:00 2001 From: Shane Tomlinson <stomlinson@mozilla.com> Date: Mon, 20 Aug 2012 10:33:26 +0100 Subject: [PATCH] Split up the PageModule into Module, DOMModule and PageModule. * Increase coherency for each of the three modules --- lib/static_resources.js | 2 + .../static/common/js/modules/dom_module.js | 83 +++++++++++++ resources/static/common/js/modules/module.js | 80 ++++++++++++ .../static/common/js/modules/page_module.js | 114 +++--------------- .../cases/common/js/modules/dom_module.js | 75 ++++++++++++ .../test/cases/common/js/modules/module.js | 95 +++++++++++++++ .../cases/common/js/modules/page_module.js | 85 ------------- resources/views/test.ejs | 5 +- 8 files changed, 353 insertions(+), 186 deletions(-) create mode 100644 resources/static/common/js/modules/dom_module.js create mode 100644 resources/static/common/js/modules/module.js create mode 100644 resources/static/test/cases/common/js/modules/dom_module.js create mode 100644 resources/static/test/cases/common/js/modules/module.js diff --git a/lib/static_resources.js b/lib/static_resources.js index 1a1845561..72d9ea339 100644 --- a/lib/static_resources.js +++ b/lib/static_resources.js @@ -51,6 +51,8 @@ var common_js = [ '/common/js/network.js', '/common/js/provisioning.js', '/common/js/user.js', + '/common/js/modules/module.js', + '/common/js/modules/dom_module.js', '/common/js/modules/page_module.js', '/common/js/modules/xhr_delay.js', '/common/js/modules/xhr_disable_form.js', diff --git a/resources/static/common/js/modules/dom_module.js b/resources/static/common/js/modules/dom_module.js new file mode 100644 index 000000000..d3d94dbff --- /dev/null +++ b/resources/static/common/js/modules/dom_module.js @@ -0,0 +1,83 @@ +/*jshint browser:true, jquery: true, forin: true, laxbreak:true */ +/*global 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.Modules.DOMModule = (function() { +"use strict"; + + var bid = BrowserID, + dom = bid.DOM, + helpers = bid.Helpers, + cancelEvent = helpers.cancelEvent, + mediator = bid.Mediator, + sc; + + /* + * DOMModule provides modules DOM related functionality. + */ + + var Module = bid.Modules.Module.extend({ + init: function(options) { + sc.init.call(this, options); + + this.domEvents = []; + }, + + stop: function() { + this.unbindAll(); + sc.stop.call(this); + }, + + /** + * Bind a dom event + * @method bind + * @param {string} target - css selector + * @param {string} type - event type + * @param {function} callback + * @param {object} [context] - optional context, if not given, use this. + */ + bind: function(target, type, callback, context) { + var self=this, + cb = callback.bind(context || this); + + dom.bindEvent(target, type, cb); + + self.domEvents.push({ + target: target, + type: type, + cb: cb + }); + }, + + /** + * Shortcut to bind a click handler + * @method click + * @param {string} + * @param {function} callback + * @param {object} [context] - optional context, if not given, use this. + */ + click: function(target, callback, context) { + this.bind(target, "click", cancelEvent(callback), context); + }, + + /** + * Unbind all DOM event handlers + * @method unbindAll + */ + unbindAll: function() { + var self=this, + evt; + + while(evt = self.domEvents.pop()) { + dom.unbindEvent(evt.target, evt.type, evt.cb); + } + } + + }); + + sc = Module.sc; + + return Module; + +}()); diff --git a/resources/static/common/js/modules/module.js b/resources/static/common/js/modules/module.js new file mode 100644 index 000000000..7f67e17b4 --- /dev/null +++ b/resources/static/common/js/modules/module.js @@ -0,0 +1,80 @@ +/*jshint browser:true, jquery: true, forin: true, laxbreak:true */ +/*global 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.Modules = BrowserID.Modules || {}; +BrowserID.Modules.Module = (function() { +"use strict"; + + var bid = BrowserID, + mediator = bid.Mediator; + + /* + * Module is the root of all modules. Provides basic pub/sub mechanisms, + * ability to start and stop, check for required arguments, etc. + */ + + var Module = BrowserID.Class({ + init: function(options) { + this.subscriptions = []; + }, + + checkRequired: function(options) { + var list = [].slice.call(arguments, 1); + for(var item, index = 0; item = list[index]; ++index) { + if(!options.hasOwnProperty(item)) { + throw "missing config option: " + item; + } + } + }, + + start: function(options) { + var self=this; + self.options = options || {}; + }, + + stop: function() { + _.each(this.subscriptions, mediator.unsubscribe); + this.subscriptions = []; + }, + + destroy: function() { + this.stop(); + }, + + /** + * Publish a message to the mediator. + * @method publish + * @param {string} message + * @param {object} data + */ + publish: mediator.publish.bind(mediator), + + /** + * Subscribe to a message on the mediator. + * @method subscribe + * @param {string} message + * @param {function} callback + * @param {object} [context] - context, if not given, use this. + */ + subscribe: function(message, callback, context) { + var id = mediator.subscribe(message, callback, context || this); + this.subscriptions.push(id); + }, + + /** + * Subscribe to all messages on the mediator. + * @method subscribeAll + * @param {function} callback + * @param {object} [context] - context, if not given, use this. + */ + subscribeAll: function(callback, context) { + var id = mediator.subscribeAll(callback, context || this); + this.subscriptions.push(id); + } + }); + + return Module; + +}()); diff --git a/resources/static/common/js/modules/page_module.js b/resources/static/common/js/modules/page_module.js index abf39c126..52deb2332 100644 --- a/resources/static/common/js/modules/page_module.js +++ b/resources/static/common/js/modules/page_module.js @@ -3,17 +3,20 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -BrowserID.Modules = BrowserID.Modules || {}; BrowserID.Modules.PageModule = (function() { "use strict"; - var ANIMATION_TIME = 250, - bid = BrowserID, + /* + * PageModule provides functionality for screens on + * either the main site or in the dialog. + */ + + var bid = BrowserID, dom = bid.DOM, screens = bid.Screens, helpers = bid.Helpers, cancelEvent = helpers.cancelEvent, - mediator = bid.Mediator; + sc; function onSubmit() { if (!dom.hasClass("body", "submit_disabled") && this.validate()) { @@ -36,80 +39,18 @@ BrowserID.Modules.PageModule = (function() { screen.hide(); } - var Module = BrowserID.Class({ - init: function(options) { - options = options || {}; - - var self=this; - - self.domEvents = []; - }, - - checkRequired: function(options) { - var list = [].slice.call(arguments, 1); - for(var item, index = 0; item = list[index]; ++index) { - if(!options.hasOwnProperty(item)) { - throw "missing config option: " + item; - } - } - }, - + var Module = bid.Modules.DOMModule.extend({ start: function(options) { var self=this; - self.options = options || {}; + + sc.start.call(self, options); self.bind("form", "submit", cancelEvent(onSubmit)); }, stop: function() { - this.unbindAll(); - dom.removeClass("body", "waiting"); - }, - - destroy: function() { - this.stop(); - }, - - /** - * Bind a dom event - * @method bind - * @param {string} target - css selector - * @param {string} type - event type - * @param {function} callback - * @param {object} [context] - optional context, if not given, use this. - */ - bind: function(target, type, callback, context) { - var self=this, - cb = callback.bind(context || this); - - dom.bindEvent(target, type, cb); - - self.domEvents.push({ - target: target, - type: type, - cb: cb - }); - }, - - /** - * Shortcut to bind a click handler - * @method click - * @param {string} - * @param {function} callback - * @param {object} [context] - optional context, if not given, use this. - */ - click: function(target, callback, context) { - this.bind(target, "click", cancelEvent(callback), context); - }, - - unbindAll: function() { - var self=this, - evt; - - while(evt = self.domEvents.pop()) { - dom.unbindEvent(evt.target, evt.type, evt.cb); - } + sc.stop.call(this); }, renderDialog: function(template, data) { @@ -155,7 +96,7 @@ BrowserID.Modules.PageModule = (function() { submit: function() { }, - // XXX maybe we should not get rid of this. + // XXX maybe we should get rid of this. close: function(message) { this.destroy(); if (message) { @@ -163,35 +104,6 @@ BrowserID.Modules.PageModule = (function() { } }, - /** - * Publish a message to the mediator. - * @method publish - * @param {string} message - * @param {object} data - */ - publish: mediator.publish.bind(mediator), - - /** - * Subscribe to a message on the mediator. - * @method subscribe - * @param {string} message - * @param {function} callback - * @param {object} [context] - context, if not given, use this. - */ - subscribe: function(message, callback, context) { - mediator.subscribe(message, callback, context || this); - }, - - /** - * Subscribe to all messages on the mediator. - * @method subscribeAll - * @param {function} callback - * @param {object} [context] - context, if not given, use this. - */ - subscribeAll: function(callback, context) { - mediator.subscribeAll(callback, context || this); - }, - /** * Get a curried function to an error dialog. * @method getErrorDialog @@ -215,6 +127,8 @@ BrowserID.Modules.PageModule = (function() { // END TESTING API }); + sc = Module.sc; + return Module; }()); diff --git a/resources/static/test/cases/common/js/modules/dom_module.js b/resources/static/test/cases/common/js/modules/dom_module.js new file mode 100644 index 000000000..9adc40a53 --- /dev/null +++ b/resources/static/test/cases/common/js/modules/dom_module.js @@ -0,0 +1,75 @@ +/*jshint browser: true, forin: true, laxbreak: true */ +/*global test: true, start: true, stop: 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 controller, el, + bid = BrowserID, + mediator = bid.Mediator; + + function createController(options) { + controller = bid.Modules.DOMModule.create(options); + controller.start(); + } + + module("common/js/modules/dom_module", { + setup: function() { + bid.TestHelpers.setup(); + }, + + teardown: function() { + controller.destroy(); + bid.TestHelpers.teardown(); + } + }); + + asyncTest("bind DOM Events", function() { + createController(); + + controller.bind("body", "click", function(event) { + event.preventDefault(); + + strictEqual(this, controller, "context is correct"); + start(); + }); + + $("body").trigger("click"); + }); + + asyncTest("click - bind a click handler, handler does not get event", function() { + createController(); + + controller.click("body", function(event) { + equal(typeof event, "undefined", "event is undefined"); + strictEqual(this, controller, "context is correct"); + start(); + }); + + $("body").trigger("click"); + }); + + asyncTest("unbindAll removes all listeners", function() { + createController(); + var listenerCalled = false; + + controller.bind("body", "click", function(event) { + event.preventDefault(); + + listenerCalled = true; + }); + + controller.unbindAll(); + + $("body").trigger("click"); + + setTimeout(function() { + equal(listenerCalled, false, "all events are unbound, listener should not be called"); + start(); + }, 1); + }); + +}()); + diff --git a/resources/static/test/cases/common/js/modules/module.js b/resources/static/test/cases/common/js/modules/module.js new file mode 100644 index 000000000..e26e67a8b --- /dev/null +++ b/resources/static/test/cases/common/js/modules/module.js @@ -0,0 +1,95 @@ +/*jshint browser: true, forin: true, laxbreak: true */ +/*global test: true, start: true, stop: true, module: true, ok: true, equal: true, strictEqual: 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 controller, + bid = BrowserID, + mediator = bid.Mediator; + + function createController(options) { + controller = bid.Modules.Module.create(options); + controller.start(); + } + + module("common/js/modules/module", { + setup: function() { + bid.TestHelpers.setup(); + }, + + teardown: function() { + controller.destroy(); + bid.TestHelpers.teardown(); + } + }); + + test("subscribe/stop - listens to messages from the mediator. All bindings are removed whenever stop is called", function() { + createController(); + var count = 0; + controller.subscribe("message", function(msg, data) { + strictEqual(this, controller, "context set to the controller"); + equal(msg, "message", "correct message passed"); + equal(data.field, 1, "correct data passed"); + count++; + }); + + mediator.publish("message", { field: 1 }); + equal(1, count, "subscriber called"); + + // Subscriptions should be removed on stop + controller.stop(); + mediator.publish("after_stop"); + equal(1, count, "subscriptions are removed on stop"); + }); + + test("subscribeAll - listen for ALL messages", function() { + createController(); + var count = 0; + controller.subscribeAll(function(msg, data) { + count++; + }); + + var messages = ["message1", "message2", "message3"]; + _.each(messages, mediator.publish); + + equal(count, messages.length, "subscriber called for all messages"); + + // Subscriptions should be removed on stop + controller.stop(); + mediator.publish("after_stop"); + equal(count, messages.length, "subscriptions are removed on stop"); + }); + + asyncTest("publish - publish messages to the mediator", function() { + createController(); + + mediator.subscribe("message", function(msg, data) { + equal(msg, "message", "message is correct"); + equal(data.field, 1, "data passed correctly"); + start(); + }); + + controller.publish("message", { + field: 1 + }); + }); + + test("checkRequired", function() { + createController(); + + var error; + try { + controller.checkRequired({}, "requiredField"); + } + catch(e) { + error = e; + } + + equal(error, "missing config option: requiredField"); + }); + +}()); + diff --git a/resources/static/test/cases/common/js/modules/page_module.js b/resources/static/test/cases/common/js/modules/page_module.js index 37f5f8bbc..26722c2da 100644 --- a/resources/static/test/cases/common/js/modules/page_module.js +++ b/resources/static/test/cases/common/js/modules/page_module.js @@ -96,91 +96,6 @@ func(); }); - asyncTest("bind DOM Events", function() { - createController(); - - controller.bind("body", "click", function(event) { - event.preventDefault(); - - strictEqual(this, controller, "context is correct"); - start(); - }); - - $("body").trigger("click"); - }); - - asyncTest("click - bind a click handler, handler does not get event", function() { - createController(); - - controller.click("body", function(event) { - equal(typeof event, "undefined", "event is undefined"); - strictEqual(this, controller, "context is correct"); - start(); - }); - - $("body").trigger("click"); - }); - - asyncTest("unbindAll removes all listeners", function() { - createController(); - var listenerCalled = false; - - controller.bind("body", "click", function(event) { - event.preventDefault(); - - listenerCalled = true; - }); - - controller.unbindAll(); - - $("body").trigger("click"); - - setTimeout(function() { - equal(listenerCalled, false, "all events are unbound, listener should not be called"); - start(); - }, 1); - }); - - asyncTest("subscribe - listens to messages from the mediator", function() { - createController(); - controller.subscribe("message", function(msg, data) { - strictEqual(this, controller, "context set to the controller"); - equal(msg, "message", "correct message passed"); - equal(data.field, 1, "correct data passed"); - start(); - }); - - mediator.publish("message", { field: 1 }); - }); - - asyncTest("publish - publish messages to the mediator", function() { - createController(); - - mediator.subscribe("message", function(msg, data) { - equal(msg, "message", "message is correct"); - equal(data.field, 1, "data passed correctly"); - start(); - }); - - controller.publish("message", { - field: 1 - }); - }); - - test("checkRequired", function() { - createController(); - - var error; - try { - controller.checkRequired({}, "requiredField"); - } - catch(e) { - error = e; - } - - equal(error, "missing config option: requiredField"); - }); - test("form is not submitted when 'submit_disabled' class is added to body", function() { createController(); diff --git a/resources/views/test.ejs b/resources/views/test.ejs index 8daa136d4..01012a801 100644 --- a/resources/views/test.ejs +++ b/resources/views/test.ejs @@ -109,10 +109,11 @@ <script src="/common/js/command.js"></script> <script src="/common/js/history.js"></script> <script src="/common/js/state_machine.js"></script> - <script src="/common/js/models/models.js"></script> <script src="/common/js/models/interaction_data.js"></script> + <script src="/common/js/modules/module.js"></script> + <script src="/common/js/modules/dom_module.js"></script> <script src="/common/js/modules/page_module.js"></script> <script src="/common/js/modules/xhr_delay.js"></script> <script src="/common/js/modules/xhr_disable_form.js"></script> @@ -168,6 +169,8 @@ <script src="cases/common/js/models/interaction_data.js"></script> + <script src="cases/common/js/modules/module.js"></script> + <script src="cases/common/js/modules/dom_module.js"></script> <script src="cases/common/js/modules/page_module.js"></script> <script src="cases/common/js/modules/xhr_delay.js"></script> <script src="cases/common/js/modules/xhr_disable_form.js"></script> -- GitLab