From 4f31ce57cf0a389b94c48c3f95ea3cc2ad9f180c Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 5 Dec 2013 16:43:42 +0100 Subject: [PATCH] Update Ember.js to 1.2.0 --- assets/scripts/vendor/ember.js | 7478 +++++++++++++++++++------------- 1 file changed, 4442 insertions(+), 3036 deletions(-) diff --git a/assets/scripts/vendor/ember.js b/assets/scripts/vendor/ember.js index 9a26a814..735ee4b1 100644 --- a/assets/scripts/vendor/ember.js +++ b/assets/scripts/vendor/ember.js @@ -1,7 +1,15 @@ -// Version: v1.1.0-beta.4 -// Last commit: 89513c5 (2013-10-11 15:24:11 -0700) +// ========================================================================== +// 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 + (function() { /*global __fail__*/ @@ -176,10 +184,18 @@ if (!Ember.testing) { })(); -// Version: v1.1.0-beta.4 -// Last commit: 89513c5 (2013-10-11 15:24:11 -0700) +// ========================================================================== +// 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 + (function() { var define, requireModule; @@ -243,7 +259,7 @@ var define, requireModule; @class Ember @static - @version 1.1.0-beta.4 + @version 1.2.0 */ if ('undefined' === typeof Ember) { @@ -270,10 +286,10 @@ Ember.toString = function() { return "Ember"; }; /** @property VERSION @type String - @default '1.1.0-beta.4' + @default '1.2.0' @final */ -Ember.VERSION = '1.1.0-beta.4'; +Ember.VERSION = '1.2.0'; /** Standard environmental variables. You can define these in a global `ENV` @@ -301,22 +317,40 @@ 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 the following configuration options: + + * `ENV.ENABLE_ALL_FEATURES` - force all features to be enabled. + * `ENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly + enabled/disabled. + @method isEnabled @param {string} feature */ Ember.FEATURES.isEnabled = function(feature) { - return Ember.FEATURES[feature]; + var featureValue = Ember.FEATURES[feature]; + + if (Ember.ENV.ENABLE_ALL_FEATURES) { + return true; + } else if (featureValue === true || featureValue === false || featureValue === undefined) { + return featureValue; + } else if (Ember.ENV.ENABLE_OPTIONAL_FEATURES) { + return true; + } else { + return false; + } }; // .......................................................... @@ -400,189 +434,6 @@ if ('undefined' === typeof Ember.deprecateFunc) { */ 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. @@ -686,7 +537,7 @@ var platform = Ember.platform = {}; */ Ember.create = Object.create; -// IE8 has Object.create but it couldn't treat property descripters. +// IE8 has Object.create but it couldn't treat property descriptors. if (Ember.create) { if (Ember.create({a: 1}, {a: {value: 2}}).a !== 2) { Ember.create = null; @@ -930,11 +781,93 @@ if (Ember.SHIM_ES5) { +(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, @@ -989,13 +922,13 @@ var GUID_DESC = { @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; }; /** @@ -1532,9 +1465,9 @@ var toString = Object.prototype.toString; | Return Value | Meaning | |---------------|------------------------------------------------------| - | 'string' | String primitive | - | 'number' | Number primitive | - | 'boolean' | Boolean primitive | + | 'string' | String primitive or String object. | + | 'number' | Number primitive or Number object. | + | 'boolean' | Boolean primitive or Boolean object. | | 'null' | Null value | | 'undefined' | Undefined value | | 'function' | A function | @@ -1551,8 +1484,11 @@ var toString = Object.prototype.toString; Ember.typeOf(null); // 'null' Ember.typeOf(undefined); // 'undefined' Ember.typeOf('michael'); // 'string' + Ember.typeOf(new String('michael')); // 'string' Ember.typeOf(101); // 'number' + Ember.typeOf(new Number(101)); // 'number' Ember.typeOf(true); // 'boolean' + Ember.typeOf(new Boolean(true)); // 'boolean' Ember.typeOf(Ember.makeArray); // 'function' Ember.typeOf([1,2,90]); // 'array' Ember.typeOf(Ember.Object.extend()); // 'class' @@ -1729,6 +1665,8 @@ Ember.Instrumentation.instrument = function(name, payload, callback, binding) { @param {String} [pattern] Namespaced event name. @param {Object} [object] Before and After hooks. + + @return {Subscriber} */ Ember.Instrumentation.subscribe = function(pattern, object) { var paths = pattern.split("."), path, regex = []; @@ -1993,7 +1931,7 @@ var normalizeTuple = Ember.normalizeTuple = function(target, path) { } // 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 ]; }; @@ -2034,7 +1972,6 @@ Ember.getWithDefault = function(root, key, defaultValue) { Ember.get = get; -Ember.getPath = Ember.deprecateFunc('getPath is deprecated since get now supports paths', Ember.get); })(); @@ -2680,6 +2617,7 @@ Ember.overrideChains = function(obj, keyName, m) { /** @method beginPropertyChanges @chainable + @private */ function beginPropertyChanges() { deferred++; @@ -2689,6 +2627,7 @@ Ember.beginPropertyChanges = beginPropertyChanges; /** @method endPropertyChanges + @private */ function endPropertyChanges() { deferred--; @@ -2861,19 +2800,18 @@ function setPath(root, path, value, tolerant) { } 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 @@ -2891,7 +2829,6 @@ Ember.setPath = Ember.deprecateFunc('setPath is deprecated since set now support Ember.trySet = function(root, path, value) { return set(root, path, value, true); }; -Ember.trySetPath = Ember.deprecateFunc('trySetPath has been renamed to trySet', Ember.trySet); })(); @@ -3276,6 +3213,142 @@ MapWithDefault.prototype.copy = function() { +(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 @@ -3901,6 +3974,16 @@ Ember.finishChains = function(obj) { +(function() { +/** + @module ember-metal +*/ + + +})(); + + + (function() { var metaFor = Ember.meta, // utils.js typeOf = Ember.typeOf, // utils.js @@ -3964,6 +4047,7 @@ var metaFor = Ember.meta, // utils.js generateGuid = Ember.generateGuid, IS_PATH = /[\.\*]/; + // returns true if the passed path is just a keyName function isKeyName(path) { return path==='*' || !IS_PATH.test(path); @@ -3983,15 +4067,17 @@ function isKeyName(path) { @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); - } + + if (isKeyName(_keyPath)) { + watchKey(obj, _keyPath); + } else { + watchPath(obj, _keyPath); + } + }; Ember.isWatching = function isWatching(obj, key) { @@ -4001,15 +4087,17 @@ Ember.isWatching = function isWatching(obj, key) { 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); - } + + if (isKeyName(_keyPath)) { + unwatchKey(obj, _keyPath); + } else { + unwatchPath(obj, _keyPath); + } + }; /** @@ -4028,7 +4116,7 @@ Ember.rewatch = function(obj) { // 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. @@ -4101,6 +4189,7 @@ var get = Ember.get, watch = Ember.watch, unwatch = Ember.unwatch; + // .......................................................... // DEPENDENT KEYS // @@ -4354,9 +4443,14 @@ ComputedPropertyPrototype.readOnly = function(readOnly) { @chainable */ ComputedPropertyPrototype.property = function() { + var addArg; + + var args = []; for (var i = 0, l = arguments.length; i < l; i++) { - args.push(arguments[i]); + + args.push(arguments[i]); + } this._dependentKeys = args; return this; @@ -4485,7 +4579,7 @@ ComputedPropertyPrototype.set = function(obj, keyName, value) { 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; @@ -4575,7 +4669,7 @@ Ember.computed = function(func) { } 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); @@ -4598,7 +4692,7 @@ Ember.computed = function(func) { @param {Object} obj the object whose property you want to check @param {String} key the name of the property whose cached value you want to return - @return {*} the cached value + @return {Object} the cached value */ Ember.cacheFor = function cacheFor(obj, key) { var cache = metaFor(obj, false).cache; @@ -4642,14 +4736,14 @@ function registerComputedWithProperties(name, macro) { property is null, an empty string, empty array, or empty function. Note: When using `Ember.computed.empty` to watch an array make sure to - use the `array.length` syntax so the computed can subscribe to transitions + use the `array.[]` syntax so the computed can subscribe to transitions from empty to non-empty states. Example ```javascript var ToDoList = Ember.Object.extend({ - done: Ember.computed.empty('todos.length') + done: Ember.computed.empty('todos.[]') // detect array changes }); var todoList = ToDoList.create({todos: ['Unit Test', 'Documentation', 'Release']}); todoList.get('done'); // false @@ -4674,13 +4768,13 @@ registerComputed('empty', function(dependentKey) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ hasStuff: Ember.computed.notEmpty('backpack') }); - var hampster = Hampster.create({backpack: ['Food', 'Sleeping Bag', 'Tent']}); - hampster.get('hasStuff'); // true - hampster.get('backpack').clear(); // [] - hampster.get('hasStuff'); // false + var hamster = Hamster.create({backpack: ['Food', 'Sleeping Bag', 'Tent']}); + hamster.get('hasStuff'); // true + hamster.get('backpack').clear(); // [] + hamster.get('hasStuff'); // false ``` @method computed.notEmpty @@ -4701,15 +4795,15 @@ registerComputed('notEmpty', function(dependentKey) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ isHungry: Ember.computed.none('food') }); - var hampster = Hampster.create(); - hampster.get('isHungry'); // true - hampster.set('food', 'Banana'); - hampster.get('isHungry'); // false - hampster.set('food', null); - hampster.get('isHungry'); // true + var hamster = Hamster.create(); + hamster.get('isHungry'); // true + hamster.set('food', 'Banana'); + hamster.get('isHungry'); // false + hamster.set('food', null); + hamster.get('isHungry'); // true ``` @method computed.none @@ -4753,23 +4847,23 @@ registerComputed('not', function(dependentKey) { into a boolean value. ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ hasBananas: Ember.computed.bool('numBananas') }); - var hampster = Hampster.create(); - hampster.get('hasBananas'); // false - hampster.set('numBananas', 0); - hampster.get('hasBananas'); // false - hampster.set('numBananas', 1); - hampster.get('hasBananas'); // true - hampster.set('numBananas', null); - hampster.get('hasBananas'); // false + var hamster = Hamster.create(); + hamster.get('hasBananas'); // false + hamster.set('numBananas', 0); + hamster.get('hasBananas'); // false + hamster.set('numBananas', 1); + hamster.get('hasBananas'); // true + hamster.set('numBananas', null); + hamster.get('hasBananas'); // false ``` @method computed.bool @for Ember @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which convert + @return {Ember.ComputedProperty} computed property which converts to boolean the original value for property */ registerComputed('bool', function(dependentKey) { @@ -4791,7 +4885,7 @@ registerComputed('bool', function(dependentKey) { user.get('hasValidEmail'); // false user.set('email', ''); user.get('hasValidEmail'); // false - user.set('email', 'ember_hampster@example.com'); + user.set('email', 'ember_hamster@example.com'); user.get('hasValidEmail'); // true ``` @@ -4804,7 +4898,7 @@ registerComputed('bool', function(dependentKey) { */ registerComputed('match', function(dependentKey, regexp) { var value = get(this, dependentKey); - return typeof value === 'string' ? !!value.match(regexp) : false; + return typeof value === 'string' ? regexp.test(value) : false; }); /** @@ -4814,15 +4908,15 @@ registerComputed('match', function(dependentKey, regexp) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ napTime: Ember.computed.equal('state', 'sleepy') }); - var hampster = Hampster.create(); - hampster.get('napTime'); // false - hampster.set('state', 'sleepy'); - hampster.get('napTime'); // true - hampster.set('state', 'hungry'); - hampster.get('napTime'); // false + var hamster = Hamster.create(); + hamster.get('napTime'); // false + hamster.set('state', 'sleepy'); + hamster.get('napTime'); // true + hamster.set('state', 'hungry'); + hamster.get('napTime'); // false ``` @method computed.equal @@ -4843,15 +4937,15 @@ registerComputed('equal', function(dependentKey, value) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ hasTooManyBananas: Ember.computed.gt('numBananas', 10) }); - var hampster = Hampster.create(); - hampster.get('hasTooManyBananas'); // false - hampster.set('numBananas', 3); - hampster.get('hasTooManyBananas'); // false - hampster.set('numBananas', 11); - hampster.get('hasTooManyBananas'); // true + var hamster = Hamster.create(); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 11); + hamster.get('hasTooManyBananas'); // true ``` @method computed.gt @@ -4872,15 +4966,15 @@ registerComputed('gt', function(dependentKey, value) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ hasTooManyBananas: Ember.computed.gte('numBananas', 10) }); - var hampster = Hampster.create(); - hampster.get('hasTooManyBananas'); // false - hampster.set('numBananas', 3); - hampster.get('hasTooManyBananas'); // false - hampster.set('numBananas', 10); - hampster.get('hasTooManyBananas'); // true + var hamster = Hamster.create(); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 10); + hamster.get('hasTooManyBananas'); // true ``` @method computed.gte @@ -4901,15 +4995,15 @@ registerComputed('gte', function(dependentKey, value) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ needsMoreBananas: Ember.computed.lt('numBananas', 3) }); - var hampster = Hampster.create(); - hampster.get('needsMoreBananas'); // true - hampster.set('numBananas', 3); - hampster.get('needsMoreBananas'); // false - hampster.set('numBananas', 2); - hampster.get('needsMoreBananas'); // true + var hamster = Hamster.create(); + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 2); + hamster.get('needsMoreBananas'); // true ``` @method computed.lt @@ -4930,15 +5024,15 @@ registerComputed('lt', function(dependentKey, value) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ needsMoreBananas: Ember.computed.lte('numBananas', 3) }); - var hampster = Hampster.create(); - hampster.get('needsMoreBananas'); // true - hampster.set('numBananas', 5); - hampster.get('needsMoreBananas'); // false - hampster.set('numBananas', 3); - hampster.get('needsMoreBananas'); // true + var hamster = Hamster.create(); + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 5); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // true ``` @method computed.lte @@ -4956,24 +5050,23 @@ registerComputed('lte', function(dependentKey, value) { A computed property that performs a logical `and` on the original values for the provided dependent properties. - Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ readyForCamp: Ember.computed.and('hasTent', 'hasBackpack') }); - var hampster = Hampster.create(); - hampster.get('readyForCamp'); // false - hampster.set('hasTent', true); - hampster.get('readyForCamp'); // false - hampster.set('hasBackpack', true); - hampster.get('readyForCamp'); // true + var hamster = Hamster.create(); + hamster.get('readyForCamp'); // false + hamster.set('hasTent', true); + hamster.get('readyForCamp'); // false + hamster.set('hasBackpack', true); + hamster.get('readyForCamp'); // true ``` @method computed.and @for Ember - @param {String} dependentKey, [dependentKey...] + @param {String} dependentKey* @return {Ember.ComputedProperty} computed property which performs a logical `and` on the values of all the original values for properties. */ @@ -4987,24 +5080,24 @@ registerComputedWithProperties('and', function(properties) { }); /** - A computed property that which performs a logical `or` on the + A computed property which performs a logical `or` on the original values for the provided dependent properties. Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella') }); - var hampster = Hampster.create(); - hampster.get('readyForRain'); // false - hampster.set('hasJacket', true); - hampster.get('readyForRain'); // true + var hamster = Hamster.create(); + hamster.get('readyForRain'); // false + hamster.set('hasJacket', true); + hamster.get('readyForRain'); // true ``` @method computed.or @for Ember - @param {String} dependentKey, [dependentKey...] + @param {String} dependentKey* @return {Ember.ComputedProperty} computed property which performs a logical `or` on the values of all the original values for properties. */ @@ -5024,18 +5117,18 @@ registerComputedWithProperties('or', function(properties) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ hasClothes: Ember.computed.any('hat', 'shirt') }); - var hampster = Hampster.create(); - hampster.get('hasClothes'); // null - hampster.set('shirt', 'Hawaiian Shirt'); - hampster.get('hasClothes'); // 'Hawaiian Shirt' + var hamster = Hamster.create(); + hamster.get('hasClothes'); // null + hamster.set('shirt', 'Hawaiian Shirt'); + hamster.get('hasClothes'); // 'Hawaiian Shirt' ``` @method computed.any @for Ember - @param {String} dependentKey, [dependentKey...] + @param {String} dependentKey* @return {Ember.ComputedProperty} computed property which returns the first truthy value of given list of properties. */ @@ -5055,19 +5148,19 @@ registerComputedWithProperties('any', function(properties) { Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ clothes: Ember.computed.map('hat', 'shirt') }); - var hampster = Hampster.create(); - hampster.get('clothes'); // [null, null] - hampster.set('hat', 'Camp Hat'); - hampster.set('shirt', 'Camp Shirt'); - hampster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] + var hamster = Hamster.create(); + hamster.get('clothes'); // [null, null] + hamster.set('hat', 'Camp Hat'); + hamster.set('shirt', 'Camp Shirt'); + hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] ``` @method computed.map @for Ember - @param {String} dependentKey, [dependentKey...] + @param {String} dependentKey* @return {Ember.ComputedProperty} computed property which maps values of all passed properties in to an array. */ @@ -5154,7 +5247,7 @@ Ember.computed.alias = function(dependentKey) { @method computed.oneWay @for Ember @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates an + @return {Ember.ComputedProperty} computed property which creates a one way computed property to the original value for property. */ Ember.computed.oneWay = function(dependentKey) { @@ -5166,20 +5259,20 @@ Ember.computed.oneWay = function(dependentKey) { /** A computed property that acts like a standard getter and setter, - but retruns the value at the provided `defaultPath` if the + but returns the value at the provided `defaultPath` if the property itself has not been set to a value Example ```javascript - var Hampster = Ember.Object.extend({ + var Hamster = Ember.Object.extend({ wishList: Ember.computed.defaultTo('favoriteFood') }); - var hampster = Hampster.create({favoriteFood: 'Banana'}); - hampster.get('wishList'); // 'Banana' - hampster.set('wishList', 'More Unit Tests'); - hampster.get('wishList'); // 'More Unit Tests' - hampster.get('favoriteFood'); // 'Banana' + var hamster = Hamster.create({favoriteFood: 'Banana'}); + hamster.get('wishList'); // 'Banana' + hamster.set('wishList', 'More Unit Tests'); + hamster.get('wishList'); // 'More Unit Tests' + hamster.get('favoriteFood'); // 'Banana' ``` @method computed.defaultTo @@ -5198,7 +5291,6 @@ Ember.computed.defaultTo = function(defaultPath) { }; - })(); @@ -5209,8 +5301,9 @@ Ember.computed.defaultTo = function(defaultPath) { @module ember-metal */ -var AFTER_OBSERVERS = ':change'; -var BEFORE_OBSERVERS = ':before'; +var AFTER_OBSERVERS = ':change', + BEFORE_OBSERVERS = ':before'; + function changeEvent(keyName) { return keyName+AFTER_OBSERVERS; @@ -5227,9 +5320,12 @@ function beforeEvent(keyName) { @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) { + + Ember.addListener(obj, changeEvent(_path), target, method); + Ember.watch(obj, _path); + + return this; }; @@ -5244,9 +5340,11 @@ Ember.observersFor = function(obj, 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) { + + Ember.unwatch(obj, _path); + Ember.removeListener(obj, changeEvent(_path), target, method); + return this; }; @@ -5257,9 +5355,11 @@ Ember.removeObserver = function(obj, path, target, method) { @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) { + + Ember.addListener(obj, beforeEvent(_path), target, method); + Ember.watch(obj, _path); + return this; }; @@ -5298,11 +5398,14 @@ Ember.beforeObserversFor = function(obj, 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) { + + Ember.unwatch(obj, _path); + Ember.removeListener(obj, beforeEvent(_path), target, method); + return this; }; + })(); @@ -5534,7 +5637,12 @@ define("backburner", debouncees = [], timers = [], autorun, laterTimer, laterTimerExpiresAt, - global = this; + global = this, + NUMBER = /\d+/; + + function isCoercableNumber(number) { + return typeof number === 'number' || NUMBER.test(number); + } function Backburner(queueNames, options) { this.queueNames = queueNames; @@ -5649,32 +5757,60 @@ define("backburner", }, setTimeout: function() { - var self = this, - wait = pop.call(arguments), - target = arguments[0], - method = arguments[1], - executeAt = (+new Date()) + wait; + var args = slice.call(arguments); + var length = args.length; + var method, wait, target; + var self = this; + var methodOrTarget, methodOrWait, methodOrArgs; - if (!method) { - method = target; - target = null; + if (length === 0) { + return; + } else if (length === 1) { + method = args.shift(); + wait = 0; + } else if (length === 2) { + methodOrTarget = args[0]; + methodOrWait = args[1]; + + if (typeof methodOrWait === 'function' || typeof methodOrTarget[methodOrWait] === 'function') { + target = args.shift(); + method = args.shift(); + wait = 0; + } else if (isCoercableNumber(methodOrWait)) { + method = args.shift(); + wait = args.shift(); + } else { + method = args.shift(); + wait = 0; + } + } else { + var last = args[args.length - 1]; + + if (isCoercableNumber(last)) { + wait = args.pop(); + } + + methodOrTarget = args[0]; + methodOrArgs = args[1]; + + if (typeof methodOrArgs === 'function' || (typeof methodOrArgs === 'string' && + methodOrTarget !== null && + methodOrArgs in methodOrTarget)) { + target = args.shift(); + method = args.shift(); + } else { + method = args.shift(); + } } + var executeAt = (+new Date()) + parseInt(wait, 10); + if (typeof method === 'string') { method = target[method]; } - var fn, args; - if (arguments.length > 2) { - args = slice.call(arguments, 2); - - fn = function() { - method.apply(target, args); - }; - } else { - fn = function() { - method.call(target); - }; + function fn() { + method.apply(target, args); } // find position to insert - TODO: binary search @@ -5704,7 +5840,7 @@ define("backburner", 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++) { @@ -5739,13 +5875,14 @@ define("backburner", 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); @@ -5875,6 +6012,7 @@ define("backburner", __exports__.Backburner = Backburner; }); + })(); @@ -7010,9 +7148,7 @@ function applyConcatenatedProperties(obj, key, value, values) { return Ember.makeArray(baseValue).concat(value); } } else { - // Make sure this mixin has its own array so it is not - // accidentally mutated by another child's interactions - return Ember.makeArray(value).slice(); + return Ember.makeArray(value); } } @@ -7529,9 +7665,9 @@ Ember.aliasMethod = function(methodName) { ```javascript Ember.Object.extend({ - valueObserver: Ember.observer(function() { + valueObserver: Ember.observer('value', function() { // Executes whenever the "value" property changes - }, 'value') + }) }); ``` @@ -7543,12 +7679,25 @@ Ember.aliasMethod = function(methodName) { @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; }; @@ -7558,9 +7707,9 @@ Ember.observer = function(func) { ```javascript Ember.Object.extend({ - valueObserver: Ember.immediateObserver(function() { + valueObserver: Ember.immediateObserver('value', function() { // Executes whenever the "value" property changes - }, 'value') + }) }); ``` @@ -7572,8 +7721,8 @@ Ember.observer = function(func) { @method immediateObserver @for Ember - @param {Function} func @param {String} propertyNames* + @param {Function} func @return func */ Ember.immediateObserver = function() { @@ -7600,22 +7749,22 @@ Ember.immediateObserver = function() { 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') + }) }); ``` @@ -7624,12 +7773,25 @@ Ember.immediateObserver = function() { @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; }; @@ -9544,30 +9706,2004 @@ if (!Ember.keys || Ember.create.isSimulated) { }; } -// .......................................................... -// ERROR -// +})(); -var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var STRING_DASHERIZE_REGEXP = (/[ _]/g); +var STRING_DASHERIZE_CACHE = {}; +var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); +var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); +var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); +var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); /** - A subclass of the JavaScript Error object for use in Ember. + Defines the hash of localized strings for the current language. Used by + the `Ember.String.loc()` helper. To localize, add string values to this + hash. - @class Error - @namespace Ember - @extends Error - @constructor + @property STRINGS + @for Ember + @type Hash */ -Ember.Error = function() { - var tmp = Error.apply(this, arguments); +Ember.STRINGS = {}; - // 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]]; +/** + Defines string helper methods including string formatting and localization. + Unless `Ember.EXTEND_PROTOTYPES.String` is `false` these methods will also be + added to the `String.prototype` as well. + + @class String + @namespace Ember + @static +*/ +Ember.String = { + + /** + Apply formatting options to the string. This will look for occurrences + of "%@" in your string and substitute them with the arguments you pass into + this method. If you want to control the specific order of replacement, + you can add a number after the key as well to indicate which argument + you want to insert. + + Ordered insertions are most useful when building loc strings where values + you need to insert may appear in different orders. + + ```javascript + "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" + "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" + ``` + + @method fmt + @param {String} str The string to format + @param {Array} formats An array of parameters to interpolate into string. + @return {String} formatted string + */ + fmt: function(str, formats) { + // first, replace any ORDERED replacements. + var idx = 0; // the current index for non-numerical replacements + return str.replace(/%@([0-9]+)?/g, function(s, argIndex) { + argIndex = (argIndex) ? parseInt(argIndex, 10) - 1 : idx++; + s = formats[argIndex]; + return (s === null) ? '(null)' : (s === undefined) ? '' : Ember.inspect(s); + }) ; + }, + + /** + Formats the passed string, but first looks up the string in the localized + strings hash. This is a convenient way to localize text. See + `Ember.String.fmt()` for more information on formatting. + + Note that it is traditional but not required to prefix localized string + keys with an underscore or other character so you can easily identify + localized strings. + + ```javascript + Ember.STRINGS = { + '_Hello World': 'Bonjour le monde', + '_Hello %@ %@': 'Bonjour %@ %@' + }; + + Ember.String.loc("_Hello World"); // 'Bonjour le monde'; + Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; + ``` + + @method loc + @param {String} str The string to format + @param {Array} formats Optional array of parameters to interpolate into string. + @return {String} formatted string + */ + loc: function(str, formats) { + str = Ember.STRINGS[str] || str; + return Ember.String.fmt(str, formats) ; + }, + + /** + Splits a string into separate units separated by spaces, eliminating any + empty strings in the process. This is a convenience method for split that + is mostly useful when applied to the `String.prototype`. + + ```javascript + Ember.String.w("alpha beta gamma").forEach(function(key) { + console.log(key); + }); + + // > alpha + // > beta + // > gamma + ``` + + @method w + @param {String} str The string to split + @return {String} split string + */ + w: function(str) { return str.split(/\s+/); }, + + /** + Converts a camelized string into all lower case separated by underscores. + + ```javascript + 'innerHTML'.decamelize(); // 'inner_html' + 'action_name'.decamelize(); // 'action_name' + 'css-class-name'.decamelize(); // 'css-class-name' + 'my favorite items'.decamelize(); // 'my favorite items' + ``` + + @method decamelize + @param {String} str The string to decamelize. + @return {String} the decamelized string. + */ + decamelize: function(str) { + return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); + }, + + /** + Replaces underscores, spaces, or camelCase with dashes. + + ```javascript + 'innerHTML'.dasherize(); // 'inner-html' + 'action_name'.dasherize(); // 'action-name' + 'css-class-name'.dasherize(); // 'css-class-name' + 'my favorite items'.dasherize(); // 'my-favorite-items' + ``` + + @method dasherize + @param {String} str The string to dasherize. + @return {String} the dasherized string. + */ + dasherize: function(str) { + var cache = STRING_DASHERIZE_CACHE, + hit = cache.hasOwnProperty(str), + ret; + + if (hit) { + return cache[str]; + } else { + ret = Ember.String.decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-'); + cache[str] = ret; + } + + return ret; + }, + + /** + Returns the lowerCamelCase form of a string. + + ```javascript + 'innerHTML'.camelize(); // 'innerHTML' + 'action_name'.camelize(); // 'actionName' + 'css-class-name'.camelize(); // 'cssClassName' + 'my favorite items'.camelize(); // 'myFavoriteItems' + 'My Favorite Items'.camelize(); // 'myFavoriteItems' + ``` + + @method camelize + @param {String} str The string to camelize. + @return {String} the camelized string. + */ + camelize: function(str) { + return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { + return chr ? chr.toUpperCase() : ''; + }).replace(/^([A-Z])/, function(match, separator, chr) { + return match.toLowerCase(); + }); + }, + + /** + Returns the UpperCamelCase form of a string. + + ```javascript + 'innerHTML'.classify(); // 'InnerHTML' + 'action_name'.classify(); // 'ActionName' + 'css-class-name'.classify(); // 'CssClassName' + 'my favorite items'.classify(); // 'MyFavoriteItems' + ``` + + @method classify + @param {String} str the string to classify + @return {String} the classified string + */ + classify: function(str) { + var parts = str.split("."), + out = []; + + for (var i=0, l=parts.length; i= 0) { + var baseValue = this[keyName]; + + if (baseValue) { + if ('function' === typeof baseValue.concat) { + value = baseValue.concat(value); + } else { + value = Ember.makeArray(baseValue).concat(value); + } + } else { + value = Ember.makeArray(value); + } + } + + if (desc) { + desc.set(this, keyName, value); + } else { + if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) { + this.setUnknownProperty(keyName, value); + } else if (MANDATORY_SETTER) { + Ember.defineProperty(this, keyName, null, value); // setup mandatory setter + } else { + this[keyName] = value; + } + } + } + } + } + finishPartial(this, m); + this.init.apply(this, arguments); + m.proto = proto; + finishChains(this); + sendEvent(this, "init"); + }; + + Class.toString = Mixin.prototype.toString; + Class.willReopen = function() { + if (wasApplied) { + Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); + } + + wasApplied = false; + }; + Class._initMixins = function(args) { initMixins = args; }; + Class._initProperties = function(args) { initProperties = args; }; + + Class.proto = function() { + var superclass = Class.superclass; + if (superclass) { superclass.proto(); } + + if (!wasApplied) { + wasApplied = true; + Class.PrototypeMixin.applyPartial(Class.prototype); + rewatch(Class.prototype); + } + + return this.prototype; + }; + + return Class; + +} + +/** + @class CoreObject + @namespace Ember +*/ +var CoreObject = makeCtor(); +CoreObject.toString = function() { return "Ember.CoreObject"; }; + +CoreObject.PrototypeMixin = Mixin.create({ + reopen: function() { + applyMixin(this, arguments, true); + return this; + }, + + /** + An overridable method called when objects are instantiated. By default, + does nothing unless it is overridden during class definition. + + Example: + + ```javascript + App.Person = Ember.Object.extend({ + init: function() { + alert('Name is ' + this.get('name')); + } + }); + + var steve = App.Person.create({ + name: "Steve" + }); + + // alerts 'Name is Steve'. + ``` + + NOTE: If you do override `init` for a framework class like `Ember.View` or + `Ember.ArrayController`, be sure to call `this._super()` in your + `init` declaration! If you don't, Ember may not have an opportunity to + do important setup work, and you'll see strange behavior in your + application. + + @method init + */ + init: function() {}, + + /** + Defines the properties that will be concatenated from the superclass + (instead of overridden). + + By default, when you extend an Ember class a property defined in + the subclass overrides a property with the same name that is defined + in the superclass. However, there are some cases where it is preferable + to build up a property's value by combining the superclass' property + value with the subclass' value. An example of this in use within Ember + is the `classNames` property of `Ember.View`. + + Here is some sample code showing the difference between a concatenated + property and a normal one: + + ```javascript + App.BarView = Ember.View.extend({ + someNonConcatenatedProperty: ['bar'], + classNames: ['bar'] + }); + + App.FooBarView = App.BarView.extend({ + someNonConcatenatedProperty: ['foo'], + classNames: ['foo'], + }); + + var fooBarView = App.FooBarView.create(); + fooBarView.get('someNonConcatenatedProperty'); // ['foo'] + fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] + ``` + + This behavior extends to object creation as well. Continuing the + above example: + + ```javascript + var view = App.FooBarView.create({ + someNonConcatenatedProperty: ['baz'], + classNames: ['baz'] + }) + view.get('someNonConcatenatedProperty'); // ['baz'] + view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] + ``` + Adding a single property that is not an array will just add it in the array: + + ```javascript + var view = App.FooBarView.create({ + classNames: 'baz' + }) + view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] + ``` + + Using the `concatenatedProperties` property, we can tell to Ember that mix + the content of the properties. + + In `Ember.View` the `classNameBindings` and `attributeBindings` properties + 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 + mislead your users to think they can override the property in a subclass). + + @property concatenatedProperties + @type Array + @default null + */ + concatenatedProperties: null, + + /** + Destroyed object property flag. + + if this property is `true` the observers and bindings were already + removed by the effect of calling the `destroy()` method. + + @property isDestroyed + @default false + */ + isDestroyed: false, + + /** + Destruction scheduled flag. The `destroy()` method has been called. + + The object stays intact until the end of the run loop at which point + the `isDestroyed` flag is set. + + @property isDestroying + @default false + */ + isDestroying: false, + + /** + Destroys an object by setting the `isDestroyed` flag and removing its + metadata, which effectively destroys observers and bindings. + + If you try to set a property on a destroyed object, an exception will be + raised. + + Note that destruction is scheduled for the end of the run loop and does not + happen immediately. It will set an isDestroying flag immediately. + + @method destroy + @return {Ember.Object} receiver + */ + destroy: function() { + if (this.isDestroying) { return; } + this.isDestroying = true; + + schedule('actions', this, this.willDestroy); + schedule('destroy', this, this._scheduledDestroy); + return this; + }, + + /** + Override to implement teardown. + + @method willDestroy + */ + willDestroy: Ember.K, + + /** + @private + + Invoked by the run loop to actually destroy the object. This is + scheduled for execution by the `destroy` method. + + @method _scheduledDestroy + */ + _scheduledDestroy: function() { + if (this.isDestroyed) { return; } + destroy(this); + this.isDestroyed = true; + }, + + bind: function(to, from) { + if (!(from instanceof Ember.Binding)) { from = Ember.Binding.from(from); } + from.to(to).connect(this); + return from; + }, + + /** + Returns a string representation which attempts to provide more information + than Javascript's `toString` typically does, in a generic way for all Ember + objects. + + App.Person = Em.Object.extend() + person = App.Person.create() + person.toString() //=> "" + + If the object's class is not defined on an Ember namespace, it will + indicate it is a subclass of the registered superclass: + + Student = App.Person.extend() + student = Student.create() + student.toString() //=> "<(subclass of App.Person):ember1025>" + + If the method `toStringExtension` is defined, its return value will be + included in the output. + + App.Teacher = App.Person.extend({ + toStringExtension: function() { + return this.get('fullName'); + } + }); + teacher = App.Teacher.create() + teacher.toString(); //=> "" + + @method toString + @return {String} string representation + */ + toString: function toString() { + var hasToStringExtension = typeof this.toStringExtension === 'function', + extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; + var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; + this.toString = makeToString(ret); + return ret; + } +}); + +CoreObject.PrototypeMixin.ownerConstructor = CoreObject; + +function makeToString(ret) { + return function() { return ret; }; +} + +if (Ember.config.overridePrototypeMixin) { + Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin); +} + +CoreObject.__super__ = null; + +var ClassMixin = Mixin.create({ + + ClassMixin: Ember.required(), + + PrototypeMixin: Ember.required(), + + isClass: true, + + isMethod: false, + + /** + Creates a new subclass. + + ```javascript + App.Person = Ember.Object.extend({ + say: function(thing) { + alert(thing); + } + }); + ``` + + This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. + + You can also create a subclass from any existing class by calling its `extend()` method. For example, you might want to create a subclass of Ember's built-in `Ember.View` class: + + ```javascript + App.PersonView = Ember.View.extend({ + tagName: 'li', + classNameBindings: ['isAdministrator'] + }); + ``` + + When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: + + ```javascript + App.Person = Ember.Object.extend({ + say: function(thing) { + var name = this.get('name'); + alert(name + ' says: ' + thing); + } + }); + + App.Soldier = App.Person.extend({ + say: function(thing) { + this._super(thing + ", sir!"); + }, + march: function(numberOfHours) { + alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') + } + }); + + var yehuda = App.Soldier.create({ + name: "Yehuda Katz" + }); + + yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!" + ``` + + The `create()` on line #17 creates an *instance* of the `App.Soldier` class. The `extend()` on line #8 creates a *subclass* of `App.Person`. Any instance of the `App.Person` class will *not* have the `march()` method. + + You can also pass `Ember.Mixin` classes to add additional properties to the subclass. + + ```javascript + App.Person = Ember.Object.extend({ + say: function(thing) { + alert(this.get('name') + ' says: ' + thing); + } + }); + + App.SingingMixin = Ember.Mixin.create({ + sing: function(thing){ + alert(this.get('name') + ' sings: la la la ' + thing); + } + }); + + App.BroadwayStar = App.Person.extend(App.SingingMixin, { + dance: function() { + alert(this.get('name') + ' dances: tap tap tap tap '); + } + }); + ``` + + The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. + + @method extend + @static + + @param {Ember.Mixin} [mixins]* One or more Ember.Mixin classes + @param {Object} [arguments]* Object containing values to use within the new class + */ + extend: function() { + var Class = makeCtor(), proto; + Class.ClassMixin = Mixin.create(this.ClassMixin); + Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); + + Class.ClassMixin.ownerConstructor = Class; + Class.PrototypeMixin.ownerConstructor = Class; + + reopen.apply(Class.PrototypeMixin, arguments); + + Class.superclass = this; + Class.__super__ = this.prototype; + + proto = Class.prototype = o_create(this.prototype); + proto.constructor = Class; + generateGuid(proto); + meta(proto).proto = proto; // this will disable observers on prototype + + Class.ClassMixin.apply(Class); + return Class; + }, + + /** + Equivalent to doing `extend(arguments).create()`. + If possible use the normal `create` method instead. + + @method createWithMixins + @static + @param [arguments]* + */ + createWithMixins: function() { + var C = this; + if (arguments.length>0) { this._initMixins(arguments); } + return new C(); + }, + + /** + Creates an instance of a class. Accepts either no arguments, or an object + containing values to initialize the newly instantiated object with. + + ```javascript + App.Person = Ember.Object.extend({ + helloWorld: function() { + alert("Hi, my name is " + this.get('name')); + } + }); + + var tom = App.Person.create({ + name: 'Tom Dale' + }); + + tom.helloWorld(); // alerts "Hi, my name is Tom Dale". + ``` + + `create` will call the `init` function if defined during + `Ember.AnyObject.extend` + + If no arguments are passed to `create`, it will not set values to the new + instance during initialization: + + ```javascript + var noName = App.Person.create(); + noName.helloWorld(); // alerts undefined + ``` + + NOTE: For performance reasons, you cannot declare methods or computed + properties during `create`. You should instead declare methods and computed + properties when using `extend` or use the `createWithMixins` shorthand. + + @method create + @static + @param [arguments]* + */ + create: function() { + var C = this; + 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' + }); + + o = MyObject.create(); + o.get('name'); // 'an object' + + MyObject.reopen({ + say: function(msg){ + console.log(msg); + } + }) + + 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 + */ + reopen: function() { + this.willReopen(); + reopen.apply(this.PrototypeMixin, arguments); + 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 + and not on any instance of that class. + + ```javascript + App.Person = Ember.Object.extend({ + name : "", + sayHello : function(){ + alert("Hello. My name is " + this.get('name')); + } + }); + + App.Person.reopenClass({ + species : "Homo sapiens", + createPerson: function(newPersonsName){ + return App.Person.create({ + name:newPersonsName + }); + } + }); + + var tom = App.Person.create({ + name : "Tom Dale" + }); + var yehuda = App.Person.createPerson("Yehuda Katz"); + + 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` + 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; + }, + + detect: function(obj) { + if ('function' !== typeof obj) { return false; } + while(obj) { + if (obj===this) { return true; } + obj = obj.superclass; + } + return false; + }, + + detectInstance: function(obj) { + return obj instanceof this; + }, + + /** + In some cases, you may want to annotate computed properties with additional + metadata about how they function or what values they operate on. For + example, computed property functions may close over variables that are then + no longer available for introspection. + + You can pass a hash of these values to a computed property like this: + + ```javascript + person: function() { + var personId = this.get('personId'); + return App.Person.create({ id: personId }); + }.property().meta({ type: App.Person }) + ``` + + Once you've done this, you can retrieve the values saved to the computed + property from your class like this: + + ```javascript + MyClass.metaForProperty('person'); + ``` + + This will return the original hash that was passed to `meta()`. + + @method metaForProperty + @param key {String} property name + */ + metaForProperty: function(key) { + var desc = meta(this.proto(), false).descs[key]; + + Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof Ember.ComputedProperty); + return desc._meta || {}; + }, + + /** + Iterate over each computed property for the class, passing its name + and any associated metadata (see `metaForProperty`) to the callback. + + @method eachComputedProperty + @param {Function} callback + @param {Object} binding + */ + eachComputedProperty: function(callback, binding) { + var proto = this.proto(), + descs = meta(proto).descs, + empty = {}, + property; + + for (var name in descs) { + property = descs[name]; + + if (property instanceof Ember.ComputedProperty) { + callback.call(binding || this, name, property._meta || empty); + } + } + } + +}); + +ClassMixin.ownerConstructor = CoreObject; + +if (Ember.config.overrideClassMixin) { + Ember.config.overrideClassMixin(ClassMixin); +} + +CoreObject.ClassMixin = ClassMixin; +ClassMixin.apply(CoreObject); + +Ember.CoreObject = CoreObject; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +/** + `Ember.Object` is the main base class for all Ember objects. It is a subclass + of `Ember.CoreObject` with the `Ember.Observable` mixin applied. For details, + see the documentation for each of these. + + @class Object + @namespace Ember + @extends Ember.CoreObject + @uses Ember.Observable +*/ +Ember.Object = Ember.CoreObject.extend(Ember.Observable); +Ember.Object.toString = function() { return "Ember.Object"; }; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, indexOf = Ember.ArrayPolyfills.indexOf; + +/** + A Namespace is an object usually used to contain other objects or methods + such as an application or framework. Create a namespace anytime you want + to define one of these new containers. + + # Example Usage + + ```javascript + MyFramework = Ember.Namespace.create({ + VERSION: '1.0.0' + }); + ``` + + @class Namespace + @namespace Ember + @extends Ember.Object +*/ +var Namespace = Ember.Namespace = Ember.Object.extend({ + isNamespace: true, + + init: function() { + Ember.Namespace.NAMESPACES.push(this); + Ember.Namespace.PROCESSED = false; + }, + + toString: function() { + var name = get(this, 'name'); + if (name) { return name; } + + findNamespaces(); + return this[Ember.GUID_KEY+'_name']; + }, + + nameClasses: function() { + processNamespace([this.toString()], this, {}); + }, + + destroy: function() { + var namespaces = Ember.Namespace.NAMESPACES; + Ember.lookup[this.toString()] = undefined; + namespaces.splice(indexOf.call(namespaces, this), 1); + this._super(); + } +}); + +Namespace.reopenClass({ + NAMESPACES: [Ember], + NAMESPACES_BY_ID: {}, + PROCESSED: false, + processAll: processAllNamespaces, + byName: function(name) { + if (!Ember.BOOTED) { + processAllNamespaces(); + } + + return NAMESPACES_BY_ID[name]; + } +}); + +var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; + +var hasOwnProp = ({}).hasOwnProperty, + guidFor = Ember.guidFor; + +function processNamespace(paths, root, seen) { + var idx = paths.length; + + NAMESPACES_BY_ID[paths.join('.')] = root; + + // Loop over all of the keys in the namespace, looking for classes + for(var key in root) { + if (!hasOwnProp.call(root, key)) { continue; } + var obj = root[key]; + + // If we are processing the `Ember` namespace, for example, the + // `paths` will start with `["Ember"]`. Every iteration through + // the loop will update the **second** element of this list with + // the key, so processing `Ember.View` will make the Array + // `['Ember', 'View']`. + paths[idx] = key; + + // If we have found an unprocessed class + if (obj && obj.toString === classToString) { + // Replace the class' `toString` with the dot-separated path + // and set its `NAME_KEY` + obj.toString = makeToString(paths.join('.')); + obj[NAME_KEY] = paths.join('.'); + + // Support nested namespaces + } else if (obj && obj.isNamespace) { + // Skip aliased namespaces + if (seen[guidFor(obj)]) { continue; } + seen[guidFor(obj)] = true; + + // Process the child namespace + processNamespace(paths, obj, seen); + } + } + + paths.length = idx; // cut out last item +} + +function findNamespaces() { + var Namespace = Ember.Namespace, lookup = Ember.lookup, obj, isNamespace; + + if (Namespace.PROCESSED) { return; } + + for (var prop in lookup) { + // These don't raise exceptions but can cause warnings + if (prop === "parent" || prop === "top" || prop === "frameElement" || prop === "webkitStorageInfo") { continue; } + + // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox. + // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage + if (prop === "globalStorage" && lookup.StorageList && lookup.globalStorage instanceof lookup.StorageList) { continue; } + // Unfortunately, some versions of IE don't support window.hasOwnProperty + if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } + + // At times we are not allowed to access certain properties for security reasons. + // There are also times where even if we can access them, we are not allowed to access their properties. + try { + obj = Ember.lookup[prop]; + isNamespace = obj && obj.isNamespace; + } catch (e) { + continue; + } + + if (isNamespace) { + Ember.deprecate("Namespaces should not begin with lowercase.", /^[A-Z]/.test(prop)); + obj[NAME_KEY] = prop; + } + } +} + +var NAME_KEY = Ember.NAME_KEY = Ember.GUID_KEY + '_name'; + +function superClassString(mixin) { + var superclass = mixin.superclass; + if (superclass) { + if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } + else { return superClassString(superclass); } + } else { + return; + } +} + +function classToString() { + if (!Ember.BOOTED && !this[NAME_KEY]) { + processAllNamespaces(); + } + + var ret; + + if (this[NAME_KEY]) { + ret = this[NAME_KEY]; + } else if (this._toString) { + ret = this._toString; + } else { + var str = superClassString(this); + if (str) { + ret = "(subclass of " + str + ")"; + } else { + ret = "(unknown mixin)"; + } + this.toString = makeToString(ret); + } + + return ret; +} + +function processAllNamespaces() { + var unprocessedNamespaces = !Namespace.PROCESSED, + unprocessedMixins = Ember.anyUnprocessedMixins; + + if (unprocessedNamespaces) { + findNamespaces(); + Namespace.PROCESSED = true; + } + + if (unprocessedNamespaces || unprocessedMixins) { + var namespaces = Namespace.NAMESPACES, namespace; + for (var i=0, l=namespaces.length; i= 0; --sliceIndex) { - itemIndex = index + sliceIndex; + for (sliceIndex = normalizedRemoveCount - 1; sliceIndex >= 0; --sliceIndex) { + itemIndex = normalizedIndex + sliceIndex; + if (itemIndex >= length) { break; } + item = dependentArray.objectAt(itemIndex); forEach(itemPropertyKeys, removeObservers, this); @@ -11211,31 +13407,35 @@ DependentArraysObserver.prototype = { }, dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) { + if (this.suspended) { return; } + 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(index, length, 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) { @@ -11276,6 +13476,20 @@ DependentArraysObserver.prototype = { } }; +function normalizeIndex(index, length, 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 normalizeRemoveCount(index, length, removedCount) { + return Math.min(removedCount, length - index); +} + function createChangeMeta(dependentArray, item, index, propertyName, property, previousValues) { var meta = { arrayChanged: dependentArray, @@ -11394,30 +13608,32 @@ function ReduceComputedProperty(options) { reset.call(this, cp, propertyName); - forEach(cp._dependentKeys, function (dependentKey) { - var dependentArray = get(this, dependentKey), - previousDependentArray = meta.dependentArrays[dependentKey]; + meta.dependentArraysObserver.suspendArrayObservers(function () { + forEach(cp._dependentKeys, function (dependentKey) { + var dependentArray = get(this, dependentKey), + previousDependentArray = meta.dependentArrays[dependentKey]; - if (dependentArray === previousDependentArray) { - // The array may be the same, but our item property keys may have - // changed, so we set them up again. We can't easily tell if they've - // changed: the array may be the same object, but with different - // contents. - if (cp._previousItemPropertyKeys[dependentKey]) { - delete cp._previousItemPropertyKeys[dependentKey]; - meta.dependentArraysObserver.setupPropertyObservers(dependentKey, cp._itemPropertyKeys[dependentKey]); - } - } else { - meta.dependentArrays[dependentKey] = dependentArray; + if (dependentArray === previousDependentArray) { + // The array may be the same, but our item property keys may have + // changed, so we set them up again. We can't easily tell if they've + // changed: the array may be the same object, but with different + // contents. + if (cp._previousItemPropertyKeys[dependentKey]) { + delete cp._previousItemPropertyKeys[dependentKey]; + meta.dependentArraysObserver.setupPropertyObservers(dependentKey, cp._itemPropertyKeys[dependentKey]); + } + } else { + meta.dependentArrays[dependentKey] = dependentArray; - if (previousDependentArray) { - meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey); - } + if (previousDependentArray) { + meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey); + } - if (dependentArray) { - meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey); + if (dependentArray) { + meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey); + } } - } + }, this); }, this); forEach(cp._dependentKeys, function(dependentKey) { @@ -11640,6 +13856,37 @@ ReduceComputedProperty.prototype.property = function () { }; ``` + 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*] @@ -11866,7 +14113,8 @@ var get = Ember.get, merge = Ember.merge, a_slice = [].slice, forEach = Ember.EnumerableUtils.forEach, - map = Ember.EnumerableUtils.map; + map = Ember.EnumerableUtils.map, + SearchProxy; /** A computed property that calculates the maximum value in the @@ -11966,14 +14214,14 @@ Ember.computed.min = function (dependentKey) { Example ```javascript - App.Hampster = Ember.Object.extend({ + App.Hamster = Ember.Object.extend({ excitingChores: Ember.computed.map('chores', function(chore) { return chore.toUpperCase() + '!'; }) }); - var hampster = App.Hampster.create({chores: ['cook', 'clean', 'write more unit tests']}); - hampster.get('excitingChores'); // ['COOK!', 'CLEAN!', 'WRITE MORE UNIT TESTS!'] + var hamster = App.Hamster.create({chores: ['cook', 'clean', 'write more unit tests']}); + hamster.get('excitingChores'); // ['COOK!', 'CLEAN!', 'WRITE MORE UNIT TESTS!'] ``` @method computed.map @@ -12050,18 +14298,18 @@ Ember.computed.mapProperty = Ember.computed.mapBy; Example ```javascript - App.Hampster = Ember.Object.extend({ + App.Hamster = Ember.Object.extend({ remainingChores: Ember.computed.filter('chores', function(chore) { return !chore.done; }) }); - var hampster = App.Hampster.create({chores: [ + var hamster = App.Hamster.create({chores: [ {name: 'cook', done: true}, {name: 'clean', done: true}, {name: 'write more unit tests', done: false} ]}); - hampster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] + hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] ``` @method computed.filter @@ -12107,16 +14355,16 @@ Ember.computed.filter = function(dependentKey, callback) { Example ```javascript - App.Hampster = Ember.Object.extend({ + App.Hamster = Ember.Object.extend({ remainingChores: Ember.computed.filterBy('chores', 'done', false) }); - var hampster = App.Hampster.create({chores: [ + var hamster = App.Hamster.create({chores: [ {name: 'cook', done: true}, {name: 'clean', done: true}, {name: 'write more unit tests', done: false} ]}); - hampster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] + hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] ``` @method computed.filterBy @@ -12159,17 +14407,17 @@ Ember.computed.filterProperty = Ember.computed.filterBy; Example ```javascript - App.Hampster = Ember.Object.extend({ + App.Hamster = Ember.Object.extend({ uniqueFruits: Ember.computed.uniq('fruits') }); - var hampster = App.Hampster.create({fruits: [ + var hamster = App.Hamster.create({fruits: [ 'banana', 'grape', 'kale', 'banana' ]}); - hampster.get('uniqueFruits'); // ['banana', 'grape', 'kale'] + hamster.get('uniqueFruits'); // ['banana', 'grape', 'kale'] ``` @method computed.uniq @@ -12304,16 +14552,16 @@ Ember.computed.intersect = function () { Example ```javascript - App.Hampster = Ember.Object.extend({ + App.Hamster = Ember.Object.extend({ likes: ['banana', 'grape', 'kale'], wants: Ember.computed.setDiff('likes', 'fruits') }); - var hampster = App.Hampster.create({fruits: [ + var hamster = App.Hamster.create({fruits: [ 'grape', 'kale', ]}); - hampster.get('wants'); // ['banana'] + hamster.get('wants'); // ['banana'] ``` @method computed.setDiff @@ -12394,13 +14642,16 @@ function binarySearch(array, item, low, high) { return mid; function _guidFor(item) { - if (Ember.ObjectProxy.detectInstance(item)) { + if (SearchProxy.detectInstance(item)) { return guidFor(get(item, 'content')); } return guidFor(item); } } + +SearchProxy = Ember.ObjectProxy.extend(); + /** A computed property which returns a new array with all the properties from the first dependent array sorted based on a property @@ -12542,10 +14793,10 @@ Ember.computed.sort = function (itemsKey, sortDefinition) { if (changeMeta.previousValues) { proxyProperties = merge({ content: item }, changeMeta.previousValues); - searchItem = Ember.ObjectProxy.create(proxyProperties); - } else { - searchItem = item; - } + searchItem = SearchProxy.create(proxyProperties); + } else { + searchItem = item; + } index = instanceMeta.binarySearch(array, searchItem); array.removeAt(index); @@ -12574,378 +14825,6 @@ Ember.RSVP = requireModule('rsvp'); -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var STRING_DASHERIZE_REGEXP = (/[ _]/g); -var STRING_DASHERIZE_CACHE = {}; -var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); -var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); -var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); -var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); - -/** - Defines the hash of localized strings for the current language. Used by - the `Ember.String.loc()` helper. To localize, add string values to this - hash. - - @property STRINGS - @for Ember - @type Hash -*/ -Ember.STRINGS = {}; - -/** - Defines string helper methods including string formatting and localization. - Unless `Ember.EXTEND_PROTOTYPES.String` is `false` these methods will also be - added to the `String.prototype` as well. - - @class String - @namespace Ember - @static -*/ -Ember.String = { - - /** - Apply formatting options to the string. This will look for occurrences - of "%@" in your string and substitute them with the arguments you pass into - this method. If you want to control the specific order of replacement, - you can add a number after the key as well to indicate which argument - you want to insert. - - Ordered insertions are most useful when building loc strings where values - you need to insert may appear in different orders. - - ```javascript - "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" - "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" - ``` - - @method fmt - @param {String} str The string to format - @param {Array} formats An array of parameters to interpolate into string. - @return {String} formatted string - */ - fmt: function(str, formats) { - // first, replace any ORDERED replacements. - var idx = 0; // the current index for non-numerical replacements - return str.replace(/%@([0-9]+)?/g, function(s, argIndex) { - argIndex = (argIndex) ? parseInt(argIndex, 10) - 1 : idx++; - s = formats[argIndex]; - return (s === null) ? '(null)' : (s === undefined) ? '' : Ember.inspect(s); - }) ; - }, - - /** - Formats the passed string, but first looks up the string in the localized - strings hash. This is a convenient way to localize text. See - `Ember.String.fmt()` for more information on formatting. - - Note that it is traditional but not required to prefix localized string - keys with an underscore or other character so you can easily identify - localized strings. - - ```javascript - Ember.STRINGS = { - '_Hello World': 'Bonjour le monde', - '_Hello %@ %@': 'Bonjour %@ %@' - }; - - Ember.String.loc("_Hello World"); // 'Bonjour le monde'; - Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; - ``` - - @method loc - @param {String} str The string to format - @param {Array} formats Optional array of parameters to interpolate into string. - @return {String} formatted string - */ - loc: function(str, formats) { - str = Ember.STRINGS[str] || str; - return Ember.String.fmt(str, formats) ; - }, - - /** - Splits a string into separate units separated by spaces, eliminating any - empty strings in the process. This is a convenience method for split that - is mostly useful when applied to the `String.prototype`. - - ```javascript - Ember.String.w("alpha beta gamma").forEach(function(key) { - console.log(key); - }); - - // > alpha - // > beta - // > gamma - ``` - - @method w - @param {String} str The string to split - @return {String} split string - */ - w: function(str) { return str.split(/\s+/); }, - - /** - Converts a camelized string into all lower case separated by underscores. - - ```javascript - 'innerHTML'.decamelize(); // 'inner_html' - 'action_name'.decamelize(); // 'action_name' - 'css-class-name'.decamelize(); // 'css-class-name' - 'my favorite items'.decamelize(); // 'my favorite items' - ``` - - @method decamelize - @param {String} str The string to decamelize. - @return {String} the decamelized string. - */ - decamelize: function(str) { - return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); - }, - - /** - Replaces underscores, spaces, or camelCase with dashes. - - ```javascript - 'innerHTML'.dasherize(); // 'inner-html' - 'action_name'.dasherize(); // 'action-name' - 'css-class-name'.dasherize(); // 'css-class-name' - 'my favorite items'.dasherize(); // 'my-favorite-items' - ``` - - @method dasherize - @param {String} str The string to dasherize. - @return {String} the dasherized string. - */ - dasherize: function(str) { - var cache = STRING_DASHERIZE_CACHE, - hit = cache.hasOwnProperty(str), - ret; - - if (hit) { - return cache[str]; - } else { - ret = Ember.String.decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-'); - cache[str] = ret; - } - - return ret; - }, - - /** - Returns the lowerCamelCase form of a string. - - ```javascript - 'innerHTML'.camelize(); // 'innerHTML' - 'action_name'.camelize(); // 'actionName' - 'css-class-name'.camelize(); // 'cssClassName' - 'my favorite items'.camelize(); // 'myFavoriteItems' - 'My Favorite Items'.camelize(); // 'myFavoriteItems' - ``` - - @method camelize - @param {String} str The string to camelize. - @return {String} the camelized string. - */ - camelize: function(str) { - return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { - return chr ? chr.toUpperCase() : ''; - }).replace(/^([A-Z])/, function(match, separator, chr) { - return match.toLowerCase(); - }); - }, - - /** - Returns the UpperCamelCase form of a string. - - ```javascript - 'innerHTML'.classify(); // 'InnerHTML' - 'action_name'.classify(); // 'ActionName' - 'css-class-name'.classify(); // 'CssClassName' - 'my favorite items'.classify(); // 'MyFavoriteItems' - ``` - - @method classify - @param {String} str the string to classify - @return {String} the classified string - */ - classify: function(str) { - var parts = str.split("."), - out = []; - - for (var i=0, l=parts.length; i= 0) { - var baseValue = this[keyName]; - - if (baseValue) { - if ('function' === typeof baseValue.concat) { - value = baseValue.concat(value); - } else { - value = Ember.makeArray(baseValue).concat(value); - } - } else { - value = Ember.makeArray(value); - } - } - - if (desc) { - desc.set(this, keyName, value); - } else { - if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) { - this.setUnknownProperty(keyName, value); - } else if (MANDATORY_SETTER) { - Ember.defineProperty(this, keyName, null, value); // setup mandatory setter - } else { - this[keyName] = value; - } - } - } - } - } - finishPartial(this, m); - this.init.apply(this, arguments); - m.proto = proto; - finishChains(this); - sendEvent(this, "init"); - }; - - Class.toString = Mixin.prototype.toString; - Class.willReopen = function() { - if (wasApplied) { - Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); - } - - wasApplied = false; - }; - Class._initMixins = function(args) { initMixins = args; }; - Class._initProperties = function(args) { initProperties = args; }; - - Class.proto = function() { - var superclass = Class.superclass; - if (superclass) { superclass.proto(); } - - if (!wasApplied) { - wasApplied = true; - Class.PrototypeMixin.applyPartial(Class.prototype); - rewatch(Class.prototype); - } - - return this.prototype; - }; - - return Class; - -} - -/** - @class CoreObject - @namespace Ember -*/ -var CoreObject = makeCtor(); -CoreObject.toString = function() { return "Ember.CoreObject"; }; - -CoreObject.PrototypeMixin = Mixin.create({ - reopen: function() { - applyMixin(this, arguments, true); - return this; - }, - - /** - An overridable method called when objects are instantiated. By default, - does nothing unless it is overridden during class definition. - - Example: - - ```javascript - App.Person = Ember.Object.extend({ - init: function() { - this._super(); - alert('Name is ' + this.get('name')); - } - }); - - var steve = App.Person.create({ - name: "Steve" - }); - - // alerts 'Name is Steve'. - ``` - - NOTE: If you do override `init` for a framework class like `Ember.View` or - `Ember.ArrayController`, be sure to call `this._super()` in your - `init` declaration! If you don't, Ember may not have an opportunity to - do important setup work, and you'll see strange behavior in your - application. - - @method init - */ - init: function() {}, - - /** - Defines the properties that will be concatenated from the superclass - (instead of overridden). - - By default, when you extend an Ember class a property defined in - the subclass overrides a property with the same name that is defined - in the superclass. However, there are some cases where it is preferable - to build up a property's value by combining the superclass' property - value with the subclass' value. An example of this in use within Ember - is the `classNames` property of `Ember.View`. - - Here is some sample code showing the difference between a concatenated - property and a normal one: - - ```javascript - App.BarView = Ember.View.extend({ - someNonConcatenatedProperty: ['bar'], - classNames: ['bar'] - }); - - App.FooBarView = App.BarView.extend({ - someNonConcatenatedProperty: ['foo'], - classNames: ['foo'], - }); - - var fooBarView = App.FooBarView.create(); - fooBarView.get('someNonConcatenatedProperty'); // ['foo'] - fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] - ``` - - This behavior extends to object creation as well. Continuing the - above example: - - ```javascript - var view = App.FooBarView.create({ - someNonConcatenatedProperty: ['baz'], - classNames: ['baz'] - }) - view.get('someNonConcatenatedProperty'); // ['baz'] - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` - Adding a single property that is not an array will just add it in the array: - - ```javascript - var view = App.FooBarView.create({ - classNames: 'baz' - }) - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` - - Using the `concatenatedProperties` property, we can tell to Ember that mix - the content of the properties. - - In `Ember.View` the `classNameBindings` and `attributeBindings` properties - 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 - mislead your users to think they can override the property in a subclass). - - @property concatenatedProperties - @type Array - @default null - */ - concatenatedProperties: null, - - /** - Destroyed object property flag. - - if this property is `true` the observers and bindings were already - removed by the effect of calling the `destroy()` method. - - @property isDestroyed - @default false - */ - isDestroyed: false, - - /** - Destruction scheduled flag. The `destroy()` method has been called. - - The object stays intact until the end of the run loop at which point - the `isDestroyed` flag is set. - - @property isDestroying - @default false - */ - isDestroying: false, - - /** - Destroys an object by setting the `isDestroyed` flag and removing its - metadata, which effectively destroys observers and bindings. - - If you try to set a property on a destroyed object, an exception will be - raised. - - Note that destruction is scheduled for the end of the run loop and does not - happen immediately. It will set an isDestroying flag immediately. - - @method destroy - @return {Ember.Object} receiver - */ - destroy: function() { - if (this.isDestroying) { return; } - this.isDestroying = true; - - schedule('actions', this, this.willDestroy); - schedule('destroy', this, this._scheduledDestroy); - return this; - }, - - /** - Override to implement teardown. - - @method willDestroy - */ - willDestroy: Ember.K, - - /** - @private - - Invoked by the run loop to actually destroy the object. This is - scheduled for execution by the `destroy` method. - - @method _scheduledDestroy - */ - _scheduledDestroy: function() { - if (this.isDestroyed) { return; } - destroy(this); - this.isDestroyed = true; - }, - - bind: function(to, from) { - if (!(from instanceof Ember.Binding)) { from = Ember.Binding.from(from); } - from.to(to).connect(this); - return from; - }, - - /** - Returns a string representation which attempts to provide more information - than Javascript's `toString` typically does, in a generic way for all Ember - objects. - - App.Person = Em.Object.extend() - person = App.Person.create() - person.toString() //=> "" - - If the object's class is not defined on an Ember namespace, it will - indicate it is a subclass of the registered superclass: - - Student = App.Person.extend() - student = Student.create() - student.toString() //=> "<(subclass of App.Person):ember1025>" - - If the method `toStringExtension` is defined, its return value will be - included in the output. - - App.Teacher = App.Person.extend({ - toStringExtension: function() { - return this.get('fullName'); - } - }); - teacher = App.Teacher.create() - teacher.toString(); //=> "" - - @method toString - @return {String} string representation - */ - toString: function toString() { - var hasToStringExtension = typeof this.toStringExtension === 'function', - extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; - var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; - this.toString = makeToString(ret); - return ret; - } -}); - -CoreObject.PrototypeMixin.ownerConstructor = CoreObject; - -function makeToString(ret) { - return function() { return ret; }; -} - -if (Ember.config.overridePrototypeMixin) { - Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin); -} - -CoreObject.__super__ = null; - -var ClassMixin = Mixin.create({ - - ClassMixin: Ember.required(), - - PrototypeMixin: Ember.required(), - - isClass: true, - - isMethod: false, - - /** - Creates a new subclass. - - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(thing); - } - }); - ``` - - This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. - - You can also create a subclass from any existing class by calling its `extend()` method. For example, you might want to create a subclass of Ember's built-in `Ember.View` class: - - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] - }); - ``` - - When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: - - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - var name = this.get('name'); - alert(name + ' says: ' + thing); - } - }); - - App.Soldier = App.Person.extend({ - say: function(thing) { - this._super(thing + ", sir!"); - }, - march: function(numberOfHours) { - alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') - } - }); - - var yehuda = App.Soldier.create({ - name: "Yehuda Katz" - }); - - yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!" - ``` - - The `create()` on line #17 creates an *instance* of the `App.Soldier` class. The `extend()` on line #8 creates a *subclass* of `App.Person`. Any instance of the `App.Person` class will *not* have the `march()` method. - - You can also pass `Ember.Mixin` classes to add additional properties to the subclass. - - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(this.get('name') + ' says: ' + thing); - } - }); - - App.SingingMixin = Ember.Mixin.create({ - sing: function(thing){ - alert(this.get('name') + ' sings: la la la ' + thing); - } - }); - - App.BroadwayStar = App.Person.extend(App.SingingMixin, { - dance: function() { - alert(this.get('name') + ' dances: tap tap tap tap '); - } - }); - ``` - - The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. - - @method extend - @static - - @param {Ember.Mixin} [mixins]* One or more Ember.Mixin classes - @param {Object} [arguments]* Object containing values to use within the new class - */ - extend: function() { - var Class = makeCtor(), proto; - Class.ClassMixin = Mixin.create(this.ClassMixin); - Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); - - Class.ClassMixin.ownerConstructor = Class; - Class.PrototypeMixin.ownerConstructor = Class; - - reopen.apply(Class.PrototypeMixin, arguments); - - Class.superclass = this; - Class.__super__ = this.prototype; - - proto = Class.prototype = o_create(this.prototype); - proto.constructor = Class; - generateGuid(proto, 'ember'); - meta(proto).proto = proto; // this will disable observers on prototype - - Class.ClassMixin.apply(Class); - return Class; - }, - - /** - Equivalent to doing `extend(arguments).create()`. - If possible use the normal `create` method instead. - - @method createWithMixins - @static - @param [arguments]* - */ - createWithMixins: function() { - var C = this; - if (arguments.length>0) { this._initMixins(arguments); } - return new C(); - }, - - /** - Creates an instance of a class. Accepts either no arguments, or an object - containing values to initialize the newly instantiated object with. - - ```javascript - App.Person = Ember.Object.extend({ - helloWorld: function() { - alert("Hi, my name is " + this.get('name')); - } - }); - - var tom = App.Person.create({ - name: 'Tom Dale' - }); - - tom.helloWorld(); // alerts "Hi, my name is Tom Dale". - ``` - - `create` will call the `init` function if defined during - `Ember.AnyObject.extend` - - If no arguments are passed to `create`, it will not set values to the new - instance during initialization: - - ```javascript - var noName = App.Person.create(); - noName.helloWorld(); // alerts undefined - ``` - - NOTE: For performance reasons, you cannot declare methods or computed - properties during `create`. You should instead declare methods and computed - properties when using `extend` or use the `createWithMixins` shorthand. - - @method create - @static - @param [arguments]* - */ - create: function() { - var C = this; - 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' - }); - - o = MyObject.create(); - o.get('name'); // 'an object' - - MyObject.reopen({ - say: function(msg){ - console.log(msg); - } - }) - - 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 - */ - reopen: function() { - this.willReopen(); - reopen.apply(this.PrototypeMixin, arguments); - 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 - and not on any instance of that class. - - ```javascript - App.Person = Ember.Object.extend({ - name : "", - sayHello : function(){ - alert("Hello. My name is " + this.get('name')); - } - }); - - App.Person.reopenClass({ - species : "Homo sapiens", - createPerson: function(newPersonsName){ - return App.Person.create({ - name:newPersonsName - }); - } - }); - - var tom = App.Person.create({ - name : "Tom Dale" - }); - var yehuda = App.Person.createPerson("Yehuda Katz"); - - 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` - 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; - }, - - detect: function(obj) { - if ('function' !== typeof obj) { return false; } - while(obj) { - if (obj===this) { return true; } - obj = obj.superclass; - } - return false; - }, - - detectInstance: function(obj) { - return obj instanceof this; - }, - - /** - In some cases, you may want to annotate computed properties with additional - metadata about how they function or what values they operate on. For - example, computed property functions may close over variables that are then - no longer available for introspection. - - You can pass a hash of these values to a computed property like this: - - ```javascript - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` - - Once you've done this, you can retrieve the values saved to the computed - property from your class like this: - - ```javascript - MyClass.metaForProperty('person'); - ``` - - This will return the original hash that was passed to `meta()`. - - @method metaForProperty - @param key {String} property name - */ - metaForProperty: function(key) { - var desc = meta(this.proto(), false).descs[key]; - - Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof Ember.ComputedProperty); - return desc._meta || {}; - }, - - /** - Iterate over each computed property for the class, passing its name - and any associated metadata (see `metaForProperty`) to the callback. - - @method eachComputedProperty - @param {Function} callback - @param {Object} binding - */ - eachComputedProperty: function(callback, binding) { - var proto = this.proto(), - descs = meta(proto).descs, - empty = {}, - property; - - for (var name in descs) { - property = descs[name]; - - if (property instanceof Ember.ComputedProperty) { - callback.call(binding || this, name, property._meta || empty); - } - } - } - -}); - -ClassMixin.ownerConstructor = CoreObject; - -if (Ember.config.overrideClassMixin) { - Ember.config.overrideClassMixin(ClassMixin); -} - -CoreObject.ClassMixin = ClassMixin; -ClassMixin.apply(CoreObject); - -Ember.CoreObject = CoreObject; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -/** - `Ember.Object` is the main base class for all Ember objects. It is a subclass - of `Ember.CoreObject` with the `Ember.Observable` mixin applied. For details, - see the documentation for each of these. - - @class Object - @namespace Ember - @extends Ember.CoreObject - @uses Ember.Observable -*/ -Ember.Object = Ember.CoreObject.extend(Ember.Observable); -Ember.Object.toString = function() { return "Ember.Object"; }; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, indexOf = Ember.ArrayPolyfills.indexOf; - -/** - A Namespace is an object usually used to contain other objects or methods - such as an application or framework. Create a namespace anytime you want - to define one of these new containers. - - # Example Usage - - ```javascript - MyFramework = Ember.Namespace.create({ - VERSION: '1.0.0' - }); - ``` - - @class Namespace - @namespace Ember - @extends Ember.Object -*/ -var Namespace = Ember.Namespace = Ember.Object.extend({ - isNamespace: true, - - init: function() { - Ember.Namespace.NAMESPACES.push(this); - Ember.Namespace.PROCESSED = false; - }, - - toString: function() { - var name = get(this, 'name'); - if (name) { return name; } - - findNamespaces(); - return this[Ember.GUID_KEY+'_name']; - }, - - nameClasses: function() { - processNamespace([this.toString()], this, {}); - }, - - destroy: function() { - var namespaces = Ember.Namespace.NAMESPACES; - Ember.lookup[this.toString()] = undefined; - namespaces.splice(indexOf.call(namespaces, this), 1); - this._super(); - } -}); - -Namespace.reopenClass({ - NAMESPACES: [Ember], - NAMESPACES_BY_ID: {}, - PROCESSED: false, - processAll: processAllNamespaces, - byName: function(name) { - if (!Ember.BOOTED) { - processAllNamespaces(); - } - - return NAMESPACES_BY_ID[name]; - } -}); - -var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; - -var hasOwnProp = ({}).hasOwnProperty, - guidFor = Ember.guidFor; - -function processNamespace(paths, root, seen) { - var idx = paths.length; - - NAMESPACES_BY_ID[paths.join('.')] = root; - - // Loop over all of the keys in the namespace, looking for classes - for(var key in root) { - if (!hasOwnProp.call(root, key)) { continue; } - var obj = root[key]; - - // If we are processing the `Ember` namespace, for example, the - // `paths` will start with `["Ember"]`. Every iteration through - // the loop will update the **second** element of this list with - // the key, so processing `Ember.View` will make the Array - // `['Ember', 'View']`. - paths[idx] = key; - - // If we have found an unprocessed class - if (obj && obj.toString === classToString) { - // Replace the class' `toString` with the dot-separated path - // and set its `NAME_KEY` - obj.toString = makeToString(paths.join('.')); - obj[NAME_KEY] = paths.join('.'); - - // Support nested namespaces - } else if (obj && obj.isNamespace) { - // Skip aliased namespaces - if (seen[guidFor(obj)]) { continue; } - seen[guidFor(obj)] = true; - - // Process the child namespace - processNamespace(paths, obj, seen); - } - } - - paths.length = idx; // cut out last item -} - -function findNamespaces() { - var Namespace = Ember.Namespace, lookup = Ember.lookup, obj, isNamespace; - - if (Namespace.PROCESSED) { return; } - - for (var prop in lookup) { - // These don't raise exceptions but can cause warnings - if (prop === "parent" || prop === "top" || prop === "frameElement" || prop === "webkitStorageInfo") { continue; } - - // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox. - // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage - if (prop === "globalStorage" && lookup.StorageList && lookup.globalStorage instanceof lookup.StorageList) { continue; } - // Unfortunately, some versions of IE don't support window.hasOwnProperty - if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } - - // At times we are not allowed to access certain properties for security reasons. - // There are also times where even if we can access them, we are not allowed to access their properties. - try { - obj = Ember.lookup[prop]; - isNamespace = obj && obj.isNamespace; - } catch (e) { - continue; - } - - if (isNamespace) { - Ember.deprecate("Namespaces should not begin with lowercase.", /^[A-Z]/.test(prop)); - obj[NAME_KEY] = prop; - } - } -} - -var NAME_KEY = Ember.NAME_KEY = Ember.GUID_KEY + '_name'; - -function superClassString(mixin) { - var superclass = mixin.superclass; - if (superclass) { - if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } - else { return superClassString(superclass); } - } else { - return; - } -} - -function classToString() { - if (!Ember.BOOTED && !this[NAME_KEY]) { - processAllNamespaces(); - } - - var ret; - - if (this[NAME_KEY]) { - ret = this[NAME_KEY]; - } else if (this._toString) { - ret = this._toString; - } else { - var str = superClassString(this); - if (str) { - ret = "(subclass of " + str + ")"; - } else { - ret = "(unknown mixin)"; - } - this.toString = makeToString(ret); - } - - return ret; -} - -function processAllNamespaces() { - var unprocessedNamespaces = !Namespace.PROCESSED, - unprocessedMixins = Ember.anyUnprocessedMixins; - - if (unprocessedNamespaces) { - findNamespaces(); - Namespace.PROCESSED = true; - } - - if (unprocessedNamespaces || unprocessedMixins) { - var namespaces = Namespace.NAMESPACES, namespace; - for (var i=0, l=namespaces.length; i