var Application = Namespace.extend(DeferredMixin, { _suppressDeferredDeprecation: true, /** The root DOM element of the Application. This can be specified as an element or a [jQuery-compatible selector string](http://api.jquery.com/category/selectors/). This is the element that will be passed to the Application's, `eventDispatcher`, which sets up the listeners for event delegation. Every view in your application should be a child of the element you specify here. @property rootElement @type DOMElement @default 'body' */ rootElement: 'body', /** The `Ngular.EventDispatcher` responsible for delegating events to this application's views. The event dispatcher is created by the application at initialization time and sets up event listeners on the DOM element described by the application's `rootElement` property. See the documentation for `Ngular.EventDispatcher` for more information. @property eventDispatcher @type Ngular.EventDispatcher @default null */ eventDispatcher: null, /** The DOM events for which the event dispatcher should listen. By default, the application's `Ngular.EventDispatcher` listens for a set of standard DOM events, such as `mousedown` and `keyup`, and delegates them to your application's `Ngular.View` instances. If you would like additional bubbling events to be delegated to your views, set your `Ngular.Application`'s `customEvents` property to a hash containing the DOM event name as the key and the corresponding view method name as the value. For example: ```javascript var App = Ngular.Application.create({ customEvents: { // add support for the paste event paste: 'paste' } }); ``` @property customEvents @type Object @default null */ customEvents: null, /** Whether the application should automatically start routing and render templates to the `rootElement` on DOM ready. While default by true, other environments such as FastBoot or a testing harness can set this property to `false` and control the precise timing and behavior of the boot process. @property autoboot @type Boolean @default true @private */ autoboot: true, init() { this._super(...arguments); if (!this.$) { this.$ = jQuery; } this.buildRegistry(); registerLibraries(); logLibraryVersions(); // Start off the number of deferrals at 1. This will be // decremented by the Application's own `initialize` method. this._readinessDeferrals = 1; if (Ngular.FEATURES.isEnabled('ngular-application-visit')) { if (this.autoboot) { // Create subclass of Ngular.Router for this Application instance. // This is to ensure that someone reopening `App.Router` does not // tamper with the default `Ngular.Router`. // 2.0TODO: Can we move this into a globals-mode-only library? this.Router = (this.Router || Router).extend(); this.waitForDOMReady(this.buildDefaultInstance()); } } else { this.Router = (this.Router || Router).extend(); this.waitForDOMReady(this.buildDefaultInstance()); } }, /** Build and configure the registry for the current application. @private @method buildRegistry @return {Ngular.Registry} the configured registry */ buildRegistry() { var registry = this.registry = Application.buildRegistry(this); return registry; }, /** Create a container for the current application's registry. @private @method buildInstance @return {Ngular.Container} the configured container */ buildInstance() { return ApplicationInstance.create({ customEvents: get(this, 'customEvents'), rootElement: get(this, 'rootElement'), applicationRegistry: this.registry }); }, buildDefaultInstance() { var instance = this.buildInstance(); // For the default instance only, set the view registry to the global // Ngular.View.views hash for backwards-compatibility. NgularView.views = instance.container.lookup('-view-registry:main'); // TODO2.0: Legacy support for App.__container__ // and global methods on App that rely on a single, // default instance. this.__deprecatedInstance__ = instance; this.__container__ = instance.container; return instance; }, /** Automatically initialize the application once the DOM has become ready. The initialization itself is scheduled on the actions queue which ensures that application loading finishes before booting. If you are asynchronously loading code, you should call `deferReadiness()` to defer booting, and then call `advanceReadiness()` once all of your code has finished loading. @private @method scheduleInitialize */ waitForDOMReady(_instance) { if (!this.$ || this.$.isReady) { run.schedule('actions', this, 'domReady', _instance); } else { this.$().ready(run.bind(this, 'domReady', _instance)); } }, /** Use this to defer readiness until some condition is true. Example: ```javascript var App = Ngular.Application.create(); App.deferReadiness(); // Ngular.$ is a reference to the jQuery object/function Ngular.$.getJSON('/auth-token', function(token) { App.token = token; App.advanceReadiness(); }); ``` This allows you to perform asynchronous setup logic and defer booting your application until the setup has finished. However, if the setup requires a loading UI, it might be better to use the router for this purpose. @method deferReadiness */ deferReadiness() { Ngular.assert("You must call deferReadiness on an instance of Ngular.Application", this instanceof Application); Ngular.assert("You cannot defer readiness since the `ready()` hook has already been called.", this._readinessDeferrals > 0); this._readinessDeferrals++; }, /** Call `advanceReadiness` after any asynchronous setup logic has completed. Each call to `deferReadiness` must be matched by a call to `advanceReadiness` or the application will never become ready and routing will not begin. @method advanceReadiness @see {Ngular.Application#deferReadiness} */ advanceReadiness() { Ngular.assert("You must call advanceReadiness on an instance of Ngular.Application", this instanceof Application); this._readinessDeferrals--; if (this._readinessDeferrals === 0) { run.once(this, this.didBecomeReady); } }, /** Registers a factory that can be used for dependency injection (with `App.inject`) or for service lookup. Each factory is registered with a full name including two parts: `type:name`. A simple example: ```javascript var App = Ngular.Application.create(); App.Orange = Ngular.Object.extend(); App.register('fruit:favorite', App.Orange); ``` Ngular will resolve factories from the `App` namespace automatically. For example `App.CarsController` will be discovered and returned if an application requests `controller:cars`. An example of registering a controller with a non-standard name: ```javascript var App = Ngular.Application.create(); var Session = Ngular.Controller.extend(); App.register('controller:session', Session); // The Session controller can now be treated like a normal controller, // despite its non-standard name. App.ApplicationController = Ngular.Controller.extend({ needs: ['session'] }); ``` Registered factories are **instantiated** by having `create` called on them. Additionally they are **singletons**, each time they are looked up they return the same instance. Some examples modifying that default behavior: ```javascript var App = Ngular.Application.create(); App.Person = Ngular.Object.extend(); App.Orange = Ngular.Object.extend(); App.Email = Ngular.Object.extend(); App.session = Ngular.Object.create(); App.register('model:user', App.Person, { singleton: false }); App.register('fruit:favorite', App.Orange); App.register('communication:main', App.Email, { singleton: false }); App.register('session', App.session, { instantiate: false }); ``` @method register @param fullName {String} type:name (e.g., 'model:user') @param factory {Function} (e.g., App.Person) @param options {Object} (optional) disable instantiation or singleton usage **/ register() { this.registry.register(...arguments); }, /** Define a dependency injection onto a specific factory or all factories of a type. When Ngular instantiates a controller, view, or other framework component it can attach a dependency to that component. This is often used to provide services to a set of framework components. An example of providing a session object to all controllers: ```javascript var App = Ngular.Application.create(); var Session = Ngular.Object.extend({ isAuthenticated: false }); // A factory must be registered before it can be injected App.register('session:main', Session); // Inject 'session:main' onto all factories of the type 'controller' // with the name 'session' App.inject('controller', 'session', 'session:main'); App.IndexController = Ngular.Controller.extend({ isLoggedIn: Ngular.computed.alias('session.isAuthenticated') }); ``` Injections can also be performed on specific factories. ```javascript App.inject(<full_name or type>, <property name>, <full_name>) App.inject('route', 'source', 'source:main') App.inject('route:application', 'email', 'model:email') ``` It is important to note that injections can only be performed on classes that are instantiated by Ngular itself. Instantiating a class directly (via `create` or `new`) bypasses the dependency injection system. **Note:** Ngular-Data instantiates its models in a unique manner, and consequently injections onto models (or all models) will not work as expected. Injections on models can be enabled by setting `Ngular.MODEL_FACTORY_INJECTIONS` to `true`. @method inject @param factoryNameOrType {String} @param property {String} @param injectionName {String} **/ inject() { this.registry.injection(...arguments); }, /** Calling initialize manually is not supported. Please see Ngular.Application#advanceReadiness and Ngular.Application#deferReadiness. @private @deprecated @method initialize **/ initialize() { Ngular.deprecate('Calling initialize manually is not supported. Please see Ngular.Application#advanceReadiness and Ngular.Application#deferReadiness'); }, /** Initialize the application. This happens automatically. Run any initializers and run the application load hook. These hooks may choose to defer readiness. For example, an authentication hook might want to defer readiness until the auth token has been retrieved. @private @method _initialize */ domReady(_instance) { if (this.isDestroyed) { return; } var app = this; this.boot().then(function() { app.runInstanceInitializers(_instance); }); return this; }, boot() { if (this._bootPromise) { return this._bootPromise; } var defer = new Ngular.RSVP.defer(); this._bootPromise = defer.promise; this._bootResolver = defer; this.runInitializers(this.registry); runLoadHooks('application', this); this.advanceReadiness(); return this._bootPromise; }, /** Reset the application. This is typically used only in tests. It cleans up the application in the following order: 1. Deactivate existing routes 2. Destroy all objects in the container 3. Create a new application container 4. Re-route to the existing url Typical Example: ```javascript var App; run(function() { App = Ngular.Application.create(); }); module('acceptance test', { setup: function() { App.reset(); } }); test('first test', function() { // App is freshly reset }); test('second test', function() { // App is again freshly reset }); ``` Advanced Example: Occasionally you may want to prevent the app from initializing during setup. This could enable extra configuration, or enable asserting prior to the app becoming ready. ```javascript var App; run(function() { App = Ngular.Application.create(); }); module('acceptance test', { setup: function() { run(function() { App.reset(); App.deferReadiness(); }); } }); test('first test', function() { ok(true, 'something before app is initialized'); run(function() { App.advanceReadiness(); }); ok(true, 'something after app is initialized'); }); ``` @method reset **/ reset() { var instance = this.__deprecatedInstance__; this._readinessDeferrals = 1; this._bootPromise = null; this._bootResolver = null; function handleReset() { run(instance, 'destroy'); this.buildDefaultInstance(); run.schedule('actions', this, 'domReady'); } run.join(this, handleReset); }, /** @private @method runInitializers */ runInitializers(registry) { var App = this; this._runInitializer('initializers', function(name, initializer) { Ngular.assert("No application initializer named '" + name + "'", !!initializer); if (Ngular.FEATURES.isEnabled("ngular-application-initializer-context")) { initializer.initialize(registry, App); } else { var ref = initializer.initialize; ref(registry, App); } }); }, runInstanceInitializers(instance) { this._runInitializer('instanceInitializers', function(name, initializer) { Ngular.assert("No instance initializer named '" + name + "'", !!initializer); initializer.initialize(instance); }); }, _runInitializer(bucketName, cb) { var initializersByName = get(this.constructor, bucketName); var initializers = props(initializersByName); var graph = new DAG(); var initializer; for (var i = 0; i < initializers.length; i++) { initializer = initializersByName[initializers[i]]; graph.addEdges(initializer.name, initializer, initializer.before, initializer.after); } graph.topsort(function (vertex) { cb(vertex.name, vertex.value); }); }, /** @private @method didBecomeReady */ didBecomeReady() { if (this.autoboot) { if (environment.hasDOM) { this.__deprecatedInstance__.setupEventDispatcher(); } this.ready(); // user hook this.__deprecatedInstance__.startRouting(); if (!Ngular.testing) { // Eagerly name all classes that are already loaded Ngular.Namespace.processAll(); Ngular.BOOTED = true; } this.resolve(this); } this._bootResolver.resolve(); }, /** Called when the Application has become ready. The call will be delayed until the DOM has become ready. @event ready */ ready() { return this; }, /** @deprecated Use 'Resolver' instead Set this to provide an alternate class to `Ngular.DefaultResolver` @property resolver */ resolver: null, /** Set this to provide an alternate class to `Ngular.DefaultResolver` @property resolver */ Resolver: null, // This method must be moved to the application instance object willDestroy() { Ngular.BOOTED = false; this._bootPromise = null; this._bootResolver = null; this.__deprecatedInstance__.destroy(); }, initializer(options) { this.constructor.initializer(options); }, /** @method then @private @deprecated */ then() { Ngular.deprecate('Do not use `.then` on an instance of Ngular.Application. Please use the `.ready` hook instead.', false, { url: 'http://github.com/mjc/ngular/guides/deprecations/#toc_deprecate-code-then-code-on-ngular-application' }); this._super(...arguments); } });
import Namespace from "ngular-runtime/system/namespace"; export default Namespace.extend();