dist/ember-template-compiler.js in ember-source-1.13.0.beta.1 vs dist/ember-template-compiler.js in ember-source-1.13.0.beta.2

- old
+ new

@@ -3,11 +3,11 @@ * @copyright Copyright 2011-2015 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.13.0-beta.1 + * @version 1.13.0-beta.2 */ (function() { var enifed, requireModule, eriuqer, requirejs, Ember; var mainContext = this; @@ -114,20 +114,10 @@ 'use strict'; exports._warnIfUsingStrippedFeatureFlags = _warnIfUsingStrippedFeatureFlags; - /** - Will call `Ember.warn()` if ENABLE_ALL_FEATURES, ENABLE_OPTIONAL_FEATURES, or - any specific FEATURES flag is truthy. - - This method is called automatically in debug canary builds. - - @private - @method _warnIfUsingStrippedFeatureFlags - @return {void} - */ function isPlainFunction(test) { return typeof test === "function" && test.PrototypeMixin === undefined; } /** @@ -307,10 +297,21 @@ @since 1.5.0 */ Ember['default'].runInDebug = function (func) { func(); }; + + /** + Will call `Ember.warn()` if ENABLE_ALL_FEATURES, ENABLE_OPTIONAL_FEATURES, or + any specific FEATURES flag is truthy. + + This method is called automatically in debug canary builds. + + @private + @method _warnIfUsingStrippedFeatureFlags + @return {void} + */ function _warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped) { if (featuresWereStripped) { Ember['default'].warn("Ember.ENV.ENABLE_ALL_FEATURES is only available in canary builds.", !Ember['default'].ENV.ENABLE_ALL_FEATURES); Ember['default'].warn("Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.", !Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES); @@ -809,154 +810,10 @@ exports.bind = bind; exports.oneWay = oneWay; exports.Binding = Binding; - /** - An `Ember.Binding` connects the properties of two objects so that whenever - the value of one property changes, the other property will be changed also. - - ## Automatic Creation of Bindings with `/^*Binding/`-named Properties - - You do not usually create Binding objects directly but instead describe - bindings in your class or object definition using automatic binding - detection. - - Properties ending in a `Binding` suffix will be converted to `Ember.Binding` - instances. The value of this property should be a string representing a path - to another object or a custom binding instance created using Binding helpers - (see "One Way Bindings"): - - ``` - valueBinding: "MyApp.someController.title" - ``` - - This will create a binding from `MyApp.someController.title` to the `value` - property of your object instance automatically. Now the two values will be - kept in sync. - - ## One Way Bindings - - One especially useful binding customization you can use is the `oneWay()` - helper. This helper tells Ember that you are only interested in - receiving changes on the object you are binding from. For example, if you - are binding to a preference and you want to be notified if the preference - has changed, but your object will not be changing the preference itself, you - could do: - - ``` - bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") - ``` - - This way if the value of `MyApp.preferencesController.bigTitles` changes the - `bigTitles` property of your object will change also. However, if you - change the value of your `bigTitles` property, it will not update the - `preferencesController`. - - One way bindings are almost twice as fast to setup and twice as fast to - execute because the binding only has to worry about changes to one side. - - You should consider using one way bindings anytime you have an object that - may be created frequently and you do not intend to change a property; only - to monitor it for changes (such as in the example above). - - ## Adding Bindings Manually - - All of the examples above show you how to configure a custom binding, but the - result of these customizations will be a binding template, not a fully active - Binding instance. The binding will actually become active only when you - instantiate the object the binding belongs to. It is useful however, to - understand what actually happens when the binding is activated. - - For a binding to function it must have at least a `from` property and a `to` - property. The `from` property path points to the object/key that you want to - bind from while the `to` path points to the object/key you want to bind to. - - When you define a custom binding, you are usually describing the property - you want to bind from (such as `MyApp.someController.value` in the examples - above). When your object is created, it will automatically assign the value - you want to bind `to` based on the name of your binding key. In the - examples above, during init, Ember objects will effectively call - something like this on your binding: - - ```javascript - binding = Ember.Binding.from("valueBinding").to("value"); - ``` - - This creates a new binding instance based on the template you provide, and - sets the to path to the `value` property of the new object. Now that the - binding is fully configured with a `from` and a `to`, it simply needs to be - connected to become active. This is done through the `connect()` method: - - ```javascript - binding.connect(this); - ``` - - Note that when you connect a binding you pass the object you want it to be - connected to. This object will be used as the root for both the from and - to side of the binding when inspecting relative paths. This allows the - binding to be automatically inherited by subclassed objects as well. - - This also allows you to bind between objects using the paths you declare in - `from` and `to`: - - ```javascript - // Example 1 - binding = Ember.Binding.from("App.someObject.value").to("value"); - binding.connect(this); - - // Example 2 - binding = Ember.Binding.from("parentView.value").to("App.someObject.value"); - binding.connect(this); - ``` - - Now that the binding is connected, it will observe both the from and to side - and relay changes. - - If you ever needed to do so (you almost never will, but it is useful to - understand this anyway), you could manually create an active binding by - using the `Ember.bind()` helper method. (This is the same method used by - to setup your bindings on objects): - - ```javascript - Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); - ``` - - Both of these code fragments have the same effect as doing the most friendly - form of binding creation like so: - - ```javascript - MyApp.anotherObject = Ember.Object.create({ - valueBinding: "MyApp.someController.value", - - // OTHER CODE FOR THIS OBJECT... - }); - ``` - - Ember's built in binding creation method makes it easy to automatically - create bindings for you. You should always use the highest-level APIs - available, even if you understand how it works underneath. - - @class Binding - @namespace Ember - @since Ember 0.9 - */ - // Ember.Binding = Binding; ES6TODO: where to put this? - - /** - Global helper method to create a new binding. Just pass the root object - along with a `to` and `from` path to create and connect the binding. - - @method bind - @for Ember - @param {Object} obj The root object of the transform. - @param {String} to The path to the 'to' side of the binding. - Must be relative to obj. - @param {String} from The path to the 'from' side of the binding. - Must be relative to obj or a global path. - @return {Ember.Binding} binding instance - */ Ember['default'].LOG_BINDINGS = false || !!Ember['default'].ENV.LOG_BINDINGS; /** Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) instead of local (`foo.bar.baz`). @@ -1240,14 +1097,168 @@ var C = this; return new C(undefined, from).oneWay(flag); } }); + /** + An `Ember.Binding` connects the properties of two objects so that whenever + the value of one property changes, the other property will be changed also. + + ## Automatic Creation of Bindings with `/^*Binding/`-named Properties + + You do not usually create Binding objects directly but instead describe + bindings in your class or object definition using automatic binding + detection. + + Properties ending in a `Binding` suffix will be converted to `Ember.Binding` + instances. The value of this property should be a string representing a path + to another object or a custom binding instance created using Binding helpers + (see "One Way Bindings"): + + ``` + valueBinding: "MyApp.someController.title" + ``` + + This will create a binding from `MyApp.someController.title` to the `value` + property of your object instance automatically. Now the two values will be + kept in sync. + + ## One Way Bindings + + One especially useful binding customization you can use is the `oneWay()` + helper. This helper tells Ember that you are only interested in + receiving changes on the object you are binding from. For example, if you + are binding to a preference and you want to be notified if the preference + has changed, but your object will not be changing the preference itself, you + could do: + + ``` + bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") + ``` + + This way if the value of `MyApp.preferencesController.bigTitles` changes the + `bigTitles` property of your object will change also. However, if you + change the value of your `bigTitles` property, it will not update the + `preferencesController`. + + One way bindings are almost twice as fast to setup and twice as fast to + execute because the binding only has to worry about changes to one side. + + You should consider using one way bindings anytime you have an object that + may be created frequently and you do not intend to change a property; only + to monitor it for changes (such as in the example above). + + ## Adding Bindings Manually + + All of the examples above show you how to configure a custom binding, but the + result of these customizations will be a binding template, not a fully active + Binding instance. The binding will actually become active only when you + instantiate the object the binding belongs to. It is useful however, to + understand what actually happens when the binding is activated. + + For a binding to function it must have at least a `from` property and a `to` + property. The `from` property path points to the object/key that you want to + bind from while the `to` path points to the object/key you want to bind to. + + When you define a custom binding, you are usually describing the property + you want to bind from (such as `MyApp.someController.value` in the examples + above). When your object is created, it will automatically assign the value + you want to bind `to` based on the name of your binding key. In the + examples above, during init, Ember objects will effectively call + something like this on your binding: + + ```javascript + binding = Ember.Binding.from("valueBinding").to("value"); + ``` + + This creates a new binding instance based on the template you provide, and + sets the to path to the `value` property of the new object. Now that the + binding is fully configured with a `from` and a `to`, it simply needs to be + connected to become active. This is done through the `connect()` method: + + ```javascript + binding.connect(this); + ``` + + Note that when you connect a binding you pass the object you want it to be + connected to. This object will be used as the root for both the from and + to side of the binding when inspecting relative paths. This allows the + binding to be automatically inherited by subclassed objects as well. + + This also allows you to bind between objects using the paths you declare in + `from` and `to`: + + ```javascript + // Example 1 + binding = Ember.Binding.from("App.someObject.value").to("value"); + binding.connect(this); + + // Example 2 + binding = Ember.Binding.from("parentView.value").to("App.someObject.value"); + binding.connect(this); + ``` + + Now that the binding is connected, it will observe both the from and to side + and relay changes. + + If you ever needed to do so (you almost never will, but it is useful to + understand this anyway), you could manually create an active binding by + using the `Ember.bind()` helper method. (This is the same method used by + to setup your bindings on objects): + + ```javascript + Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); + ``` + + Both of these code fragments have the same effect as doing the most friendly + form of binding creation like so: + + ```javascript + MyApp.anotherObject = Ember.Object.create({ + valueBinding: "MyApp.someController.value", + + // OTHER CODE FOR THIS OBJECT... + }); + ``` + + Ember's built in binding creation method makes it easy to automatically + create bindings for you. You should always use the highest-level APIs + available, even if you understand how it works underneath. + + @class Binding + @namespace Ember + @since Ember 0.9 + */ + // Ember.Binding = Binding; ES6TODO: where to put this? + + /** + Global helper method to create a new binding. Just pass the root object + along with a `to` and `from` path to create and connect the binding. + + @method bind + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance + */ function bind(obj, to, from) { return new Binding(to, from).connect(obj); } + /** + @method oneWay + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance + */ function oneWay(obj, to, from) { return new Binding(to, from).oneWay().connect(obj); } exports.isGlobalPath = path_cache.isGlobal; @@ -1317,13 +1328,10 @@ exports.flushPendingChains = flushPendingChains; exports.finishChains = finishChains; exports.removeChainWatcher = removeChainWatcher; exports.ChainNode = ChainNode; - // attempts to add the pendingQueue chains again. If some of them end up - // back in the queue and reschedule is true, schedules a timeout to try - // again. var warn = Ember['default'].warn; var FIRST_KEY = /^([^\.]+)/; function firstKey(path) { return path.match(FIRST_KEY)[0]; @@ -1332,10 +1340,15 @@ function isObject(obj) { return obj && typeof obj === "object"; } var pendingQueue = []; + + // attempts to add the pendingQueue chains again. If some of them end up + // back in the queue and reschedule is true, schedules a timeout to try + // again. + function flushPendingChains() { if (pendingQueue.length === 0) { return; } @@ -1813,28 +1826,27 @@ @namespace Ember @constructor */ function ComputedProperty(config, opts) { this.isDescriptor = true; - - if (typeof config === "function") { - config.__ember_arity = config.length; - this._getter = config; - if (config.__ember_arity > 1) { - Ember.deprecate("Using the same function as getter and setter is deprecated.", false, { - url: "http://emberjs.com/deprecations/v1.x/#toc_computed-properties-with-a-shared-getter-and-setter" - }); - this._setter = config; - } - } else { - this._getter = config.get; - this._setter = config.set; - if (this._setter && this._setter.__ember_arity === undefined) { - this._setter.__ember_arity = this._setter.length; - } + if (typeof config === "function") { + config.__ember_arity = config.length; + this._getter = config; + if (config.__ember_arity > 1) { + Ember.deprecate("Using the same function as getter and setter is deprecated.", false, { + url: "http://emberjs.com/deprecations/v1.x/#toc_deprecate-using-the-same-function-as-getter-and-setter-in-computed-properties" + }); + this._setter = config; } - + } else { + this._getter = config.get; + this._setter = config.set; + if (this._setter && this._setter.__ember_arity === undefined) { + this._setter.__ember_arity = this._setter.length; + } + } + this._dependentKeys = undefined; this._suspended = undefined; this._meta = undefined; Ember.deprecate("Passing opts.cacheable to the CP constructor is deprecated. Invoke `volatile()` on the CP instead.", !opts || !opts.hasOwnProperty("cacheable")); @@ -2280,12 +2292,11 @@ args = [].slice.call(arguments); func = args.pop(); } var cp = new ComputedProperty(func); - // jscs:disable - + if (args) { cp.property.apply(cp, args); } return cp; @@ -2354,10 +2365,32 @@ exports.oneWay = oneWay; exports.readOnly = readOnly; exports.defaultTo = defaultTo; exports.deprecatingAlias = deprecatingAlias; + function getProperties(self, propertyNames) { + var ret = {}; + for (var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = property_get.get(self, propertyNames[i]); + } + return ret; + } + + function generateComputedWithProperties(macro) { + return function () { + for (var _len = arguments.length, properties = Array(_len), _key = 0; _key < _len; _key++) { + properties[_key] = arguments[_key]; + } + + var computedFunc = computed.computed(function () { + return macro.apply(this, [getProperties(this, properties)]); + }); + + return computedFunc.property.apply(computedFunc, properties); + }; + } + /** A computed property that returns true if the value of the dependent property is null, an empty string, empty array, or empty function. Example @@ -2381,93 +2414,336 @@ @for Ember.computed @param {String} dependentKey @return {Ember.ComputedProperty} computed property which negate the original value for property */ - function getProperties(self, propertyNames) { - var ret = {}; - for (var i = 0; i < propertyNames.length; i++) { - ret[propertyNames[i]] = property_get.get(self, propertyNames[i]); - } - return ret; - } - - function generateComputedWithProperties(macro) { - return function () { - for (var _len = arguments.length, properties = Array(_len), _key = 0; _key < _len; _key++) { - properties[_key] = arguments[_key]; - } - - var computedFunc = computed.computed(function () { - return macro.apply(this, [getProperties(this, properties)]); - }); - - return computedFunc.property.apply(computedFunc, properties); - }; - } function empty(dependentKey) { return computed.computed(dependentKey + ".length", function () { return isEmpty['default'](property_get.get(this, dependentKey)); }); } + /** + A computed property that returns true if the value of the dependent + property is NOT null, an empty string, empty array, or empty function. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasStuff: Ember.computed.notEmpty('backpack') + }); + + var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] }); + + hamster.get('hasStuff'); // true + hamster.get('backpack').clear(); // [] + hamster.get('hasStuff'); // false + ``` + + @method notEmpty + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns true if + original value for property is not empty. + */ function notEmpty(dependentKey) { return computed.computed(dependentKey + ".length", function () { return !isEmpty['default'](property_get.get(this, dependentKey)); }); } + /** + A computed property that returns true if the value of the dependent + property is null or undefined. This avoids errors from JSLint complaining + about use of ==, which can be technically confusing. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + isHungry: Ember.computed.none('food') + }); + + 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 none + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which + returns true if original value for property is null or undefined. + */ function none(dependentKey) { return computed.computed(dependentKey, function () { return isNone['default'](property_get.get(this, dependentKey)); }); } + /** + A computed property that returns the inverse boolean value + of the original value for the dependent property. + + Example + + ```javascript + var User = Ember.Object.extend({ + isAnonymous: Ember.computed.not('loggedIn') + }); + + var user = User.create({loggedIn: false}); + + user.get('isAnonymous'); // true + user.set('loggedIn', true); + user.get('isAnonymous'); // false + ``` + + @method not + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns + inverse of the original value for property + */ function not(dependentKey) { return computed.computed(dependentKey, function () { return !property_get.get(this, dependentKey); }); } + /** + A computed property that converts the provided dependent property + into a boolean value. + + ```javascript + var Hamster = Ember.Object.extend({ + hasBananas: Ember.computed.bool('numBananas') + }); + + 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 bool + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which converts + to boolean the original value for property + */ function bool(dependentKey) { return computed.computed(dependentKey, function () { return !!property_get.get(this, dependentKey); }); } + /** + A computed property which matches the original value for the + dependent property against a given RegExp, returning `true` + if they values matches the RegExp and `false` if it does not. + + Example + + ```javascript + var User = Ember.Object.extend({ + hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) + }); + + var user = User.create({loggedIn: false}); + + user.get('hasValidEmail'); // false + user.set('email', ''); + user.get('hasValidEmail'); // false + user.set('email', 'ember_hamster@example.com'); + user.get('hasValidEmail'); // true + ``` + + @method match + @for Ember.computed + @param {String} dependentKey + @param {RegExp} regexp + @return {Ember.ComputedProperty} computed property which match + the original value for property against a given RegExp + */ function match(dependentKey, regexp) { return computed.computed(dependentKey, function () { var value = property_get.get(this, dependentKey); return typeof value === "string" ? regexp.test(value) : false; }); } + /** + A computed property that returns true if the provided dependent property + is equal to the given value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + napTime: Ember.computed.equal('state', 'sleepy') + }); + + 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 equal + @for Ember.computed + @param {String} dependentKey + @param {String|Number|Object} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is equal to the given value. + */ function equal(dependentKey, value) { return computed.computed(dependentKey, function () { return property_get.get(this, dependentKey) === value; }); } + /** + A computed property that returns true if the provided dependent property + is greater than the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gt('numBananas', 10) + }); + + 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 gt + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater than given value. + */ function gt(dependentKey, value) { return computed.computed(dependentKey, function () { return property_get.get(this, dependentKey) > value; }); } + /** + A computed property that returns true if the provided dependent property + is greater than or equal to the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gte('numBananas', 10) + }); + + 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 gte + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater or equal then given value. + */ function gte(dependentKey, value) { return computed.computed(dependentKey, function () { return property_get.get(this, dependentKey) >= value; }); } + /** + A computed property that returns true if the provided dependent property + is less than the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lt('numBananas', 3) + }); + + 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 lt + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less then given value. + */ function lt(dependentKey, value) { return computed.computed(dependentKey, function () { return property_get.get(this, dependentKey) < value; }); } + /** + A computed property that returns true if the provided dependent property + is less than or equal to the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lte('numBananas', 3) + }); + + 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 lte + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less or equal than given value. + */ function lte(dependentKey, value) { return computed.computed(dependentKey, function () { return property_get.get(this, dependentKey) <= value; }); } @@ -2539,18 +2815,92 @@ res.push(properties[key]); } } } return res; - });function oneWay(dependentKey) { + }); + + function oneWay(dependentKey) { return alias['default'](dependentKey).oneWay(); } + /** + This is a more semantically meaningful alias of `computed.oneWay`, + whose name is somewhat ambiguous as to which direction the data flows. + + @method reads + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates a + one way computed property to the original value for property. + */ + + /** + Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides + a readOnly one way binding. Very often when using `computed.oneWay` one does + not also want changes to propagate back up, as they will replace the value. + + This prevents the reverse flow, and also throws an exception when it occurs. + + Example + + ```javascript + var User = Ember.Object.extend({ + firstName: null, + lastName: null, + nickName: Ember.computed.readOnly('firstName') + }); + + var teddy = User.create({ + firstName: 'Teddy', + lastName: 'Zeenny' + }); + + teddy.get('nickName'); // 'Teddy' + teddy.set('nickName', 'TeddyBear'); // throws Exception + // throw new Ember.Error('Cannot Set: nickName on: <User:ember27288>' );` + teddy.get('firstName'); // 'Teddy' + ``` + + @method readOnly + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates a + one way computed property to the original value for property. + @since 1.5.0 + */ function readOnly(dependentKey) { return alias['default'](dependentKey).readOnly(); } + /** + A computed property that acts like a standard getter and setter, + but returns the value at the provided `defaultPath` if the + property itself has not been set to a value + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + wishList: Ember.computed.defaultTo('favoriteFood') + }); + + 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 defaultTo + @for Ember.computed + @param {String} defaultPath + @return {Ember.ComputedProperty} computed property which acts like + a standard getter and setter, but defaults to the value from `defaultPath`. + @deprecated Use `Ember.computed.oneWay` or custom CP with default instead. + */ function defaultTo(defaultPath) { return computed.computed({ get: function (key) { Ember['default'].deprecate("Usage of Ember.computed.defaultTo is deprecated, use `Ember.computed.oneWay` instead."); return property_get.get(this, defaultPath); @@ -2561,10 +2911,23 @@ return newValue != null ? newValue : property_get.get(this, defaultPath); } }); } + /** + Creates a new property that is an alias for another property + on an object. Calls to `get` or `set` this property behave as + though they were called on the original property, but also + print a deprecation warning. + + @method deprecatingAlias + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + alias with a deprecation to the original value for property. + @since 1.7.0 + */ function deprecatingAlias(dependentKey) { return computed.computed(dependentKey, { get: function (key) { Ember['default'].deprecate("Usage of `" + key + "` is deprecated, use `" + dependentKey + "` instead."); return property_get.get(this, dependentKey); @@ -2595,27 +2958,25 @@ @module ember @submodule ember-metal */ /** - All Ember methods and functions are defined inside of this namespace. You - generally should not add new properties to this namespace as it may be - overwritten by future versions of Ember. + This namespace contains all Ember methods and functions. Future versions of + Ember may overwrite this namespace and therefore, you should avoid adding any + new properties. You can also use the shorthand `Em` instead of `Ember`. - Ember-Runtime is a framework that provides core functions for Ember including - cross-platform functions, support for property observing and objects. Its - focus is on small size and performance. You can use this in place of or - along-side other cross-platform libraries such as jQuery. + At the heart of Ember is Ember-Runtime, a set of core functions that provide + cross-platform compatibility and object property observing. Ember-Runtime is + small and performance-focused so you can use it alongside other + cross-platform libraries such as jQuery. For more details, see + [Ember-Runtime](http://emberjs.com/api/modules/ember-runtime.html). - The core Runtime framework is based on the jQuery API with a number of - performance optimizations. - @class Ember @static - @version 1.13.0-beta.1 + @version 1.13.0-beta.2 */ if ('undefined' === typeof Ember) { // Create core object. Make it act like an instance of Ember.Namespace so that // objects assigned to it are given a sane string representation. @@ -2638,24 +2999,26 @@ Ember.toString = function () { return 'Ember'; }; /** + The semantic version. + @property VERSION @type String - @default '1.13.0-beta.1' + @default '1.13.0-beta.2' @static */ - Ember.VERSION = '1.13.0-beta.1'; + Ember.VERSION = '1.13.0-beta.2'; /** - Standard environmental variables. You can define these in a global `EmberENV` - variable before loading Ember to control various configuration settings. + The hash of environment variables used to control various configuration + settings. To specify your own or override default settings, add the + desired properties to a global hash named `EmberENV` (or `ENV` for + backwards compatibility with earlier versions of Ember). The `EmberENV` + hash must be created before loading Ember. - For backwards compatibility with earlier versions of Ember the global `ENV` - variable will be used if `EmberENV` is not defined. - @property ENV @type Hash */ if (Ember.ENV) { @@ -2675,41 +3038,43 @@ if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) { Ember.ENV.DISABLE_RANGE_API = true; } /** - Hash of enabled Canary features. Add to this before creating your application. + The hash of enabled Canary features. Add to this, any canary features + before creating your application. - You can also define `EmberENV.FEATURES` if you need to enable features flagged at runtime. + Alternatively (and recommended), you can also define `EmberENV.FEATURES` + if you need to enable features flagged at runtime. @class FEATURES @namespace Ember @static @since 1.1.0 */ - Ember.FEATURES = { 'features-stripped-test': false, 'ember-routing-named-substates': true, 'mandatory-setter': true, 'ember-htmlbars-component-generation': true, 'ember-htmlbars-component-helper': true, 'ember-htmlbars-inline-if-helper': true, 'ember-htmlbars-attribute-syntax': true, 'ember-routing-transitioning-classes': true, 'new-computed-syntax': true, 'ember-testing-checkbox-helpers': false, 'ember-metal-stream': false, 'ember-application-instance-initializers': true, 'ember-application-initializer-context': true, 'ember-router-willtransition': true, 'ember-application-visit': false, 'ember-views-component-block-info': true, 'ember-routing-core-outlet': false, 'ember-libraries-isregistered': false, 'ember-routing-htmlbars-improved-actions': true }; //jshint ignore:line + Ember.FEATURES = { 'features-stripped-test': false, 'ember-routing-named-substates': true, 'mandatory-setter': true, 'ember-htmlbars-component-generation': false, 'ember-htmlbars-component-helper': true, 'ember-htmlbars-inline-if-helper': true, 'ember-htmlbars-attribute-syntax': true, 'ember-routing-transitioning-classes': true, 'ember-testing-checkbox-helpers': false, 'ember-metal-stream': false, 'ember-application-instance-initializers': true, 'ember-application-initializer-context': true, 'ember-router-willtransition': true, 'ember-application-visit': false, 'ember-views-component-block-info': true, 'ember-routing-core-outlet': false, 'ember-libraries-isregistered': false, 'ember-routing-htmlbars-improved-actions': true }; //jshint ignore:line if (Ember.ENV.FEATURES) { for (var feature in Ember.ENV.FEATURES) { if (Ember.ENV.FEATURES.hasOwnProperty(feature)) { Ember.FEATURES[feature] = Ember.ENV.FEATURES[feature]; } } } /** - Test that a feature is enabled. Parsed by Ember's build tools to leave - experimental features out of beta/stable builds. + Determine whether the specified `feature` is enabled. Used by Ember's + build tools to exclude experimental features from beta/stable builds. You can define the following configuration options: * `EmberENV.ENABLE_ALL_FEATURES` - force all features to be enabled. * `EmberENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly enabled/disabled. @method isEnabled - @param {String} feature + @param {String} feature The feature to check @return {Boolean} @for Ember.FEATURES @since 1.1.0 */ @@ -2730,19 +3095,22 @@ // .......................................................... // BOOTSTRAP // /** - Determines whether Ember should enhance some built-in object prototypes to - provide a more friendly API. If enabled, a few methods will be added to - `Function`, `String`, and `Array`. `Object.prototype` will not be enhanced, - which is the one that causes most trouble for people. + Determines whether Ember should add to `Array`, `Function`, and `String` + native object prototypes, a few extra methods in order to provide a more + friendly API. - In general we recommend leaving this option set to true since it rarely - conflicts with other code. If you need to turn it off however, you can - define an `EmberENV.EXTEND_PROTOTYPES` config to disable it. + We generally recommend leaving this option set to true however, if you need + to turn it off, you can add the configuration property + `EXTEND_PROTOTYPES` to `EmberENV` and set it to `false`. + Note, when disabled (the default configuration for Ember Addons), you will + instead have to access all methods and functions from the Ember + namespace. + @property EXTEND_PROTOTYPES @type Boolean @default true @for Ember */ @@ -2751,38 +3119,41 @@ if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { Ember.EXTEND_PROTOTYPES = true; } /** - Determines whether Ember logs a full stack trace during deprecation warnings + The `LOG_STACKTRACE_ON_DEPRECATION` property, when true, tells Ember to log + a full stack trace during deprecation warnings. @property LOG_STACKTRACE_ON_DEPRECATION @type Boolean @default true */ Ember.LOG_STACKTRACE_ON_DEPRECATION = Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false; /** - Determines whether Ember should add ECMAScript 5 Array shims to older browsers. + The `SHIM_ES5` property, when true, tells Ember to add ECMAScript 5 Array + shims to older browsers. @property SHIM_ES5 @type Boolean @default Ember.EXTEND_PROTOTYPES */ Ember.SHIM_ES5 = Ember.ENV.SHIM_ES5 === false ? false : Ember.EXTEND_PROTOTYPES; /** - Determines whether Ember logs info about version of used libraries + The `LOG_VERSION` property, when true, tells Ember to log versions of all + dependent libraries in use. @property LOG_VERSION @type Boolean @default true */ Ember.LOG_VERSION = Ember.ENV.LOG_VERSION === false ? false : true; /** - Empty function. Useful for some operations. Always returns `this`. + An empty function useful for some operations. Always returns `this`. @method K @private @return {Object} */ @@ -2909,22 +3280,10 @@ 'use strict'; exports.deprecateProperty = deprecateProperty; - /** - Used internally to allow changing properties in a backwards compatible way, and print a helpful - deprecation warning. - - @method deprecateProperty - @param {Object} object The object to add the deprecated property to. - @param {String} deprecatedKey The property to add (and print deprecation warnings upon accessing). - @param {String} newKey The property that will be aliased. - @private - @since 1.7.0 - */ - function deprecateProperty(object, deprecatedKey, newKey) { function deprecate() { Ember['default'].deprecate("Usage of `" + deprecatedKey + "` is deprecated, use `" + newKey + "` instead."); } @@ -2948,16 +3307,10 @@ enifed('ember-metal/dictionary', ['exports', 'ember-metal/platform/create'], function (exports, create) { 'use strict'; - - // the delete is meant to hint at runtimes that this object should remain in - // dictionary mode. This is clearly a runtime specific hack, but currently it - // appears worthwhile in some usecases. Please note, these deletes do increase - // the cost of creation dramatically over a plain Object.create. And as this - // only makes sense for long-lived dictionaries that aren't instantiated often. exports['default'] = makeDictionary; function makeDictionary(parent) { var dict = create['default'](parent); dict['_dict'] = null; delete dict['_dict']; @@ -2978,10 +3331,12 @@ exports.removeObject = removeObject; exports._replace = _replace; exports.replace = replace; exports.intersection = intersection; + var splice = Array.prototype.splice; + /** * Defines some convenience methods for working with Enumerables. * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary. * * @class EnumerableUtils @@ -2998,40 +3353,110 @@ * @param {Function} callback The callback to execute * @param {Object} thisArg Value to use as this when executing *callback* * * @return {Array} An array of mapped values. */ - var splice = Array.prototype.splice; function map(obj, callback, thisArg) { return obj.map ? obj.map(callback, thisArg) : ember_metal__array.map.call(obj, callback, thisArg); } + /** + * Calls the forEach function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-forEach method when necessary. + * + * @method forEach + * @param {Object} obj The object to call forEach on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + */ function forEach(obj, callback, thisArg) { return obj.forEach ? obj.forEach(callback, thisArg) : ember_metal__array.forEach.call(obj, callback, thisArg); } + /** + * Calls the filter function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-filter method when necessary. + * + * @method filter + * @param {Object} obj The object to call filter on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + * @return {Array} An array containing the filtered values + * @since 1.4.0 + */ function filter(obj, callback, thisArg) { return obj.filter ? obj.filter(callback, thisArg) : ember_metal__array.filter.call(obj, callback, thisArg); } + /** + * Calls the indexOf function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-indexOf method when necessary. + * + * @method indexOf + * @param {Object} obj The object to call indexOn on + * @param {Function} callback The callback to execute + * @param {Object} index The index to start searching from + * + */ function indexOf(obj, element, index) { return obj.indexOf ? obj.indexOf(element, index) : ember_metal__array.indexOf.call(obj, element, index); } + /** + * Returns an array of indexes of the first occurrences of the passed elements + * on the passed object. + * + * ```javascript + * var array = [1, 2, 3, 4, 5]; + * Ember.EnumerableUtils.indexesOf(array, [2, 5]); // [1, 4] + * + * var fubar = "Fubarr"; + * Ember.EnumerableUtils.indexesOf(fubar, ['b', 'r']); // [2, 4] + * ``` + * + * @method indexesOf + * @param {Object} obj The object to check for element indexes + * @param {Array} elements The elements to search for on *obj* + * + * @return {Array} An array of indexes. + * + */ function indexesOf(obj, elements) { return elements === undefined ? [] : map(elements, function (item) { return indexOf(obj, item); }); } + /** + * Adds an object to an array. If the array already includes the object this + * method has no effect. + * + * @method addObject + * @param {Array} array The array the passed item should be added to + * @param {Object} item The item to add to the passed array + * + * @return 'undefined' + */ function addObject(array, item) { var index = indexOf(array, item); if (index === -1) { array.push(item); } } + /** + * Removes an object from an array. If the array does not contain the passed + * object this method has no effect. + * + * @method removeObject + * @param {Array} array The array to remove the item from. + * @param {Object} item The item to remove from the passed array. + * + * @return 'undefined' + */ function removeObject(array, item) { var index = indexOf(array, item); if (index !== -1) { array.splice(index, 1); } @@ -3061,18 +3486,66 @@ ret = ret.concat(splice.apply(array, chunk)); } return ret; } + /** + * Replaces objects in an array with the passed objects. + * + * ```javascript + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] + * ``` + * + * @method replace + * @param {Array} array The array the objects should be inserted into. + * @param {Number} idx Starting index in the array to replace. If *idx* >= + * length, then append to the end of the array. + * @param {Number} amt Number of elements that should be removed from the array, + * starting at *idx* + * @param {Array} objects An array of zero or more objects that should be + * inserted into the array at *idx* + * + * @return {Array} The modified array. + */ function replace(array, idx, amt, objects) { if (array.replace) { return array.replace(idx, amt, objects); } else { return _replace(array, idx, amt, objects); } } + /** + * Calculates the intersection of two arrays. This method returns a new array + * filled with the records that the two passed arrays share with each other. + * If there is no intersection, an empty array will be returned. + * + * ```javascript + * var array1 = [1, 2, 3, 4, 5]; + * var array2 = [1, 3, 5, 6, 7]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [1, 3, 5] + * + * var array1 = [1, 2, 3]; + * var array2 = [4, 5, 6]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [] + * ``` + * + * @method intersection + * @param {Array} array1 The first array + * @param {Array} array2 The second array + * + * @return {Array} The intersection of the two passed arrays. + */ function intersection(array1, array2) { var result = []; forEach(array1, function (element) { if (indexOf(array2, element) >= 0) { result.push(element); @@ -3273,10 +3746,21 @@ } return newActions; } + /** + Add an event listener + + @method addListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Boolean} once A flag whether a function should only be called once + */ function addListener(obj, eventName, target, method, once) { Ember['default'].assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); if (!method && "function" === typeof target) { method = target; @@ -3350,10 +3834,29 @@ for (var i = actions.length - 3; i >= 0; i -= 3) { _removeListener(actions[i], actions[i + 1]); } } } + + /** + Suspend listener during callback. + + This should only be used by the target of the event listener + when it is taking an action that would cause the event, e.g. + an object might suspend its property change listener while it is + setting that property. + + @method suspendListener + @for Ember + + @private + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback + */ function suspendListener(obj, eventName, target, method, callback) { if (!method && "function" === typeof target) { method = target; target = null; } @@ -3375,10 +3878,23 @@ } return utils.tryFinally(tryable, finalizer); } + /** + Suspends multiple listeners during a callback. + + @method suspendListeners + @for Ember + + @private + @param obj + @param {Array} eventNames Array of event names + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback + */ function suspendListeners(obj, eventNames, target, method, callback) { if (!method && "function" === typeof target) { method = target; target = null; } @@ -3411,10 +3927,18 @@ } return utils.tryFinally(tryable, finalizer); } + /** + Return a list of currently watched events + + @private + @method watchedEvents + @for Ember + @param obj + */ function watchedEvents(obj) { var listeners = obj["__ember_meta__"].listeners; var ret = []; if (listeners) { @@ -3425,10 +3949,24 @@ } } return ret; } + /** + Send an event. The execution of suspended listeners + is skipped, and once listeners are removed. A listener without + a target is executed on the passed object. If an array of actions + is not passed, the actions stored on the passed object are invoked. + + @method sendEvent + @for Ember + @param obj + @param {String} eventName + @param {Array} params Optional parameters for each listener. + @param {Array} actions Optional array of actions (listeners). + @return true + */ function sendEvent(obj, eventName, params, actions) { // first give object a chance to handle it if (obj !== Ember['default'] && "function" === typeof obj.sendEvent) { obj.sendEvent(eventName, params); } @@ -3475,17 +4013,31 @@ } } return true; } + /** + @private + @method hasListeners + @for Ember + @param obj + @param {String} eventName + */ function hasListeners(obj, eventName) { var meta = obj["__ember_meta__"]; var actions = meta && meta.listeners && meta.listeners[eventName]; return !!(actions && actions.length); } + /** + @private + @method listenersFor + @for Ember + @param obj + @param {String} eventName + */ function listenersFor(obj, eventName) { var ret = []; var meta = obj["__ember_meta__"]; var actions = meta && meta.listeners && meta.listeners[eventName]; @@ -3500,10 +4052,33 @@ } return ret; } + /** + Define a property as a function that should be executed when + a specified event or events are triggered. + + + ``` javascript + var Job = Ember.Object.extend({ + logCompleted: Ember.on('completed', function() { + console.log('Job completed!'); + }) + }); + + var job = Job.create(); + + Ember.sendEvent(job, 'completed'); // Logs 'Job completed!' + ``` + + @method on + @for Ember + @param {String} eventNames* + @param {Function} func + @return func + */ function on() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } @@ -3517,11 +4092,14 @@ enifed('ember-metal/expand_properties', ['exports', 'ember-metal/error', 'ember-metal/array'], function (exports, EmberError, array) { 'use strict'; + exports['default'] = expandProperties; + var SPLIT_REGEX = /\{|\}/; + /** Expands `pattern`, invoking `callback` for each expansion. The only pattern supported is brace-expansion, anything else will be passed once to `callback` directly. @@ -3544,13 +4122,10 @@ @private @param {String} pattern The property pattern to expand. @param {Function} callback The callback to invoke. It is invoked once per expansion, and is passed the expansion. */ - exports['default'] = expandProperties; - - var SPLIT_REGEX = /\{|\}/; function expandProperties(pattern, callback) { if (pattern.indexOf(' ') > -1) { throw new EmberError['default']('Brace expanded properties cannot contain spaces, e.g. \'user.{firstName, lastName}\' should be \'user.{firstName,lastName}\''); } @@ -3590,33 +4165,10 @@ enifed('ember-metal/get_properties', ['exports', 'ember-metal/property_get', 'ember-metal/utils'], function (exports, property_get, utils) { 'use strict'; - - /** - To get multiple properties at once, call `Ember.getProperties` - with an object followed by a list of strings or an array: - - ```javascript - Ember.getProperties(record, 'firstName', 'lastName', 'zipCode'); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` - - is equivalent to: - - ```javascript - Ember.getProperties(record, ['firstName', 'lastName', 'zipCode']); - // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` - - @method getProperties - @for Ember - @param {Object} obj - @param {String...|Array} list of keys to get - @return {Object} - */ exports['default'] = getProperties; function getProperties(obj) { var ret = {}; var propertyNames = arguments; var i = 1; @@ -3677,21 +4229,10 @@ exports._instrumentStart = _instrumentStart; exports.subscribe = subscribe; exports.unsubscribe = unsubscribe; exports.reset = reset; - /** - Notifies event's subscribers, calls `before` and `after` hooks. - - @method instrument - @namespace Ember.Instrumentation - - @param {String} [name] Namespaced event name. - @param {Object} payload - @param {Function} callback Function that you're instrumenting. - @param {Object} binding Context that instrument function is called with. - */ var subscribers = []; var cache = {}; var populateListeners = function (name) { var listeners = []; @@ -3714,10 +4255,22 @@ // fn.bind will be available in all the browsers that support the advanced window.performance... ;-) return fn ? fn.bind(perf) : function () { return +new Date(); }; })(); + + /** + Notifies event's subscribers, calls `before` and `after` hooks. + + @method instrument + @namespace Ember.Instrumentation + + @param {String} [name] Namespaced event name. + @param {Object} payload + @param {Function} callback Function that you're instrumenting. + @param {Object} binding Context that instrument function is called with. + */ function instrument(name, _payload, callback, binding) { if (arguments.length <= 3 && typeof _payload === "function") { binding = callback; callback = _payload; _payload = undefined; @@ -3740,10 +4293,12 @@ } else { return callback.call(binding); } } + // private for now + function _instrumentStart(name, _payload) { var listeners = cache[name]; if (!listeners) { listeners = populateListeners(name); @@ -3783,10 +4338,21 @@ console.timeEnd(timeName); } }; } + /** + Subscribes to a particular event or instrumented block of code. + + @method subscribe + @namespace Ember.Instrumentation + + @param {String} [pattern] Namespaced event name. + @param {Object} [object] Before and After hooks. + + @return {Subscriber} + */ function subscribe(pattern, object) { var paths = pattern.split("."); var path; var regex = []; @@ -3812,10 +4378,18 @@ cache = {}; return subscriber; } + /** + Unsubscribes from a particular event or instrumented block of code. + + @method unsubscribe + @namespace Ember.Instrumentation + + @param {Object} [subscriber] + */ function unsubscribe(subscriber) { var index; for (var i = 0, l = subscribers.length; i < l; i++) { if (subscribers[i] === subscriber) { @@ -3825,10 +4399,16 @@ subscribers.splice(index, 1); cache = {}; } + /** + Resets `Ember.Instrumentation` by flushing list of subscribers. + + @method reset + @namespace Ember.Instrumentation + */ function reset() { subscribers.length = 0; cache = {}; } @@ -3838,34 +4418,10 @@ enifed('ember-metal/is_blank', ['exports', 'ember-metal/is_empty'], function (exports, isEmpty) { 'use strict'; - - /** - A value is blank if it is empty or a whitespace string. - - ```javascript - Ember.isBlank(); // true - Ember.isBlank(null); // true - Ember.isBlank(undefined); // true - Ember.isBlank(''); // true - Ember.isBlank([]); // true - Ember.isBlank('\n\t'); // true - Ember.isBlank(' '); // true - Ember.isBlank({}); // false - Ember.isBlank('\n\t Hello'); // false - Ember.isBlank('Hello world'); // false - Ember.isBlank([1,2,3]); // false - ``` - - @method isBlank - @for Ember - @param {Object} obj Value to test - @return {Boolean} - @since 1.5.0 - */ exports['default'] = isBlank; function isBlank(obj) { return isEmpty['default'](obj) || typeof obj === 'string' && obj.match(/\S/) === null; } @@ -3943,34 +4499,10 @@ enifed('ember-metal/is_present', ['exports', 'ember-metal/is_blank'], function (exports, isBlank) { 'use strict'; - - /** - A value is present if it not `isBlank`. - - ```javascript - Ember.isPresent(); // false - Ember.isPresent(null); // false - Ember.isPresent(undefined); // false - Ember.isPresent(''); // false - Ember.isPresent([]); // false - Ember.isPresent('\n\t'); // false - Ember.isPresent(' '); // false - Ember.isPresent({}); // true - Ember.isPresent('\n\t Hello'); // true - Ember.isPresent('Hello world'); // true - Ember.isPresent([1,2,3]); // true - ``` - - @method isPresent - @for Ember - @param {Object} obj Value to test - @return {Boolean} - @since 1.8.0 - */ exports['default'] = isPresent; function isPresent(obj) { return !isBlank['default'](obj); } @@ -4736,26 +5268,10 @@ 'use strict'; exports.assign = assign; - /** - Merge the contents of two objects together into the first object. - - ```javascript - Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'} - var a = {first: 'Yehuda'}; - var b = {last: 'Katz'}; - Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'} - ``` - - @method merge - @for Ember - @param {Object} original The object to merge into - @param {Object} updates The object to copy properties from - @return {Object} - */ exports['default'] = merge; function merge(original, updates) { if (!updates || typeof updates !== 'object') { return original; @@ -4804,17 +5320,10 @@ exports.observer = observer; exports.immediateObserver = immediateObserver; exports.beforeObserver = beforeObserver; exports.Mixin = Mixin; - /** - @method mixin - @for Ember - @param obj - @param mixins* - @return obj - */ "REMOVE_USE_STRICT: true";var REQUIRED; var a_slice = [].slice; function superFunction() { var func = this.__nextSuper; @@ -5262,10 +5771,18 @@ finishPartial(obj, m); } return obj; } + + /** + @method mixin + @for Ember + @param obj + @param mixins* + @return obj + */ function mixin(obj) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } @@ -5537,10 +6054,17 @@ REQUIRED = new ember_metal__properties.Descriptor(); REQUIRED.toString = function () { return "(Required Property)"; }; + + /** + Denotes a required property for a mixin + + @method required + @for Ember + */ function required() { Ember['default'].deprecate("Ember.required is deprecated as its behavior is inconsistent and unreliable.", false); return REQUIRED; } @@ -5548,14 +6072,63 @@ this.isDescriptor = true; this.methodName = methodName; } Alias.prototype = new ember_metal__properties.Descriptor(); + + /** + Makes a method available via an additional name. + + ```javascript + App.Person = Ember.Object.extend({ + name: function() { + return 'Tomhuda Katzdale'; + }, + moniker: Ember.aliasMethod('name') + }); + + var goodGuy = App.Person.create(); + + goodGuy.name(); // 'Tomhuda Katzdale' + goodGuy.moniker(); // 'Tomhuda Katzdale' + ``` + + @method aliasMethod + @for Ember + @param {String} methodName name of the method to alias + */ function aliasMethod(methodName) { return new Alias(methodName); } + // .......................................................... + // OBSERVER HELPER + // + + /** + Specify a method that observes property changes. + + ```javascript + Ember.Object.extend({ + valueObserver: Ember.observer('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` + + In the future this method may become asynchronous. If you want to ensure + synchronous behavior, use `immediateObserver`. + + Also available as `Function.prototype.observes` if prototype extensions are + enabled. + + @method observer + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ function observer() { for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } @@ -5586,19 +6159,84 @@ func.__ember_observes__ = paths; return func; } + /** + Specify a method that observes property changes. + + ```javascript + Ember.Object.extend({ + valueObserver: Ember.immediateObserver('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` + + In the future, `Ember.observer` may become asynchronous. In this event, + `Ember.immediateObserver` will maintain the synchronous behavior. + + Also available as `Function.prototype.observesImmediately` if prototype extensions are + enabled. + + @method immediateObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ function immediateObserver() { for (var i = 0, l = arguments.length; i < l; i++) { var arg = arguments[i]; Ember['default'].assert("Immediate observers must observe internal properties only, not properties on other objects.", typeof arg !== "string" || arg.indexOf(".") === -1); } return observer.apply(this, arguments); } + /** + When observers fire, they are called with the arguments `obj`, `keyName`. + + Note, `@each.property` observer is called per each add or replace of an element + and it's not called with a specific enumeration item. + + A `beforeObserver` fires before a property changes. + + A `beforeObserver` is an alternative form of `.observesBefore()`. + + ```javascript + App.PersonView = Ember.View.extend({ + friends: [{ name: 'Tom' }, { name: 'Stefan' }, { name: 'Kris' }], + + valueWillChange: Ember.beforeObserver('content.value', function(obj, keyName) { + this.changingFrom = obj.get(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 + } + }), + + friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { + // some logic + // obj.get(keyName) returns friends array + }) + }); + ``` + + Also available as `Function.prototype.observesBefore` if prototype extensions are + enabled. + + @method beforeObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ function beforeObserver() { for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { args[_key5] = arguments[_key5]; } @@ -5649,28 +6287,29 @@ exports._suspendBeforeObservers = _suspendBeforeObservers; exports._suspendObservers = _suspendObservers; exports.beforeObserversFor = beforeObserversFor; exports.removeBeforeObserver = removeBeforeObserver; - /** - @method addObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] - */ var AFTER_OBSERVERS = ":change"; var BEFORE_OBSERVERS = ":before"; function changeEvent(keyName) { return keyName + AFTER_OBSERVERS; } function beforeEvent(keyName) { return keyName + BEFORE_OBSERVERS; } + + /** + @method addObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] + */ function addObserver(obj, _path, target, method) { ember_metal__events.addListener(obj, changeEvent(_path), target, method); watching.watch(obj, _path); return this; @@ -5678,24 +6317,45 @@ function observersFor(obj, path) { return ember_metal__events.listenersFor(obj, changeEvent(path)); } + /** + @method removeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ function removeObserver(obj, path, target, method) { watching.unwatch(obj, path); ember_metal__events.removeListener(obj, changeEvent(path), target, method); return this; } + /** + @method addBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ function addBeforeObserver(obj, path, target, method) { ember_metal__events.addListener(obj, beforeEvent(path), target, method); watching.watch(obj, path); return this; } + // Suspend observer during callback. + // + // This should only be used by the target of the observer + // while it is setting the observed path. + function _suspendBeforeObserver(obj, path, target, method, callback) { return ember_metal__events.suspendListener(obj, beforeEvent(path), target, method, callback); } function _suspendObserver(obj, path, target, method, callback) { @@ -5714,10 +6374,18 @@ function beforeObserversFor(obj, path) { return ember_metal__events.listenersFor(obj, beforeEvent(path)); } + /** + @method removeBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ function removeBeforeObserver(obj, path, target, method) { watching.unwatch(obj, path); ember_metal__events.removeListener(obj, beforeEvent(path), target, method); return this; @@ -6132,22 +6800,18 @@ exports.Descriptor = Descriptor; exports.MANDATORY_SETTER_FUNCTION = MANDATORY_SETTER_FUNCTION; exports.DEFAULT_GETTER_FUNCTION = DEFAULT_GETTER_FUNCTION; exports.defineProperty = defineProperty; - // .......................................................... - // DESCRIPTOR - // - - /** - Objects of this type can implement an interface to respond to requests to - get and set. The default implementation handles simple properties. - */ function Descriptor() { this.isDescriptor = true; } + // .......................................................... + // DEFINING PROPERTIES API + // + function MANDATORY_SETTER_FUNCTION(name) { return function SETTER_FUNCTION(value) { Ember['default'].assert("You must use Ember.set() to set the `" + name + "` property (of " + this + ") to `" + value + "`.", false); }; } @@ -6157,10 +6821,55 @@ var meta = this["__ember_meta__"]; return meta && meta.values[name]; }; } + /** + NOTE: This is a low-level method used by other parts of the API. You almost + never want to call this method directly. Instead you should use + `Ember.mixin()` to define new properties. + + Defines a property on an object. This method works much like the ES5 + `Object.defineProperty()` method except that it can also accept computed + properties and other special descriptors. + + Normally this method takes only three parameters. However if you pass an + instance of `Descriptor` as the third param then you can pass an + optional value as the fourth parameter. This is often more efficient than + creating new descriptor hashes for each property. + + ## Examples + + ```javascript + // ES5 compatible mode + Ember.defineProperty(contact, 'firstName', { + writable: true, + configurable: false, + enumerable: true, + value: 'Charles' + }); + + // define a simple property + Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); + + // define a computed property + Ember.defineProperty(contact, 'fullName', Ember.computed(function() { + return this.firstName+' '+this.lastName; + }).property('firstName', 'lastName')); + ``` + + @private + @method defineProperty + @for Ember + @param {Object} obj the object to define this property on. This may be a prototype. + @param {String} keyName the name of the property + @param {Descriptor} [desc] an instance of `Descriptor` (typically a + computed property) or an ES5 descriptor. + You must provide this or `data` but not both. + @param {*} [data] something other than a descriptor, that will + become the explicit value of this property. + */ function defineProperty(obj, keyName, desc, data, meta) { var possibleDesc, existingDesc, watching, value; if (!meta) { meta = utils.meta(obj); @@ -6544,54 +7253,25 @@ } exports.PROPERTY_DID_CHANGE = PROPERTY_DID_CHANGE; }); -enifed('ember-metal/property_get', ['exports', 'ember-metal/core', 'ember-metal/error', 'ember-metal/path_cache', 'ember-metal/platform/define_property', 'ember-metal/utils'], function (exports, Ember, EmberError, path_cache, define_property, utils) { +enifed('ember-metal/property_get', ['exports', 'ember-metal/core', 'ember-metal/error', 'ember-metal/path_cache', 'ember-metal/platform/define_property', 'ember-metal/utils', 'ember-metal/is_none'], function (exports, Ember, EmberError, path_cache, define_property, utils, isNone) { 'use strict'; exports.get = get; exports.normalizeTuple = normalizeTuple; exports._getPath = _getPath; exports.getWithDefault = getWithDefault; - // .......................................................... - // GET AND SET - // - // If we are on a platform that supports accessors we can use those. - // Otherwise simulate accessors by looking up the property directly on the - // object. - - /** - Gets the value of a property on an object. If the property is computed, - the function will be invoked. If the property is not defined but the - object implements the `unknownProperty` method then that will be invoked. - - If you plan to run on IE8 and older browsers then you should use this - method anytime you want to retrieve a property on an object that you don't - know for sure is private. (Properties beginning with an underscore '_' - are considered private.) - - On all newer browsers, you only need to use this method to retrieve - properties if the property might not be defined on the object and you want - to respect the `unknownProperty` handler. Otherwise you can ignore this - method. - - Note that if the object itself is `undefined`, this method will throw - an error. - - @method get - @for Ember - @param {Object} obj The object to retrieve from. - @param {String} keyName The property key to retrieve - @return {Object} the property value or `null`. - */ var FIRST_KEY = /^([^\.]+)/; var INTERCEPT_GET = utils.symbol("INTERCEPT_GET"); - var UNHANDLED_GET = utils.symbol("UNHANDLED_GET");function get(obj, keyName) { + var UNHANDLED_GET = utils.symbol("UNHANDLED_GET"); + + function get(obj, keyName) { // Helpers that operate with 'this' within an #each if (keyName === "") { return obj; } @@ -6601,11 +7281,11 @@ } Ember['default'].assert("Cannot call get with " + keyName + " key.", !!keyName); Ember['default'].assert("Cannot call get with '" + keyName + "' on an undefined object.", obj !== undefined); - if (!obj) { + if (isNone['default'](obj)) { return _getPath(obj, keyName); } if (obj && typeof obj[INTERCEPT_GET] === "function") { var result = obj[INTERCEPT_GET](obj, keyName); @@ -6639,10 +7319,23 @@ return ret; } } + /** + Normalizes a target/path pair to reflect that actual target/path that should + be observed, etc. This takes into account passing in global property + paths (i.e. a path beginning with a capital letter not defined on the + target). + + @private + @method normalizeTuple + @for Ember + @param {Object} target The current target. May be `null`. + @param {String} path A path on the target or a global property path. + @return {Array} a temporary array with the normalized target/path pair. + */ function normalizeTuple(target, path) { var hasThis = path_cache.hasThis(path); var isGlobal = !hasThis && path_cache.isGlobal(path); var key; @@ -6719,25 +7412,14 @@ 'use strict'; exports.set = set; exports.trySet = trySet; - /** - Sets the value of a property on an object, respecting computed properties - and notifying observers and other listeners of the change. If the - property is not defined but the object implements the `setUnknownProperty` - method then that will be invoked as well. - - @method set - @for Ember - @param {Object} obj The object to modify. - @param {String} keyName The property key to set - @param {Object} value The value to set - @return {Object} the passed value. - */ var INTERCEPT_SET = utils.symbol("INTERCEPT_SET"); - var UNHANDLED_SET = utils.symbol("UNHANDLED_SET");function set(obj, keyName, value, tolerant) { + var UNHANDLED_SET = utils.symbol("UNHANDLED_SET"); + + function set(obj, keyName, value, tolerant) { if (typeof obj === "string") { Ember['default'].assert("Path '" + obj + "' must be global if no obj is given.", path_cache.isGlobalPath(obj)); value = keyName; keyName = obj; obj = Ember['default'].lookup; @@ -6850,10 +7532,24 @@ } } return set(root, keyName, value); } + + /** + Error-tolerant form of `Ember.set`. Will not blow up if any part of the + chain is `undefined`, `null`, or destroyed. + + This is primarily used when syncing bindings, which may try to update after + an object has been destroyed. + + @method trySet + @for Ember + @param {Object} obj The object to modify. + @param {String} path The property path to set + @param {Object} value The value to set + */ function trySet(root, path, value) { return set(root, path, value, true); } exports.INTERCEPT_SET = INTERCEPT_SET; @@ -7519,31 +8215,10 @@ enifed('ember-metal/set_properties', ['exports', 'ember-metal/property_events', 'ember-metal/property_set', 'ember-metal/keys'], function (exports, property_events, property_set, keys) { 'use strict'; - - /** - Set a list of properties on an object. These properties are set inside - a single `beginPropertyChanges` and `endPropertyChanges` batch, so - observers will be buffered. - - ```javascript - var anObject = Ember.Object.create(); - - anObject.setProperties({ - firstName: 'Stanley', - lastName: 'Stuart', - age: 21 - }); - ``` - - @method setProperties - @param obj - @param {Object} properties - @return obj - */ exports['default'] = setProperties; function setProperties(obj, properties) { if (!properties || typeof properties !== "object") { return obj; } @@ -8159,60 +8834,119 @@ exports.zip = zip; exports.zipHash = zipHash; exports.chain = chain; exports.setValue = setValue; - /* - Check whether an object is a stream or not - - @public - @for Ember.stream - @function isStream - @param {Object|Stream} object object to check whether it is a stream - @return {Boolean} `true` if the object is a stream, `false` otherwise - */ function isStream(object) { return object && object.isStream; } + /* + A method of subscribing to a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. + + @public + @for Ember.stream + @function subscribe + @param {Object|Stream} object object or stream to potentially subscribe to + @param {Function} callback function to run when stream value changes + @param {Object} [context] the callback will be executed with this context if it + is provided + */ function subscribe(object, callback, context) { if (object && object.isStream) { return object.subscribe(callback, context); } } + /* + A method of unsubscribing from a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. + + @public + @for Ember.stream + @function unsubscribe + @param {Object|Stream} object object or stream to potentially unsubscribe from + @param {Function} callback function originally passed to `subscribe()` + @param {Object} [context] object originally passed to `subscribe()` + */ function unsubscribe(object, callback, context) { if (object && object.isStream) { object.unsubscribe(callback, context); } } + /* + Retrieve the value of a stream, or in the case a non-stream object is passed, + return the object itself. + + @public + @for Ember.stream + @function read + @param {Object|Stream} object object to return the value of + @return the stream's current value, or the non-stream object itself + */ function read(object) { if (object && object.isStream) { return object.value(); } else { return object; } } + /* + Map an array, replacing any streams with their values. + + @public + @for Ember.stream + @function readArray + @param {Array} array The array to read values from + @return {Array} a new array of the same length with the values of non-stream + objects mapped from their original positions untouched, and + the values of stream objects retaining their original position + and replaced with the stream's current value. + */ function readArray(array) { var length = array.length; var ret = new Array(length); for (var i = 0; i < length; i++) { ret[i] = read(array[i]); } return ret; } + /* + Map a hash, replacing any stream property values with the current value of that + stream. + + @public + @for Ember.stream + @function readHash + @param {Object} object The hash to read keys and values from + @return {Object} a new object with the same keys as the passed object. The + property values in the new object are the original values in + the case of non-stream objects, and the streams' current + values in the case of stream objects. + */ function readHash(object) { var ret = {}; for (var key in object) { ret[key] = read(object[key]); } return ret; } + /* + Check whether an array contains any stream values + + @public + @for Ember.stream + @function scanArray + @param {Array} array array given to a handlebars helper + @return {Boolean} `true` if the array contains a stream/bound value, `false` + otherwise + */ function scanArray(array) { var length = array.length; var containsStream = false; for (var i = 0; i < length; i++) { @@ -8223,10 +8957,20 @@ } return containsStream; } + /* + Check whether a hash has any stream property values + + @public + @for Ember.stream + @function scanHash + @param {Object} hash "hash" argument given to a handlebars helper + @return {Boolean} `true` if the object contains a stream/bound value, `false` + otherwise + */ function scanHash(hash) { var containsStream = false; for (var prop in hash) { if (isStream(hash[prop])) { @@ -8236,10 +8980,23 @@ } return containsStream; } + /* + Join an array, with any streams replaced by their current values + + @public + @for Ember.stream + @function concat + @param {Array} array An array containing zero or more stream objects and + zero or more non-stream objects + @param {String} separator string to be used to join array elements + @return {String} String with array elements concatenated and joined by the + provided separator, and any stream array members having been + replaced by the current value of the stream + */ function concat(array, separator) { // TODO: Create subclass ConcatStream < Stream. Defer // subscribing to streams until the value() is called. var hasStream = scanArray(array); if (hasStream) { @@ -8358,10 +9115,42 @@ } return stream; } + /** + Generate a new stream by providing a source stream and a function that can + be used to transform the stream's value. In the case of a non-stream object, + returns the result of the function. + + The value to transform would typically be available to the function you pass + to `chain()` via scope. For example: + + ```javascript + var source = ...; // stream returning a number + // or a numeric (non-stream) object + var result = chain(source, function() { + var currentValue = read(source); + return currentValue + 1; + }); + ``` + + In the example, result is a stream if source is a stream, or a number of + source was numeric. + + @public + @for Ember.stream + @function chain + @param {Object|Stream} value A stream or non-stream object + @param {Function} fn function to be run when the stream value changes, or to + be run once in the case of a non-stream object + @return {Object|Stream} In the case of a stream `value` parameter, a new + stream that will be updated with the return value of + the provided function `fn`. In the case of a + non-stream object, the return value of the provided + function `fn`. + */ function chain(value, fn, label) { Ember.assert('Must call chain with a label', !!label); if (isStream(value)) { var stream = new Stream['default'](fn, function () { return '' + label + '(' + labelFor(value) + ')'; @@ -8402,19 +9191,10 @@ exports.apply = apply; exports.applyStr = applyStr; exports.meta = meta; exports.canInvoke = canInvoke; - /** - Generates a universally unique identifier. This method - is used internally by Ember for assisting with - the generation of GUID's and other unique identifiers - such as `bind-attr` data attributes. - - @public - @return {Number} [description] - */ "REMOVE_USE_STRICT: true"; /** @module ember-metal */ /** @@ -8423,10 +9203,20 @@ @private @return {Number} the uuid */ var _uuid = 0; + + /** + Generates a universally unique identifier. This method + is used internally by Ember for assisting with + the generation of GUID's and other unique identifiers + such as `bind-attr` data attributes. + + @public + @return {Number} [description] + */ function uuid() { return ++_uuid; } /** @@ -8555,11 +9345,13 @@ }; var NEXT_SUPER_PROPERTY = { name: "__nextSuper", descriptor: undefinedDescriptor - };function generateGuid(obj, prefix) { + }; + + function generateGuid(obj, prefix) { if (!prefix) { prefix = GUID_PREFIX; } var ret = prefix + uuid(); @@ -8576,10 +9368,24 @@ } } return ret; } + /** + Returns a unique id for the object. If the object does not yet have a guid, + one will be assigned to it. You can call this on any object, + `Ember.Object`-based or not, but be aware that it will add a `_guid` + property. + + You can also use this method on DOM Element objects. + + @private + @method guidFor + @for Ember + @param {Object} obj any object, string, number, Element, or primitive + @return {String} the unique guid for this instance. + */ function guidFor(obj) { // special cases where we don't want to add a key to object if (obj === undefined) { return "(undefined)"; @@ -8761,10 +9567,43 @@ var _meta = meta(obj, true); _meta[property] = value; return value; } + /** + @deprecated + @private + + In order to store defaults for a class, a prototype may need to create + a default meta object, which will be inherited by any objects instantiated + from the class's constructor. + + However, the properties of that meta object are only shallow-cloned, + so if a property is a hash (like the event system's `listeners` hash), + it will by default be shared across all instances of that class. + + This method allows extensions to deeply clone a series of nested hashes or + other complex objects. For instance, the event system might pass + `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will + walk down the keys provided. + + For each key, if the key does not exist, it is created. If it already + exists and it was inherited from its constructor, the constructor's + key is cloned. + + You can also pass false for `writable`, which will simply return + undefined if `prepareMetaPath` discovers any part of the path that + shared or undefined. + + @method metaPath + @for Ember + @param {Object} obj The object whose meta we are examining + @param {Array} path An array of keys to walk down + @param {Boolean} writable whether or not to create a new meta + (or meta property) if one does not already exist or if it's + shared with its constructor + */ function metaPath(obj, path, writable) { Ember['default'].deprecate("Ember.metaPath is deprecated and will be removed from future releases."); var _meta = meta(obj, writable); var keyName, value; @@ -8789,10 +9628,22 @@ } return value; } + /** + Wraps the passed function so that `this._super` will point to the superFunc + when the function is invoked. This is the primitive we use to implement + calls to super. + + @private + @method wrap + @for Ember + @param {Function} func The function to call + @param {Function} superFunc The super function. + @return {Function} wrapped function. + */ function wrap(func, superFunc) { function superWrapper() { var ret; var sup = this && this.__nextSuper; var length = arguments.length; @@ -8848,10 +9699,30 @@ @return {Boolean} */ function canInvoke(obj, methodName) { return !!(obj && typeof obj[methodName] === "function"); } + + /** + Checks to see if the `methodName` exists on the `obj`, + and if it does, invokes it with the arguments passed. + + ```javascript + var d = new Date('03/15/2013'); + + Ember.tryInvoke(d, 'getTime'); // 1363320000000 + Ember.tryInvoke(d, 'setFullYear', [2014]); // 1394856000000 + Ember.tryInvoke(d, 'noSuchMethod', [2014]); // undefined + ``` + + @method tryInvoke + @for Ember + @param {Object} obj The object to check for the method + @param {String} methodName The method name to check for + @param {Array} [args] The arguments to pass to the method + @return {*} the return value of the invoked method or undefined if it cannot be invoked + */ function tryInvoke(obj, methodName, args) { if (canInvoke(obj, methodName)) { return args ? applyStr(obj, methodName, args) : applyStr(obj, methodName); } } @@ -9038,17 +9909,53 @@ var toString = Object.prototype.toString; var isArray = Array.isArray || function (value) { return value !== null && value !== undefined && typeof value === "object" && typeof value.length === "number" && toString.call(value) === "[object Array]"; }; + + /** + Forces the passed object to be part of an array. If the object is already + an array, it will return the object. Otherwise, it will add the object to + an array. If obj is `null` or `undefined`, it will return an empty array. + + ```javascript + Ember.makeArray(); // [] + Ember.makeArray(null); // [] + Ember.makeArray(undefined); // [] + Ember.makeArray('lindsay'); // ['lindsay'] + Ember.makeArray([1, 2, 42]); // [1, 2, 42] + + var controller = Ember.ArrayProxy.create({ content: [] }); + + Ember.makeArray(controller) === controller; // true + ``` + + @method makeArray + @for Ember + @param {Object} obj the object + @return {Array} + */ function makeArray(obj) { if (obj === null || obj === undefined) { return []; } return isArray(obj) ? obj : [obj]; } + /** + Convenience method to inspect an object. This method will attempt to + convert the object into a useful string description. + + It is a pretty simple implementation. If you want something more robust, + use something like JSDump: https://github.com/NV/jsDump + + @method inspect + @for Ember + @param {Object} obj The object you want to inspect. + @return {String} A description of the object + @since 1.4.0 + */ function inspect(obj) { if (obj === null) { return "null"; } if (obj === undefined) { @@ -9087,10 +9994,17 @@ } } return "{" + ret.join(", ") + "}"; } + // The following functions are intentionally minified to keep the functions + // below Chrome's function body size inlining limit of 600 chars. + /** + @param {Object} target + @param {Function} method + @param {Array} args + */ function apply(t, m, a) { var l = a && a.length; if (!a || !l) { return m.call(t); } @@ -9108,10 +10022,15 @@ default: return m.apply(t, a); } } + /** + @param {Object} target + @param {String} method + @param {Array} args + */ function applyStr(t, m, a) { var l = a && a.length; if (!a || !l) { return t[m](); } @@ -9211,11 +10130,12 @@ }; // This is super annoying, but required until // https://github.com/babel/babel/issues/906 is resolved - ; + ; // jshint ignore:line + function unwatchKey(obj, keyName, meta) { var m = meta || utils.meta(obj); var watching = m.watching; if (watching[keyName] === 1) { @@ -9344,10 +10264,20 @@ watch_path.unwatchPath(obj, _keyPath, m); } } var NODE_STACK = []; + + /** + Tears down the meta on an object so that it can be garbage collected. + Multiple calls will have no effect. + + @method destroy + @for Ember + @param {Object} obj the object to destroy + @return {void} + */ function destroy(obj) { var meta = obj["__ember_meta__"]; var node, nodes, key, nodeObject; if (meta) { @@ -9379,11 +10309,11 @@ } } } }); -enifed('ember-template-compiler', ['exports', 'ember-metal/core', 'ember-template-compiler/system/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template', 'ember-template-compiler/plugins', 'ember-template-compiler/plugins/transform-each-in-to-block-params', 'ember-template-compiler/plugins/transform-with-as-to-hash', 'ember-template-compiler/plugins/transform-bind-attr-to-attributes', 'ember-template-compiler/plugins/transform-each-into-collection', 'ember-template-compiler/plugins/transform-single-arg-each', 'ember-template-compiler/plugins/transform-old-binding-syntax', 'ember-template-compiler/plugins/transform-old-class-binding-syntax', 'ember-template-compiler/plugins/transform-item-class', 'ember-template-compiler/plugins/transform-component-attrs-into-mut', 'ember-template-compiler/plugins/transform-component-curly-to-readonly', 'ember-template-compiler/plugins/transform-angle-bracket-components', 'ember-template-compiler/compat'], function (exports, _Ember, precompile, compile, template, plugins, TransformEachInToBlockParams, TransformWithAsToHash, TransformBindAttrToAttributes, TransformEachIntoCollection, TransformSingleArgEach, TransformOldBindingSyntax, TransformOldClassBindingSyntax, TransformItemClass, TransformComponentAttrsIntoMut, TransformComponentCurlyToReadonly, TransformAngleBracketComponents) { +enifed('ember-template-compiler', ['exports', 'ember-metal/core', 'ember-template-compiler/system/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template', 'ember-template-compiler/plugins', 'ember-template-compiler/plugins/transform-each-in-to-block-params', 'ember-template-compiler/plugins/transform-with-as-to-hash', 'ember-template-compiler/plugins/transform-bind-attr-to-attributes', 'ember-template-compiler/plugins/transform-each-into-collection', 'ember-template-compiler/plugins/transform-single-arg-each', 'ember-template-compiler/plugins/transform-old-binding-syntax', 'ember-template-compiler/plugins/transform-old-class-binding-syntax', 'ember-template-compiler/plugins/transform-item-class', 'ember-template-compiler/plugins/transform-component-attrs-into-mut', 'ember-template-compiler/plugins/transform-component-curly-to-readonly', 'ember-template-compiler/plugins/transform-angle-bracket-components', 'ember-template-compiler/plugins/transform-input-on-to-onEvent', 'ember-template-compiler/compat'], function (exports, _Ember, precompile, compile, template, plugins, TransformEachInToBlockParams, TransformWithAsToHash, TransformBindAttrToAttributes, TransformEachIntoCollection, TransformSingleArgEach, TransformOldBindingSyntax, TransformOldClassBindingSyntax, TransformItemClass, TransformComponentAttrsIntoMut, TransformComponentCurlyToReadonly, TransformAngleBracketComponents, TransformInputOnToOnEvent) { 'use strict'; plugins.registerPlugin("ast", TransformWithAsToHash['default']); plugins.registerPlugin("ast", TransformEachInToBlockParams['default']); @@ -9394,10 +10324,11 @@ plugins.registerPlugin("ast", TransformOldClassBindingSyntax['default']); plugins.registerPlugin("ast", TransformItemClass['default']); plugins.registerPlugin("ast", TransformComponentAttrsIntoMut['default']); plugins.registerPlugin("ast", TransformComponentCurlyToReadonly['default']); plugins.registerPlugin("ast", TransformAngleBracketComponents['default']); + plugins.registerPlugin("ast", TransformInputOnToOnEvent['default']); exports._Ember = _Ember['default']; exports.precompile = precompile['default']; exports.compile = compile['default']; exports.template = template['default']; @@ -9448,19 +10379,20 @@ 'use strict'; exports.registerPlugin = registerPlugin; + var plugins = { + ast: [] + }; + /** Adds an AST plugin to be used by Ember.HTMLBars.compile. @private @method registerASTPlugin */ - var plugins = { - ast: [] - }; function registerPlugin(type, Plugin) { if (!plugins[type]) { throw new Error('Attempting to register "' + Plugin + '" as "' + type + '" which is not a valid HTMLBars plugin type.'); } @@ -9512,30 +10444,32 @@ /** @module ember @submodule ember-htmlbars */ - function TransformBindAttrToAttributes() { + function TransformBindAttrToAttributes(options) { // set later within HTMLBars to the syntax package this.syntax = null; + this.options = options || {}; } /** @private @method transform @param {AST} The AST to be transformed. */ TransformBindAttrToAttributes.prototype.transform = function TransformBindAttrToAttributes_transform(ast) { var plugin = this; + var moduleName = this.options.moduleName; var walker = new this.syntax.Walker(); walker.visit(ast, function (node) { if (node.type === "ElementNode") { for (var i = 0; i < node.modifiers.length; i++) { var modifier = node.modifiers[i]; - if (isBindAttrModifier(modifier)) { + if (isBindAttrModifier(modifier, moduleName)) { node.modifiers.splice(i--, 1); plugin.assignAttrs(node, modifier.hash); } } } @@ -9632,15 +10566,34 @@ default: Ember['default'].assert("Unsupported bind-attr class syntax: `" + value + "`"); } }; - function isBindAttrModifier(modifier) { + function isBindAttrModifier(modifier, moduleName) { var name = modifier.path.original; + var _ref = modifier.path.loc.start || {}; + + var column = _ref.column; + var line = _ref.line; + + var moduleInfo = ""; + + if (moduleName) { + moduleInfo += "'" + moduleName + "' @ "; + } + + if (line && column) { + moduleInfo += "L" + line + ":C" + column; + } + + if (moduleInfo) { + moduleInfo = "(" + moduleInfo + ") "; + } + if (name === "bind-attr" || name === "bindAttr") { - Ember['default'].deprecate("The `" + name + "` helper is deprecated in favor of " + "HTMLBars-style bound attributes"); + Ember['default'].deprecate("The `" + name + "` helper " + moduleInfo + "is deprecated in favor of " + "HTMLBars-style bound attributes."); return true; } else { return false; } } @@ -9985,10 +10938,147 @@ return false; } }); +enifed('ember-template-compiler/plugins/transform-input-on-to-onEvent', ['exports'], function (exports) { + + 'use strict'; + + /** + @module ember + @submodule ember-htmlbars + */ + + /** + An HTMLBars AST transformation that replaces all instances of + + ```handlebars + {{input on="enter" action="doStuff"}} + {{input on="key-press" action="doStuff"}} + ``` + + with + + ```handlebars + {{input enter="doStuff"}} + {{input key-press="doStuff"}} + ``` + + @private + @class TransformInputOnToOnEvent + */ + function TransformInputOnToOnEvent(options) { + // set later within HTMLBars to the syntax package + this.syntax = null; + this.options = options || {}; + } + + /** + @private + @method transform + @param {AST} The AST to be transformed. + */ + TransformInputOnToOnEvent.prototype.transform = function TransformInputOnToOnEvent_transform(ast) { + var pluginContext = this; + var b = pluginContext.syntax.builders; + var walker = new pluginContext.syntax.Walker(); + + walker.visit(ast, function (node) { + if (pluginContext.validate(node)) { + var action = hashPairForKey(node.hash, 'action'); + var on = hashPairForKey(node.hash, 'on'); + var onEvent = hashPairForKey(node.hash, 'onEvent'); + var normalizedOn = on || onEvent; + var moduleInfo = pluginContext.calculateModuleInfo(node.loc); + + if (normalizedOn && normalizedOn.value.type !== 'StringLiteral') { + Ember.deprecate('Using a dynamic value for \'#{normalizedOn.key}=\' with the \'{{input}}\' helper ' + moduleInfo + ' is deprecated.'); + + normalizedOn.key = 'onEvent'; + return; // exit early, as we cannot transform further + } + + removeFromHash(node.hash, normalizedOn); + removeFromHash(node.hash, action); + + if (!action) { + Ember.deprecate('Using \'{{input ' + normalizedOn.key + '="' + normalizedOn.value.value + '" ...}}\' without specifying an action ' + moduleInfo + ' will do nothing.'); + + return; // exit early, if no action was available there is nothing to do + } + + var specifiedOn = normalizedOn ? '' + normalizedOn.key + '="' + normalizedOn.value.value + '" ' : ''; + if (normalizedOn && normalizedOn.value.value === 'keyPress') { + // using `keyPress` in the root of the component will + // clobber the keyPress event handler + normalizedOn.value.value = 'key-press'; + } + + var expected = '' + (normalizedOn ? normalizedOn.value.value : 'enter') + '="' + action.value.original + '"'; + + Ember.deprecate('Using \'{{input ' + specifiedOn + 'action="' + action.value.original + '"}} ' + moduleInfo + ' is deprecated. Please use \'{{input ' + expected + '}}\' instead.'); + if (!normalizedOn) { + normalizedOn = b.pair('onEvent', b.string('enter')); + } + + node.hash.pairs.push(b.pair(normalizedOn.value.value, action.value)); + } + }); + + return ast; + }; + + TransformInputOnToOnEvent.prototype.validate = function TransformWithAsToHash_validate(node) { + return node.type === 'MustacheStatement' && node.path.original === 'input' && (hashPairForKey(node.hash, 'action') || hashPairForKey(node.hash, 'on') || hashPairForKey(node.hash, 'onEvent')); + }; + + TransformInputOnToOnEvent.prototype.calculateModuleInfo = function TransformInputOnToOnEvent_calculateModuleInfo(loc) { + var _ref = loc.start || {}; + + var column = _ref.column; + var line = _ref.line; + + var moduleInfo = ''; + if (this.options.moduleName) { + moduleInfo += '\'' + this.options.moduleName + '\' '; + } + + if (line !== undefined && column !== undefined) { + moduleInfo += '@L' + line + ':C' + column; + } + + return moduleInfo; + }; + + function hashPairForKey(hash, key) { + for (var i = 0, l = hash.pairs.length; i < l; i++) { + var pair = hash.pairs[i]; + if (pair.key === key) { + return pair; + } + } + + return false; + } + + function removeFromHash(hash, pairToRemove) { + var newPairs = []; + for (var i = 0, l = hash.pairs.length; i < l; i++) { + var pair = hash.pairs[i]; + + if (pair !== pairToRemove) { + newPairs.push(pair); + } + } + + hash.pairs = newPairs; + } + + exports['default'] = TransformInputOnToOnEvent; + +}); enifed('ember-template-compiler/plugins/transform-item-class', ['exports'], function (exports) { 'use strict'; exports['default'] = TransformItemClass; @@ -10416,13 +11506,10 @@ */ exports['default'] = function (_options) { var disableComponentGeneration = true; - disableComponentGeneration = false; - - var options = _options || {}; // When calling `Ember.Handlebars.compile()` a second argument of `true` // had a special meaning (long since lost), this just gaurds against // `options` being true, and causing an error during compilation. if (options === true) { @@ -10432,11 +11519,11 @@ options.disableComponentGeneration = disableComponentGeneration; options.plugins = plugins['default']; options.buildMeta = function buildMeta(program) { return { - revision: "Ember@1.13.0-beta.1", + revision: "Ember@1.13.0-beta.2", loc: program.loc, moduleName: options.moduleName }; }; @@ -10529,36 +11616,57 @@ exports.compileSpec = compileSpec; exports.template = template; exports.compile = compile; - /* - * Compile a string into a template spec string. The template spec is a string - * representation of a template. Usually, you would use compileSpec for - * pre-compilation of a template on the server. - * - * Example usage: - * - * var templateSpec = compileSpec("Howdy {{name}}"); - * // This next step is basically what plain compile does - * var template = new Function("return " + templateSpec)(); - * - * @method compileSpec - * @param {String} string An HTMLBars template string - * @return {TemplateSpec} A template spec string - */ function compileSpec(string, options) { var ast = parser.preprocess(string, options); var compiler = new TemplateCompiler['default'](options); var program = compiler.compile(ast); return program; } + /* + * @method template + * @param {TemplateSpec} templateSpec A precompiled template + * @return {Template} A template spec string + */ function template(templateSpec) { return new Function("return " + templateSpec)(); } + /* + * Compile a string into a template rendering function + * + * Example usage: + * + * // Template is the hydration portion of the compiled template + * var template = compile("Howdy {{name}}"); + * + * // Template accepts three arguments: + * // + * // 1. A context object + * // 2. An env object + * // 3. A contextualElement (optional, document.body is the default) + * // + * // The env object *must* have at least these two properties: + * // + * // 1. `hooks` - Basic hooks for rendering a template + * // 2. `dom` - An instance of DOMHelper + * // + * import {hooks} from 'htmlbars-runtime'; + * import {DOMHelper} from 'morph'; + * var context = {name: 'whatever'}, + * env = {hooks: hooks, dom: new DOMHelper()}, + * contextualElement = document.body; + * var domFragment = template(context, env, contextualElement); + * + * @method compile + * @param {String} string An HTMLBars template string + * @param {Object} options A set of options to provide to the compiler + * @return {Template} A function for rendering the template + */ function compile(string, options) { return hooks.wrap(template(compileSpec(string, options)), render['default']); } }); @@ -12064,82 +13172,10 @@ exports.hasHelper = hasHelper; exports.lookupHelper = lookupHelper; exports.bindScope = bindScope; exports.updateScope = updateScope; - /** - HTMLBars delegates the runtime behavior of a template to - hooks provided by the host environment. These hooks explain - the lexical environment of a Handlebars template, the internal - representation of references, and the interaction between an - HTMLBars template and the DOM it is managing. - - While HTMLBars host hooks have access to all of this internal - machinery, templates and helpers have access to the abstraction - provided by the host hooks. - - ## The Lexical Environment - - The default lexical environment of an HTMLBars template includes: - - * Any local variables, provided by *block arguments* - * The current value of `self` - - ## Simple Nesting - - Let's look at a simple template with a nested block: - - ```hbs - <h1>{{title}}</h1> - - {{#if author}} - <p class="byline">{{author}}</p> - {{/if}} - ``` - - In this case, the lexical environment at the top-level of the - template does not change inside of the `if` block. This is - achieved via an implementation of `if` that looks like this: - - ```js - registerHelper('if', function(params) { - if (!!params[0]) { - return this.yield(); - } - }); - ``` - - A call to `this.yield` invokes the child template using the - current lexical environment. - - ## Block Arguments - - It is possible for nested blocks to introduce new local - variables: - - ```hbs - {{#count-calls as |i|}} - <h1>{{title}}</h1> - <p>Called {{i}} times</p> - {{/count}} - ``` - - In this example, the child block inherits its surrounding - lexical environment, but augments it with a single new - variable binding. - - The implementation of `count-calls` supplies the value of - `i`, but does not otherwise alter the environment: - - ```js - var count = 0; - registerHelper('count-calls', function() { - return this.yield([ ++count ]); - }); - ``` - */ - function wrap(template) { if (template === null) { return null; } @@ -12326,10 +13362,34 @@ yield: options.template.yield, yieldItem: options.template.yieldItem, yieldIn: options.template.yieldIn }; } + + /** + Host Hook: createScope + + @param {Scope?} parentScope + @return Scope + + Corresponds to entering a new HTMLBars block. + + This hook is invoked when a block is entered with + a new `self` or additional local variables. + + When invoked for a top-level template, the + `parentScope` is `null`, and this hook should return + a fresh Scope. + + When invoked for a child template, the `parentScope` + is the scope for the parent environment. + + Note that the `Scope` is an opaque value that is + passed to other host hooks. For example, the `get` + hook uses the scope to retrieve a value for a given + scope and variable name. + */ function createScope(env, parentScope) { if (parentScope) { return env.hooks.createChildScope(parentScope); } else { return env.hooks.createFreshScope(); @@ -12341,43 +13401,189 @@ // separate dictionary to track whether a local was bound. // See `bindLocal` for more information. return { self: null, blocks: {}, locals: {}, localPresent: {} }; } + /** + Host Hook: bindShadowScope + + @param {Scope?} parentScope + @return Scope + + Corresponds to rendering a new template into an existing + render tree, but with a new top-level lexical scope. This + template is called the "shadow root". + + If a shadow template invokes `{{yield}}`, it will render + the block provided to the shadow root in the original + lexical scope. + + ```hbs + {{!-- post template --}} + <p>{{props.title}}</p> + {{yield}} + + {{!-- blog template --}} + {{#post title="Hello world"}} + <p>by {{byline}}</p> + <article>This is my first post</article> + {{/post}} + + {{#post title="Goodbye world"}} + <p>by {{byline}}</p> + <article>This is my last post</article> + {{/post}} + ``` + + ```js + helpers.post = function(params, hash, options) { + options.template.yieldIn(postTemplate, { props: hash }); + }; + + blog.render({ byline: "Yehuda Katz" }); + ``` + + Produces: + + ```html + <p>Hello world</p> + <p>by Yehuda Katz</p> + <article>This is my first post</article> + + <p>Goodbye world</p> + <p>by Yehuda Katz</p> + <article>This is my last post</article> + ``` + + In short, `yieldIn` creates a new top-level scope for the + provided template and renders it, making the original block + available to `{{yield}}` in that template. + */ function bindShadowScope(env /*, parentScope, shadowScope */) { return env.hooks.createFreshScope(); } function createChildScope(parent) { var scope = object_utils.createObject(parent); scope.locals = object_utils.createObject(parent.locals); return scope; } + /** + Host Hook: bindSelf + + @param {Scope} scope + @param {any} self + + Corresponds to entering a template. + + This hook is invoked when the `self` value for a scope is ready to be bound. + + The host must ensure that child scopes reflect the change to the `self` in + future calls to the `get` hook. + */ function bindSelf(env, scope, self) { scope.self = self; } function updateSelf(env, scope, self) { env.hooks.bindSelf(env, scope, self); } + /** + Host Hook: bindLocal + + @param {Environment} env + @param {Scope} scope + @param {String} name + @param {any} value + + Corresponds to entering a template with block arguments. + + This hook is invoked when a local variable for a scope has been provided. + + The host must ensure that child scopes reflect the change in future calls + to the `get` hook. + */ function bindLocal(env, scope, name, value) { scope.localPresent[name] = true; scope.locals[name] = value; } function updateLocal(env, scope, name, value) { env.hooks.bindLocal(env, scope, name, value); } + /** + Host Hook: bindBlock + + @param {Environment} env + @param {Scope} scope + @param {Function} block + + Corresponds to entering a shadow template that was invoked by a block helper with + `yieldIn`. + + This hook is invoked with an opaque block that will be passed along + to the shadow template, and inserted into the shadow template when + `{{yield}}` is used. Optionally provide a non-default block name + that can be targeted by `{{yield to=blockName}}`. + */ function bindBlock(env, scope, block) { var name = arguments[3] === undefined ? "default" : arguments[3]; scope.blocks[name] = block; } + /** + Host Hook: block + + @param {RenderNode} renderNode + @param {Environment} env + @param {Scope} scope + @param {String} path + @param {Array} params + @param {Object} hash + @param {Block} block + @param {Block} elseBlock + + Corresponds to: + + ```hbs + {{#helper param1 param2 key1=val1 key2=val2}} + {{!-- child template --}} + {{/helper}} + ``` + + This host hook is a workhorse of the system. It is invoked + whenever a block is encountered, and is responsible for + resolving the helper to call, and then invoke it. + + The helper should be invoked with: + + - `{Array} params`: the parameters passed to the helper + in the template. + - `{Object} hash`: an object containing the keys and values passed + in the hash position in the template. + + The values in `params` and `hash` will already be resolved + through a previous call to the `get` host hook. + + The helper should be invoked with a `this` value that is + an object with one field: + + `{Function} yield`: when invoked, this function executes the + block with the current scope. It takes an optional array of + block parameters. If block parameters are supplied, HTMLBars + will invoke the `bindLocal` host hook to bind the supplied + values to the block arguments provided by the template. + + In general, the default implementation of `block` should work + for most host environments. It delegates to other host hooks + where appropriate, and properly invokes the helper with the + appropriate arguments. + */ function block(morph, env, scope, path, params, hash, template, inverse, visitor) { if (handleRedirect(morph, env, scope, path, params, hash, template, inverse, visitor)) { return; } @@ -12395,10 +13601,14 @@ var options = optionsFor(template, inverse, env, scope, morph, visitor); template_utils.renderAndCleanup(morph, env, options, shadowOptions, callback); } function handleRedirect(morph, env, scope, path, params, hash, template, inverse, visitor) { + if (!path) { + return false; + } + var redirect = env.hooks.classify(env, scope, path); if (redirect) { switch (redirect) { case "component": env.hooks.component(morph, env, scope, path, params, hash, { "default": template, inverse: inverse }, visitor);break; @@ -12506,10 +13716,48 @@ } function linkRenderNode() { return; } + /** + Host Hook: inline + + @param {RenderNode} renderNode + @param {Environment} env + @param {Scope} scope + @param {String} path + @param {Array} params + @param {Hash} hash + + Corresponds to: + + ```hbs + {{helper param1 param2 key1=val1 key2=val2}} + ``` + + This host hook is similar to the `block` host hook, but it + invokes helpers that do not supply an attached block. + + Like the `block` hook, the helper should be invoked with: + + - `{Array} params`: the parameters passed to the helper + in the template. + - `{Object} hash`: an object containing the keys and values passed + in the hash position in the template. + + The values in `params` and `hash` will already be resolved + through a previous call to the `get` host hook. + + In general, the default implementation of `inline` should work + for most host environments. It delegates to other host hooks + where appropriate, and properly invokes the helper with the + appropriate arguments. + + The default implementation of `inline` also makes `partial` + a keyword. Instead of invoking a helper named `partial`, + it invokes the `partial` host hook. + */ function inline(morph, env, scope, path, params, hash, visitor) { if (handleRedirect(morph, env, scope, path, params, hash, null, null, visitor)) { return; } @@ -12586,15 +13834,35 @@ hasBlockParams: function (morph, env, scope, params) { var name = env.hooks.getValue(params[0]) || "default"; return !!(scope.blocks[name] && scope.blocks[name].arity); } - };function partial(renderNode, env, scope, path) { + }; + + function partial(renderNode, env, scope, path) { var template = env.partials[path]; return template.render(scope.self, env, {}).fragment; } + /** + Host hook: range + + @param {RenderNode} renderNode + @param {Environment} env + @param {Scope} scope + @param {any} value + + Corresponds to: + + ```hbs + {{content}} + {{{unescaped}}} + ``` + + This hook is responsible for updating a render node + that represents a range of content with a value. + */ function range(morph, env, scope, path, value, visitor) { if (handleRedirect(morph, env, scope, path, [value], {}, null, null, visitor)) { return; } @@ -12605,10 +13873,37 @@ } morph.lastValue = value; } + /** + Host hook: element + + @param {RenderNode} renderNode + @param {Environment} env + @param {Scope} scope + @param {String} path + @param {Array} params + @param {Hash} hash + + Corresponds to: + + ```hbs + <div {{bind-attr foo=bar}}></div> + ``` + + This hook is responsible for invoking a helper that + modifies an element. + + Its purpose is largely legacy support for awkward + idioms that became common when using the string-based + Handlebars engine. + + Most of the uses of the `element` hook are expected + to be superseded by component syntax and the + `attribute` hook. + */ function element(morph, env, scope, path, params, hash, visitor) { if (handleRedirect(morph, env, scope, path, params, hash, null, null, visitor)) { return; } @@ -12616,10 +13911,31 @@ if (helper) { env.hooks.invokeHelper(null, env, scope, null, params, hash, helper, { element: morph.element }); } } + /** + Host hook: attribute + + @param {RenderNode} renderNode + @param {Environment} env + @param {String} name + @param {any} value + + Corresponds to: + + ```hbs + <div foo={{bar}}></div> + ``` + + This hook is responsible for updating a render node + that represents an element's attribute with a value. + + It receives the name of the attribute as well as an + already-resolved value, and should update the render + node with the value if appropriate. + */ function attribute(morph, env, scope, name, value) { value = env.hooks.getValue(value); if (morph.lastValue !== value) { morph.setContent(value); @@ -12634,10 +13950,30 @@ if (result && result.value) { return result.value; } } + /** + Host Hook: get + + @param {Environment} env + @param {Scope} scope + @param {String} path + + Corresponds to: + + ```hbs + {{foo.bar}} + ^ + + {{helper foo.bar key=value}} + ^ ^ + ``` + + This hook is the "leaf" hook of the system. It is used to + resolve a path relative to the current scope. + */ function get(env, scope, path) { if (path === "") { return scope.self; } @@ -12816,11 +14152,11 @@ prototype.super$constructor = MorphBase['default']; exports['default'] = HTMLBarsMorph; }); -enifed('htmlbars-runtime/render', ['exports', '../htmlbars-util/array-utils', '../htmlbars-util/morph-utils', './expression-visitor', './morph', '../htmlbars-util/template-utils'], function (exports, array_utils, morph_utils, ExpressionVisitor, Morph, template_utils) { +enifed('htmlbars-runtime/render', ['exports', '../htmlbars-util/array-utils', '../htmlbars-util/morph-utils', './expression-visitor', './morph', '../htmlbars-util/template-utils', '../htmlbars-util/void-tag-names'], function (exports, array_utils, morph_utils, ExpressionVisitor, Morph, template_utils, voidMap) { 'use strict'; exports.manualElement = manualElement; exports.createChildMorph = createChildMorph; @@ -12929,13 +14265,17 @@ continue; } dom.setAttribute(el1, key, attributes[key]); } - var el2 = dom.createComment(""); - dom.appendChild(el1, el2); + if (!voidMap['default'][tagName]) { + var el2 = dom.createComment(""); + dom.appendChild(el1, el2); + } + dom.appendChild(el0, el1); + return el0; }, buildRenderNodes: function buildRenderNodes(dom, fragment) { var element = dom.childAt(fragment, [0]); var morphs = []; @@ -13141,12 +14481,10 @@ exports.buildUndefined = buildUndefined; exports.buildHash = buildHash; exports.buildPair = buildPair; exports.buildProgram = buildProgram; - // Statements - function buildMustache(path, params, hash, raw) { return { type: "MustacheStatement", path: path, params: params || [], @@ -13197,10 +14535,12 @@ type: "ConcatStatement", parts: parts || [] }; } + // Nodes + function buildElement(tag, attributes, modifiers, children) { return { type: "ElementNode", tag: tag, attributes: attributes || [], @@ -13231,10 +14571,12 @@ type: "TextNode", chars: chars }; } + // Expressions + function buildSexpr(path, params, hash) { return { type: "SubExpression", path: path, params: params || [], @@ -13288,10 +14630,12 @@ value: undefined, original: undefined }; } + // Miscellaneous + function buildHash(pairs) { return { type: "Hash", pairs: pairs || [] }; @@ -13584,10 +14928,11 @@ function stripComment(comment) { return comment.replace(/^\{\{~?\!-?-?/, '').replace(/-?-?~?\}\}$/, ''); } function preparePath(data, parts, locInfo) { + /*jshint -W040 */ locInfo = this.locInfo(locInfo); var original = data ? '@' : '', dig = [], depth = 0, @@ -13615,18 +14960,20 @@ return new this.PathExpression(data, depth, dig, original, locInfo); } function prepareMustache(path, params, hash, open, strip, locInfo) { + /*jshint -W040 */ // Must use charAt to support IE pre-10 var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== '{' && escapeFlag !== '&'; return new this.MustacheStatement(path, params, hash, escaped, strip, this.locInfo(locInfo)); } function prepareRawBlock(openRawBlock, content, close, locInfo) { + /*jshint -W040 */ if (openRawBlock.path.original !== close) { var errorNode = { loc: openRawBlock.path.loc }; throw new Exception['default'](openRawBlock.path.original + ' doesn\'t match ' + close, errorNode); } @@ -13636,10 +14983,11 @@ return new this.BlockStatement(openRawBlock.path, openRawBlock.params, openRawBlock.hash, program, undefined, {}, {}, {}, locInfo); } function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { + /*jshint -W040 */ // When we are chaining inverse calls, we will not have a close path if (close && close.path && openBlock.path.original !== close.path.original) { var errorNode = { loc: openBlock.path.loc }; throw new Exception['default'](openBlock.path.original + ' doesn\'t match ' + close.path.original, errorNode); @@ -13671,10 +15019,11 @@ }); enifed('htmlbars-syntax/handlebars/compiler/parser', ['exports'], function (exports) { 'use strict'; + /* jshint ignore:start */ /* istanbul ignore next */ /* Jison generated parser */ var handlebars = (function () { var parser = { trace: function trace() {}, yy: {}, @@ -14341,10 +15690,11 @@ function Parser() { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; return new Parser(); })();exports['default'] = handlebars; + /* jshint ignore:end */ }); enifed('htmlbars-syntax/handlebars/compiler/visitor', ['exports', '../exception', './ast'], function (exports, Exception, AST) { 'use strict'; @@ -14782,11 +16132,13 @@ }; } var isFunction; var isArray = Array.isArray || function (value) { return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; - };function indexOf(array, value) { + }; + + function indexOf(array, value) { for (var i = 0, len = array.length; i < len; i++) { if (array[i] === value) { return i; } } @@ -15101,23 +16453,14 @@ return string.join("\n"); }; }); -enifed('htmlbars-syntax/token-handlers', ['exports', '../htmlbars-util/array-utils', './builders', './utils'], function (exports, array_utils, builders, utils) { +enifed('htmlbars-syntax/token-handlers', ['exports', './builders', './utils', '../htmlbars-util/void-tag-names'], function (exports, builders, utils, voidMap) { 'use strict'; - var voidTagNames = "area base br col command embed hr img input keygen link meta param source track wbr"; - var voidMap = {}; - - array_utils.forEach(voidTagNames.split(" "), function (tagName) { - voidMap[tagName] = true; - }); - - // Except for `mustache`, all tokens are only allowed outside of - // a start or end tag. var tokenHandlers = { Comment: function (token) { var current = this.currentElement(); var comment = builders.buildComment(token.chars); utils.appendChild(current, comment); @@ -15135,11 +16478,11 @@ start: { line: tag.firstLine, column: tag.firstColumn }, end: { line: null, column: null } }; this.elementStack.push(element); - if (voidMap.hasOwnProperty(tag.tagName) || tag.selfClosing) { + if (voidMap['default'].hasOwnProperty(tag.tagName) || tag.selfClosing) { tokenHandlers.EndTag.call(this, tag, true); } }, BlockStatement: function () { @@ -15212,11 +16555,11 @@ }; function validateEndTag(tag, element, selfClosing) { var error; - if (voidMap[tag.tagName] && !selfClosing) { + if (voidMap['default'][tag.tagName] && !selfClosing) { // EngTag is also called by StartTag for void and self-closing tags (i.e. // <input> or <br />, so we need to check for that here. Otherwise, we would // throw an error for those cases. error = "Invalid end tag " + formatEndTagInfo(tag) + " (void elements cannot have end tags)."; } else if (element.tag === undefined) { @@ -15342,15 +16685,16 @@ exports.childrenFor = childrenFor; exports.appendChild = appendChild; exports.isHelper = isHelper; exports.unwrapMustache = unwrapMustache; + var ID_INVERSE_PATTERN = /[!"#%-,\.\/;->@\[-\^`\{-~]/; + // Checks the component's attributes to see if it uses block params. // If it does, registers the block params with the program and // removes the corresponding attributes from the element. - var ID_INVERSE_PATTERN = /[!"#%-,\.\/;->@\[-\^`\{-~]/; function parseComponentBlockParams(element, program) { var l = element.attributes.length; var attrNames = []; for (var i = 0; i < l; i++) { @@ -15635,10 +16979,13 @@ } else { return el[textProperty]; } } + // IE8 does not have Object.create, so use a polyfill if needed. + // Polyfill based on Mozilla's (MDN) + function createObject(obj) { if (typeof Object.create === "function") { return Object.create(obj); } else { var Temp = function () {}; @@ -15786,11 +17133,13 @@ }; } var isFunction; var isArray = Array.isArray || function (value) { return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; - };function indexOf(array, value) { + }; + + function indexOf(array, value) { for (var i = 0, len = array.length; i < len; i++) { if (array[i] === value) { return i; } } @@ -15851,12 +17200,10 @@ exports.visitChildren = visitChildren; exports.validateChildMorphs = validateChildMorphs; exports.linkParams = linkParams; exports.dump = dump; - /*globals console*/ - function visitChildren(nodes, callback) { if (!nodes || nodes.length === 0) { return; } @@ -15982,10 +17329,13 @@ options[prop] = defaults[prop]; } return options; } + // IE8 does not have Object.create, so use a polyfill if needed. + // Polyfill based on Mozilla's (MDN) + function createObject(obj) { if (typeof Object.create === 'function') { return Object.create(obj); } else { var Temp = function () {}; @@ -16209,10 +17559,24 @@ morph.lastYielded = null; morph.childNodes = null; } }); +enifed('htmlbars-util/void-tag-names', ['exports', './array-utils'], function (exports, array_utils) { + + 'use strict'; + + var voidTagNames = "area base br col command embed hr img input keygen link meta param source track wbr"; + var voidMap = {}; + + array_utils.forEach(voidTagNames.split(" "), function (tagName) { + voidMap[tagName] = true; + }); + + exports['default'] = voidMap; + +}); enifed('morph-range', ['exports', './morph-range/utils'], function (exports, utils) { 'use strict'; function Morph(domHelper, contextualElement) { @@ -16592,10 +17956,9 @@ 'use strict'; exports.clear = clear; exports.insertBefore = insertBefore; - // inclusive of both nodes function clear(parentNode, firstNode, lastNode) { if (!parentNode) { return; } \ No newline at end of file