dist/ember.prod.js in ember-source-1.0.0.rc1.4 vs dist/ember.prod.js in ember-source-1.0.0.rc2.0

- old
+ new

@@ -53,11 +53,11 @@ The core Runtime framework is based on the jQuery API with a number of performance optimizations. @class Ember @static - @version 1.0.0-rc.1 + @version 1.0.0-rc.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. @@ -80,14 +80,14 @@ /** @property VERSION @type String - @default '1.0.0-rc.1' + @default '1.0.0-rc.2' @final */ -Ember.VERSION = '1.0.0-rc.1'; +Ember.VERSION = '1.0.0-rc.2'; /** Standard environmental variables. You can define these in a global `ENV` variable before loading Ember to control various configuration settings. @@ -666,11 +666,11 @@ @private @param {Object} obj The object to retrieve meta for @param {Boolean} [writable=true] Pass `false` if you do not intend to modify the meta hash, allowing the method to avoid making an unnecessary copy. - @return {Hash} + @return {Object} the meta hash for an object */ Ember.meta = function meta(obj, writable) { var ret = obj[META_KEY]; if (writable===false) return ret || EMPTY_META; @@ -818,11 +818,11 @@ ``` @method isArray @for Ember @param {Object} obj The object to test - @return {Boolean} + @return {Boolean} true if the passed object is an array or Array-like */ Ember.isArray = function(obj) { if (!obj || obj.setInterval) { return false; } if (Array.isArray && Array.isArray(obj)) { return true; } if (Ember.Array && Ember.Array.detect(obj)) { return true; } @@ -3208,10 +3208,11 @@ Properties are cacheable by default. @method cacheable @param {Boolean} aFlag optional set to `false` to disable caching + @return {Ember.ComputedProperty} this @chainable */ ComputedPropertyPrototype.cacheable = function(aFlag) { this._cacheable = aFlag !== false; return this; @@ -3228,10 +3229,11 @@ }.property().volatile() }); ``` @method volatile + @return {Ember.ComputedProperty} this @chainable */ ComputedPropertyPrototype.volatile = function() { return this.cacheable(false); }; @@ -3249,10 +3251,11 @@ MyApp.person.set('guid', 'new-guid'); // will throw an exception ``` @method readOnly + @return {Ember.ComputedProperty} this @chainable */ ComputedPropertyPrototype.readOnly = function(readOnly) { this._readOnly = readOnly === undefined || !!readOnly; return this; @@ -3273,10 +3276,11 @@ }); ``` @method property @param {String} path* zero or more property paths + @return {Ember.ComputedProperty} this @chainable */ ComputedPropertyPrototype.property = function() { var args = []; for (var i = 0, l = arguments.length; i < l; i++) { @@ -3502,71 +3506,253 @@ @method cacheFor @for Ember @param {Object} obj the object whose property you want to check @param {String} key the name of the property whose cached value you want to return + @return {any} the cached value */ Ember.cacheFor = function cacheFor(obj, key) { var cache = metaFor(obj, false).cache; if (cache && key in cache) { return cache[key]; } }; +function getProperties(self, propertyNames) { + var ret = {}; + for(var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = get(self, propertyNames[i]); + } + return ret; +} + +function registerComputed(name, macro) { + Ember.computed[name] = function(dependentKey) { + var args = a_slice.call(arguments); + return Ember.computed(dependentKey, function() { + return macro.apply(this, args); + }); + }; +} + +function registerComputedWithProperties(name, macro) { + Ember.computed[name] = function() { + var properties = a_slice.call(arguments); + + var computed = Ember.computed(function() { + return macro.apply(this, [getProperties(this, properties)]); + }); + + return computed.property.apply(computed, properties); + }; +} + /** - @method computed.not + @method computed.empty @for Ember @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which negate + the original value for property */ -Ember.computed.not = function(dependentKey) { - return Ember.computed(dependentKey, function(key) { - return !get(this, dependentKey); - }); -}; +registerComputed('empty', function(dependentKey) { + return Ember.isEmpty(get(this, dependentKey)); +}); /** + @method computed.notEmpty + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns true if + original value for property is not empty. +*/ +registerComputed('notEmpty', function(dependentKey) { + return !Ember.isEmpty(get(this, dependentKey)); +}); + +/** @method computed.none @for Ember @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which + rturns true if original value for property is null or undefined. */ -Ember.computed.none = function(dependentKey) { - return Ember.computed(dependentKey, function(key) { - var val = get(this, dependentKey); - return Ember.isNone(val); - }); -}; +registerComputed('none', function(dependentKey) { + return Ember.isNone(get(this, dependentKey)); +}); /** - @method computed.empty + @method computed.not @for Ember @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns + inverse of the original value for property */ -Ember.computed.empty = function(dependentKey) { - return Ember.computed(dependentKey, function(key) { - var val = get(this, dependentKey); - return Ember.isEmpty(val); - }); -}; +registerComputed('not', function(dependentKey) { + return !get(this, dependentKey); +}); /** @method computed.bool @for Ember @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which convert + to boolean the original value for property */ -Ember.computed.bool = function(dependentKey) { - return Ember.computed(dependentKey, function(key) { - return !!get(this, dependentKey); - }); -}; +registerComputed('bool', function(dependentKey) { + return !!get(this, dependentKey); +}); /** - @method computed.alias + @method computed.match @for Ember + @param {String} dependentKey + @param {RegExp} regexp + @return {Ember.ComputedProperty} computed property which match + the original value for property against a given RegExp +*/ +registerComputed('match', function(dependentKey, regexp) { + var value = get(this, dependentKey); + return typeof value === 'string' ? !!value.match(regexp) : false; +}); +/** + @method computed.equal + @for Ember @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. */ +registerComputed('equal', function(dependentKey, value) { + return get(this, dependentKey) === value; +}); + +/** + @method computed.gt + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater then given value. +*/ +registerComputed('gt', function(dependentKey, value) { + return get(this, dependentKey) > value; +}); + +/** + @method computed.gte + @for Ember + @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. +*/ +registerComputed('gte', function(dependentKey, value) { + return get(this, dependentKey) >= value; +}); + +/** + @method computed.lt + @for Ember + @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. +*/ +registerComputed('lt', function(dependentKey, value) { + return get(this, dependentKey) < value; +}); + +/** + @method computed.lte + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less or equal then given value. +*/ +registerComputed('lte', function(dependentKey, value) { + return get(this, dependentKey) <= value; +}); + +/** + @method computed.and + @for Ember + @param {String} dependentKey, [dependentKey...] + @return {Ember.ComputedProperty} computed property which peforms + a logical `and` on the values of all the original values for properties. +*/ +registerComputedWithProperties('and', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && !properties[key]) { + return false; + } + } + return true; +}); + +/** + @method computed.or + @for Ember + @param {String} dependentKey, [dependentKey...] + @return {Ember.ComputedProperty} computed property which peforms + a logical `or` on the values of all the original values for properties. +*/ +registerComputedWithProperties('or', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return true; + } + } + return false; +}); + +/** + @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. +*/ +registerComputedWithProperties('any', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return properties[key]; + } + } + return null; +}); + +/** + @method computed.map + @for Ember + @param {String} dependentKey, [dependentKey...] + @return {Ember.ComputedProperty} computed property which maps + values of all passed properties in to an array. +*/ +registerComputedWithProperties('map', function(properties) { + var res = []; + for (var key in properties) { + if (properties.hasOwnProperty(key)) { + if (Ember.isNone(properties[key])) { + res.push(null); + } else { + res.push(properties[key]); + } + } + } + return res; +}); + +/** + @method computed.alias + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + alias to the original value for property. +*/ Ember.computed.alias = function(dependentKey) { return Ember.computed(dependentKey, function(key, value){ if (arguments.length > 1) { set(this, dependentKey, value); return value; @@ -3574,10 +3760,27 @@ 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`. +*/ +Ember.computed.defaultTo = function(defaultPath) { + return Ember.computed(function(key, newValue, cachedValue) { + var result; + if (arguments.length === 1) { + return cachedValue != null ? cachedValue : get(this, defaultPath); + } + return newValue != null ? newValue : get(this, defaultPath); + }); +}; + })(); (function() { @@ -4406,12 +4609,11 @@ @param {Object} [target] target of method to invoke @param {Function|String} method The method to invoke. If you pass a string it will be resolved on the target at the time the method is invoked. @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} wait - Number of milliseconds to wait. + @param {Number} wait Number of milliseconds to wait. @return {String} a string you can use to cancel the timer in {{#crossLink "Ember/run.cancel"}}{{/crossLink}} later. */ Ember.run.later = function(target, method) { var args, expires, timer, guid, wait; @@ -4529,19 +4731,58 @@ Ember.run.scheduleOnce = function(queue, target, method, args) { return scheduleOnce(queue, target, method, slice.call(arguments, 3)); }; /** - Schedules an item to run after control has been returned to the system. - This is equivalent to calling `Ember.run.later` with a wait time of 1ms. + Schedules an item to run from within a separate run loop, after + control has been returned to the system. This is equivalent to calling + `Ember.run.later` with a wait time of 1ms. ```javascript Ember.run.next(myContext, function(){ - // code to be executed in the next RunLoop, which will be scheduled after the current one + // code to be executed in the next run loop, which will be scheduled after the current one }); ``` + Multiple operations scheduled with `Ember.run.next` will coalesce + into the same later run loop, along with any other operations + scheduled by `Ember.run.later` that expire right around the same + time that `Ember.run.next` operations will fire. + + Note that there are often alternatives to using `Ember.run.next`. + For instance, if you'd like to schedule an operation to happen + after all DOM element operations have completed within the current + run loop, you can make use of the `afterRender` run loop queue (added + by the `ember-views` package, along with the preceding `render` queue + where all the DOM element operations happen). Example: + + ```javascript + App.MyCollectionView = Ember.CollectionView.extend({ + didInsertElement: function() { + Ember.run.scheduleOnce('afterRender', this, 'processChildElements'); + }, + processChildElements: function() { + // ... do something with collectionView's child view + // elements after they've finished rendering, which + // can't be done within the CollectionView's + // `didInsertElement` hook because that gets run + // before the child elements have been added to the DOM. + } + }); + ``` + + One benefit of the above approach compared to using `Ember.run.next` is + that you will be able to perform DOM/CSS operations before unprocessed + elements are rendered to the screen, which may prevent flickering or + other artifacts caused by delaying processing until after rendering. + + The other major benefit to the above approach is that `Ember.run.next` + introduces an element of non-determinism, which can make things much + harder to test, due to its reliance on `setTimeout`; it's much harder + to guarantee the order of scheduled operations when they are scheduled + outside of the current run loop, i.e. with `Ember.run.next`. + @method next @param {Object} [target] target of method to invoke @param {Function|String} method The method to invoke. If you pass a string it will be resolved on the target at the time the method is invoked. @@ -4704,10 +4945,14 @@ oneWay: function() { this._oneWay = true; return this; }, + /** + @method toString + @return {String} string representation of binding + */ toString: function() { var oneWay = this._oneWay ? '[oneWay]' : ''; return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; }, @@ -7839,21 +8084,23 @@ this.forEach(function(o, idx) { ret[idx] = o; }); return ret ; }, /** - Returns a copy of the array with all null elements removed. + Returns a copy of the array with all null and undefined elements removed. ```javascript - var arr = ["a", null, "c", null]; + var arr = ["a", null, "c", undefined]; arr.compact(); // ["a", "c"] ``` @method compact - @return {Array} the array without null elements. + @return {Array} the array without null and undefined elements. */ - compact: function() { return this.without(null); }, + compact: function() { + return this.filter(function(value) { return value != null; }); + }, /** Returns a new enumerable that excludes the passed value. The default implementation returns an array regardless of the receiver type unless the receiver does not contain the value. @@ -7904,10 +8151,11 @@ For plain enumerables, this property is read only. `Ember.Array` overrides this method. @property [] @type Ember.Array + @return this */ '[]': Ember.computed(function(key, value) { return this; }), @@ -7920,10 +8168,11 @@ mixin. @method addEnumerableObserver @param {Object} target @param {Hash} [opts] + @return this */ addEnumerableObserver: function(target, opts) { var willChange = (opts && opts.willChange) || 'enumerableWillChange', didChange = (opts && opts.didChange) || 'enumerableDidChange'; @@ -7939,10 +8188,11 @@ Removes a registered enumerable observer. @method removeEnumerableObserver @param {Object} target @param {Hash} [opts] + @return this */ removeEnumerableObserver: function(target, opts) { var willChange = (opts && opts.willChange) || 'enumerableWillChange', didChange = (opts && opts.didChange) || 'enumerableDidChange'; @@ -8128,10 +8378,11 @@ arr.objectAt(5); // undefined ``` @method objectAt @param {Number} idx The index of the item to return. + @return {any} item at index or undefined */ objectAt: function(idx) { if ((idx < 0) || (idx>=get(this, 'length'))) return undefined ; return get(this, idx); }, @@ -8145,10 +8396,11 @@ arr.objectsAt([2, 3, 4]); // ["c", "d", undefined] ``` @method objectsAt @param {Array} indexes An array of indexes of items to return. + @return {Array} */ objectsAt: function(indexes) { var self = this; return map(indexes, function(idx){ return self.objectAt(idx); }); }, @@ -8164,10 +8416,11 @@ array, it will replace the current content. This property overrides the default property defined in `Ember.Enumerable`. @property [] + @return this */ '[]': Ember.computed(function(key, value) { if (value !== undefined) this.replace(0, get(this, 'length'), value) ; return this ; }), @@ -8881,10 +9134,11 @@ ``` @method insertAt @param {Number} idx index of insert the object at. @param {Object} object object to insert + @return this */ insertAt: function(idx, object) { if (idx > get(this, 'length')) throw new Error(OUT_OF_RANGE_EXCEPTION) ; this.replace(idx, 0, [object]) ; return this ; @@ -8934,10 +9188,11 @@ colors.pushObject(["yellow", "orange"]); // ["red", "green", "blue", "black", ["yellow", "orange"]] ``` @method pushObject @param {anything} obj object to push + @return {any} the same obj passed as param */ pushObject: function(obj) { this.insertAt(get(this, 'length'), obj) ; return obj ; }, @@ -9013,10 +9268,11 @@ colors.unshiftObject(["black", "white"]); // [["black", "white"], "yellow", "red", "green", "blue"] ``` @method unshiftObject @param {anything} obj object to unshift + @return {any} the same obj passed as param */ unshiftObject: function(obj) { this.insertAt(0, obj) ; return obj ; }, @@ -9739,10 +9995,11 @@ @method on @param {String} name The name of the event @param {Object} [target] The "this" binding for the callback @param {Function} method The callback to execute + @return this */ on: function(name, target, method) { Ember.addListener(this, name, target, method); return this; }, @@ -9758,10 +10015,11 @@ @method one @param {String} name The name of the event @param {Object} [target] The "this" binding for the callback @param {Function} method The callback to execute + @return this */ one: function(name, target, method) { if (!method) { method = target; target = null; @@ -9807,10 +10065,11 @@ @method off @param {String} name The name of the event @param {Object} target The target of the subscription @param {Function} method The function of the subscription + @return this */ off: function(name, target, method) { Ember.removeListener(this, name, target, method); return this; }, @@ -10162,16 +10421,26 @@ @default null */ concatenatedProperties: null, /** + Destroyed object property flag. + + if this property is `true` the observers and bindings were already + removed by the effect of calling the `destroy()` method. + @property isDestroyed @default false */ isDestroyed: false, /** + Destruction scheduled flag. The `destroy()` method has been called. + + The object stays intact until the end of the run loop at which point + the `isDestroyed` flag is set. + @property isDestroying @default false */ isDestroying: false, @@ -12763,13 +13032,15 @@ var tagName = element.tagName; if (canSetInnerHTML(tagName)) { setInnerHTMLWithoutFix(element, html); } else { + // Firefox versions < 11 do not have support for element.outerHTML. + var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); - var startTag = element.outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], + var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], endTag = '</'+tagName+'>'; var wrapper = document.createElement('div'); setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); element = wrapper.firstChild; @@ -13248,11 +13519,13 @@ @method string @return {String} The generated HTML */ string: function() { if (this._element) { - return this.element().outerHTML; + // Firefox versions < 11 do not have support for element.outerHTML. + return this.element().outerHTML || + new XMLSerializer().serializeToString(this.element()); } else { return this.innerString(); } }, @@ -15113,13 +15386,13 @@ @event willInsertElement */ willInsertElement: Ember.K, /** - Called when the element of the view has been inserted into the DOM. - Override this function to do any set up that requires an element in the - document body. + Called when the element of the view has been inserted into the DOM + or after the view was re-rendered. Override this function to do any + set up that requires an element in the document body. @event didInsertElement */ didInsertElement: Ember.K, @@ -15714,18 +15987,23 @@ registerObserver: function(root, path, target, observer) { if (!observer && 'function' === typeof target) { observer = target; target = null; } + var view = this, stateCheckedObserver = function(){ view.currentState.invokeObserver(this, observer); + }, + scheduledObserver = function() { + Ember.run.scheduleOnce('render', this, stateCheckedObserver); }; - Ember.addObserver(root, path, target, stateCheckedObserver); + Ember.addObserver(root, path, target, scheduledObserver); + this.one('willClearRender', function() { - Ember.removeObserver(root, path, target, stateCheckedObserver); + Ember.removeObserver(root, path, target, scheduledObserver); }); } }); @@ -18024,14 +18302,14 @@ return fn.call(view, value, options); }; view.appendChild(bindView); - view.registerObserver(pathRoot, path, bindView, rerenderBoundHelperView); + view.registerObserver(pathRoot, path, bindView, bindView.rerender); for (var i=0, l=dependentKeys.length; i<l; i++) { - view.registerObserver(pathRoot, path + '.' + dependentKeys[i], bindView, rerenderBoundHelperView); + view.registerObserver(pathRoot, path + '.' + dependentKeys[i], bindView, bindView.rerender); } } helper._rawFunction = fn; Ember.Handlebars.registerHelper(name, helper); @@ -18091,29 +18369,18 @@ watchedProperties = watchedProperties.concat(normalizedProperties); // Observe each property. for (loc = 0, len = watchedProperties.length; loc < len; ++loc) { property = watchedProperties[loc]; - view.registerObserver(property.root, property.path, bindView, rerenderBoundHelperView); + view.registerObserver(property.root, property.path, bindView, bindView.rerender); } } /** @private - An observer function used with bound helpers which - will schedule a re-render of the _SimpleHandlebarsView - connected with the helper. -*/ -function rerenderBoundHelperView() { - Ember.run.scheduleOnce('render', this, 'rerender'); -} - -/** - @private - Renders the unbound form of an otherwise bound helper function. @param {Function} fn @param {Object} context @param {Array} normalizedProperties @@ -19143,21 +19410,17 @@ } Ember.View.applyAttributeBindings(elem, attr, result); }; - invoker = function() { - Ember.run.scheduleOnce('render', observer); - }; - // Add an observer to the view for when the property changes. // When the observer fires, find the element using the // unique data id and update the attribute to the new value. // Note: don't add observer when path is 'this' or path // is whole keyword e.g. {{#each x in list}} ... {{bindAttr attr="x"}} if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { - view.registerObserver(normalized.root, normalized.path, invoker); + view.registerObserver(normalized.root, normalized.path, observer); } // if this changes, also change the logic in ember-views/lib/views/view.js if ((type === 'string' || (type === 'number' && !isNaN(value)))) { ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); @@ -19267,16 +19530,12 @@ oldClass = null; } } }; - invoker = function() { - Ember.run.scheduleOnce('render', observer); - }; - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, invoker); + view.registerObserver(pathRoot, path, observer); } // We've already setup the observer; now we just need to figure out the // correct behavior right now on the first pass through. value = classStringForPath(context, parsedPath, options); @@ -20322,14 +20581,15 @@ @for Ember.Handlebars.helpers @param {String} templateName the template to render */ Ember.Handlebars.registerHelper('template', function(name, options) { - var template = Ember.TEMPLATES[name]; + var view = options.data.view, + template = view.templateForName(name); - Ember.TEMPLATES[name](this, { data: options.data }); + template(this, { data: options.data }); }); })(); @@ -20340,11 +20600,11 @@ @submodule ember-handlebars */ /** `partial` renders a template directly using the current context. - If needed the context can be set using the `{{#with foo}}` helper. + If needed the context can be set using the `{{#with foo}}` helper. ```html <script type="text/x-handlebars" data-template-name="header_bar"> {{#with currentUser}} {{partial user_info}} @@ -20369,17 +20629,17 @@ var nameParts = name.split("/"), lastPart = nameParts[nameParts.length - 1]; nameParts[nameParts.length - 1] = "_" + lastPart; - var underscoredName = nameParts.join("/"); + var view = options.data.view, + underscoredName = nameParts.join("/"), + template = view.templateForName(underscoredName), + deprecatedTemplate = view.templateForName(name); - var template = Ember.TEMPLATES[underscoredName], - deprecatedTemplate = Ember.TEMPLATES[name]; - template = template || deprecatedTemplate; template(this, { data: options.data }); }); @@ -20524,11 +20784,11 @@ Ember.Checkbox = Ember.View.extend({ classNames: ['ember-checkbox'], tagName: 'input', - attributeBindings: ['type', 'checked', 'disabled', 'tabindex'], + attributeBindings: ['type', 'checked', 'disabled', 'tabindex', 'name'], type: "checkbox", checked: false, disabled: false, @@ -20656,11 +20916,11 @@ Ember.TextField = Ember.View.extend(Ember.TextSupport, /** @scope Ember.TextField.prototype */ { classNames: ['ember-text-field'], tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern'], + attributeBindings: ['type', 'value', 'size', 'pattern', 'name'], /** The `value` attribute of the input element. As the user inputs text, this property is updated live. @@ -20916,11 +21176,11 @@ */ Ember.TextArea = Ember.View.extend(Ember.TextSupport, { classNames: ['ember-text-area'], tagName: "textarea", - attributeBindings: ['rows', 'cols'], + attributeBindings: ['rows', 'cols', 'name'], rows: null, cols: null, _updateElementValue: Ember.observer(function() { // We do this check so cursor position doesn't get affected in IE @@ -21241,11 +21501,11 @@ 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}); if(stack1 || stack1 === 0) { data.buffer.push(stack1); } return buffer; }), - attributeBindings: ['multiple', 'disabled', 'tabindex'], + attributeBindings: ['multiple', 'disabled', 'tabindex', 'name'], /** The `multiple` attribute of the select element. Indicates whether multiple options can be selected. @@ -22773,11 +23033,12 @@ this.push(options.path, name); } }, push: function(url, name, callback) { - if (url === "" || url === "/") { this.explicitIndex = true; } + var parts = name.split('.'); + if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } this.matches.push([url, name, callback]); }, route: function(name, options) { @@ -22839,11 +23100,11 @@ The type of generated controller depends on the context. You can customize your generated controllers by defining `App.ObjectController` and `App.ArrayController` */ Ember.generateController = function(container, controllerName, context) { - var controller, DefaultController; + var controller, DefaultController, fullName; if (context && Ember.isArray(context)) { DefaultController = container.resolve('controller:array'); controller = DefaultController.extend({ content: context @@ -22860,12 +23121,14 @@ controller.toString = function() { return "(generated " + controllerName + " controller)"; }; - container.register('controller', controllerName, controller); - return container.lookup('controller:' + controllerName); + + fullName = 'controller:' + controllerName; + container.register(fullName, controller); + return container.lookup(fullName); }; })(); @@ -22924,12 +23187,12 @@ container = this.container, self = this; setupRouter(this, router, location); - container.register('view', 'default', DefaultView); - container.register('view', 'toplevel', Ember.View.extend()); + container.register('view:default', DefaultView); + container.register('view:toplevel', Ember.View.extend()); location.onUpdateURL(function(url) { self.handleURL(url); }); @@ -23021,41 +23284,30 @@ function getHandlerFunction(router) { var seen = {}, container = router.container, DefaultRoute = container.resolve('route:basic'); return function(name) { - var handler = container.lookup('route:' + name); + var routeName = 'route:' + name, + handler = container.lookup(routeName); + if (seen[name]) { return handler; } seen[name] = true; if (!handler) { if (name === 'loading') { return {}; } if (name === 'failure') { return router.constructor.defaultFailureHandler; } - container.register('route', name, DefaultRoute.extend()); - handler = container.lookup('route:' + name); + container.register(routeName, DefaultRoute.extend()); + handler = container.lookup(routeName); } handler.routeName = name; return handler; }; } -function handlerIsActive(router, handlerName) { - var handler = router.container.lookup('route:' + handlerName), - currentHandlerInfos = router.router.currentHandlerInfos, - handlerInfo; - - for (var i=0, l=currentHandlerInfos.length; i<l; i++) { - handlerInfo = currentHandlerInfos[i]; - if (handlerInfo.handler === handler) { return true; } - } - - return false; -} - function routePath(handlerInfos) { var path = []; for (var i=1, l=handlerInfos.length; i<l; i++) { var name = handlerInfos[i].name, @@ -23322,10 +23574,17 @@ * The model class is determined from the segment (`post_id`'s class is `App.Post`) * The find method is called on the model class with the value of the dynamic segment. + Note that for routes with dynamic segments, this hook is only + executed when entered via the URL. If the route is entered + through a transition (e.g. when using the `linkTo` Handlebars + helper), then a model context is already provided and this hook + is not called. Routes without dynamic segments will always + execute the model hook. + @method model @param {Object} params the parameters extracted from the URL */ model: function(params) { var match, name, sawParams, value; @@ -23744,11 +24003,29 @@ var ret = [ routeName ]; return ret.concat(resolvedPaths(linkView.parameters)); } - var LinkView = Ember.View.extend({ + /** + Renders a link to the supplied route. + + When the rendered link matches the current route, and the same object instance is passed into the helper, + then the link is given class="active" by default. + + You may re-open LinkView in order to change the default active class: + + ``` javascript + Ember.LinkView.reopen({ + activeClass: "is-active" + }) + ``` + + @class LinkView + @namespace Ember + @extends Ember.View + **/ + var LinkView = Ember.LinkView = Ember.View.extend({ tagName: 'a', namedRoute: null, currentWhen: null, title: null, activeClass: 'active', @@ -23987,11 +24264,11 @@ options.hash.viewName = Ember.String.camelize(name); options.hash.template = container.lookup('template:' + name); options.hash.controller = controller; - if (router) { + if (router && !context) { router._connectActiveView(name, view); } Ember.Handlebars.helpers.view.call(this, view, options); }); @@ -24280,11 +24557,10 @@ EmberHandlebars.registerHelper('action', function(actionName) { var options = arguments[arguments.length - 1], contexts = a_slice.call(arguments, 1, -1); var hash = options.hash, - view = options.data.view, controller; // create a hash to pass along to registerAction var action = { eventName: hash.on || "click" @@ -24294,11 +24570,11 @@ context: this, options: options, params: contexts }; - action.view = view = get(view, 'concreteView'); + action.view = options.data.view; var root, target; if (hash.target) { root = this; @@ -25333,11 +25609,11 @@ ```javascript Ember.Application.initializer({ name: "store", initialize: function(container, application) { - container.register('store', 'main', application.Store); + container.register('store:main', application.Store); } }); ``` ### Routing @@ -25375,12 +25651,13 @@ @class Application @namespace Ember @extends Ember.Namespace */ -var Application = Ember.Application = Ember.Namespace.extend({ +var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin, { + /** The root DOM element of the Application. This can be specified as an element or a [jQuery-compatible selector string](http://api.jquery.com/category/selectors/). @@ -25437,12 +25714,10 @@ @type Object @default null */ customEvents: null, - isInitialized: false, - // Start off the number of deferrals at 1. This will be // decremented by the Application's own `initialize` method. _readinessDeferrals: 1, init: function() { @@ -25529,14 +25804,18 @@ @method scheduleInitialize */ scheduleInitialize: function() { var self = this; - this.$().ready(function() { - if (self.isDestroyed || self.isInitialized) { return; } - Ember.run.schedule('actions', self, 'initialize'); - }); + + if (!this.$ || this.$.isReady) { + Ember.run.schedule('actions', self, '_initialize'); + } else { + this.$().ready(function(){ + Ember.run(self, '_initialize'); + }); + } }, /** Use this to defer readiness until some condition is true. @@ -25625,26 +25904,38 @@ container.injection.apply(container, arguments); }, /** @private + @deprecated + Calling initialize manually is not supported. + + Please see Ember.Application#advanceReadiness and + Ember.Application#deferReadiness. + + @method initialize + **/ + initialize: function(){ + + }, + /** + @private + Initialize the application. This happens automatically. Run any initializers and run the application load hook. These hooks may choose to defer readiness. For example, an authentication hook might want to defer readiness until the auth token has been retrieved. - @method initialize + @method _initialize */ - initialize: function() { + _initialize: function() { + if (this.isDestroyed) { return; } - - this.isInitialized = true; - // At this point, the App.Router must already be assigned - this.register('router', 'main', this.Router); + this.register('router:main', this.Router); this.runInitializers(); Ember.runLoadHooks('application', this); // At this point, any initializers or load hooks that would have wanted @@ -25657,13 +25948,14 @@ reset: function() { get(this, '__container__').destroy(); this.buildContainer(); - this.isInitialized = false; - this.initialize(); - this.startRouting(); + Ember.run.schedule('actions', this, function(){ + this._initialize(); + this.startRouting(); + }); }, /** @private @method runInitializers @@ -25699,10 +25991,12 @@ if (!Ember.testing) { // Eagerly name all classes that are already loaded Ember.Namespace.processAll(); Ember.BOOTED = true; } + + this.resolve(this); }, /** @private @@ -25832,10 +26126,10 @@ container.set = Ember.set; container.normalize = normalize; container.resolver = resolverFor(namespace); container.optionsForType('view', { singleton: false }); container.optionsForType('template', { instantiate: false }); - container.register('application', 'main', namespace, { instantiate: false }); + container.register('application:main', namespace, { instantiate: false }); container.register('controller:basic', Ember.Controller, { instantiate: false }); container.register('controller:object', Ember.ObjectController, { instantiate: false }); container.register('controller:array', Ember.ArrayController, { instantiate: false }); container.register('route:basic', Ember.Route, { instantiate: false });