dist/ember.js in ember-source-1.0.0.rc3.3 vs dist/ember.js in ember-source-1.0.0.rc3.4

- old
+ new

@@ -1,7 +1,7 @@ -// Version: v1.0.0-rc.3-130-g99bc3b5 -// Last commit: 99bc3b5 (2013-04-29 12:12:25 -0700) +// Version: v1.0.0-rc.3-251-gcd5dfe3 +// Last commit: cd5dfe3 (2013-05-18 11:06:43 -0700) (function() { /*global __fail__*/ @@ -149,12 +149,12 @@ }; }; })(); -// Version: v1.0.0-rc.3-156-g9d1d3cd -// Last commit: 9d1d3cd (2013-05-01 22:51:16 -0700) +// Version: v1.0.0-rc.3-251-gcd5dfe3 +// Last commit: cd5dfe3 (2013-05-18 11:06:43 -0700) (function() { var define, requireModule; @@ -825,11 +825,11 @@ // special cases where we don't want to add a key to object if (obj === undefined) return "(undefined)"; if (obj === null) return "(null)"; - var cache, ret; + var ret; var type = typeof obj; // Don't allow prototype changes to String etc. to change the guidFor switch(type) { case 'number': @@ -1226,11 +1226,11 @@ unless that value is undefined, in which case it is the return value of the tryable. */ if (needsFinallyFix) { Ember.tryCatchFinally = function(tryable, catchable, finalizer, binding) { - var result, finalResult, finalError, finalReturn; + var result, finalResult, finalError; binding = binding || this; try { result = tryable.call(binding); @@ -2122,11 +2122,11 @@ var o_create = Ember.create, metaFor = Ember.meta, META_KEY = Ember.META_KEY, /* listener flags */ - ONCE = 1, SUSPENDED = 2, IMMEDIATE = 4; + ONCE = 1, SUSPENDED = 2; /* The event system uses a series of nested hashes to store listeners on an object. When a listener is registered, or when an event arrives, these hashes are consulted to determine which target and action pair to invoke. @@ -2739,15 +2739,15 @@ obj1.set('foo', mayBlowUpWhenSet); obj2.set('bar', baz); }); ``` - @method changePropertiess + @method changeProperties @param {Function} callback @param [binding] */ -var changeProperties = Ember.changeProperties = function(cb, binding){ +Ember.changeProperties = function(cb, binding){ beginPropertyChanges(); tryFinally(cb, endPropertyChanges, binding); }; var notifyBeforeObservers = function(obj, keyName) { @@ -2948,11 +2948,11 @@ @class Descriptor @namespace Ember @private @constructor */ -var Descriptor = Ember.Descriptor = function() {}; +Ember.Descriptor = function() {}; // .......................................................... // DEFINING PROPERTIES API // @@ -3509,11 +3509,11 @@ watching[keyPath] = (watching[keyPath] || 0) + 1; } }; Ember.unwatchPath = function(obj, keyPath) { - var m = metaFor(obj), watching = m.watching, desc; + var m = metaFor(obj), watching = m.watching; if (watching[keyPath] === 1) { watching[keyPath] = 0; chainsFor(obj).remove(keyPath); } else if (watching[keyPath] > 1) { @@ -4055,11 +4055,10 @@ The function you pass will be used to both get and set property values. The function should accept two parameters, key and value. If value is not undefined you should set the value first. In either case return the current value of the property. - @method computed @for Ember @param {Function} func The computed property function. @return {Ember.ComputedProperty} property descriptor instance */ @@ -4297,11 +4296,11 @@ /** @method computed.any @for Ember @param {String} dependentKey, [dependentKey...] @return {Ember.ComputedProperty} computed property which returns - the first trouthy value of given list of properties. + the first truthy value of given list of properties. */ registerComputedWithProperties('any', function(properties) { for (var key in properties) { if (properties.hasOwnProperty(key) && properties[key]) { return properties[key]; @@ -4348,10 +4347,52 @@ } }); }; /** + @method computed.oneWay + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + one way computed property to the original value for property. + + Where `computed.alias` aliases `get` and `set`, and allows for bidirectional + data flow, `computed.oneWay` only provides an aliased `get`. The `set` will + not mutate the upstream property, rather causes the current property to + become the value set. This causes the downstream property to permentantly + diverge from the upstream property. + + ```javascript + User = Ember.Object.extend({ + firstName: null, + lastName: null, + nickName: Ember.computed.oneWay('firstName') + }); + + user = User.create({ + firstName: 'Teddy', + lastName: 'Zeenny' + }); + + user.get('nickName'); + # 'Teddy' + + user.set('nickName', 'TeddyBear'); + # 'TeddyBear' + + user.get('firstName'); + # 'Teddy' + ``` +*/ +Ember.computed.oneWay = function(dependentKey) { + return Ember.computed(dependentKey, function() { + return get(this, dependentKey); + }); +}; + + +/** @method computed.defaultTo @for Ember @param {String} defaultPath @return {Ember.ComputedProperty} computed property which acts like a standard getter and setter, but defaults to the value from `defaultPath`. @@ -4376,12 +4417,10 @@ */ var AFTER_OBSERVERS = ':change'; var BEFORE_OBSERVERS = ':before'; -var guidFor = Ember.guidFor; - function changeEvent(keyName) { return keyName+AFTER_OBSERVERS; } function beforeEvent(keyName) { @@ -5536,11 +5575,11 @@ 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). + 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 @@ -5837,18 +5876,10 @@ if (mixin._without) { a_forEach.call(mixin._without, removeKeys); } } } } -function writableReq(obj) { - var m = Ember.meta(obj), req = m.required; - if (!req || !m.hasOwnProperty('required')) { - req = m.required = req ? o_create(req) : {}; - } - return req; -} - var IS_BINDING = Ember.IS_BINDING = /^.+Binding$/; function detectBinding(obj, key, value, m) { if (IS_BINDING.test(key)) { var bindings = m.bindings; @@ -7503,11 +7534,11 @@ @namespace Ember @extends Error @constructor */ Ember.Error = function() { - var tmp = Error.prototype.constructor.apply(this, arguments); + var tmp = Error.apply(this, arguments); // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. for (var idx = 0; idx < errorProps.length; idx++) { this[errorProps[idx]] = tmp[errorProps[idx]]; } @@ -7765,14 +7796,16 @@ }, /** Returns the Capitalized form of a string - 'innerHTML'.capitalize() // 'InnerHTML' - 'action_name'.capitalize() // 'Action_name' - 'css-class-name'.capitalize() // 'Css-class-name' - 'my favorite items'.capitalize() // 'My favorite items' + ```javascript + 'innerHTML'.capitalize() // 'InnerHTML' + 'action_name'.capitalize() // 'Action_name' + 'css-class-name'.capitalize() // 'Css-class-name' + 'my favorite items'.capitalize() // 'My favorite items' + ``` @method capitalize @param {String} str @return {String} */ @@ -8936,11 +8969,11 @@ flexibility of using both true JavaScript arrays and "virtual" arrays such as controllers and collections. You can use the methods defined in this module to access and modify array contents in a KVO-friendly way. You can also be notified whenever the - membership if an array changes by changing the syntax of the property to + membership of an array changes by changing the syntax of the property to `.observes('*myProperty.[]')`. To support `Ember.Array` in your own class, you must override two primitives to use it: `replace()` and `objectAt()`. @@ -8953,13 +8986,10 @@ @uses Ember.Enumerable @since Ember 0.9.0 */ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.prototype */ { - // compatibility - isSCArray: true, - /** Your array must support the `length` property. Your replace methods should set this property whenever it changes. @property {Number} length @@ -9412,12 +9442,11 @@ @class Copyable @namespace Ember @extends Ember.Mixin @since Ember 0.9 */ -Ember.Copyable = Ember.Mixin.create( -/** @scope Ember.Copyable.prototype */ { +Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ { /** Override to return a copy of the receiver. Default implementation raises an exception. @@ -9518,12 +9547,11 @@ @class Freezable @namespace Ember @extends Ember.Mixin @since Ember 0.9 */ -Ember.Freezable = Ember.Mixin.create( -/** @scope Ember.Freezable.prototype */ { +Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ { /** Set to `true` when the object is frozen. Use this property to detect whether your object is frozen or not. @@ -9700,12 +9728,11 @@ @namespace Ember @extends Ember.Mixin @uses Ember.Array @uses Ember.MutableEnumerable */ -Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable, - /** @scope Ember.MutableArray.prototype */ { +Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** @scope Ember.MutableArray.prototype */ { /** __Required.__ You must implement this method to apply this mixin. This is one of the primitives you must implement to support `Ember.Array`. @@ -9972,11 +9999,10 @@ return this ; } }); - })(); (function() { @@ -10020,11 +10046,11 @@ You typically observe property changes simply by adding the `observes` call to the end of your method declarations in classes that you write. For example: ```javascript - Ember.Object.create({ + Ember.Object.extend({ valueObserver: function() { // Executes whenever the "value" property changes }.observes('value') }); ``` @@ -10039,12 +10065,12 @@ ```javascript object.addObserver('propertyKey', targetObject, targetAction) ``` - This will call the `targetAction` method on the `targetObject` to be called - whenever the value of the `propertyKey` changes. + This will call the `targetAction` method on the `targetObject` whenever + the value of the `propertyKey` changes. Note that if `propertyKey` is a computed property, the observer will be called when any of the property dependencies are changed, even if the resulting value of the computed property is unchanged. This is necessary because computed properties are not computed until `get` is called. @@ -10300,12 +10326,12 @@ /** Adds an observer on a property. This is the core method used to register an observer for a property. - Once you call this method, anytime the key's value is set, your observer - will be notified. Note that the observers are triggered anytime the + Once you call this method, any time the key's value is set, your observer + will be notified. Note that the observers are triggered any time the value is set, regardless of whether it has actually changed. Your observer should be prepared to handle that. You can also pass an optional context parameter to this method. The context will be passed to your observer method whenever it is triggered. @@ -10426,12 +10452,12 @@ team.incrementProperty('score', 2); ``` @method incrementProperty @param {String} keyName The name of the property to increment - @param {Object} increment The amount to increment by. Defaults to 1 - @return {Object} The new property value + @param {Number} increment The amount to increment by. Defaults to 1 + @return {Number} The new property value */ incrementProperty: function(keyName, increment) { if (Ember.isNone(increment)) { increment = 1; } set(this, keyName, (get(this, keyName) || 0)+increment); return get(this, keyName); @@ -10445,25 +10471,25 @@ orc.decrementProperty('health', 5); ``` @method decrementProperty @param {String} keyName The name of the property to decrement - @param {Object} increment The amount to decrement by. Defaults to 1 - @return {Object} The new property value + @param {Number} decrement The amount to decrement by. Defaults to 1 + @return {Number} The new property value */ - decrementProperty: function(keyName, increment) { - if (Ember.isNone(increment)) { increment = 1; } - set(this, keyName, (get(this, keyName) || 0)-increment); + decrementProperty: function(keyName, decrement) { + if (Ember.isNone(decrement)) { decrement = 1; } + set(this, keyName, (get(this, keyName) || 0)-decrement); return get(this, keyName); }, /** Set the value of a boolean property to the opposite of it's current value. ```javascript - starship.toggleProperty('warpDriveEnaged'); + starship.toggleProperty('warpDriveEngaged'); ``` @method toggleProperty @param {String} keyName The name of the property to toggle @return {Object} The new property value @@ -10491,11 +10517,10 @@ observersForKey: function(keyName) { return Ember.observersFor(this, keyName); } }); - })(); (function() { @@ -10817,19 +10842,19 @@ entity = this; deferred = get(this, '_deferred'); promise = deferred.promise; - return promise.then(function(fulfillment) { + function fulfillmentHandler(fulfillment) { if (fulfillment === promise) { return resolve(entity); } else { return resolve(fulfillment); } - }, function(reason) { - return reject(reason); - }); + } + + return promise.then(resolve && fulfillmentHandler, reject); }, /** Resolve a Deferred object and call any `doneCallbacks` with the given args. @@ -11689,12 +11714,11 @@ @class ArrayProxy @namespace Ember @extends Ember.Object @uses Ember.MutableArray */ -Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray, -/** @scope Ember.ArrayProxy.prototype */ { +Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.ArrayProxy.prototype */ { /** The content array. Must be an object that implements `Ember.Array` and/or `Ember.MutableArray.` @@ -11966,11 +11990,10 @@ this._teardownArrangedContent(); this._teardownContent(); } }); - })(); (function() { @@ -12066,12 +12089,11 @@ @class ObjectProxy @namespace Ember @extends Ember.Object */ -Ember.ObjectProxy = Ember.Object.extend( -/** @scope Ember.ObjectProxy.prototype */ { +Ember.ObjectProxy = Ember.Object.extend(/** @scope Ember.ObjectProxy.prototype */ { /** The object whose properties will be forwarded. @property content @type Ember.Object @@ -13088,24 +13110,23 @@ */ target: null, container: null, + parentController: null, + store: null, model: Ember.computed.alias('content'), send: function(actionName) { - var args = [].slice.call(arguments, 1), target, - bubble = true; + var args = [].slice.call(arguments, 1), target; if (this[actionName]) { Ember.assert("The controller " + this + " does not have the action " + actionName, typeof this[actionName] === 'function'); - bubble = this[actionName].apply(this, args) === true; - } - - if (bubble && (target = get(this, 'target'))) { + this[actionName].apply(this, args); + } else if(target = get(this, 'target')) { Ember.assert("The target for controller " + this + " (" + target + ") did not define a `send` method", typeof target.send === 'function'); target.send.apply(target, arguments); } } }); @@ -13433,10 +13454,14 @@ } } }); ``` + The itemController instances will have a `parentController` property set to + either the the `parentController` property of the `ArrayController` + or to the `ArrayController` instance itself. + @class ArrayController @namespace Ember @extends Ember.ArrayProxy @uses Ember.SortableMixin @uses Ember.ControllerMixin @@ -13543,10 +13568,11 @@ if (!subController) { throw new Error('Could not resolve itemController: "' + controllerClass + '"'); } subController.set('target', this); + subController.set('parentController', get(this, 'parentController') || this); subController.set('content', object); return subController; }, @@ -13656,11 +13682,11 @@ /** @module ember @submodule ember-views */ -/*** BEGIN METAMORPH HELPERS ***/ +/* BEGIN METAMORPH HELPERS */ // Internet Explorer prior to 9 does not allow setting innerHTML if the first element // is a "zero-scope" element. This problem can be worked around by making // the first node an invisible text node. We, like Modernizr, use &shy; @@ -13729,11 +13755,11 @@ shyElement.nodeValue = shyElement.nodeValue.slice(1); } } }; -/*** END METAMORPH HELPERS */ +/* END METAMORPH HELPERS */ var innerHTMLTags = {}; var canSetInnerHTML = function(tagName) { if (innerHTMLTags[tagName] !== undefined) { @@ -14312,12 +14338,11 @@ @class EventDispatcher @namespace Ember @private @extends Ember.Object */ -Ember.EventDispatcher = Ember.Object.extend( -/** @scope Ember.EventDispatcher.prototype */{ +Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.prototype */{ /** @private The root DOM element to which event listeners should be attached. Event @@ -17613,10 +17638,12 @@ } }, replace: function(idx, removedCount, addedViews) { var addedCount = addedViews ? get(addedViews, 'length') : 0; + var self = this; + Ember.assert("You can't add a child to a container that is already a child of another view", Ember.A(addedViews).every(function(item) { return !get(item, '_parentView') || get(item, '_parentView') === self; })); this.arrayContentWillChange(idx, removedCount, addedCount); this.childViewsWillChange(this._childViews, idx, removedCount); if (addedCount === 0) { @@ -17733,10 +17760,11 @@ }, 'currentView'), _currentViewDidChange: Ember.observer(function() { var currentView = get(this, 'currentView'); if (currentView) { + Ember.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !get(currentView, '_parentView')); this.pushObject(currentView); } }, 'currentView'), _ensureChildrenAreInDOM: function () { @@ -17973,12 +18001,11 @@ @class CollectionView @namespace Ember @extends Ember.ContainerView @since Ember 0.9 */ -Ember.CollectionView = Ember.ContainerView.extend( -/** @scope Ember.CollectionView.prototype */ { +Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionView.prototype */ { /** A list of items to be displayed by the `Ember.CollectionView`. @property content @@ -18741,12 +18768,12 @@ var Handlebars = this.Handlebars || (Ember.imports && Ember.imports.Handlebars); if(!Handlebars && typeof require === 'function') { Handlebars = require('handlebars'); } -Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.3. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars) -Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.3, COMPILER_REVISION 2. Builds of master may have other COMPILER_REVISION values.", Handlebars.COMPILER_REVISION === 2); +Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.4. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars) +Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.4, COMPILER_REVISION expected: 3, got: " + Handlebars.COMPILER_REVISION + " – Please note: Builds of master may have other COMPILER_REVISION values.", Handlebars.COMPILER_REVISION === 3); /** Prepares the Handlebars templating library for use inside Ember's view system. @@ -19154,29 +19181,33 @@ var values = arguments[arguments.length - 1]; return values.join('||'); }); ``` - Which allows for template syntax such as {{concatenate prop1 prop2}} or - {{concatenate prop1 prop2 prop3}}. If any of the properties change, + Which allows for template syntax such as `{{concatenate prop1 prop2}}` or + `{{concatenate prop1 prop2 prop3}}`. If any of the properties change, the helpr will re-render. Note that dependency keys cannot be using in conjunction with multi-property helpers, since it is ambiguous which property the dependent keys would belong to. ## Use with unbound helper - The {{unbound}} helper can be used with bound helper invocations + The `{{unbound}}` helper can be used with bound helper invocations to render them in their unbound form, e.g. ```handlebars {{unbound capitalize name}} ``` In this example, if the name property changes, the helper will not re-render. + ## Use with blocks not supported + Bound helpers do not support use with Handlebars blocks or + the addition of child views of any kind. + @method registerBoundHelper @for Ember.Handlebars @param {String} name @param {Function} function @param {String} dependentKeys* @@ -19195,10 +19226,12 @@ currentContext = (options.contexts && options.contexts[0]) || this, normalized, pathRoot, path, loc, hashOption; + Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn); + // Detect bound options (e.g. countBinding="otherCount") hash.boundOptions = {}; for (hashOption in hash) { if (!hash.hasOwnProperty(hashOption)) { continue; } @@ -19355,11 +19388,10 @@ var t = Handlebars.template(spec); t.isTop = true; return t; }; - })(); (function() { @@ -21169,10 +21201,11 @@ var controller = Ember.ArrayController.create(); set(controller, 'itemController', itemController); set(controller, 'container', get(this, 'controller.container')); set(controller, '_eachView', this); set(controller, 'target', get(this, 'controller')); + set(controller, 'parentController', get(this, 'controller')); this.disableContentObservers(function() { set(this, 'content', controller); binding = new Ember.Binding('content', '_eachView.dataSource').oneWay(); binding.connect(controller); @@ -21442,10 +21475,13 @@ {{#each person in developers itemController="developer"}} {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} {{/each}} ``` + Each itemController will receive a reference to the current controller as + a `parentController` property. + @method each @for Ember.Handlebars.helpers @param [name] {String} name for item (used with `in`) @param [path] {String} path @param [options] {Object} Handlebars key/value pairs of options @@ -21581,11 +21617,11 @@ nameParts[nameParts.length - 1] = "_" + lastPart; var view = options.data.view, underscoredName = nameParts.join("/"), template = view.templateForName(underscoredName), - deprecatedTemplate = view.templateForName(name); + deprecatedTemplate = !template && view.templateForName(name); Ember.deprecate("You tried to render the partial " + name + ", which should be at '" + underscoredName + "', but Ember found '" + name + "'. Please use a leading underscore in your partials", template); Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); template = template || deprecatedTemplate; @@ -21707,11 +21743,11 @@ ``` You can add a `label` tag yourself in the template where the `Ember.Checkbox` is being used. - ```html + ```handlebars <label> {{view Ember.Checkbox classNames="applicaton-specific-checkbox"}} Some Title </label> ``` @@ -21927,11 +21963,11 @@ Options are: * `enter`: the user pressed enter * `keypress`: the user pressed a key - @property on + @property onEvent @type String @default enter */ onEvent: 'enter', @@ -22494,38 +22530,42 @@ /** @scope Ember.Select.prototype */ { tagName: 'select', classNames: ['ember-select'], defaultTemplate: Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) { -this.compilerInfo = [2,'>= 1.0.0-rc.3']; +this.compilerInfo = [3,'>= 1.0.0-rc.4']; helpers = helpers || Ember.Handlebars.helpers; data = data || {}; - var buffer = '', stack1, hashTypes, escapeExpression=this.escapeExpression, self=this; + var buffer = '', stack1, hashTypes, hashContexts, escapeExpression=this.escapeExpression, self=this; function program1(depth0,data) { - var buffer = '', hashTypes; + var buffer = '', hashTypes, hashContexts; data.buffer.push("<option value=\"\">"); hashTypes = {}; - data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "view.prompt", {hash:{},contexts:[depth0],types:["ID"],hashTypes:hashTypes,data:data}))); + hashContexts = {}; + data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "view.prompt", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}))); data.buffer.push("</option>"); return buffer; } function program3(depth0,data) { - var hashTypes; + var hashContexts, hashTypes; + hashContexts = {'contentBinding': depth0}; hashTypes = {'contentBinding': "STRING"}; data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ 'contentBinding': ("this") - },contexts:[depth0],types:["ID"],hashTypes:hashTypes,data:data}))); + },contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}))); } hashTypes = {}; - stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashTypes:hashTypes,data:data}); + hashContexts = {}; + stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}); if(stack1 || stack1 === 0) { data.buffer.push(stack1); } hashTypes = {}; - stack1 = helpers.each.call(depth0, "view.content", {hash:{},inverse:self.noop,fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashTypes:hashTypes,data:data}); + hashContexts = {}; + stack1 = helpers.each.call(depth0, "view.content", {hash:{},inverse:self.noop,fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}); if(stack1 || stack1 === 0) { data.buffer.push(stack1); } return buffer; }), attributeBindings: ['multiple', 'disabled', 'tabindex', 'name'], @@ -23662,10 +23702,12 @@ var contexts = [].slice.call(arguments, 1); var targetHandlerInfos = this.targetHandlerInfos, found = false, names, object, handlerInfo, handlerObj; + if (!targetHandlerInfos) { return; } + for (var i=targetHandlerInfos.length-1; i>=0; i--) { handlerInfo = targetHandlerInfos[i]; if (handlerInfo.name === handlerName) { found = true; } if (found) { @@ -24498,19 +24540,20 @@ enter: function() { this.activate(); }, /** - The collection of functions keyed by name available on this route as + The collection of functions, keyed by name, available on this route as action targets. These functions will be invoked when a matching `{{action}}` is triggered from within a template and the application's current route is this route. - Events can also be invoked from other parts of your application via `Route#send`. + Events can also be invoked from other parts of your application via `Route#send` + or `Controller#send`. - The context of event will be the this route. + The context of the event will be this route. @see {Ember.Route#send} @see {Handlebars.helpers.action} @property events @@ -25107,11 +25150,11 @@ @module ember @submodule ember-routing */ -/** +/* A TransitionEvent is passed as the argument for `transitionTo` events and contains information about an attempted transition that can be modified or decorated by leafier `transitionTo` event handlers before the actual transition is committed by ApplicationRoute. @@ -25119,11 +25162,11 @@ @namespace Ember @extends Ember.Deferred */ Ember.TransitionEvent = Ember.Object.extend({ - /** + /* The Ember.Route method used to perform the transition. Presently, the only valid values are 'transitionTo' and 'replaceWith'. */ transitionMethod: 'transitionTo', destinationRouteName: null, @@ -25133,22 +25176,22 @@ init: function() { this._super(); this.contexts = this.contexts || []; }, - /** + /* Convenience method that returns an array that can be used for legacy `transitionTo` and `replaceWith`. */ transitionToArgs: function() { return [this.destinationRouteName].concat(this.contexts); } }); Ember.TransitionEvent.reopenClass({ - /** + /* This is the default transition event handler that will be injected into ApplicationRoute. The context, like all route event handlers in the events hash, will be an `Ember.Route`. */ defaultHandler: function(transitionEvent) { @@ -25800,11 +25843,11 @@ aView = AView.create(); aView.appendTo('body'); ``` - Will results in the following rendered HTML + Will result in the following rendered HTML ```html <div class="ember-view"> <div data-ember-action="1"> click me @@ -26163,10 +26206,18 @@ set(this, '_outlets', {}); this._super(); }, connectOutlet: function(outletName, view) { + if (this._pendingDisconnections) { + delete this._pendingDisconnections[outletName]; + } + + if (this._hasEquivalentView(outletName, view)) { + return; + } + var outlets = get(this, '_outlets'), container = get(this, 'container'), router = container && container.lookup('router:main'), renderedName = get(view, 'renderedName'); @@ -26175,14 +26226,34 @@ if (router && renderedName) { router._connectActiveView(renderedName, view); } }, + _hasEquivalentView: function(outletName, view) { + var existingView = get(this, '_outlets.'+outletName); + return existingView && + existingView.prototype === view.prototype && + existingView.get('template') === view.get('template') && + existingView.get('context') === view.get('context'); + }, + disconnectOutlet: function(outletName) { + if (!this._pendingDisconnections) { + this._pendingDisconnections = {}; + } + this._pendingDisconnections[outletName] = true; + Ember.run.once(this, '_finishDisconnections'); + }, + + _finishDisconnections: function() { var outlets = get(this, '_outlets'); + var pendingDisconnections = this._pendingDisconnections; + this._pendingDisconnections = null; - set(outlets, outletName, null); + for (var outletName in pendingDisconnections) { + set(outlets, outletName, null); + } } }); })(); @@ -26948,11 +27019,11 @@ DeprecatedContainer.deprecate = function(method) { return function() { var container = this._container; Ember.deprecate('Using the defaultContainer is no longer supported. [defaultContainer#' + method + ']', false); - container[method].apply(container, arguments); + return container[method].apply(container, arguments); }; }; DeprecatedContainer.prototype = { _container: null, @@ -27396,24 +27467,92 @@ this.advanceReadiness(); return this; }, + /** + Reset the application. This is typically used only in tests. + + Typical Example: + + ```javascript + + var App; + + Ember.run(function(){ + App = Ember.Application.create(); + }); + + module("acceptance test", { + setup: function() { + App.reset(); + } + }); + + test("first test", function(){ + // App is freshly reset + }); + + test("first test", function(){ + // App is again freshly reset + }); + ``` + + Advanced Example: + + Occasionally you may want to prevent the app from initializing during + setup. This could enable extra configuration, or enable asserting prior + to the app becoming ready. + + ```javascript + + var App; + + Ember.run(function(){ + App = Ember.Application.create(); + }); + + module("acceptance test", { + setup: function() { + Ember.run(function() { + App.reset(); + App.deferReadiness(); + }); + } + }); + + test("first test", function(){ + ok(true, 'something before app is initialized'); + + Ember.run(function(){ + App.advanceReadiness(); + }); + ok(true, 'something after app is initialized'); + }); + ``` + + @method reset + **/ reset: function() { - Ember.assert('App#reset no longer needs to be wrapped in a run-loop', !Ember.run.currentRunLoop); - Ember.run(this, function(){ + function handleReset() { Ember.run(this.__container__, 'destroy'); this.buildContainer(); this._readinessDeferrals = 1; Ember.run.schedule('actions', this, function(){ this._initialize(); this.startRouting(); }); - }); + } + + if (Ember.run.currentRunLoop) { + handleReset.call(this); + } else { + Ember.run(this, handleReset); + } }, /** @private @method runInitializers @@ -29028,44 +29167,265 @@ */ })(); (function() { -/*globals EMBER_APP_BEING_TESTED */ +var slice = [].slice, + helpers = {}, + originalMethods = {}, + injectHelpersCallbacks = []; -var Promise = Ember.RSVP.Promise, - pendingAjaxRequests = 0, - originalFind, - slice = [].slice, - get = Ember.get; +/** + @class Test + @namespace Ember +*/ +Ember.Test = { + /** + @public + + `registerHelper` is used to register a + test helper that will be injected when + `App.injectTestHelpers` is called. + + The helper method will always be called + with the current Application as the first + parameter. + + For example: + ```javascript + Ember.Test.registerHelper('boot', function(app)) { + Ember.run(app, app.deferReadiness); + } + ``` + + This helper can later be called without arguments + because it will be called with `app` as the + first parameter. + + ```javascript + App = Ember.Application.create(); + App.injectTestHelpers(); + boot(); + ``` + + Whenever you register a helper that + performs async operations, + make sure you `return wait();` at the + end of the helper. + + If an async helper also needs to return a value, + pass it to the `wait` helper as a first argument: + `return wait(val);` + + @method registerHelper + @param name {String} + @param helperMethod {Function} + */ + registerHelper: function(name, helperMethod) { + helpers[name] = helperMethod; + }, + /** + @public + @method unregisterHelper + @param name {String} + */ + unregisterHelper: function(name) { + delete helpers[name]; + if (originalMethods[name]) { + window[name] = originalMethods[name]; + } + delete originalMethods[name]; + }, + + /** + @public + + Used to register callbacks to be fired + whenever `App.injectTestHelpers` is called + + The callback will receive the current application + as an argument. + + @method unregisterHelper + @param name {String} + */ + onInjectHelpers: function(callback) { + injectHelpersCallbacks.push(callback); + }, + + /** + @public + + This returns a thenable tailored + for testing. It catches failed + `onSuccess` callbacks and invokes + the `Ember.Test.failure` function + in the last chained then. + + This method should be returned + by async helpers such as `wait`. + + @method promise + @param resolver {Function} + */ + promise: function(resolver) { + var promise = new Ember.RSVP.Promise(resolver); + var thenable = { + chained: false + }; + thenable.then = function(onSuccess, onFailure) { + var self = this, thenPromise, nextPromise; + thenable.chained = true; + thenPromise = promise.then(onSuccess, onFailure); + // this is to ensure all downstream fulfillment + // handlers are wrapped in the error handling + nextPromise = Ember.Test.promise(function(resolve) { + resolve(thenPromise); + }); + thenPromise.then(null, function(reason) { + // ensure this is the last promise in the chain + // if not, ignore and the exception will propagate + // this prevents the same error from being fired multiple times + if (!nextPromise.chained) { + Ember.Test.failure(reason); + } + }); + return nextPromise; + }; + return thenable; + }, + + /** + @public + + Override this method with your + testing framework's false assertion + This function is called whenever + an exception occurs causing the testing + promise to fail. + + QUnit example: + + ```javascript + Ember.Test.failure = function(reason) { + ok(false, reason); + } + ``` + + @method failure + @param reason {String} + */ + failure: function(error) { + setTimeout(function() { + throw error; + }); + } +}; + + +function curry(app, fn) { + return function() { + var args = slice.call(arguments); + args.unshift(app); + return fn.apply(app, args); + }; +} + + +Ember.Application.reopen({ + testHelpers: {}, + + setupForTesting: function() { + this.deferReadiness(); + + this.Router.reopen({ + location: 'none' + }); + }, + + injectTestHelpers: function() { + this.testHelpers = {}; + for (var name in helpers) { + originalMethods[name] = window[name]; + this.testHelpers[name] = window[name] = curry(this, helpers[name]); + } + + for(var i = 0, l = injectHelpersCallbacks.length; i < l; i++) { + injectHelpersCallbacks[i](this); + } + }, + + removeTestHelpers: function() { + for (var name in helpers) { + window[name] = originalMethods[name]; + delete this.testHelpers[name]; + delete originalMethods[name]; + } + } +}); + +})(); + + + +(function() { +var get = Ember.get, + helper = Ember.Test.registerHelper, + pendingAjaxRequests = 0; + + +Ember.Test.onInjectHelpers(function() { + Ember.$(document).ajaxStart(function() { + pendingAjaxRequests++; + }); + + Ember.$(document).ajaxStop(function() { + pendingAjaxRequests--; + }); +}); + + function visit(app, url) { Ember.run(app, app.handleURL, url); + app.__container__.lookup('router:main').location.setURL(url); return wait(app); } -function click(app, selector) { +function click(app, selector, context) { + var $el = find(app, selector, context); Ember.run(function() { app.$(selector).click(); }); return wait(app); } -function fillIn(app, selector, text) { - var $el = find(app, selector); +function fillIn(app, selector, context, text) { + var $el; + if (typeof text === 'undefined') { + text = context; + context = null; + } + $el = find(app, selector, context); Ember.run(function() { - $el.val(text); + $el.val(text).change(); }); return wait(app); } -function find(app, selector) { - return app.$(get(app, 'rootElement')).find(selector); +function find(app, selector, context) { + var $el; + context = context || get(app, 'rootElement'); + $el = app.$(selector, context); + if ($el.length === 0) { + throw("Element " + selector + " not found."); + } + return $el; } function wait(app, value) { - return new Promise(function(resolve) { + return Ember.Test.promise(function(resolve) { stop(); var watcher = setInterval(function() { var routerIsLoading = app.__container__.lookup('router:main').router.isLoading; if (routerIsLoading) { return; } if (pendingAjaxRequests) { return; } @@ -29077,65 +29437,27 @@ }); }, 10); }); } -function curry(app, fn) { - return function() { - var args = slice.call(arguments); - args.unshift(app); - return fn.apply(app, args); - }; -} - -Ember.Application.reopen({ - setupForTesting: function() { - this.deferReadiness(); - - this.Router.reopen({ - location: 'none' - }); - }, - - injectTestHelpers: function() { - Ember.$(document).ajaxStart(function() { - pendingAjaxRequests++; - }); - - Ember.$(document).ajaxStop(function() { - pendingAjaxRequests--; - }); - - // todo do this safer. - window.visit = curry(this, visit); - window.click = curry(this, click); - window.fillIn = curry(this, fillIn); - originalFind = window.find; - window.find = curry(this, find); - window.wait = curry(this, wait); - }, - - removeTestHelpers: function() { - window.visit = null; - window.click = null; - window.fillIn = null; - window.wait = null; - window.find = originalFind; - } -}); - +// expose these methods as test helpers +helper('visit', visit); +helper('click', click); +helper('fillIn', fillIn); +helper('find', find); +helper('wait', wait); })(); (function() { })(); })(); -// Version: v1.0.0-rc.3-156-g9d1d3cd -// Last commit: 9d1d3cd (2013-05-01 22:51:16 -0700) +// Version: v1.0.0-rc.3-251-gcd5dfe3 +// Last commit: cd5dfe3 (2013-05-18 11:06:43 -0700) (function() { /** Ember