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

Ember: Upgrade to 1.5.1

parent ba081395
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright 2008-2011 Apple Inc. All rights reserved. * Portions Copyright 2008-2011 Apple Inc. All rights reserved.
* @license Licensed under MIT license * @license Licensed under MIT license
* See https://raw.github.com/emberjs/ember.js/master/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) { ...@@ -227,7 +227,7 @@ if (!Ember.testing) {
* Portions Copyright 2008-2011 Apple Inc. All rights reserved. * Portions Copyright 2008-2011 Apple Inc. All rights reserved.
* @license Licensed under MIT license * @license Licensed under MIT license
* See https://raw.github.com/emberjs/ember.js/master/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; ...@@ -310,7 +310,7 @@ var define, requireModule, require, requirejs;
@class Ember @class Ember
@static @static
@version 1.5.0 @version 1.5.1
*/ */
if ('undefined' === typeof Ember) { if ('undefined' === typeof Ember) {
...@@ -337,10 +337,10 @@ Ember.toString = function() { return "Ember"; }; ...@@ -337,10 +337,10 @@ Ember.toString = function() { return "Ember"; };
/** /**
@property VERSION @property VERSION
@type String @type String
@default '1.5.0' @default '1.5.1'
@static @static
*/ */
Ember.VERSION = '1.5.0'; Ember.VERSION = '1.5.1';
/** /**
Standard environmental variables. You can define these in a global `EmberENV` Standard environmental variables. You can define these in a global `EmberENV`
...@@ -15872,10 +15872,11 @@ function ReduceComputedProperty(options) { ...@@ -15872,10 +15872,11 @@ function ReduceComputedProperty(options) {
this.cacheable(); this.cacheable();
this.recomputeOnce = function(propertyName) { this.recomputeOnce = function(propertyName) {
// TODO: Coalesce recomputation by <this, propertyName, cp>. // What we really want to do is coalesce by <cp, propertyName>.
recompute.call(this, 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 recompute = function(propertyName) {
var dependentKeys = cp._dependentKeys, var dependentKeys = cp._dependentKeys,
meta = cp._instanceMeta(this, propertyName), meta = cp._instanceMeta(this, propertyName),
...@@ -34955,6 +34956,15 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { ...@@ -34955,6 +34956,15 @@ Ember.Router = Ember.Object.extend(Ember.Evented, {
*/ */
location: 'hash', 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() { init: function() {
this.router = this.constructor.router || this.constructor.map(Ember.K); this.router = this.constructor.router || this.constructor.map(Ember.K);
this._activeViews = {}; this._activeViews = {};
...@@ -35134,6 +35144,10 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { ...@@ -35134,6 +35144,10 @@ Ember.Router = Ember.Object.extend(Ember.Evented, {
var location = get(this, 'location'), var location = get(this, 'location'),
rootURL = get(this, 'rootURL'); 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) { if ('string' === typeof location && this.container) {
var resolvedLocation = this.container.lookup('location:' + location); var resolvedLocation = this.container.lookup('location:' + location);
...@@ -35147,7 +35161,7 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { ...@@ -35147,7 +35161,7 @@ Ember.Router = Ember.Object.extend(Ember.Evented, {
} }
} }
if (typeof rootURL === 'string') { if (rootURL && typeof rootURL === 'string') {
location.rootURL = rootURL; location.rootURL = rootURL;
} }
...@@ -39239,6 +39253,7 @@ Ember.Location = { ...@@ -39239,6 +39253,7 @@ Ember.Location = {
}, },
implementations: {}, implementations: {},
_location: window.location,
/** /**
Returns the current `location.hash` by parsing location.href since browsers Returns the current `location.hash` by parsing location.href since browsers
...@@ -39249,8 +39264,10 @@ Ember.Location = { ...@@ -39249,8 +39264,10 @@ Ember.Location = {
@private @private
@method getHash @method getHash
*/ */
getHash: function () { _getHash: function () {
var href = window.location.href, // AutoLocation has it at _location, HashLocation at .location.
// Being nice and not changing
var href = (this._location || this.location).href,
hashIndex = href.indexOf('#'); hashIndex = href.indexOf('#');
if (hashIndex === -1) { if (hashIndex === -1) {
...@@ -39365,8 +39382,7 @@ Ember.NoneLocation = Ember.Object.extend({ ...@@ -39365,8 +39382,7 @@ Ember.NoneLocation = Ember.Object.extend({
@submodule ember-routing @submodule ember-routing
*/ */
var get = Ember.get, set = Ember.set, var get = Ember.get, set = Ember.set;
getHash = Ember.Location.getHash;
/** /**
`Ember.HashLocation` implements the location API using the browser's `Ember.HashLocation` implements the location API using the browser's
...@@ -39381,9 +39397,18 @@ Ember.HashLocation = Ember.Object.extend({ ...@@ -39381,9 +39397,18 @@ Ember.HashLocation = Ember.Object.extend({
implementation: 'hash', implementation: 'hash',
init: function() { 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. Returns the current `location.hash`, minus the '#' at the front.
...@@ -39391,7 +39416,7 @@ Ember.HashLocation = Ember.Object.extend({ ...@@ -39391,7 +39416,7 @@ Ember.HashLocation = Ember.Object.extend({
@method getURL @method getURL
*/ */
getURL: function() { getURL: function() {
return getHash().substr(1); return this.getHash().substr(1);
}, },
/** /**
...@@ -39524,7 +39549,7 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39524,7 +39549,7 @@ Ember.HistoryLocation = Ember.Object.extend({
rootURL: '/', rootURL: '/',
/** /**
Returns the current `location.pathname` without `rootURL`. Returns the current `location.pathname` without `rootURL` or `baseURL`
@private @private
@method getURL @method getURL
...@@ -39555,7 +39580,7 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39555,7 +39580,7 @@ Ember.HistoryLocation = Ember.Object.extend({
var state = this.getState(); var state = this.getState();
path = this.formatURL(path); path = this.formatURL(path);
if (state && state.path !== path) { if (!state || state.path !== path) {
this.pushState(path); this.pushState(path);
} }
}, },
...@@ -39572,15 +39597,16 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39572,15 +39597,16 @@ Ember.HistoryLocation = Ember.Object.extend({
var state = this.getState(); var state = this.getState();
path = this.formatURL(path); path = this.formatURL(path);
if (state && state.path !== path) { if (!state || state.path !== path) {
this.replaceState(path); this.replaceState(path);
} }
}, },
/** /**
Get the current `history.state` Get the current `history.state`. Checks for if a polyfill is
Polyfill checks for native browser support and falls back to retrieving required and if so fetches this._historyState. The state returned
from a private _historyState variable from getState may be null if an iframe has changed a window's
history.
@private @private
@method getState @method getState
...@@ -39700,11 +39726,11 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39700,11 +39726,11 @@ Ember.HistoryLocation = Ember.Object.extend({
@submodule ember-routing @submodule ember-routing
*/ */
var get = Ember.get, set = Ember.set; var get = Ember.get, set = Ember.set,
var documentMode = document.documentMode, HistoryLocation = Ember.HistoryLocation,
history = window.history, HashLocation = Ember.HashLocation,
location = window.location, NoneLocation = Ember.NoneLocation,
getHash = Ember.Location.getHash; EmberLocation = Ember.Location;
/** /**
Ember.AutoLocation will select the best location option based off browser Ember.AutoLocation will select the best location option based off browser
...@@ -39723,6 +39749,19 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39723,6 +39749,19 @@ Ember.HistoryLocation = Ember.Object.extend({
var AutoLocation = Ember.AutoLocation = { 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. Will be pre-pended to path upon state change.
@property rootURL @property rootURL
...@@ -39733,21 +39772,71 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39733,21 +39772,71 @@ Ember.HistoryLocation = Ember.Object.extend({
/** /**
@private @private
Exposed for testing Attached for mocking in tests
@property _window
@default window
*/
_window: window,
/**
@private
Attached for mocking in tests
@property location @property location
@default window.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 @private
Returns location.origin or builds it if device doesn't support it. Returns location.origin or builds it if device doesn't support it.
@method getOrigin @method _getOrigin
*/ */
getOrigin: function () { _getOrigin: function () {
var location = this._location, var location = this._location,
origin = location.origin; origin = location.origin;
...@@ -39769,14 +39858,14 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39769,14 +39858,14 @@ Ember.HistoryLocation = Ember.Object.extend({
We assume that if the history object has a pushState method, the host should We assume that if the history object has a pushState method, the host should
support HistoryLocation. support HistoryLocation.
@property supportsHistory @method _getSupportsHistory
*/ */
supportsHistory: (function () { _getSupportsHistory: function () {
// Boosted from Modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js // 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 // 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 // Unfortunately support is really buggy and there is no clean way to detect
// these bugs, so we fall back to a user agent sniff :( // 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 // We only want Android 2, stock browser, and not Chrome which identifies
// itself as 'Mobile Safari' as well // itself as 'Mobile Safari' as well
...@@ -39786,8 +39875,8 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39786,8 +39875,8 @@ Ember.HistoryLocation = Ember.Object.extend({
return false; return false;
} }
return !!(history && 'pushState' in history); return !!(this._history && 'pushState' in this._history);
})(), },
/** /**
@private @private
...@@ -39795,56 +39884,13 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39795,56 +39884,13 @@ Ember.HistoryLocation = Ember.Object.extend({
IE8 running in IE7 compatibility mode gives false positive, so we must also IE8 running in IE7 compatibility mode gives false positive, so we must also
check documentMode. 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) { return ('onhashchange' in window && (documentMode === undefined || documentMode > 7 ));
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;
}, },
/** /**
...@@ -39853,10 +39899,18 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39853,10 +39899,18 @@ Ember.HistoryLocation = Ember.Object.extend({
Redirects the browser using location.replace, prepending the locatin.origin Redirects the browser using location.replace, prepending the locatin.origin
to prevent phishing attempts to prevent phishing attempts
@method replacePath @method _replacePath
*/ */
replacePath: function (path) { _replacePath: function (path) {
this._location.replace(this.getOrigin() + path); this._location.replace(this._getOrigin() + path);
},
/**
@private
@method _getRootURL
*/
_getRootURL: function () {
return this.rootURL;
}, },
/** /**
...@@ -39864,10 +39918,10 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39864,10 +39918,10 @@ Ember.HistoryLocation = Ember.Object.extend({
Returns the current `location.pathname`, normalized for IE inconsistencies. Returns the current `location.pathname`, normalized for IE inconsistencies.
@method getPath @method _getPath
*/ */
getPath: function () { _getPath: function () {
var pathname = location.pathname; var pathname = this._location.pathname;
// Various versions of IE/Opera don't always return a leading slash // Various versions of IE/Opera don't always return a leading slash
if (pathname.charAt(0) !== '/') { if (pathname.charAt(0) !== '/') {
pathname = '/' + pathname; pathname = '/' + pathname;
...@@ -39879,12 +39933,32 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39879,12 +39933,32 @@ Ember.HistoryLocation = Ember.Object.extend({
/** /**
@private @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 () { _getQuery: function () {
return this.getPath() + getHash().substr(1); 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({ ...@@ -39894,15 +39968,46 @@ Ember.HistoryLocation = Ember.Object.extend({
browsers. This may very well differ from the real current path (e.g. if it browsers. This may very well differ from the real current path (e.g. if it
starts off as a hashed URL) starts off as a hashed URL)
@method getHistoryPath @method _getHistoryPath
*/ */
getHistoryPath: function () { _getHistoryPath: function () {
var path = this.getPath(), var rootURL = this._getRootURL(),
hashPath = getHash().substr(1), path = this._getPath(),
url = path + hashPath; 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 if (hashParts.length) {
return url.replace(/\/\//, '/'); path += '#' + hashParts.join('#');
}
} else {
path += query;
path += hash;
}
return path;
}, },
/** /**
...@@ -39911,19 +40016,79 @@ Ember.HistoryLocation = Ember.Object.extend({ ...@@ -39911,19 +40016,79 @@ Ember.HistoryLocation = Ember.Object.extend({
Returns the current path as it should appear for HashLocation supported Returns the current path as it should appear for HashLocation supported
browsers. This may very well differ from the real current path. browsers. This may very well differ from the real current path.
@method getHashPath @method _getHashPath
*/ */
getHashPath: function () { _getHashPath: function () {
var historyPath = this.getHistoryPath(), var rootURL = this._getRootURL(),
exp = new RegExp('(' + this.rootURL + ')(.+)'), path = rootURL,
url = historyPath.replace(exp, '$1#/$2'); historyPath = this._getHistoryPath(),
routePath = historyPath.substr(rootURL.length);
// Remove any stacked double stashes if (routePath !== '') {
url = url.replace(/\/\//, '/'); 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({ ...@@ -41235,6 +41400,7 @@ Ember.Application.reopenClass({
container.injection('controller', 'namespace', 'application:main'); container.injection('controller', 'namespace', 'application:main');
container.injection('route', 'router', 'router:main'); container.injection('route', 'router', 'router:main');
container.injection('location', 'rootURL', '-location-setting:root-url');
// DEBUGGING // DEBUGGING
container.register('resolver-for-debugging:main', container.resolver.__resolver__, { instantiate: false }); container.register('resolver-for-debugging:main', container.resolver.__resolver__, { instantiate: false });
...@@ -42083,7 +42249,873 @@ Ember Extension Support ...@@ -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 @module ember
@submodule ember-testing @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