diff --git a/lib/browser/application.js b/lib/browser/application.js index e480554..fe977a0 100644 --- a/lib/browser/application.js +++ b/lib/browser/application.js @@ -1,27 +1,16 @@ -/* global APP_NAMESPACE */ -'use strict'; - /** - * A module for registering an application. It creates a property - * on global.NS using the application name, and exposes a render - * method on an object there. + * A module for registering an application. It creates a property on global.NS + * using the application name, and exposes a render method on an object there. */ +'use strict'; var NS = require('./namespace'); module.exports = function (name, config) { - if (NS.hasOwnProperty(name)) { - throw new Error( - 'Cannot register ' + name + - ' because a property with that name already exists' + - ' on window.' + APP_NAMESPACE); - } - - NS[name] = new Application(config); + NS.registerProperty(name, new Application(config)); return NS[name]; }; - function Application (config) { this.config = config; this._renderQueue = []; diff --git a/lib/browser/kernel.js b/lib/browser/kernel.js index 09914e9..30e0165 100644 --- a/lib/browser/kernel.js +++ b/lib/browser/kernel.js @@ -1,19 +1,16 @@ -/* global APP_NAMESPACE */ 'use strict'; /** * Entry point for scout files. This module sets up the namespace, requires - * all application modules, and finally exports the namespace on the global - * object. + * all application modules, and finally exports the namespace. * * @module */ var NS = require('./namespace'); -var global = require('./global'); // begin application module require blocks /* APP_MODULES */ // end application module require blocks -module.exports = global[APP_NAMESPACE] = NS; +module.exports = NS; diff --git a/lib/browser/namespace.js b/lib/browser/namespace.js index 872fb22..cd486a1 100644 --- a/lib/browser/namespace.js +++ b/lib/browser/namespace.js @@ -1,13 +1,13 @@ /* global APP_NAMESPACE */ - /** - * Define the shared namespace; if there is already an object on - * window at the namespace property, use it. + * Define the namespace name with a flag (APP_NAMESPACE) that will be replaced + * with a string in the webpack build. + * + * APP_NAMESPACE will be replaced with the value passed to webpack + * options.namespace or the default namespace name. */ 'use strict'; -var global = require('./global'); -var NS = typeof global[APP_NAMESPACE] === 'object' ? - global[APP_NAMESPACE] : {}; +var namespacer = require('./namespacer'); -module.exports = NS; +module.exports = namespacer.namespace(APP_NAMESPACE); diff --git a/lib/browser/namespacer.js b/lib/browser/namespacer.js new file mode 100644 index 0000000..48402f2 --- /dev/null +++ b/lib/browser/namespacer.js @@ -0,0 +1,83 @@ +'use strict'; + +/** + * A namespace generator. + * + * @module + */ + +var global = require('./global'); + +/** + * A namespace. + * + * @class + */ +function Namespace (name) { + this.name = name; +} + +/** + * Register a property in the namespace, such as an application instance. + * + * Attempting to register the same property twice is almost always an error, and + * thus results in a thrown exception. + * + * @param {String} name The property name. + * @param {Mixed} value The property value. + */ +Namespace.prototype.registerProperty = function (name, value) { + if (this.hasOwnProperty(name)) { + throw new Error( + 'Cannot register ' + name + + ' because a property with that name already exists on window.' + + this.name + ); + } + + this[name] = value; + return this; +}; + +module.exports = { + /** + * Obtain a namespace object, creating one if necessary. + * + * The namespace will be assigned to the global (window) object as a property + * with the provided name. + * + * @param {String} name The namespace name. + * @return {Mixed} The namespace. + */ + namespace: function (name) { + // There is no value yet assigned for this namespace. + if (global[name] === undefined) { + global[name] = new Namespace(name); + } + // A namespace object already exists, which may or may not be a Namespace + // instance. + else if (typeof global[name] === 'object') { + // If we have an existing object literal then give it the necessary + // decorations and functions that a Namespace instance would have. + // + // Other code may have a reference to the object, so we can't do anything + // more drastic such as replacing it. + if (!(global[name] instanceof Namespace)) { + Namespace.call(global[name], name); + for (var prop in Namespace.prototype) { + global[name][prop] = Namespace.prototype[prop]; + } + } + } + // Otherwise other code is using this global name for another purpose, so + // throw. + else { + throw new Error( + 'Namespace ' + name + ' cannot be created.' + + ' A non-object variable is already assigned to window.' + name + ); + } + + return global[name]; + } +};