dist/ember-runtime.js in ember-source-1.1.3 vs dist/ember-runtime.js in ember-source-1.2.0.beta.1
- old
+ new
@@ -1,7 +1,17 @@
- // Version: 1.1.3
+// ==========================================================================
+// Project: Ember - JavaScript Application Framework
+// Copyright: Copyright 2011-2013 Tilde Inc. and contributors
+// Portions Copyright 2006-2011 Strobe Inc.
+// 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.2.0-beta.1
+
(function() {
/*global __fail__*/
/**
Ember Debug
@@ -172,12 +182,22 @@
}
}
})();
- // Version: 1.1.3
+// ==========================================================================
+// Project: Ember - JavaScript Application Framework
+// Copyright: Copyright 2011-2013 Tilde Inc. and contributors
+// Portions Copyright 2006-2011 Strobe Inc.
+// 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.2.0-beta.1
+
(function() {
var define, requireModule;
(function() {
var registry = {}, seen = {};
@@ -237,11 +257,11 @@
The core Runtime framework is based on the jQuery API with a number of
performance optimizations.
@class Ember
@static
- @version 1.1.3
+ @version 1.2.0-beta.1
*/
if ('undefined' === typeof Ember) {
// Create core object. Make it act like an instance of Ember.Namespace so that
// objects assigned to it are given a sane string representation.
@@ -264,14 +284,14 @@
/**
@property VERSION
@type String
- @default '1.1.3'
+ @default '1.2.0-beta.1'
@final
*/
-Ember.VERSION = '1.1.3';
+Ember.VERSION = '1.2.0-beta.1';
/**
Standard environmental variables. You can define these in a global `ENV`
variable before loading Ember to control various configuration
settings.
@@ -295,26 +315,31 @@
Ember.config = Ember.config || {};
/**
Hash of enabled Canary features. Add to before creating your application.
+ You can also define `ENV.FEATURES` if you need to enable features flagged at runtime.
+
@property FEATURES
@type Hash
*/
-Ember.FEATURES = {};
+Ember.FEATURES = Ember.ENV.FEATURES || {};
/**
Test that a feature is enabled. Parsed by Ember's build tools to leave
experimental features out of beta/stable builds.
+ You can define an `ENV.ENABLE_ALL_FEATURES` config to force all features to
+ be enabled.
+
@method isEnabled
@param {string} feature
*/
Ember.FEATURES.isEnabled = function(feature) {
- return Ember.FEATURES[feature];
+ return Ember.ENV.ENABLE_ALL_FEATURES || Ember.FEATURES[feature];
};
// ..........................................................
// BOOTSTRAP
//
@@ -394,194 +419,11 @@
@type Number
@private
*/
Ember.uuid = 0;
-// ..........................................................
-// LOGGER
-//
-
-function consoleMethod(name) {
- var consoleObj;
- if (imports.console) {
- consoleObj = imports.console;
- } else if (typeof console !== 'undefined') {
- consoleObj = console;
- }
-
- var method = typeof consoleObj === 'object' ? consoleObj[name] : null;
-
- if (method) {
- // Older IE doesn't support apply, but Chrome needs it
- if (method.apply) {
- return function() {
- method.apply(consoleObj, arguments);
- };
- } else {
- return function() {
- var message = Array.prototype.join.call(arguments, ', ');
- method(message);
- };
- }
- }
-}
-
-function assertPolyfill(test, message) {
- if (!test) {
- try {
- // attempt to preserve the stack
- throw new Error("assertion failed: " + message);
- } catch(error) {
- setTimeout(function() {
- throw error;
- }, 0);
- }
- }
-}
-
/**
- Inside Ember-Metal, simply uses the methods from `imports.console`.
- Override this to provide more robust logging functionality.
-
- @class Logger
- @namespace Ember
-*/
-Ember.Logger = {
- /**
- Logs the arguments to the console.
- You can pass as many arguments as you want and they will be joined together with a space.
-
- ```javascript
- var foo = 1;
- Ember.Logger.log('log value of foo:', foo); // "log value of foo: 1" will be printed to the console
- ```
-
- @method log
- @for Ember.Logger
- @param {*} arguments
- */
- log: consoleMethod('log') || Ember.K,
- /**
- Prints the arguments to the console with a warning icon.
- You can pass as many arguments as you want and they will be joined together with a space.
-
- ```javascript
- Ember.Logger.warn('Something happened!'); // "Something happened!" will be printed to the console with a warning icon.
- ```
-
- @method warn
- @for Ember.Logger
- @param {*} arguments
- */
- warn: consoleMethod('warn') || Ember.K,
- /**
- Prints the arguments to the console with an error icon, red text and a stack race.
- You can pass as many arguments as you want and they will be joined together with a space.
-
- ```javascript
- Ember.Logger.error('Danger! Danger!'); // "Danger! Danger!" will be printed to the console in red text.
- ```
-
- @method error
- @for Ember.Logger
- @param {*} arguments
- */
- error: consoleMethod('error') || Ember.K,
- /**
- Logs the arguments to the console.
- You can pass as many arguments as you want and they will be joined together with a space.
-
- ```javascript
- var foo = 1;
- Ember.Logger.info('log value of foo:', foo); // "log value of foo: 1" will be printed to the console
- ```
-
- @method info
- @for Ember.Logger
- @param {*} arguments
- */
- info: consoleMethod('info') || Ember.K,
- /**
- Logs the arguments to the console in blue text.
- You can pass as many arguments as you want and they will be joined together with a space.
-
- ```javascript
- var foo = 1;
- Ember.Logger.debug('log value of foo:', foo); // "log value of foo: 1" will be printed to the console
- ```
-
- @method debug
- @for Ember.Logger
- @param {*} arguments
- */
- debug: consoleMethod('debug') || consoleMethod('info') || Ember.K,
- /**
-
- If the value passed into Ember.Logger.assert is not truthy it will throw an error with a stack trace.
-
- ```javascript
- Ember.Logger.assert(true); // undefined
- Ember.Logger.assert(true === false); // Throws an Assertion failed error.
- ```
-
- @method assert
- @for Ember.Logger
- @param {Boolean} bool Value to test
- */
- assert: consoleMethod('assert') || assertPolyfill
-};
-
-
-// ..........................................................
-// ERROR HANDLING
-//
-
-/**
- A function may be assigned to `Ember.onerror` to be called when Ember
- internals encounter an error. This is useful for specialized error handling
- and reporting code.
-
- ```javascript
- Ember.onerror = function(error) {
- Em.$.ajax('/report-error', 'POST', {
- stack: error.stack,
- otherInformation: 'whatever app state you want to provide'
- });
- };
- ```
-
- @event onerror
- @for Ember
- @param {Exception} error the error object
-*/
-Ember.onerror = null;
-
-/**
- @private
-
- Wrap code block in a try/catch if `Ember.onerror` is set.
-
- @method handleErrors
- @for Ember
- @param {Function} func
- @param [context]
-*/
-Ember.handleErrors = function(func, context) {
- // Unfortunately in some browsers we lose the backtrace if we rethrow the existing error,
- // so in the event that we don't have an `onerror` handler we don't wrap in a try/catch
- if ('function' === typeof Ember.onerror) {
- try {
- return func.call(context || this);
- } catch (error) {
- Ember.onerror(error);
- }
- } else {
- return func.call(context || this);
- }
-};
-
-/**
Merge the contents of two objects together into the first object.
```javascript
Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'}
var a = {first: 'Yehuda'}, b = {last: 'Katz'};
@@ -925,15 +767,97 @@
})();
(function() {
+var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
+
/**
+ A subclass of the JavaScript Error object for use in Ember.
+
+ @class Error
+ @namespace Ember
+ @extends Error
+ @constructor
+*/
+Ember.Error = function() {
+ var tmp = Error.apply(this, arguments);
+
+ // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
+ for (var idx = 0; idx < errorProps.length; idx++) {
+ this[errorProps[idx]] = tmp[errorProps[idx]];
+ }
+};
+
+Ember.Error.prototype = Ember.create(Error.prototype);
+
+// ..........................................................
+// ERROR HANDLING
+//
+
+/**
+ A function may be assigned to `Ember.onerror` to be called when Ember
+ internals encounter an error. This is useful for specialized error handling
+ and reporting code.
+
+ ```javascript
+ Ember.onerror = function(error) {
+ Em.$.ajax('/report-error', 'POST', {
+ stack: error.stack,
+ otherInformation: 'whatever app state you want to provide'
+ });
+ };
+ ```
+
+ @event onerror
+ @for Ember
+ @param {Exception} error the error object
+*/
+Ember.onerror = null;
+
+/**
+ @private
+
+ Wrap code block in a try/catch if `Ember.onerror` is set.
+
+ @method handleErrors
+ @for Ember
+ @param {Function} func
+ @param [context]
+*/
+Ember.handleErrors = function(func, context) {
+ // Unfortunately in some browsers we lose the backtrace if we rethrow the existing error,
+ // so in the event that we don't have an `onerror` handler we don't wrap in a try/catch
+ if ('function' === typeof Ember.onerror) {
+ try {
+ return func.call(context || this);
+ } catch (error) {
+ Ember.onerror(error);
+ }
+ } else {
+ return func.call(context || this);
+ }
+};
+
+})();
+
+
+
+(function() {
+/**
@module ember-metal
*/
+/**
+ @private
+
+ Prefix used for guids through out Ember.
+
+*/
+Ember.GUID_PREFIX = 'ember';
+
var o_defineProperty = Ember.platform.defineProperty,
o_create = Ember.create,
// Used for guid generation...
GUID_KEY = '__ember'+ (+ new Date()),
uuid = 0,
@@ -983,17 +907,17 @@
@param {String} [prefix] Prefix to place in front of the guid. Useful when you want to
separate the guid into separate namespaces.
@return {String} the guid
*/
Ember.generateGuid = function generateGuid(obj, prefix) {
- if (!prefix) prefix = 'ember';
+ if (!prefix) prefix = Ember.GUID_PREFIX;
var ret = (prefix + (uuid++));
if (obj) {
GUID_DESC.value = ret;
o_defineProperty(obj, GUID_KEY, GUID_DESC);
}
- return ret ;
+ return ret;
};
/**
@private
@@ -1987,11 +1911,11 @@
target = get(target, key);
path = path.slice(key.length+1);
}
// must return some kind of path to be valid else other things will break.
- if (!path || path.length===0) throw new Error('Invalid Path');
+ if (!path || path.length===0) throw new Ember.Error('Invalid Path');
return [ target, path ];
};
var getPath = Ember._getPath = function(root, path) {
@@ -2028,11 +1952,10 @@
return value;
};
Ember.get = get;
-Ember.getPath = Ember.deprecateFunc('getPath is deprecated since get now supports paths', Ember.get);
})();
@@ -2855,23 +2778,22 @@
if (path !== 'this') {
root = getPath(root, path);
}
if (!keyName || keyName.length === 0) {
- throw new Error('You passed an empty path');
+ throw new Ember.Error('You passed an empty path');
}
if (!root) {
if (tolerant) { return; }
- else { throw new Error('Object in path '+path+' could not be found or was destroyed.'); }
+ else { throw new Ember.Error('Object in path '+path+' could not be found or was destroyed.'); }
}
return set(root, keyName, value);
}
Ember.set = set;
-Ember.setPath = Ember.deprecateFunc('setPath is deprecated since set now supports paths', Ember.set);
/**
Error-tolerant form of `Ember.set`. Will not blow up if any part of the
chain is `undefined`, `null`, or destroyed.
@@ -2885,11 +2807,10 @@
@param {Object} value The value to set
*/
Ember.trySet = function(root, path, value) {
return set(root, path, value, true);
};
-Ember.trySetPath = Ember.deprecateFunc('trySetPath has been renamed to trySet', Ember.trySet);
})();
@@ -3271,11 +3192,147 @@
})();
(function() {
+function consoleMethod(name) {
+ var consoleObj;
+ if (Ember.imports.console) {
+ consoleObj = Ember.imports.console;
+ } else if (typeof console !== 'undefined') {
+ consoleObj = console;
+ }
+
+ var method = typeof consoleObj === 'object' ? consoleObj[name] : null;
+
+ if (method) {
+ // Older IE doesn't support apply, but Chrome needs it
+ if (method.apply) {
+ return function() {
+ method.apply(consoleObj, arguments);
+ };
+ } else {
+ return function() {
+ var message = Array.prototype.join.call(arguments, ', ');
+ method(message);
+ };
+ }
+ }
+}
+
+function assertPolyfill(test, message) {
+ if (!test) {
+ try {
+ // attempt to preserve the stack
+ throw new Ember.Error("assertion failed: " + message);
+ } catch(error) {
+ setTimeout(function() {
+ throw error;
+ }, 0);
+ }
+ }
+}
+
/**
+ Inside Ember-Metal, simply uses the methods from `imports.console`.
+ Override this to provide more robust logging functionality.
+
+ @class Logger
+ @namespace Ember
+*/
+Ember.Logger = {
+ /**
+ Logs the arguments to the console.
+ You can pass as many arguments as you want and they will be joined together with a space.
+
+ ```javascript
+ var foo = 1;
+ Ember.Logger.log('log value of foo:', foo); // "log value of foo: 1" will be printed to the console
+ ```
+
+ @method log
+ @for Ember.Logger
+ @param {*} arguments
+ */
+ log: consoleMethod('log') || Ember.K,
+ /**
+ Prints the arguments to the console with a warning icon.
+ You can pass as many arguments as you want and they will be joined together with a space.
+
+ ```javascript
+ Ember.Logger.warn('Something happened!'); // "Something happened!" will be printed to the console with a warning icon.
+ ```
+
+ @method warn
+ @for Ember.Logger
+ @param {*} arguments
+ */
+ warn: consoleMethod('warn') || Ember.K,
+ /**
+ Prints the arguments to the console with an error icon, red text and a stack race.
+ You can pass as many arguments as you want and they will be joined together with a space.
+
+ ```javascript
+ Ember.Logger.error('Danger! Danger!'); // "Danger! Danger!" will be printed to the console in red text.
+ ```
+
+ @method error
+ @for Ember.Logger
+ @param {*} arguments
+ */
+ error: consoleMethod('error') || Ember.K,
+ /**
+ Logs the arguments to the console.
+ You can pass as many arguments as you want and they will be joined together with a space.
+
+ ```javascript
+ var foo = 1;
+ Ember.Logger.info('log value of foo:', foo); // "log value of foo: 1" will be printed to the console
+ ```
+
+ @method info
+ @for Ember.Logger
+ @param {*} arguments
+ */
+ info: consoleMethod('info') || Ember.K,
+ /**
+ Logs the arguments to the console in blue text.
+ You can pass as many arguments as you want and they will be joined together with a space.
+
+ ```javascript
+ var foo = 1;
+ Ember.Logger.debug('log value of foo:', foo); // "log value of foo: 1" will be printed to the console
+ ```
+
+ @method debug
+ @for Ember.Logger
+ @param {*} arguments
+ */
+ debug: consoleMethod('debug') || consoleMethod('info') || Ember.K,
+ /**
+
+ If the value passed into Ember.Logger.assert is not truthy it will throw an error with a stack trace.
+
+ ```javascript
+ Ember.Logger.assert(true); // undefined
+ Ember.Logger.assert(true === false); // Throws an Assertion failed error.
+ ```
+
+ @method assert
+ @for Ember.Logger
+ @param {Boolean} bool Value to test
+ */
+ assert: consoleMethod('assert') || assertPolyfill
+};
+
+
+})();
+
+
+
+(function() {
+/**
@module ember-metal
*/
var META_KEY = Ember.META_KEY,
metaFor = Ember.meta,
@@ -3896,10 +3953,49 @@
})();
(function() {
+/**
+ @module ember-metal
+*/
+
+var forEach = Ember.EnumerableUtils.forEach,
+ IS_BRACE_EXPANSION = /^\{([^.]*)\}$/;
+
+/**
+ Expands `pattern`, invoking `callback` for each expansion.
+
+ The only pattern supported is brace-expansion, anything else will be passed
+ once to `callback` directly. Furthermore, brace-expansion is only applied to
+ the entire pattern, not to substrings.
+
+ Example
+ ```js
+ function echo(arg){ console.log(arg); }
+
+ Ember.expandProperties('foo.bar', echo); //=> 'foo.bar'
+ Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar'
+ Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.{bar,baz}'
+ ```
+
+ @method
+ @private
+ @param {string} pattern The property pattern to expand.
+ @param {function} callback The callback to invoke. It is invoked once per
+ expansion, and is passed the expansion.
+*/
+Ember.expandProperties = function (pattern, callback) {
+
+ callback(pattern);
+};
+
+})();
+
+
+
+(function() {
var metaFor = Ember.meta, // utils.js
typeOf = Ember.typeOf, // utils.js
ChainNode = Ember._ChainNode; // chains.js
// get the chains for the current object. If the current object has
@@ -3949,10 +4045,11 @@
*/
var metaFor = Ember.meta, // utils.js
GUID_KEY = Ember.GUID_KEY, // utils.js
META_KEY = Ember.META_KEY, // utils.js
+ expandProperties = Ember.expandProperties,
removeChainWatcher = Ember.removeChainWatcher,
watchKey = Ember.watchKey, // watch_key.js
unwatchKey = Ember.unwatchKey,
watchPath = Ember.watchPath, // watch_path.js
unwatchPath = Ember.unwatchPath,
@@ -3977,37 +4074,41 @@
@method watch
@for Ember
@param obj
@param {String} keyName
*/
-Ember.watch = function(obj, keyPath) {
+Ember.watch = function(obj, _keyPath) {
// can't watch length on Array - it is special...
- if (keyPath === 'length' && typeOf(obj) === 'array') { return; }
+ if (_keyPath === 'length' && typeOf(obj) === 'array') { return; }
- if (isKeyName(keyPath)) {
- watchKey(obj, keyPath);
- } else {
- watchPath(obj, keyPath);
- }
+ expandProperties(_keyPath, function (keyPath) {
+ if (isKeyName(keyPath)) {
+ watchKey(obj, keyPath);
+ } else {
+ watchPath(obj, keyPath);
+ }
+ });
};
Ember.isWatching = function isWatching(obj, key) {
var meta = obj[META_KEY];
return (meta && meta.watching[key]) > 0;
};
Ember.watch.flushPending = Ember.flushPendingChains;
-Ember.unwatch = function(obj, keyPath) {
+Ember.unwatch = function(obj, _keyPath) {
// can't watch length on Array - it is special...
- if (keyPath === 'length' && typeOf(obj) === 'array') { return; }
+ if (_keyPath === 'length' && typeOf(obj) === 'array') { return; }
- if (isKeyName(keyPath)) {
- unwatchKey(obj, keyPath);
- } else {
- unwatchPath(obj, keyPath);
- }
+ expandProperties(_keyPath, function (keyPath) {
+ if (isKeyName(keyPath)) {
+ unwatchKey(obj, keyPath);
+ } else {
+ unwatchPath(obj, keyPath);
+ }
+ });
};
/**
@private
@@ -4022,11 +4123,11 @@
Ember.rewatch = function(obj) {
var m = metaFor(obj, false), chains = m.chains;
// make sure the object has its own guid.
if (GUID_KEY in obj && !obj.hasOwnProperty(GUID_KEY)) {
- generateGuid(obj, 'ember');
+ generateGuid(obj);
}
// make sure any chained watchers update.
if (chains && chains.value() !== obj) {
m.chains = chains.copy(obj);
@@ -4089,10 +4190,11 @@
var get = Ember.get,
set = Ember.set,
metaFor = Ember.meta,
+ expandProperties = Ember.expandProperties,
a_slice = [].slice,
o_create = Ember.create,
META_KEY = Ember.META_KEY,
watch = Ember.watch,
unwatch = Ember.unwatch;
@@ -4348,13 +4450,17 @@
@param {String} path* zero or more property paths
@return {Ember.ComputedProperty} this
@chainable
*/
ComputedPropertyPrototype.property = function() {
+ function addArg(arg) {
+ args.push(arg);
+ }
+
var args = [];
for (var i = 0, l = arguments.length; i < l; i++) {
- args.push(arguments[i]);
+ expandProperties(arguments[i], addArg);
}
this._dependentKeys = args;
return this;
};
@@ -4479,11 +4585,11 @@
hadCachedValue = false,
cache = meta.cache,
funcArgLength, cachedValue, ret;
if (this._readOnly) {
- throw new Error('Cannot Set: ' + keyName + ' on: ' + obj.toString() );
+ throw new Ember.Error('Cannot Set: ' + keyName + ' on: ' + obj.toString() );
}
this._suspended = obj;
try {
@@ -4569,11 +4675,11 @@
args = a_slice.call(arguments, 0, -1);
func = a_slice.call(arguments, -1)[0];
}
if (typeof func !== "function") {
- throw new Error("Computed Property declared without a property function");
+ throw new Ember.Error("Computed Property declared without a property function");
}
var cp = new ComputedProperty(func);
if (args) {
@@ -5203,12 +5309,13 @@
// Ember.tryFinally
/**
@module ember-metal
*/
-var AFTER_OBSERVERS = ':change';
-var BEFORE_OBSERVERS = ':before';
+var AFTER_OBSERVERS = ':change',
+ BEFORE_OBSERVERS = ':before',
+ expandProperties = Ember.expandProperties;
function changeEvent(keyName) {
return keyName+AFTER_OBSERVERS;
}
@@ -5221,13 +5328,16 @@
@param obj
@param {String} path
@param {Object|Function} targetOrMethod
@param {Function|String} [method]
*/
-Ember.addObserver = function(obj, path, target, method) {
- Ember.addListener(obj, changeEvent(path), target, method);
- Ember.watch(obj, path);
+Ember.addObserver = function(obj, _path, target, method) {
+ expandProperties(_path, function (path) {
+ Ember.addListener(obj, changeEvent(path), target, method);
+ Ember.watch(obj, path);
+ });
+
return this;
};
Ember.observersFor = function(obj, path) {
return Ember.listenersFor(obj, changeEvent(path));
@@ -5238,26 +5348,30 @@
@param obj
@param {String} path
@param {Object|Function} targetOrMethod
@param {Function|String} [method]
*/
-Ember.removeObserver = function(obj, path, target, method) {
- Ember.unwatch(obj, path);
- Ember.removeListener(obj, changeEvent(path), target, method);
+Ember.removeObserver = function(obj, _path, target, method) {
+ expandProperties(_path, function (path) {
+ Ember.unwatch(obj, path);
+ Ember.removeListener(obj, changeEvent(path), target, method);
+ });
return this;
};
/**
@method addBeforeObserver
@param obj
@param {String} path
@param {Object|Function} targetOrMethod
@param {Function|String} [method]
*/
-Ember.addBeforeObserver = function(obj, path, target, method) {
- Ember.addListener(obj, beforeEvent(path), target, method);
- Ember.watch(obj, path);
+Ember.addBeforeObserver = function(obj, _path, target, method) {
+ expandProperties(_path, function (path) {
+ Ember.addListener(obj, beforeEvent(path), target, method);
+ Ember.watch(obj, path);
+ });
return this;
};
// Suspend observer during callback.
//
@@ -5292,15 +5406,18 @@
@param obj
@param {String} path
@param {Object|Function} targetOrMethod
@param {Function|String} [method]
*/
-Ember.removeBeforeObserver = function(obj, path, target, method) {
- Ember.unwatch(obj, path);
- Ember.removeListener(obj, beforeEvent(path), target, method);
+Ember.removeBeforeObserver = function(obj, _path, target, method) {
+ expandProperties(_path, function (path) {
+ Ember.unwatch(obj, path);
+ Ember.removeListener(obj, beforeEvent(path), target, method);
+ });
return this;
};
+
})();
(function() {
@@ -5731,11 +5848,11 @@
},
throttle: function(target, method /* , args, wait */) {
var self = this,
args = arguments,
- wait = pop.call(args),
+ wait = parseInt(pop.call(args), 10),
throttler;
for (var i = 0, l = throttlers.length; i < l; i++) {
throttler = throttlers[i];
if (throttler[0] === target && throttler[1] === method) { return; } // do nothing
@@ -5766,17 +5883,18 @@
immediate = pop.call(args),
wait,
index,
debouncee;
- if (typeof immediate === "number") {
+ if (typeof immediate === "number" || typeof immediate === "string") {
wait = immediate;
immediate = false;
} else {
wait = pop.call(args);
}
+ wait = parseInt(wait, 10);
// Remove debouncee
index = findDebouncee(target, method);
if (index !== -1) {
debouncee = debouncees[index];
@@ -7038,11 +7156,13 @@
return baseValue.concat(value);
} else {
return Ember.makeArray(baseValue).concat(value);
}
} else {
- return Ember.makeArray(value);
+ // Make sure this mixin has its own array so it is not
+ // accidentally mutated by another child's interactions
+ return Ember.makeArray(value).slice();
}
}
function applyMergedProperties(obj, key, value, values) {
var baseValue = values[key] || obj[key];
@@ -7555,13 +7675,13 @@
/**
Specify a method that observes property changes.
```javascript
Ember.Object.extend({
- valueObserver: Ember.observer(function() {
+ valueObserver: Ember.observer('value', function() {
// Executes whenever the "value" property changes
- }, 'value')
+ })
});
```
In the future this method may become asynchronous. If you want to ensure
synchronous behavior, use `immediateObserver`.
@@ -7569,28 +7689,41 @@
Also available as `Function.prototype.observes` if prototype extensions are
enabled.
@method observer
@for Ember
- @param {Function} func
@param {String} propertyNames*
+ @param {Function} func
@return func
*/
-Ember.observer = function(func) {
- var paths = a_slice.call(arguments, 1);
+Ember.observer = function() {
+ var func = a_slice.call(arguments, -1)[0];
+ var paths = a_slice.call(arguments, 0, -1);
+
+ if (typeof func !== "function") {
+ // revert to old, soft-deprecated argument ordering
+
+ func = arguments[0];
+ paths = a_slice.call(arguments, 1);
+ }
+
+ if (typeof func !== "function") {
+ throw new Ember.Error("Ember.observer called without a function");
+ }
+
func.__ember_observes__ = paths;
return func;
};
/**
Specify a method that observes property changes.
```javascript
Ember.Object.extend({
- valueObserver: Ember.immediateObserver(function() {
+ valueObserver: Ember.immediateObserver('value', function() {
// Executes whenever the "value" property changes
- }, 'value')
+ })
});
```
In the future, `Ember.observer` may become asynchronous. In this event,
`Ember.immediateObserver` will maintain the synchronous behavior.
@@ -7598,12 +7731,12 @@
Also available as `Function.prototype.observesImmediately` if prototype extensions are
enabled.
@method immediateObserver
@for Ember
- @param {Function} func
@param {String} propertyNames*
+ @param {Function} func
@return func
*/
Ember.immediateObserver = function() {
for (var i=0, l=arguments.length; i<l; i++) {
var arg = arguments[i];
@@ -7626,40 +7759,53 @@
```javascript
App.PersonView = Ember.View.extend({
friends: [{ name: 'Tom' }, { name: 'Stefan' }, { name: 'Kris' }],
- valueWillChange: Ember.beforeObserver(function(obj, keyName) {
+ valueWillChange: Ember.beforeObserver('content.value', function(obj, keyName) {
this.changingFrom = obj.get(keyName);
- }, 'content.value'),
+ }),
- valueDidChange: Ember.observer(function(obj, keyName) {
+ valueDidChange: Ember.observer('content.value', function(obj, keyName) {
// only run if updating a value already in the DOM
if (this.get('state') === 'inDOM') {
var color = obj.get(keyName) > this.changingFrom ? 'green' : 'red';
// logic
}
- }, 'content.value'),
+ }),
- friendsDidChange: Ember.observer(function(obj, keyName) {
+ friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) {
// some logic
// obj.get(keyName) returns friends array
- }, 'friends.@each.name')
+ })
});
```
Also available as `Function.prototype.observesBefore` if prototype extensions are
enabled.
@method beforeObserver
@for Ember
- @param {Function} func
@param {String} propertyNames*
+ @param {Function} func
@return func
*/
-Ember.beforeObserver = function(func) {
- var paths = a_slice.call(arguments, 1);
+Ember.beforeObserver = function() {
+ var func = a_slice.call(arguments, -1)[0];
+ var paths = a_slice.call(arguments, 0, -1);
+
+ if (typeof func !== "function") {
+ // revert to old, soft-deprecated argument ordering
+
+ func = arguments[0];
+ paths = a_slice.call(arguments, 1);
+ }
+
+ if (typeof func !== "function") {
+ throw new Ember.Error("Ember.beforeObserver called without a function");
+ }
+
func.__ember_observesBefore__ = paths;
return func;
};
})();
@@ -9570,35 +9716,10 @@
return ret;
};
}
-// ..........................................................
-// ERROR
-//
-
-var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-
-/**
- A subclass of the JavaScript Error object for use in Ember.
-
- @class Error
- @namespace Ember
- @extends Error
- @constructor
-*/
-Ember.Error = function() {
- var tmp = Error.apply(this, arguments);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-};
-
-Ember.Error.prototype = Ember.create(Error.prototype);
-
})();
(function() {
@@ -10565,12 +10686,41 @@
Ember.propertyDidChange(this, '[]');
return this ;
}
-}) ;
+});
+
+ Ember.Enumerable.reopen({
+ /**
+ Converts the enumerable into an array and sorts by the keys
+ specified in the argument.
+
+ You may provide multiple arguments to sort by multiple properties.
+
+ @method sortBy
+ @param {String} property name(s) to sort on
+ @return {Array} The sorted array.
+ */
+ sortBy: function() {
+ var sortKeys = arguments;
+ return this.toArray().sort(function(a, b){
+ for(var i = 0; i < sortKeys.length; i++) {
+ var key = sortKeys[i],
+ propA = get(a, key),
+ propB = get(b, key);
+ // return 1 or -1 else continue to the next sortKey
+ var compareValue = Ember.compare(propA, propB);
+ if (compareValue) { return compareValue; }
+ }
+ return 0;
+ });
+ }
+ });
+
+
})();
(function() {
@@ -11001,11 +11151,11 @@
})();
(function() {
-var get = Ember.get,
+var e_get = Ember.get,
set = Ember.set,
guidFor = Ember.guidFor,
metaFor = Ember.meta,
propertyWillChange = Ember.propertyWillChange,
propertyDidChange = Ember.propertyDidChange,
@@ -11020,10 +11170,20 @@
// Here we explicitly don't allow `@each.foo`; it would require some special
// testing, but there's no particular reason why it should be disallowed.
eachPropertyPattern = /^(.*)\.@each\.(.*)/,
doubleEachPropertyPattern = /(.*\.@each){2,}/;
+function get(obj, key) {
+
+ if (key === '@this') {
+ return obj;
+ }
+
+
+ return e_get(obj, key);
+}
+
/*
Tracks changes to dependent arrays, as well as to properties of items in
dependent arrays.
@class DependentArraysObserver
@@ -11211,25 +11371,29 @@
var removedItem = this.callbacks.removedItem,
changeMeta,
guid = guidFor(dependentArray),
dependentKey = this.dependentKeysByGuid[guid],
itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || [],
+ length = get(dependentArray, 'length'),
+ normalizedIndex = normalizeIndex(length, index, 1),
item,
itemIndex,
sliceIndex,
observerContexts;
- observerContexts = this.trackRemove(dependentKey, index, removedCount);
+ observerContexts = this.trackRemove(dependentKey, normalizedIndex, removedCount);
function removeObservers(propertyKey) {
observerContexts[sliceIndex].destroyed = true;
removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver);
removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer);
}
for (sliceIndex = removedCount - 1; sliceIndex >= 0; --sliceIndex) {
- itemIndex = index + sliceIndex;
+ itemIndex = normalizedIndex + sliceIndex;
+ if (itemIndex >= length) { break; }
+
item = dependentArray.objectAt(itemIndex);
forEach(itemPropertyKeys, removeObservers, this);
changeMeta = createChangeMeta(dependentArray, item, itemIndex, this.instanceMeta.propertyName, this.cp);
@@ -11242,30 +11406,32 @@
var addedItem = this.callbacks.addedItem,
guid = guidFor(dependentArray),
dependentKey = this.dependentKeysByGuid[guid],
observerContexts = new Array(addedCount),
itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey],
+ length = get(dependentArray, 'length'),
+ normalizedIndex = normalizeIndex(length, index, addedCount),
changeMeta,
observerContext;
- forEach(dependentArray.slice(index, index + addedCount), function (item, sliceIndex) {
+ forEach(dependentArray.slice(normalizedIndex, normalizedIndex + addedCount), function (item, sliceIndex) {
if (itemPropertyKeys) {
observerContext =
observerContexts[sliceIndex] =
- this.createPropertyObserverContext(dependentArray, index + sliceIndex, this.trackedArraysByGuid[dependentKey]);
+ this.createPropertyObserverContext(dependentArray, normalizedIndex + sliceIndex, this.trackedArraysByGuid[dependentKey]);
forEach(itemPropertyKeys, function (propertyKey) {
addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver);
addObserver(item, propertyKey, this, observerContext.observer);
}, this);
}
- changeMeta = createChangeMeta(dependentArray, item, index + sliceIndex, this.instanceMeta.propertyName, this.cp);
+ changeMeta = createChangeMeta(dependentArray, item, normalizedIndex + sliceIndex, this.instanceMeta.propertyName, this.cp);
this.setValue( addedItem.call(
this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta));
}, this);
- this.trackAdd(dependentKey, index, observerContexts);
+ this.trackAdd(dependentKey, normalizedIndex, observerContexts);
},
itemPropertyWillChange: function (obj, keyName, array, observerContext) {
var guid = guidFor(obj);
@@ -11302,10 +11468,20 @@
}
this.changedItems = {};
}
};
+function normalizeIndex(length, index, newItemsOffset) {
+ if (index < 0) {
+ return Math.max(0, length + index);
+ } else if (index < length) {
+ return index;
+ } else /* index > length */ {
+ return Math.min(length - newItemsOffset, index);
+ }
+}
+
function createChangeMeta(dependentArray, item, index, propertyName, property, previousValues) {
var meta = {
arrayChanged: dependentArray,
index: index,
item: item,
@@ -11666,10 +11842,41 @@
}
});
};
```
+ Dependent keys may refer to `@this` to observe changes to the object itself,
+ which must be array-like, rather than a property of the object. This is
+ mostly useful for array proxies, to ensure objects are retrieved via
+ `objectAtContent`. This is how you could sort items by properties defined on an item controller.
+
+ Example
+
+ ```javascript
+ App.PeopleController = Ember.ArrayController.extend({
+ itemController: 'person',
+
+ sortedPeople: Ember.computed.sort('@this.@each.reversedName', function(personA, personB) {
+ // `reversedName` isn't defined on Person, but we have access to it via
+ // the item controller App.PersonController. If we'd used
+ // `content.@each.reversedName` above, we would be getting the objects
+ // directly and not have access to `reversedName`.
+ //
+ var reversedNameA = get(personA, 'reversedName'),
+ reversedNameB = get(personB, 'reversedName');
+
+ return Ember.compare(reversedNameA, reversedNameB);
+ })
+ });
+
+ App.PersonController = Ember.ObjectController.extend({
+ reversedName: function () {
+ return reverse(get(this, 'name'));
+ }.property('name')
+ })
+ ```
+
@method reduceComputed
@for Ember
@param {String} [dependentKeys*]
@param {Object} options
@return {Ember.ComputedProperty}
@@ -12848,13 +13055,14 @@
@return {String} The capitalized string.
*/
capitalize: function(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
}
-
};
+
+
})();
(function() {
@@ -12873,10 +13081,11 @@
dasherize = Ember.String.dasherize,
underscore = Ember.String.underscore,
capitalize = Ember.String.capitalize,
classify = Ember.String.classify;
+
if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
/**
See [Ember.String.fmt](/api/classes/Ember.String.html#method_fmt).
@@ -12965,10 +13174,11 @@
*/
String.prototype.capitalize = function() {
return capitalize(this);
};
+
}
})();
@@ -13902,11 +14112,11 @@
Computed properties are methods defined with the `property` modifier
declared at the end, such as:
```javascript
fullName: function() {
- return this.getEach('firstName', 'lastName').compact().join(' ');
+ return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
```
When you call `get` on a computed property, the function will be
called and the return value will be returned instead of the function
@@ -14200,33 +14410,10 @@
hasObserverFor: function(key) {
return Ember.hasListeners(this, key+':change');
},
/**
- @deprecated
- @method getPath
- @param {String} path The property path to retrieve
- @return {Object} The property value or undefined.
- */
- getPath: function(path) {
- Ember.deprecate("getPath is deprecated since get now supports paths");
- return this.get(path);
- },
-
- /**
- @deprecated
- @method setPath
- @param {String} path The path to the property that will be set
- @param {Object} value The value to set or `null`.
- @return {Ember.Observable}
- */
- setPath: function(path, value) {
- Ember.deprecate("setPath is deprecated since set now supports paths");
- return this.set(path, value);
- },
-
- /**
Retrieves the value of a property, or a default value in the case that the
property returns `undefined`.
```javascript
person.getWithDefault('lastName', 'Doe');
@@ -14699,11 +14886,11 @@
/**
@module ember
@submodule ember-runtime
*/
-var get = Ember.get;
+var get = Ember.get, typeOf = Ember.typeOf;
/**
The `Ember.ActionHandler` mixin implements support for moving an `actions`
property to an `_actions` property at extend time, and adding `_actions`
to the object's mergedProperties list.
@@ -14725,13 +14912,25 @@
is practically of little consequence. This may change in the future.
@method willMergeMixin
*/
willMergeMixin: function(props) {
- if (props.actions && !props._actions) {
- props._actions = Ember.merge(props._actions || {}, props.actions);
- delete props.actions;
+ var hashName;
+
+ if (!props._actions) {
+ if (typeOf(props.actions) === 'object') {
+ hashName = 'actions';
+ } else if (typeOf(props.events) === 'object') {
+ Ember.deprecate('Action handlers contained in an `events` object are deprecated in favor of putting them in an `actions` object', false);
+ hashName = 'events';
+ }
+
+ if (hashName) {
+ props._actions = Ember.merge(props._actions || {}, props[hashName]);
+ }
+
+ delete props[hashName];
}
},
send: function(actionName) {
var args = [].slice.call(arguments, 1), target;
@@ -15385,10 +15584,18 @@
if (otherOp.type === op.type) {
op.count += otherOp.count;
this._operations.splice(index+1, 1);
}
}
+ },
+
+ toString: function () {
+ var str = "";
+ forEach(this._operations, function (operation) {
+ str += " " + operation.type + ":" + operation.count;
+ });
+ return str.substring(1);
}
};
})();
@@ -15471,14 +15678,11 @@
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
- if (properties === null || typeof properties !== 'object') {
- Ember.assert("Ember.Object.create only accepts objects.");
- continue;
- }
+ if (Ember.typeOf(properties) !== 'object') { continue; }
var keyNames = Ember.keys(properties);
for (var j = 0, ll = keyNames.length; j < ll; j++) {
var keyName = keyNames[j];
if (!properties.hasOwnProperty(keyName)) { continue; }
@@ -15666,11 +15870,11 @@
are also concatenated, in addition to `classNames`.
This feature is available for you to use throughout the Ember object model,
although typical app developers are likely to use it infrequently. Since
it changes expectations about behavior of properties, you should properly
- document its usage in each individual concatenated property (to not
+ document its usage in each individual concatenated property (to not
mislead your users to think they can override the property in a subclass).
@property concatenatedProperties
@type Array
@default null
@@ -15902,11 +16106,11 @@
Class.superclass = this;
Class.__super__ = this.prototype;
proto = Class.prototype = o_create(this.prototype);
proto.constructor = Class;
- generateGuid(proto, 'ember');
+ generateGuid(proto);
meta(proto).proto = proto; // this will disable observers on prototype
Class.ClassMixin.apply(Class);
return Class;
},
@@ -15967,14 +16171,14 @@
if (arguments.length>0) { this._initProperties(arguments); }
return new C();
},
/**
-
+
Augments a constructor's prototype with additional
properties and functions:
-
+
```javascript
MyObject = Ember.Object.extend({
name: 'an object'
});
@@ -15990,11 +16194,11 @@
o2 = MyObject.create();
o2.say("hello"); // logs "hello"
o.say("goodbye"); // logs "goodbye"
```
-
+
To add functions and properties to the constructor itself,
see `reopenClass`
@method reopen
*/
@@ -16004,26 +16208,26 @@
return this;
},
/**
Augments a constructor's own properties and functions:
-
+
```javascript
MyObject = Ember.Object.extend({
name: 'an object'
});
MyObject.reopenClass({
canBuild: false
});
-
+
MyObject.canBuild; // false
o = MyObject.create();
```
- In other words, this creates static properties and functions for the class. These are only available on the class
+ In other words, this creates static properties and functions for the class. These are only available on the class
and not on any instance of that class.
```javascript
App.Person = Ember.Object.extend({
name : "",
@@ -16049,19 +16253,19 @@
tom.sayHello(); // "Hello. My name is Tom Dale"
yehuda.sayHello(); // "Hello. My name is Yehuda Katz"
alert(App.Person.species); // "Homo sapiens"
```
- Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda`
+ Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda`
variables. They are only valid on `App.Person`.
-
+
To add functions and properties to instances of
a constructor by extending the constructor's prototype
see `reopen`
-
+
@method reopenClass
- */
+ */
reopenClass: function() {
reopen.apply(this.ClassMixin, arguments);
applyMixin(this, arguments, false);
return this;
},
@@ -16498,13 +16702,13 @@
Invoked when the content property is about to change. Notifies observers that the
entire array content will change.
@method _contentWillChange
*/
- _contentWillChange: Ember.beforeObserver(function() {
+ _contentWillChange: Ember.beforeObserver('content', function() {
this._teardownContent();
- }, 'content'),
+ }),
_teardownContent: function() {
var content = get(this, 'content');
if (content) {
@@ -16524,17 +16728,17 @@
Invoked when the content property changes. Notifies observers that the
entire array content has changed.
@method _contentDidChange
*/
- _contentDidChange: Ember.observer(function() {
+ _contentDidChange: Ember.observer('content', function() {
var content = get(this, 'content');
Ember.assert("Can't set ArrayProxy's content to itself", content !== this);
this._setupContent();
- }, 'content'),
+ }),
_setupContent: function() {
var content = get(this, 'content');
if (content) {
@@ -16543,31 +16747,31 @@
didChange: 'contentArrayDidChange'
});
}
},
- _arrangedContentWillChange: Ember.beforeObserver(function() {
+ _arrangedContentWillChange: Ember.beforeObserver('arrangedContent', function() {
var arrangedContent = get(this, 'arrangedContent'),
len = arrangedContent ? get(arrangedContent, 'length') : 0;
this.arrangedContentArrayWillChange(this, 0, len, undefined);
this.arrangedContentWillChange(this);
this._teardownArrangedContent(arrangedContent);
- }, 'arrangedContent'),
+ }),
- _arrangedContentDidChange: Ember.observer(function() {
+ _arrangedContentDidChange: Ember.observer('arrangedContent', function() {
var arrangedContent = get(this, 'arrangedContent'),
len = arrangedContent ? get(arrangedContent, 'length') : 0;
Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this);
this._setupArrangedContent();
this.arrangedContentDidChange(this);
this.arrangedContentArrayDidChange(this, 0, undefined, len);
- }, 'arrangedContent'),
+ }),
_setupArrangedContent: function() {
var arrangedContent = get(this, 'arrangedContent');
if (arrangedContent) {
@@ -16826,13 +17030,13 @@
@property content
@type Ember.Object
@default null
*/
content: null,
- _contentDidChange: Ember.observer(function() {
+ _contentDidChange: Ember.observer('content', function() {
Ember.assert("Can't set ObjectProxy's content to itself", this.get('content') !== this);
- }, 'content'),
+ }),
isTruthy: Ember.computed.bool('content'),
_debugContainerKey: null,
@@ -17119,11 +17323,11 @@
// replaced range. Otherwise, pass the full remaining array length
// since everything has shifted
var len = objects ? get(objects, 'length') : 0;
this.arrayContentWillChange(idx, amt, len);
- if (!objects || objects.length === 0) {
+ if (len === 0) {
this.splice(idx, amt);
} else {
replace(this, idx, amt, objects);
}
@@ -18050,11 +18254,11 @@
}
return content;
}),
- _contentWillChange: Ember.beforeObserver(function() {
+ _contentWillChange: Ember.beforeObserver('content', function() {
var content = get(this, 'content'),
sortProperties = get(this, 'sortProperties');
if (content && sortProperties) {
forEach(content, function(item) {
@@ -18063,21 +18267,21 @@
}, this);
}, this);
}
this._super();
- }, 'content'),
+ }),
- sortAscendingWillChange: Ember.beforeObserver(function() {
+ sortAscendingWillChange: Ember.beforeObserver('sortAscending', function() {
this._lastSortAscending = get(this, 'sortAscending');
- }, 'sortAscending'),
+ }),
- sortAscendingDidChange: Ember.observer(function() {
+ sortAscendingDidChange: Ember.observer('sortAscending', function() {
if (get(this, 'sortAscending') !== this._lastSortAscending) {
var arrangedContent = get(this, 'arrangedContent');
arrangedContent.reverseObjects();
}
- }, 'sortAscending'),
+ }),
contentArrayWillChange: function(array, idx, removedCount, addedCount) {
var isSorted = get(this, 'isSorted');
if (isSorted) {