Commit 397d38ee authored by Pascal Hartig's avatar Pascal Hartig

Ember: Upgrade to 1.5.1

parent ba081395
......@@ -5,7 +5,7 @@
* Portions Copyright 2008-2011 Apple Inc. All rights reserved.
* @license Licensed under MIT license
* See https://raw.github.com/emberjs/ember.js/master/LICENSE
* @version 1.5.0
* @version 1.5.1
*/
......@@ -227,7 +227,7 @@ if (!Ember.testing) {
* Portions Copyright 2008-2011 Apple Inc. All rights reserved.
* @license Licensed under MIT license
* See https://raw.github.com/emberjs/ember.js/master/LICENSE
* @version 1.5.0
* @version 1.5.1
*/
......@@ -310,7 +310,7 @@ var define, requireModule, require, requirejs;
@class Ember
@static
@version 1.5.0
@version 1.5.1
*/
if ('undefined' === typeof Ember) {
......@@ -337,10 +337,10 @@ Ember.toString = function() { return "Ember"; };
/**
@property VERSION
@type String
@default '1.5.0'
@default '1.5.1'
@static
*/
Ember.VERSION = '1.5.0';
Ember.VERSION = '1.5.1';
/**
Standard environmental variables. You can define these in a global `EmberENV`
......@@ -15872,10 +15872,11 @@ function ReduceComputedProperty(options) {
this.cacheable();
this.recomputeOnce = function(propertyName) {
// TODO: Coalesce recomputation by <this, propertyName, cp>.
recompute.call(this, propertyName);
// What we really want to do is coalesce by <cp, propertyName>.
// We need a form of `scheduleOnce` that accepts an arbitrary token to
// coalesce by, in addition to the target and method.
Ember.run.once(this, recompute, propertyName);
};
var recompute = function(propertyName) {
var dependentKeys = cp._dependentKeys,
meta = cp._instanceMeta(this, propertyName),
......@@ -34955,6 +34956,15 @@ Ember.Router = Ember.Object.extend(Ember.Evented, {
*/
location: 'hash',
/**
Represents the URL of the root of the application, often '/'. This prefix is
assumed on all routes defined on this router.
@property rootURL
@default '/'
*/
rootURL: '/',
init: function() {
this.router = this.constructor.router || this.constructor.map(Ember.K);
this._activeViews = {};
......@@ -35134,6 +35144,10 @@ Ember.Router = Ember.Object.extend(Ember.Evented, {
var location = get(this, 'location'),
rootURL = get(this, 'rootURL');
if (rootURL && !this.container.has('-location-setting:root-url')) {
this.container.register('-location-setting:root-url', rootURL, { instantiate: false });
}
if ('string' === typeof location && this.container) {
var resolvedLocation = this.container.lookup('location:' + location);
......@@ -35147,7 +35161,7 @@ Ember.Router = Ember.Object.extend(Ember.Evented, {
}
}
if (typeof rootURL === 'string') {
if (rootURL && typeof rootURL === 'string') {
location.rootURL = rootURL;
}
......@@ -39239,6 +39253,7 @@ Ember.Location = {
},
implementations: {},
_location: window.location,
/**
Returns the current `location.hash` by parsing location.href since browsers
......@@ -39249,8 +39264,10 @@ Ember.Location = {
@private
@method getHash
*/
getHash: function () {
var href = window.location.href,
_getHash: function () {
// AutoLocation has it at _location, HashLocation at .location.
// Being nice and not changing
var href = (this._location || this.location).href,
hashIndex = href.indexOf('#');
if (hashIndex === -1) {
......@@ -39365,8 +39382,7 @@ Ember.NoneLocation = Ember.Object.extend({
@submodule ember-routing
*/
var get = Ember.get, set = Ember.set,
getHash = Ember.Location.getHash;
var get = Ember.get, set = Ember.set;
/**
`Ember.HashLocation` implements the location API using the browser's
......@@ -39381,9 +39397,18 @@ Ember.HashLocation = Ember.Object.extend({
implementation: 'hash',
init: function() {
set(this, 'location', get(this, 'location') || window.location);
set(this, 'location', get(this, '_location') || window.location);
},
/**
@private
Returns normalized location.hash
@method getHash
*/
getHash: Ember.Location._getHash,
/**
Returns the current `location.hash`, minus the '#' at the front.
......@@ -39391,7 +39416,7 @@ Ember.HashLocation = Ember.Object.extend({
@method getURL
*/
getURL: function() {
return getHash().substr(1);
return this.getHash().substr(1);
},
/**
......@@ -39524,7 +39549,7 @@ Ember.HistoryLocation = Ember.Object.extend({
rootURL: '/',
/**
Returns the current `location.pathname` without `rootURL`.
Returns the current `location.pathname` without `rootURL` or `baseURL`
@private
@method getURL
......@@ -39555,7 +39580,7 @@ Ember.HistoryLocation = Ember.Object.extend({
var state = this.getState();
path = this.formatURL(path);
if (state && state.path !== path) {
if (!state || state.path !== path) {
this.pushState(path);
}
},
......@@ -39572,15 +39597,16 @@ Ember.HistoryLocation = Ember.Object.extend({
var state = this.getState();
path = this.formatURL(path);
if (state && state.path !== path) {
if (!state || state.path !== path) {
this.replaceState(path);
}
},
/**
Get the current `history.state`
Polyfill checks for native browser support and falls back to retrieving
from a private _historyState variable
Get the current `history.state`. Checks for if a polyfill is
required and if so fetches this._historyState. The state returned
from getState may be null if an iframe has changed a window's
history.
@private
@method getState
......@@ -39700,11 +39726,11 @@ Ember.HistoryLocation = Ember.Object.extend({
@submodule ember-routing
*/
var get = Ember.get, set = Ember.set;
var documentMode = document.documentMode,
history = window.history,
location = window.location,
getHash = Ember.Location.getHash;
var get = Ember.get, set = Ember.set,
HistoryLocation = Ember.HistoryLocation,
HashLocation = Ember.HashLocation,
NoneLocation = Ember.NoneLocation,
EmberLocation = Ember.Location;
/**
Ember.AutoLocation will select the best location option based off browser
......@@ -39723,6 +39749,19 @@ Ember.HistoryLocation = Ember.Object.extend({
var AutoLocation = Ember.AutoLocation = {
/**
@private
This property is used by router:main to know whether to cancel the routing
setup process, which is needed while we redirect the browser.
@property cancelRouterSetup
@default false
*/
cancelRouterSetup: false,
/**
@private
Will be pre-pended to path upon state change.
@property rootURL
......@@ -39733,21 +39772,71 @@ Ember.HistoryLocation = Ember.Object.extend({
/**
@private
Exposed for testing
Attached for mocking in tests
@property _window
@default window
*/
_window: window,
/**
@private
Attached for mocking in tests
@property location
@default window.location
*/
_location: location,
_location: window.location,
/**
@private
Attached for mocking in tests
@property _history
@default window.history
*/
_history: window.history,
/**
@private
Attached for mocking in tests
@property _HistoryLocation
@default Ember.HistoryLocation
*/
_HistoryLocation: HistoryLocation,
/**
@private
Attached for mocking in tests
@property _HashLocation
@default Ember.HashLocation
*/
_HashLocation: HashLocation,
/**
@private
Attached for mocking in tests
@property _NoneLocation
@default Ember.NoneLocation
*/
_NoneLocation: NoneLocation,
/**
@private
Returns location.origin or builds it if device doesn't support it.
@method getOrigin
@method _getOrigin
*/
getOrigin: function () {
_getOrigin: function () {
var location = this._location,
origin = location.origin;
......@@ -39769,14 +39858,14 @@ Ember.HistoryLocation = Ember.Object.extend({
We assume that if the history object has a pushState method, the host should
support HistoryLocation.
@property supportsHistory
@method _getSupportsHistory
*/
supportsHistory: (function () {
_getSupportsHistory: function () {
// Boosted from Modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
// The stock browser on Android 2.2 & 2.3 returns positive on history support
// Unfortunately support is really buggy and there is no clean way to detect
// these bugs, so we fall back to a user agent sniff :(
var userAgent = window.navigator.userAgent;
var userAgent = this._window.navigator.userAgent;
// We only want Android 2, stock browser, and not Chrome which identifies
// itself as 'Mobile Safari' as well
......@@ -39786,8 +39875,8 @@ Ember.HistoryLocation = Ember.Object.extend({
return false;
}
return !!(history && 'pushState' in history);
})(),
return !!(this._history && 'pushState' in this._history);
},
/**
@private
......@@ -39795,56 +39884,13 @@ Ember.HistoryLocation = Ember.Object.extend({
IE8 running in IE7 compatibility mode gives false positive, so we must also
check documentMode.
@property supportsHashChange
@method _getSupportsHashChange
*/
supportsHashChange: ('onhashchange' in window && (documentMode === undefined || documentMode > 7 )),
_getSupportsHashChange: function () {
var window = this._window,
documentMode = window.document.documentMode;
create: function (options) {
if (options && options.rootURL) {
this.rootURL = options.rootURL;
}
var implementationClass, historyPath, hashPath,
cancelRouterSetup = false,
currentPath = this.getFullPath();
if (this.supportsHistory) {
historyPath = this.getHistoryPath();
// Since we support history paths, let's be sure we're using them else
// switch the location over to it.
if (currentPath === historyPath) {
implementationClass = Ember.HistoryLocation;
} else {
cancelRouterSetup = true;
this.replacePath(historyPath);
}
} else if (this.supportsHashChange) {
hashPath = this.getHashPath();
// Be sure we're using a hashed path, otherwise let's switch over it to so
// we start off clean and consistent.
if (currentPath === hashPath) {
implementationClass = Ember.HashLocation;
} else {
cancelRouterSetup = true;
this.replacePath(hashPath);
}
}
// If none has been set
if (!implementationClass) {
implementationClass = Ember.NoneLocation;
}
var implementation = implementationClass.create.apply(implementationClass, arguments);
if (cancelRouterSetup) {
set(implementation, 'cancelRouterSetup', true);
}
return implementation;
return ('onhashchange' in window && (documentMode === undefined || documentMode > 7 ));
},
/**
......@@ -39853,10 +39899,18 @@ Ember.HistoryLocation = Ember.Object.extend({
Redirects the browser using location.replace, prepending the locatin.origin
to prevent phishing attempts
@method replacePath
@method _replacePath
*/
replacePath: function (path) {
this._location.replace(this.getOrigin() + path);
_replacePath: function (path) {
this._location.replace(this._getOrigin() + path);
},
/**
@private
@method _getRootURL
*/
_getRootURL: function () {
return this.rootURL;
},
/**
......@@ -39864,10 +39918,10 @@ Ember.HistoryLocation = Ember.Object.extend({
Returns the current `location.pathname`, normalized for IE inconsistencies.
@method getPath
@method _getPath
*/
getPath: function () {
var pathname = location.pathname;
_getPath: function () {
var pathname = this._location.pathname;
// Various versions of IE/Opera don't always return a leading slash
if (pathname.charAt(0) !== '/') {
pathname = '/' + pathname;
......@@ -39879,12 +39933,32 @@ Ember.HistoryLocation = Ember.Object.extend({
/**
@private
Returns the full pathname including the hash string.
Returns normalized location.hash as an alias to Ember.Location._getHash
@method _getHash
*/
_getHash: EmberLocation._getHash,
/**
@private
Returns location.search
@method getFullPath
@method _getQuery
*/
getFullPath: function () {
return this.getPath() + getHash().substr(1);
_getQuery: function () {
return this._location.search;
},
/**
@private
Returns the full pathname including query and hash
@method _getFullPath
*/
_getFullPath: function () {
return this._getPath() + this._getQuery() + this._getHash();
},
/**
......@@ -39894,15 +39968,46 @@ Ember.HistoryLocation = Ember.Object.extend({
browsers. This may very well differ from the real current path (e.g. if it
starts off as a hashed URL)
@method getHistoryPath
@method _getHistoryPath
*/
getHistoryPath: function () {
var path = this.getPath(),
hashPath = getHash().substr(1),
url = path + hashPath;
_getHistoryPath: function () {
var rootURL = this._getRootURL(),
path = this._getPath(),
hash = this._getHash(),
query = this._getQuery(),
rootURLIndex = path.indexOf(rootURL),
routeHash, hashParts;
Ember.assert('Path ' + path + ' does not start with the provided rootURL ' + rootURL, rootURLIndex === 0);
// By convention, Ember.js routes using HashLocation are required to start
// with `#/`. Anything else should NOT be considered a route and should
// be passed straight through, without transformation.
if (hash.substr(0, 2) === '#/') {
// There could be extra hash segments after the route
hashParts = hash.substr(1).split('#');
// The first one is always the route url
routeHash = hashParts.shift();
// If the path already has a trailing slash, remove the one
// from the hashed route so we don't double up.
if (path.slice(-1) === '/') {
routeHash = routeHash.substr(1);
}
// This is the "expected" final order
path += routeHash;
path += query;
// Removes any stacked double stashes
return url.replace(/\/\//, '/');
if (hashParts.length) {
path += '#' + hashParts.join('#');
}
} else {
path += query;
path += hash;
}
return path;
},
/**
......@@ -39911,19 +40016,79 @@ Ember.HistoryLocation = Ember.Object.extend({
Returns the current path as it should appear for HashLocation supported
browsers. This may very well differ from the real current path.
@method getHashPath
@method _getHashPath
*/
getHashPath: function () {
var historyPath = this.getHistoryPath(),
exp = new RegExp('(' + this.rootURL + ')(.+)'),
url = historyPath.replace(exp, '$1#/$2');
_getHashPath: function () {
var rootURL = this._getRootURL(),
path = rootURL,
historyPath = this._getHistoryPath(),
routePath = historyPath.substr(rootURL.length);
// Remove any stacked double stashes
url = url.replace(/\/\//, '/');
if (routePath !== '') {
if (routePath.charAt(0) !== '/') {
routePath = '/' + routePath;
}
return url;
path += '#' + routePath;
}
return path;
},
/**
Selects the best location option based off browser support and returns an
instance of that Location class.
@see Ember.AutoLocation
@method create
*/
create: function (options) {
if (options && options.rootURL) {
Ember.assert('rootURL must end with a trailing forward slash e.g. "/app/"', options.rootURL.charAt(options.rootURL.length-1) === '/');
this.rootURL = options.rootURL;
}
var historyPath, hashPath,
cancelRouterSetup = false,
implementationClass = this._NoneLocation,
currentPath = this._getFullPath();
if (this._getSupportsHistory()) {
historyPath = this._getHistoryPath();
// Since we support history paths, let's be sure we're using them else
// switch the location over to it.
if (currentPath === historyPath) {
implementationClass = this._HistoryLocation;
} else {
cancelRouterSetup = true;
this._replacePath(historyPath);
}
} else if (this._getSupportsHashChange()) {
hashPath = this._getHashPath();
// Be sure we're using a hashed path, otherwise let's switch over it to so
// we start off clean and consistent. We'll count an index path with no
// hash as "good enough" as well.
if (currentPath === hashPath || (currentPath === '/' && hashPath === '/#/')) {
implementationClass = this._HashLocation;
} else {
// Our URL isn't in the expected hash-supported format, so we want to
// cancel the router setup and replace the URL to start off clean
cancelRouterSetup = true;
this._replacePath(hashPath);
}
}
var implementation = implementationClass.create.apply(implementationClass, arguments);
if (cancelRouterSetup) {
set(implementation, 'cancelRouterSetup', true);
}
return implementation;
}
};
})();
......@@ -41235,6 +41400,7 @@ Ember.Application.reopenClass({
container.injection('controller', 'namespace', 'application:main');
container.injection('route', 'router', 'router:main');
container.injection('location', 'rootURL', '-location-setting:root-url');
// DEBUGGING
container.register('resolver-for-debugging:main', container.resolver.__resolver__, { instantiate: false });
......@@ -42083,7 +42249,873 @@ Ember Extension Support
})();
(function() {
define("container/container",
["container/inheriting_dict","exports"],
function(__dependency1__, __exports__) {
"use strict";
var InheritingDict = __dependency1__["default"];
// A lightweight container that helps to assemble and decouple components.
// Public api for the container is still in flux.
// The public api, specified on the application namespace should be considered the stable api.
function Container(parent) {
this.parent = parent;
this.children = [];
this.resolver = parent && parent.resolver || function() {};
this.registry = new InheritingDict(parent && parent.registry);
this.cache = new InheritingDict(parent && parent.cache);
this.factoryCache = new InheritingDict(parent && parent.factoryCache);
this.resolveCache = new InheritingDict(parent && parent.resolveCache);
this.typeInjections = new InheritingDict(parent && parent.typeInjections);
this.injections = {};
this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections);
this.factoryInjections = {};
this._options = new InheritingDict(parent && parent._options);
this._typeOptions = new InheritingDict(parent && parent._typeOptions);
}
Container.prototype = {
/**
@property parent
@type Container
@default null
*/
parent: null,
/**
@property children
@type Array
@default []
*/
children: null,
/**
@property resolver
@type function
*/
resolver: null,
/**
@property registry
@type InheritingDict
*/
registry: null,
/**
@property cache
@type InheritingDict
*/
cache: null,
/**
@property typeInjections
@type InheritingDict
*/
typeInjections: null,
/**
@property injections
@type Object
@default {}
*/
injections: null,
/**
@private
@property _options
@type InheritingDict
@default null
*/
_options: null,
/**
@private
@property _typeOptions
@type InheritingDict
*/
_typeOptions: null,
/**
Returns a new child of the current container. These children are configured
to correctly inherit from the current container.
@method child
@return {Container}
*/
child: function() {
var container = new Container(this);
this.children.push(container);
return container;
},
/**
Sets a key-value pair on the current container. If a parent container,
has the same key, once set on a child, the parent and child will diverge
as expected.
@method set
@param {Object} object
@param {String} key
@param {any} value
*/
set: function(object, key, value) {
object[key] = value;
},
/**
Registers a factory for later injection.
Example:
```javascript
var container = new Container();
container.register('model:user', Person, {singleton: false });
container.register('fruit:favorite', Orange);
container.register('communication:main', Email, {singleton: false});
```
@method register
@param {String} fullName
@param {Function} factory
@param {Object} options
*/
register: function(fullName, factory, options) {
validateFullName(fullName);
if (factory === undefined) {
throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`');
}
var normalizedName = this.normalize(fullName);
if (this.cache.has(normalizedName)) {
throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.');
}
this.registry.set(normalizedName, factory);
this._options.set(normalizedName, options || {});
},
/**
Unregister a fullName
```javascript
var container = new Container();
container.register('model:user', User);
container.lookup('model:user') instanceof User //=> true
container.unregister('model:user')
container.lookup('model:user') === undefined //=> true
```
@method unregister
@param {String} fullName
*/
unregister: function(fullName) {
validateFullName(fullName);
var normalizedName = this.normalize(fullName);
this.registry.remove(normalizedName);
this.cache.remove(normalizedName);
this.factoryCache.remove(normalizedName);
this.resolveCache.remove(normalizedName);
this._options.remove(normalizedName);
},
/**
Given a fullName return the corresponding factory.
By default `resolve` will retrieve the factory from
its container's registry.
```javascript
var container = new Container();
container.register('api:twitter', Twitter);
container.resolve('api:twitter') // => Twitter
```
Optionally the container can be provided with a custom resolver.
If provided, `resolve` will first provide the custom resolver
the oppertunity to resolve the fullName, otherwise it will fallback
to the registry.
```javascript
var container = new Container();
container.resolver = function(fullName) {
// lookup via the module system of choice
};
// the twitter factory is added to the module system
container.resolve('api:twitter') // => Twitter
```
@method resolve
@param {String} fullName
@return {Function} fullName's factory
*/
resolve: function(fullName) {
validateFullName(fullName);
var normalizedName = this.normalize(fullName);
var cached = this.resolveCache.get(normalizedName);
if (cached) { return cached; }
var resolved = this.resolver(normalizedName) || this.registry.get(normalizedName);
this.resolveCache.set(normalizedName, resolved);
return resolved;
},
/**
A hook that can be used to describe how the resolver will
attempt to find the factory.
For example, the default Ember `.describe` returns the full
class name (including namespace) where Ember's resolver expects
to find the `fullName`.
@method describe
@param {String} fullName
@return {string} described fullName
*/
describe: function(fullName) {
return fullName;
},
/**
A hook to enable custom fullName normalization behaviour
@method normalize
@param {String} fullName
@return {string} normalized fullName
*/
normalize: function(fullName) {
return fullName;
},
/**
@method makeToString
@param {any} factory
@param {string} fullName
@return {function} toString function
*/
makeToString: function(factory, fullName) {
return factory.toString();
},
/**
Given a fullName return a corresponding instance.
The default behaviour is for lookup to return a singleton instance.
The singleton is scoped to the container, allowing multiple containers
to all have their own locally scoped singletons.
```javascript
var container = new Container();
container.register('api:twitter', Twitter);
var twitter = container.lookup('api:twitter');
twitter instanceof Twitter; // => true
// by default the container will return singletons
var twitter2 = container.lookup('api:twitter');
twitter instanceof Twitter; // => true
twitter === twitter2; //=> true
```
If singletons are not wanted an optional flag can be provided at lookup.
```javascript
var container = new Container();
container.register('api:twitter', Twitter);
var twitter = container.lookup('api:twitter', { singleton: false });
var twitter2 = container.lookup('api:twitter', { singleton: false });
twitter === twitter2; //=> false
```
@method lookup
@param {String} fullName
@param {Object} options
@return {any}
*/
lookup: function(fullName, options) {
validateFullName(fullName);
return lookup(this, this.normalize(fullName), options);
},
/**
Given a fullName return the corresponding factory.
@method lookupFactory
@param {String} fullName
@return {any}
*/
lookupFactory: function(fullName) {
validateFullName(fullName);
return factoryFor(this, this.normalize(fullName));
},
/**
Given a fullName check if the container is aware of its factory
or singleton instance.
@method has
@param {String} fullName
@return {Boolean}
*/
has: function(fullName) {
validateFullName(fullName);
return has(this, this.normalize(fullName));
},
/**
Allow registering options for all factories of a type.
```javascript
var container = new Container();
// if all of type `connection` must not be singletons
container.optionsForType('connection', { singleton: false });
container.register('connection:twitter', TwitterConnection);
container.register('connection:facebook', FacebookConnection);
var twitter = container.lookup('connection:twitter');
var twitter2 = container.lookup('connection:twitter');
twitter === twitter2; // => false
var facebook = container.lookup('connection:facebook');
var facebook2 = container.lookup('connection:facebook');
facebook === facebook2; // => false
```
@method optionsForType
@param {String} type
@param {Object} options
*/
optionsForType: function(type, options) {
if (this.parent) { illegalChildOperation('optionsForType'); }
this._typeOptions.set(type, options);
},
/**
@method options
@param {String} type
@param {Object} options
*/
options: function(type, options) {
this.optionsForType(type, options);
},
/**
Used only via `injection`.
Provides a specialized form of injection, specifically enabling
all objects of one type to be injected with a reference to another
object.
For example, provided each object of type `controller` needed a `router`.
one would do the following:
```javascript
var container = new Container();
container.register('router:main', Router);
container.register('controller:user', UserController);
container.register('controller:post', PostController);
container.typeInjection('controller', 'router', 'router:main');
var user = container.lookup('controller:user');
var post = container.lookup('controller:post');
user.router instanceof Router; //=> true
post.router instanceof Router; //=> true
// both controllers share the same router
user.router === post.router; //=> true
```
@private
@method typeInjection
@param {String} type
@param {String} property
@param {String} fullName
*/
typeInjection: function(type, property, fullName) {
validateFullName(fullName);
if (this.parent) { illegalChildOperation('typeInjection'); }
var fullNameType = fullName.split(':')[0];
if(fullNameType === type) {
throw new Error('Cannot inject a `' + fullName + '` on other ' + type + '(s). Register the `' + fullName + '` as a different type and perform the typeInjection.');
}
addTypeInjection(this.typeInjections, type, property, fullName);
},
/**
Defines injection rules.
These rules are used to inject dependencies onto objects when they
are instantiated.
Two forms of injections are possible:
* Injecting one fullName on another fullName
* Injecting one fullName on a type
Example:
```javascript
var container = new Container();
container.register('source:main', Source);
container.register('model:user', User);
container.register('model:post', Post);
// injecting one fullName on another fullName
// eg. each user model gets a post model
container.injection('model:user', 'post', 'model:post');
// injecting one fullName on another type
container.injection('model', 'source', 'source:main');
var user = container.lookup('model:user');
var post = container.lookup('model:post');
user.source instanceof Source; //=> true
post.source instanceof Source; //=> true
user.post instanceof Post; //=> true
// and both models share the same source
user.source === post.source; //=> true
```
@method injection
@param {String} factoryName
@param {String} property
@param {String} injectionName
*/
injection: function(fullName, property, injectionName) {
if (this.parent) { illegalChildOperation('injection'); }
validateFullName(injectionName);
var normalizedInjectionName = this.normalize(injectionName);
if (fullName.indexOf(':') === -1) {
return this.typeInjection(fullName, property, normalizedInjectionName);
}
validateFullName(fullName);
var normalizedName = this.normalize(fullName);
if (this.cache.has(normalizedName)) {
throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + normalizedName + "', '" + property + "', '" + injectionName + "')");
}
addInjection(this.injections, normalizedName, property, normalizedInjectionName);
},
/**
Used only via `factoryInjection`.
Provides a specialized form of injection, specifically enabling
all factory of one type to be injected with a reference to another
object.
For example, provided each factory of type `model` needed a `store`.
one would do the following:
```javascript
var container = new Container();
container.register('store:main', SomeStore);
container.factoryTypeInjection('model', 'store', 'store:main');
var store = container.lookup('store:main');
var UserFactory = container.lookupFactory('model:user');
UserFactory.store instanceof SomeStore; //=> true
```
@private
@method factoryTypeInjection
@param {String} type
@param {String} property
@param {String} fullName
*/
factoryTypeInjection: function(type, property, fullName) {
if (this.parent) { illegalChildOperation('factoryTypeInjection'); }
addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName));
},
/**
Defines factory injection rules.
Similar to regular injection rules, but are run against factories, via
`Container#lookupFactory`.
These rules are used to inject objects onto factories when they
are looked up.
Two forms of injections are possible:
* Injecting one fullName on another fullName
* Injecting one fullName on a type
Example:
```javascript
var container = new Container();
container.register('store:main', Store);
container.register('store:secondary', OtherStore);
container.register('model:user', User);
container.register('model:post', Post);
// injecting one fullName on another type
container.factoryInjection('model', 'store', 'store:main');
// injecting one fullName on another fullName
container.factoryInjection('model:post', 'secondaryStore', 'store:secondary');
var UserFactory = container.lookupFactory('model:user');
var PostFactory = container.lookupFactory('model:post');
var store = container.lookup('store:main');
UserFactory.store instanceof Store; //=> true
UserFactory.secondaryStore instanceof OtherStore; //=> false
PostFactory.store instanceof Store; //=> true
PostFactory.secondaryStore instanceof OtherStore; //=> true
// and both models share the same source instance
UserFactory.store === PostFactory.store; //=> true
```
@method factoryInjection
@param {String} factoryName
@param {String} property
@param {String} injectionName
*/
factoryInjection: function(fullName, property, injectionName) {
if (this.parent) { illegalChildOperation('injection'); }
var normalizedName = this.normalize(fullName);
var normalizedInjectionName = this.normalize(injectionName);
validateFullName(injectionName);
if (fullName.indexOf(':') === -1) {
return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName);
}
validateFullName(fullName);
if (this.factoryCache.has(normalizedName)) {
throw new Error("Attempted to register a factoryInjection for a type that has already been looked up. ('" + normalizedName + "', '" + property + "', '" + injectionName + "')");
}
addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName);
},
/**
A depth first traversal, destroying the container, its descendant containers and all
their managed objects.
@method destroy
*/
destroy: function() {
for (var i=0, l=this.children.length; i<l; i++) {
this.children[i].destroy();
}
this.children = [];
eachDestroyable(this, function(item) {
item.destroy();
});
this.parent = undefined;
this.isDestroyed = true;
},
/**
@method reset
*/
reset: function() {
for (var i=0, l=this.children.length; i<l; i++) {
resetCache(this.children[i]);
}
resetCache(this);
}
};
function has(container, fullName){
if (container.cache.has(fullName)) {
return true;
}
return !!container.resolve(fullName);
}
function lookup(container, fullName, options) {
options = options || {};
if (container.cache.has(fullName) && options.singleton !== false) {
return container.cache.get(fullName);
}
var value = instantiate(container, fullName);
if (value === undefined) { return; }
if (isSingleton(container, fullName) && options.singleton !== false) {
container.cache.set(fullName, value);
}
return value;
}
function illegalChildOperation(operation) {
throw new Error(operation + " is not currently supported on child containers");
}
function isSingleton(container, fullName) {
var singleton = option(container, fullName, 'singleton');
return singleton !== false;
}
function buildInjections(container, injections) {
var hash = {};
if (!injections) { return hash; }
var injection, injectable;
for (var i=0, l=injections.length; i<l; i++) {
injection = injections[i];
injectable = lookup(container, injection.fullName);
if (injectable !== undefined) {
hash[injection.property] = injectable;
} else {
throw new Error('Attempting to inject an unknown injection: `' + injection.fullName + '`');
}
}
return hash;
}
function option(container, fullName, optionName) {
var options = container._options.get(fullName);
if (options && options[optionName] !== undefined) {
return options[optionName];
}
var type = fullName.split(":")[0];
options = container._typeOptions.get(type);
if (options) {
return options[optionName];
}
}
function factoryFor(container, fullName) {
var name = fullName;
var factory = container.resolve(name);
var injectedFactory;
var cache = container.factoryCache;
var type = fullName.split(":")[0];
if (factory === undefined) { return; }
if (cache.has(fullName)) {
return cache.get(fullName);
}
if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) {
// TODO: think about a 'safe' merge style extension
// for now just fallback to create time injection
return factory;
} else {
var injections = injectionsFor(container, fullName);
var factoryInjections = factoryInjectionsFor(container, fullName);
factoryInjections._toString = container.makeToString(factory, fullName);
injectedFactory = factory.extend(injections);
injectedFactory.reopenClass(factoryInjections);
cache.set(fullName, injectedFactory);
return injectedFactory;
}
}
function injectionsFor(container, fullName) {
var splitName = fullName.split(":"),
type = splitName[0],
injections = [];
injections = injections.concat(container.typeInjections.get(type) || []);
injections = injections.concat(container.injections[fullName] || []);
injections = buildInjections(container, injections);
injections._debugContainerKey = fullName;
injections.container = container;
return injections;
}
function factoryInjectionsFor(container, fullName) {
var splitName = fullName.split(":"),
type = splitName[0],
factoryInjections = [];
factoryInjections = factoryInjections.concat(container.factoryTypeInjections.get(type) || []);
factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []);
factoryInjections = buildInjections(container, factoryInjections);
factoryInjections._debugContainerKey = fullName;
return factoryInjections;
}
function instantiate(container, fullName) {
var factory = factoryFor(container, fullName);
if (option(container, fullName, 'instantiate') === false) {
return factory;
}
if (factory) {
if (typeof factory.extend === 'function') {
// assume the factory was extendable and is already injected
return factory.create();
} else {
// assume the factory was extendable
// to create time injections
// TODO: support new'ing for instantiation and merge injections for pure JS Functions
return factory.create(injectionsFor(container, fullName));
}
}
}
function eachDestroyable(container, callback) {
container.cache.eachLocal(function(key, value) {
if (option(container, key, 'instantiate') === false) { return; }
callback(value);
});
}
function resetCache(container) {
container.cache.eachLocal(function(key, value) {
if (option(container, key, 'instantiate') === false) { return; }
value.destroy();
});
container.cache.dict = {};
}
function addTypeInjection(rules, type, property, fullName) {
var injections = rules.get(type);
if (!injections) {
injections = [];
rules.set(type, injections);
}
injections.push({
property: property,
fullName: fullName
});
}
var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/;
function validateFullName(fullName) {
if (!VALID_FULL_NAME_REGEXP.test(fullName)) {
throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName);
}
}
function addInjection(rules, factoryName, property, injectionName) {
var injections = rules[factoryName] = rules[factoryName] || [];
injections.push({ property: property, fullName: injectionName });
}
__exports__["default"] = Container;
});define("ember-runtime/ext/rsvp",
["ember-metal/core","ember-metal/logger","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Ember = __dependency1__["default"];
var Logger = __dependency2__["default"];
var RSVP = requireModule("rsvp");
var Test, testModuleName = 'ember-testing/test';
RSVP.onerrorDefault = function(error) {
if (error instanceof Error) {
if (Ember.testing) {
// ES6TODO: remove when possible
if (!Test && Ember.__loader.registry[testModuleName]) {
Test = requireModule(testModuleName)['default'];
}
if (Test && Test.adapter) {
Test.adapter.exception(error);
} else {
throw error;
}
} else if (Ember.onerror) {
Ember.onerror(error);
} else {
Logger.error(error.stack);
Ember.assert(error, false);
}
}
};
RSVP.on('error', RSVP.onerrorDefault);
__exports__["default"] = RSVP;
});define("ember-runtime/system/container",
["ember-metal/property_set","exports"],
function(__dependency1__, __exports__) {
"use strict";
var set = __dependency1__["default"];
var Container = requireModule('container')["default"];
Container.set = set;
__exports__["default"] = Container;
});(function() {
/**
@module ember
@submodule ember-testing
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment