An attempt at some Perf improvements

This commit is contained in:
Robin Ward 2013-07-30 17:27:17 -04:00
parent 4b62e70417
commit a2844ea3c6
5 changed files with 561 additions and 403 deletions

View File

@ -13,13 +13,12 @@ Discourse.PostMenuView = Discourse.View.extend({
shouldRerender: Discourse.View.renderIfChanged( shouldRerender: Discourse.View.renderIfChanged(
'post.deleted_at', 'post.deleted_at',
'post.flagsAvailable.@each', 'post.flagsAvailable.@each',
'post.url',
'post.bookmarked',
'post.reply_count', 'post.reply_count',
'post.showRepliesBelow', 'post.showRepliesBelow',
'post.can_delete', 'post.can_delete',
'post.read', 'bookmarkClass',
'post.topic.last_read_post_number', 'bookmarkTooltip',
'shareUrl',
'post.topic.deleted_at'), 'post.topic.deleted_at'),
render: function(buffer) { render: function(buffer) {
@ -163,7 +162,7 @@ Discourse.PostMenuView = Discourse.View.extend({
renderShare: function(post, buffer) { renderShare: function(post, buffer) {
buffer.push("<button title=\"" + buffer.push("<button title=\"" +
(I18n.t("post.controls.share")) + (I18n.t("post.controls.share")) +
"\" data-share-url=\"" + (post.get('shareUrl')) + "\" class='share'><i class=\"icon-link\"></i></button>"); "\" data-share-url=\"" + post.get('shareUrl') + "\" class='share'><i class=\"icon-link\"></i></button>");
}, },
// Reply button // Reply button

View File

@ -21,6 +21,10 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
postStream: Em.computed.alias('controller.postStream'), postStream: Em.computed.alias('controller.postStream'),
updateBar: function() { updateBar: function() {
Em.run.scheduleOnce('afterRender', this, 'updateProgressBar');
}.observes('controller.streamPercentage'),
updateProgressBar: function() {
var $topicProgress = $('#topic-progress'); var $topicProgress = $('#topic-progress');
if (!$topicProgress.length) return; if (!$topicProgress.length) return;
@ -30,8 +34,7 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
$topicProgress.find('.bg') $topicProgress.find('.bg')
.css("border-right-width", (progressWidth === totalWidth) ? "0px" : "1px") .css("border-right-width", (progressWidth === totalWidth) ? "0px" : "1px")
.width(progressWidth); .width(progressWidth);
},
}.observes('controller.streamPercentage'),
updateTitle: function() { updateTitle: function() {
var title = this.get('topic.title'); var title = this.get('topic.title');
@ -79,7 +82,6 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
if (this.present('controller.enteredAt')) { if (this.present('controller.enteredAt')) {
var topicView = this; var topicView = this;
Em.run.schedule('afterRender', function() { Em.run.schedule('afterRender', function() {
topicView.updateBar();
topicView.updatePosition(); topicView.updatePosition();
}); });
} }
@ -337,7 +339,6 @@ Discourse.TopicView.reopenClass({
// Scroll to a given post, if in the DOM. Returns whether it was in the DOM or not. // Scroll to a given post, if in the DOM. Returns whether it was in the DOM or not.
jumpToPost: function(topicId, postNumber, avoidScrollIfPossible) { jumpToPost: function(topicId, postNumber, avoidScrollIfPossible) {
Em.run.scheduleOnce('afterRender', function() { Em.run.scheduleOnce('afterRender', function() {
var rows = $('.topic-post.ready'); var rows = $('.topic-post.ready');
// Make sure we're looking at the topic we want to scroll to // Make sure we're looking at the topic we want to scroll to

View File

@ -27,7 +27,7 @@
@property description @property description
**/ **/
description: function() { description: function() {
var result = "Rendered "; var result = "";
if (this.get('payload.template')) { if (this.get('payload.template')) {
result += "'" + this.get('payload.template') + "' "; result += "'" + this.get('payload.template') + "' ";
} }
@ -61,15 +61,15 @@
@method log @method log
**/ **/
log: function() { log: function(type) {
if ((!console) || (!console.groupCollapsed)) { return; } if ((typeof console === 'undefined') || (!console.groupCollapsed)) { return; }
// We don't care about really fast renders // We don't care about really fast renders
if (this.get('time') < 1) { return; } if (this.get('time') < 1) { return; }
console.groupCollapsed(this.get('description')); console.groupCollapsed(type + ": " + this.get('description'));
this.get('children').forEach(function (c) { this.get('children').forEach(function (c) {
c.log(); c.log(type);
}); });
console.groupEnd(); console.groupEnd();
} }
@ -94,7 +94,7 @@
profileNode.set('end', timestamp); profileNode.set('end', timestamp);
if (!this.depth) { if (!this.depth) {
profileNode.log(); profileNode.log("Render");
} }
} }
}); });

View File

@ -1,5 +1,5 @@
// Version: v1.0.0-pre.2-1804-g79d5a07 // Version: v1.0.0-pre.2-1839-ge87d164
// Last commit: 79d5a07 (2013-07-26 08:48:32 -0700) // Last commit: e87d164 (2013-07-29 18:30:35 -0400)
(function() { (function() {
@ -156,8 +156,8 @@ Ember.deprecateFunc = function(message, func) {
})(); })();
// Version: v1.0.0-pre.2-1804-g79d5a07 // Version: v1.0.0-pre.2-1839-ge87d164
// Last commit: 79d5a07 (2013-07-26 08:48:32 -0700) // Last commit: e87d164 (2013-07-29 18:30:35 -0400)
(function() { (function() {
@ -251,10 +251,10 @@ Ember.toString = function() { return "Ember"; };
/** /**
@property VERSION @property VERSION
@type String @type String
@default '1.0.0-rc.6' @default '1.0.0-rc.6.1'
@final @final
*/ */
Ember.VERSION = '1.0.0-rc.6'; Ember.VERSION = '1.0.0-rc.6.1';
/** /**
Standard environmental variables. You can define these in a global `ENV` Standard environmental variables. You can define these in a global `ENV`
@ -4844,6 +4844,9 @@ define("backburner",
run: function(target, method /*, args */) { run: function(target, method /*, args */) {
var ret; var ret;
var t2 = new Date().getTime();
this.begin(); this.begin();
if (!method) { if (!method) {
@ -4869,6 +4872,15 @@ define("backburner",
this.end(); this.end();
} }
} }
var diff = new Date().getTime() - t2;
if ((typeof console !== 'undefined') && console.log && diff > 1) {
console.log("Backburner: " + (new Date() - t2) + "ms");
}
return ret; return ret;
}, },
@ -6136,7 +6148,8 @@ Ember.oneWay = function(obj, to, from) {
(function() { (function() {
/** /**
@module ember-metal @module ember
@submodule ember-metal
*/ */
var Mixin, REQUIRED, Alias, var Mixin, REQUIRED, Alias,
@ -6512,38 +6525,6 @@ Mixin.finishPartial = finishPartial;
Ember.anyUnprocessedMixins = false; Ember.anyUnprocessedMixins = false;
/** /**
Creates an instance of a class. Accepts either no arguments, or an object
containing values to initialize the newly instantiated object with.
```javascript
App.Person = Ember.Object.extend({
helloWorld: function() {
alert("Hi, my name is " + this.get('name'));
}
});
var tom = App.Person.create({
name: 'Tom Dale'
});
tom.helloWorld(); // alerts "Hi, my name is Tom Dale".
```
`create` will call the `init` function if defined during
`Ember.AnyObject.extend`
If no arguments are passed to `create`, it will not set values to the new
instance during initialization:
```javascript
var noName = App.Person.create();
noName.helloWorld(); // alerts undefined
```
NOTE: For performance reasons, you cannot declare methods or computed
properties during `create`. You should instead declare methods and computed
properties when using `extend`.
@method create @method create
@static @static
@param arguments* @param arguments*
@ -9074,7 +9055,6 @@ function iter(key, value) {
@class Enumerable @class Enumerable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Enumerable = Ember.Mixin.create({ Ember.Enumerable = Ember.Mixin.create({
@ -9550,7 +9530,7 @@ Ember.Enumerable = Ember.Mixin.create({
@method some @method some
@param {Function} callback The callback to execute @param {Function} callback The callback to execute
@param {Object} [target] The target object to use @param {Object} [target] The target object to use
@return {Array} A filtered array. @return {Boolean} `true` if the passed function returns `true` for any item
*/ */
some: function(callback, target) { some: function(callback, target) {
return !!this.find(function(x, idx, i) { return !!this.find(function(x, idx, i) {
@ -9565,7 +9545,7 @@ Ember.Enumerable = Ember.Mixin.create({
@method someProperty @method someProperty
@param {String} key the property to test @param {String} key the property to test
@param {String} [value] optional value to test against. @param {String} [value] optional value to test against.
@return {Boolean} `true` @return {Boolean} `true` if the passed function returns `true` for any item
*/ */
someProperty: function(key, value) { someProperty: function(key, value) {
return this.some(iter.apply(this, arguments)); return this.some(iter.apply(this, arguments));
@ -9909,7 +9889,6 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.Enumera
@class Array @class Array
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.Enumerable @uses Ember.Enumerable
@since Ember 0.9.0 @since Ember 0.9.0
*/ */
@ -10307,7 +10286,6 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
@class Comparable @class Comparable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{ Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{
@ -10359,7 +10337,6 @@ var get = Ember.get, set = Ember.set;
@class Copyable @class Copyable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ { Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ {
@ -10464,7 +10441,6 @@ var get = Ember.get, set = Ember.set;
@class Freezable @class Freezable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ { Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ {
@ -10544,7 +10520,6 @@ var forEach = Ember.EnumerableUtils.forEach;
@class MutableEnumerable @class MutableEnumerable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.Enumerable @uses Ember.Enumerable
*/ */
Ember.MutableEnumerable = Ember.Mixin.create(Ember.Enumerable, { Ember.MutableEnumerable = Ember.Mixin.create(Ember.Enumerable, {
@ -10644,7 +10619,6 @@ var get = Ember.get, set = Ember.set;
@class MutableArray @class MutableArray
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.Array @uses Ember.Array
@uses Ember.MutableEnumerable @uses Ember.MutableEnumerable
*/ */
@ -10998,9 +10972,8 @@ var get = Ember.get, set = Ember.set;
@class Observable @class Observable
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { Ember.Observable = Ember.Mixin.create({
/** /**
Retrieves the value of a property from the object. Retrieves the value of a property from the object.
@ -11380,6 +11353,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
*/ */
incrementProperty: function(keyName, increment) { incrementProperty: function(keyName, increment) {
if (Ember.isNone(increment)) { increment = 1; } if (Ember.isNone(increment)) { increment = 1; }
Ember.assert("Must pass a numeric value to incrementProperty", (!isNaN(parseFloat(increment)) && isFinite(increment)));
set(this, keyName, (get(this, keyName) || 0) + increment); set(this, keyName, (get(this, keyName) || 0) + increment);
return get(this, keyName); return get(this, keyName);
}, },
@ -11399,6 +11373,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
*/ */
decrementProperty: function(keyName, decrement) { decrementProperty: function(keyName, decrement) {
if (Ember.isNone(decrement)) { decrement = 1; } if (Ember.isNone(decrement)) { decrement = 1; }
Ember.assert("Must pass a numeric value to decrementProperty", (!isNaN(parseFloat(decrement)) && isFinite(decrement)));
set(this, keyName, (get(this, keyName) || 0) - decrement); set(this, keyName, (get(this, keyName) || 0) - decrement);
return get(this, keyName); return get(this, keyName);
}, },
@ -11616,7 +11591,6 @@ Ember.TargetActionSupport = Ember.Mixin.create({
@class Evented @class Evented
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.Evented = Ember.Mixin.create({ Ember.Evented = Ember.Mixin.create({
@ -11748,7 +11722,6 @@ var get = Ember.get;
/** /**
@class Deferred @class Deferred
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.DeferredMixin = Ember.Mixin.create({ Ember.DeferredMixin = Ember.Mixin.create({
/** /**
@ -11980,6 +11953,10 @@ function makeCtor() {
} }
/**
@class CoreObject
@namespace Ember
*/
var CoreObject = makeCtor(); var CoreObject = makeCtor();
CoreObject.toString = function() { return "Ember.CoreObject"; }; CoreObject.toString = function() { return "Ember.CoreObject"; };
@ -12239,12 +12216,57 @@ var ClassMixin = Mixin.create({
return Class; return Class;
}, },
/**
Equivalent to doing `extend(arguments).create()`.
If possible use the normal `create` method instead.
@method createWithMixins
@static
@param [arguments]*
*/
createWithMixins: function() { createWithMixins: function() {
var C = this; var C = this;
if (arguments.length>0) { this._initMixins(arguments); } if (arguments.length>0) { this._initMixins(arguments); }
return new C(); return new C();
}, },
/**
Creates an instance of a class. Accepts either no arguments, or an object
containing values to initialize the newly instantiated object with.
```javascript
App.Person = Ember.Object.extend({
helloWorld: function() {
alert("Hi, my name is " + this.get('name'));
}
});
var tom = App.Person.create({
name: 'Tom Dale'
});
tom.helloWorld(); // alerts "Hi, my name is Tom Dale".
```
`create` will call the `init` function if defined during
`Ember.AnyObject.extend`
If no arguments are passed to `create`, it will not set values to the new
instance during initialization:
```javascript
var noName = App.Person.create();
noName.helloWorld(); // alerts undefined
```
NOTE: For performance reasons, you cannot declare methods or computed
properties during `create`. You should instead declare methods and computed
properties when using `extend` or use the `createWithMixins` shorthand.
@method create
@static
@param [arguments]*
*/
create: function() { create: function() {
var C = this; var C = this;
if (arguments.length>0) { this._initProperties(arguments); } if (arguments.length>0) { this._initProperties(arguments); }
@ -12344,10 +12366,6 @@ if (Ember.config.overrideClassMixin) {
CoreObject.ClassMixin = ClassMixin; CoreObject.ClassMixin = ClassMixin;
ClassMixin.apply(CoreObject); ClassMixin.apply(CoreObject);
/**
@class CoreObject
@namespace Ember
*/
Ember.CoreObject = CoreObject; Ember.CoreObject = CoreObject;
})(); })();
@ -13122,6 +13140,7 @@ function addObserverForContentKey(content, keyName, proxy, idx, loc) {
while(--loc>=idx) { while(--loc>=idx) {
var item = content.objectAt(loc); var item = content.objectAt(loc);
if (item) { if (item) {
Ember.assert('When using @each to observe the array ' + content + ', the array must return an object', Ember.typeOf(item) === 'instance' || Ember.typeOf(item) === 'object');
Ember.addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); Ember.addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange');
Ember.addObserver(item, keyName, proxy, 'contentKeyDidChange'); Ember.addObserver(item, keyName, proxy, 'contentKeyDidChange');
@ -13205,7 +13224,7 @@ Ember.EachProxy = Ember.Object.extend({
for(key in keys) { for(key in keys) {
if (!keys.hasOwnProperty(key)) { continue; } if (!keys.hasOwnProperty(key)) { continue; }
if (lim>0) removeObserverForContentKey(content, key, this, idx, lim); if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); }
Ember.propertyWillChange(this, key); Ember.propertyWillChange(this, key);
} }
@ -13215,21 +13234,20 @@ Ember.EachProxy = Ember.Object.extend({
}, },
arrayDidChange: function(content, idx, removedCnt, addedCnt) { arrayDidChange: function(content, idx, removedCnt, addedCnt) {
var keys = this._keys, key, lim; var keys = this._keys, lim;
lim = addedCnt>0 ? idx+addedCnt : -1; lim = addedCnt>0 ? idx+addedCnt : -1;
Ember.beginPropertyChanges(this); Ember.changeProperties(function() {
for(var key in keys) {
for(key in keys) {
if (!keys.hasOwnProperty(key)) { continue; } if (!keys.hasOwnProperty(key)) { continue; }
if (lim>0) addObserverForContentKey(content, key, this, idx, lim); if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); }
Ember.propertyDidChange(this, key); Ember.propertyDidChange(this, key);
} }
Ember.propertyDidChange(this._content, '@each'); Ember.propertyDidChange(this._content, '@each');
Ember.endPropertyChanges(this); }, this);
}, },
// .......................................................... // ..........................................................
@ -13404,7 +13422,6 @@ if (ignore.length>0) {
@class NativeArray @class NativeArray
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.MutableArray @uses Ember.MutableArray
@uses Ember.Observable @uses Ember.Observable
@uses Ember.Copyable @uses Ember.Copyable
@ -14015,7 +14032,6 @@ var get = Ember.get;
@class ControllerMixin @class ControllerMixin
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.ControllerMixin = Ember.Mixin.create({ Ember.ControllerMixin = Ember.Mixin.create({
/* ducktype as a controller */ /* ducktype as a controller */
@ -14103,7 +14119,6 @@ var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach;
@class SortableMixin @class SortableMixin
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.MutableEnumerable @uses Ember.MutableEnumerable
*/ */
Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, { Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, {
@ -19216,7 +19231,7 @@ Ember.CollectionView.CONTAINER_MAP = {
(function() { (function() {
var set = Ember.set; var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
/** /**
@module ember @module ember
@ -19301,12 +19316,66 @@ var set = Ember.set;
@namespace Ember @namespace Ember
@extends Ember.View @extends Ember.View
*/ */
Ember.Component = Ember.View.extend({ Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
init: function() { init: function() {
this._super(); this._super();
set(this, 'context', this); set(this, 'context', this);
set(this, 'controller', this); set(this, 'controller', this);
set(this, 'templateData', {keywords: {}}); set(this, 'templateData', {keywords: {}});
},
targetObject: Ember.computed(function(key) {
var parentView = get(this, '_parentView');
return parentView ? get(parentView, 'controller') : null;
}).property('_parentView'),
/**
Sends an action to component's controller. A component inherits its
controller from the context in which it is used.
By default, calling `sendAction()` will send an action with the name
of the component's `action` property.
For example, if the component had a property `action` with the value
`"addItem"`, calling `sendAction()` would send the `addItem` action
to the component's controller.
If you provide an argument to `sendAction()`, that key will be used to look
up the action name.
For example, if the component had a property `playing` with the value
`didStartPlaying`, calling `sendAction('playing')` would send the
`didStartPlaying` action to the component's controller.
Whether or not you are using the default action or a named action, if
the action name is not defined on the component, calling `sendAction()`
does not have any effect.
For example, if you call `sendAction()` on a component that does not have
an `action` property defined, no action will be sent to the controller,
nor will an exception be raised.
@param [action] {String} the action to trigger
*/
sendAction: function(action) {
var actionName;
// Send the default action
if (action === undefined) {
actionName = get(this, 'action');
Ember.assert("The default action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
} else {
actionName = get(this, action);
Ember.assert("The " + action + " action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
}
// If no action name for that action could be found, just abort.
if (actionName === undefined) { return; }
this.triggerAction({
action: actionName
});
} }
}); });
@ -19410,7 +19479,7 @@ define("metamorph",
document = this.document, document = this.document,
// Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges
supportsRange = document && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, supportsRange = false,
// Internet Explorer prior to 9 does not allow setting innerHTML if the first element // 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 // is a "zero-scope" element. This problem can be worked around by making
@ -20394,7 +20463,7 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) {
view = data.view, view = data.view,
currentContext = (options.contexts && options.contexts[0]) || this, currentContext = (options.contexts && options.contexts[0]) || this,
normalized, normalized,
pathRoot, path, pathRoot, path, prefixPathForDependentKeys = '',
loc, hashOption; loc, hashOption;
Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn); Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn);
@ -20445,8 +20514,11 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) {
view.registerObserver(pathRoot, path, bindView, bindView.rerender); view.registerObserver(pathRoot, path, bindView, bindView.rerender);
if(!Ember.isEmpty(path)) {
prefixPathForDependentKeys = path + '.';
}
for (var i=0, l=dependentKeys.length; i<l; i++) { for (var i=0, l=dependentKeys.length; i<l; i++) {
view.registerObserver(pathRoot, path + '.' + dependentKeys[i], bindView, bindView.rerender); view.registerObserver(pathRoot, prefixPathForDependentKeys + dependentKeys[i], bindView, bindView.rerender);
} }
} }
@ -20674,21 +20746,23 @@ var DOMManager = {
view.clearRenderedChildren(); view.clearRenderedChildren();
var buffer = view.renderToBuffer(); var buffer = view.renderToBuffer();
view.invokeRecursively(function(view) { // view.invokeRecursively(function(view) {
view.propertyWillChange('element'); // view.propertyWillChange('element');
}); // });
view.triggerRecursively('willInsertElement'); view.triggerRecursively('willInsertElement');
morph.replaceWith(buffer.string()); morph.replaceWith(buffer.string());
view.transitionTo('inDOM'); view.transitionTo('inDOM');
view.invokeRecursively(function(view) { // view.invokeRecursively(function(view) {
view.propertyDidChange('element'); // view.propertyDidChange('element');
}); // });
view.triggerRecursively('didInsertElement'); view.triggerRecursively('didInsertElement');
notifyMutationListeners(); notifyMutationListeners();
}); });
}, },
empty: function(view) { empty: function(view) {
@ -20703,7 +20777,6 @@ var DOMManager = {
/** /**
@class _Metamorph @class _Metamorph
@namespace Ember @namespace Ember
@extends Ember.Mixin
@private @private
*/ */
Ember._Metamorph = Ember.Mixin.create({ Ember._Metamorph = Ember.Mixin.create({
@ -22788,18 +22861,15 @@ Ember.Handlebars.registerHelper('each', function(path, options) {
Ember.TEMPLATES["my_cool_template"] = Ember.Handlebars.compile('<b>{{user}}</b>'); Ember.TEMPLATES["my_cool_template"] = Ember.Handlebars.compile('<b>{{user}}</b>');
``` ```
@deprecated
@method template @method template
@for Ember.Handlebars.helpers @for Ember.Handlebars.helpers
@param {String} templateName the template to render @param {String} templateName the template to render
*/ */
Ember.Handlebars.registerHelper('template', function(name, options) { Ember.Handlebars.registerHelper('template', function(name, options) {
var view = options.data.view, Ember.deprecate("The `template` helper has been deprecated in favor of the `partial` helper. Please use `partial` instead, which will work the same way.");
template = view.templateForName(name); return Ember.Handlebars.helpers.partial.apply(this, arguments);
Ember.assert("Unable to find template with name '"+name+"'.", !!template);
template(this, { data: options.data });
}); });
})(); })();
@ -22849,7 +22919,6 @@ Ember.Handlebars.registerHelper('partial', function(name, options) {
template = view.templateForName(underscoredName), template = view.templateForName(underscoredName),
deprecatedTemplate = !template && 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); Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate);
template = template || deprecatedTemplate; template = template || deprecatedTemplate;
@ -22934,6 +23003,7 @@ Ember.Handlebars.registerHelper('yield', function(options) {
var keywords = view._parentView.cloneKeywords(); var keywords = view._parentView.cloneKeywords();
currentView.appendChild(Ember.View, { currentView.appendChild(Ember.View, {
isVirtual: true,
tagName: '', tagName: '',
template: template, template: template,
context: get(view._parentView, 'context'), context: get(view._parentView, 'context'),
@ -23080,7 +23150,6 @@ var get = Ember.get, set = Ember.set;
@class TextSupport @class TextSupport
@namespace Ember @namespace Ember
@extends Ember.Mixin
@private @private
*/ */
Ember.TextSupport = Ember.Mixin.create({ Ember.TextSupport = Ember.Mixin.create({
@ -25288,7 +25357,7 @@ define("router",
} }
function isParam(object) { function isParam(object) {
return object && (typeof object === "string" || object instanceof String || !isNaN(object)); return (typeof object === "string" || object instanceof String || !isNaN(object));
} }
/** /**
@ -25763,8 +25832,7 @@ define("router",
handlerInfo = handlerInfos[index], handlerInfo = handlerInfos[index],
handler = handlerInfo.handler, handler = handlerInfo.handler,
handlerName = handlerInfo.name, handlerName = handlerInfo.name,
seq = transition.sequence, seq = transition.sequence;
errorAlreadyHandled = false;
if (index < matchPoint) { if (index < matchPoint) {
log(router, seq, handlerName + ": using context from already-active handler"); log(router, seq, handlerName + ": using context from already-active handler");
@ -25777,21 +25845,17 @@ define("router",
return RSVP.resolve().then(handleAbort) return RSVP.resolve().then(handleAbort)
.then(beforeModel) .then(beforeModel)
.then(null, handleError)
.then(handleAbort) .then(handleAbort)
.then(model) .then(model)
.then(null, handleError)
.then(handleAbort) .then(handleAbort)
.then(afterModel) .then(afterModel)
.then(null, handleError)
.then(handleAbort) .then(handleAbort)
.then(proceed); .then(proceed)
.then(null, handleError);
function handleAbort(result) { function handleAbort(result) {
if (transition.isAborted) { if (transition.isAborted) {
log(transition.router, transition.sequence, "detected abort."); log(transition.router, transition.sequence, "detected abort.");
errorAlreadyHandled = true;
return RSVP.reject(new Router.TransitionAborted()); return RSVP.reject(new Router.TransitionAborted());
} }
@ -25799,9 +25863,13 @@ define("router",
} }
function handleError(reason) { function handleError(reason) {
if (reason instanceof Router.TransitionAborted) {
// if the transition was aborted and *no additional* error was thrown,
// reject with the Router.TransitionAborted instance
return RSVP.reject(reason);
}
if (errorAlreadyHandled) { return RSVP.reject(reason); } // otherwise, we're here because of a different error
errorAlreadyHandled = true;
transition.abort(); transition.abort();
log(router, seq, handlerName + ": handling error: " + reason); log(router, seq, handlerName + ": handling error: " + reason);
@ -26066,9 +26134,8 @@ var get = Ember.get;
@submodule ember-routing @submodule ember-routing
*/ */
Ember.controllerFor = function(container, controllerName, context, lookupOptions) { Ember.controllerFor = function(container, controllerName, lookupOptions) {
return container.lookup('controller:' + controllerName, lookupOptions) || return container.lookup('controller:' + controllerName, lookupOptions);
Ember.generateController(container, controllerName, context);
}; };
/* /*
Generates a controller automatically if none was provided. Generates a controller automatically if none was provided.
@ -26082,14 +26149,12 @@ Ember.generateController = function(container, controllerName, context) {
if (context && Ember.isArray(context)) { if (context && Ember.isArray(context)) {
DefaultController = container.resolve('controller:array'); DefaultController = container.resolve('controller:array');
controller = DefaultController.extend({ controller = DefaultController.extend({
isGenerated: true, isGenerated: true
content: context
}); });
} else if (context) { } else if (context) {
DefaultController = container.resolve('controller:object'); DefaultController = container.resolve('controller:object');
controller = DefaultController.extend({ controller = DefaultController.extend({
isGenerated: true, isGenerated: true
content: context
}); });
} else { } else {
DefaultController = container.resolve('controller:basic'); DefaultController = container.resolve('controller:basic');
@ -26204,9 +26269,7 @@ Ember.Router = Ember.Object.extend({
}, },
handleURL: function(url) { handleURL: function(url) {
scheduleLoadingStateEntry(this); return doTransition(this, 'handleURL', [url]);
return this.router.handleURL(url).then(transitionCompleted);
}, },
transitionTo: function() { transitionTo: function() {
@ -26372,9 +26435,13 @@ function doTransition(router, method, args) {
Ember.assert("The route " + passedName + " was not found", router.router.hasRoute(name)); Ember.assert("The route " + passedName + " was not found", router.router.hasRoute(name));
} }
scheduleLoadingStateEntry(router);
var transitionPromise = router.router[method].apply(router.router, args); var transitionPromise = router.router[method].apply(router.router, args);
// Don't schedule loading state entry if user has already aborted the transition.
if (router.router.activeTransition) {
scheduleLoadingStateEntry(router);
}
transitionPromise.then(transitionCompleted); transitionPromise.then(transitionCompleted);
// We want to return the configurable promise object // We want to return the configurable promise object
@ -26664,7 +26731,11 @@ Ember.Route = Ember.Object.extend({
@method setup @method setup
*/ */
setup: function(context) { setup: function(context) {
var controller = this.controllerFor(this.controllerName || this.routeName, context); var controllerName = this.controllerName || this.routeName,
controller = this.controllerFor(controllerName, true);
if (!controller) {
controller = this.generateController(controllerName, context);
}
// Assign the route's controller so that it can more easily be // Assign the route's controller so that it can more easily be
// referenced in event handlers // referenced in event handlers
@ -26981,27 +27052,40 @@ Ember.Route = Ember.Object.extend({
}); });
``` ```
By default, the controller for `post` is the shared instance of
`App.PostController`.
@method controllerFor @method controllerFor
@param {String} name the name of the route @param {String} name the name of the route
@param {Object} model the model associated with the route (optional)
@return {Ember.Controller} @return {Ember.Controller}
*/ */
controllerFor: function(name, model) { controllerFor: function(name, _skipAssert) {
var container = this.router.container, var container = this.router.container,
controller = container.lookup('controller:' + name); controller = container.lookup('controller:' + name);
if (!controller) { // NOTE: We're specifically checking that skipAssert is true, because according
// to the old API the second parameter was model. We do not want people who
// passed a model to skip the assertion.
Ember.assert("The controller "+name+" could not be found. Make sure the controller has been generated first. This will happen the first time the associated route is entered.", controller || _skipAssert === true);
return controller;
},
/**
Generates a controller for a route.
If the optional model is passed then the controller type is determined automatically,
e.g., an ArrayController for arrays.
@method generateController
@param {String} name the name of the controller
@param {Object} model the model to infer the type of the controller (optional)
*/
generateController: function(name, model) {
var container = this.router.container;
model = model || this.modelFor(name); model = model || this.modelFor(name);
Ember.assert("You are trying to look up a controller that you did not define, and for which Ember does not know the model.\n\nThis is not a controller for a route, so you must explicitly define the controller ("+this.router.namespace.toString() + "." + Ember.String.capitalize(Ember.String.camelize(name))+"Controller) or pass a model as the second parameter to `controllerFor`, so that Ember knows which type of controller to create for you.", model || this.container.lookup('route:' + name)); Ember.assert("You are trying to look up a controller that you did not define, and for which Ember does not know the model.\n\nThis is not a controller for a route, so you must explicitly define the controller ("+this.router.namespace.toString() + "." + Ember.String.capitalize(Ember.String.camelize(name))+"Controller) or pass a model as the second parameter to `controllerFor`, so that Ember knows which type of controller to create for you.", model || this.container.lookup('route:' + name));
controller = Ember.generateController(container, name, model); return Ember.generateController(container, name, model);
}
return controller;
}, },
/** /**
@ -27103,6 +27187,8 @@ Ember.Route = Ember.Object.extend({
render: function(name, options) { render: function(name, options) {
Ember.assert("The name in the given arguments is undefined", arguments.length > 0 ? !Ember.isNone(arguments[0]) : true); Ember.assert("The name in the given arguments is undefined", arguments.length > 0 ? !Ember.isNone(arguments[0]) : true);
var namePassed = !!name;
if (typeof name === 'object' && !options) { if (typeof name === 'object' && !options) {
options = name; options = name;
name = this.routeName; name = this.routeName;
@ -27115,6 +27201,7 @@ Ember.Route = Ember.Object.extend({
template = container.lookup('template:' + name); template = container.lookup('template:' + name);
if (!view && !template) { if (!view && !template) {
Ember.assert("Could not find \"" + name + "\" template or view.", !namePassed);
if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) { if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) {
Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name });
} }
@ -27205,17 +27292,15 @@ function parentRoute(route) {
} }
} }
function parentTemplate(route, isRecursive) { function parentTemplate(route) {
var parent = parentRoute(route), template; var parent = parentRoute(route), template;
if (!parent) { return; } if (!parent) { return; }
Ember.warn(fmt("The immediate parent route ('%@') did not render into the main outlet and the default 'into' option ('%@') may not be expected", [get(parent, 'routeName'), get(route, 'routeName')]), !isRecursive);
if (template = parent.lastRenderedTemplate) { if (template = parent.lastRenderedTemplate) {
return template; return template;
} else { } else {
return parentTemplate(parent, true); return parentTemplate(parent);
} }
} }
@ -27227,7 +27312,7 @@ function normalizeOptions(route, name, template, options) {
options.template = template; options.template = template;
options.LOG_VIEW_LOOKUPS = get(route.router, 'namespace.LOG_VIEW_LOOKUPS'); options.LOG_VIEW_LOOKUPS = get(route.router, 'namespace.LOG_VIEW_LOOKUPS');
Ember.assert("An outlet ("+options.outlet+") was specified but this view will render at the root level.", options.outlet === 'main' || options.into); Ember.assert("An outlet ("+options.outlet+") was specified but was not found.", options.outlet === 'main' || options.into);
var controller = options.controller, namedController; var controller = options.controller, namedController;
@ -27435,6 +27520,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
Determines whether the `LinkView` will trigger routing via Determines whether the `LinkView` will trigger routing via
the `replaceWith` routing strategy. the `replaceWith` routing strategy.
@property replace
@type Boolean @type Boolean
@default false @default false
**/ **/
@ -27488,15 +27574,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
} }
var observer = function(object, path) { var observer = function(object, path) {
var notify = true, i;
for(i=0; i < paths.length; i++) {
if (!get(this, paths[i])) {
notify = false;
}
}
if (notify) {
this.notifyPropertyChange('routeArgs'); this.notifyPropertyChange('routeArgs');
}
}; };
for(i=0; i < length; i++) { for(i=0; i < length; i++) {
@ -27587,7 +27665,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
if (get(this, '_isDisabled')) { return false; } if (get(this, '_isDisabled')) { return false; }
if (get(this, 'loading')) { if (get(this, 'loading')) {
Ember.Logger.warn("This linkTo's parameters are either not yet loaded or point to an invalid route."); Ember.Logger.warn("This linkTo is in an inactive loading state because at least one of its parameters' presently has a null/undefined value, or the provided route name is invalid.");
return false; return false;
} }
@ -28023,7 +28101,8 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
controller = container.lookup('controller:' + controllerName, lookupOptions); controller = container.lookup('controller:' + controllerName, lookupOptions);
Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", !!controller); Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", !!controller);
} else { } else {
controller = Ember.controllerFor(container, name, context, lookupOptions); controller = container.lookup('controller:' + name, lookupOptions) ||
Ember.generateController(container, name, context);
} }
if (controller && context) { if (controller && context) {
@ -30287,8 +30366,7 @@ Ember.ControllerMixin.reopen({
controllerFor: function(controllerName) { controllerFor: function(controllerName) {
Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead"); Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead");
var container = get(this, 'container'); return Ember.controllerFor(get(this, 'container'), controllerName);
return container.lookup('controller:' + controllerName);
}, },
controllers: Ember.computed(function() { controllers: Ember.computed(function() {
@ -31906,7 +31984,7 @@ Test.QUnitAdapter = Test.Adapter.extend({
start(); start();
}, },
exception: function(error) { exception: function(error) {
ok(false, error); ok(false, Ember.inspect(error));
} }
}); });
@ -31990,7 +32068,7 @@ function fillIn(app, selector, context, text) {
function findWithAssert(app, selector, context) { function findWithAssert(app, selector, context) {
var $el = find(app, selector, context); var $el = find(app, selector, context);
if ($el.length === 0) { if ($el.length === 0) {
throw("Element " + selector + " not found."); throw new Error("Element " + selector + " not found.");
} }
return $el; return $el;
} }

View File

@ -89,10 +89,10 @@ Ember.toString = function() { return "Ember"; };
/** /**
@property VERSION @property VERSION
@type String @type String
@default '1.0.0-rc.6' @default '1.0.0-rc.6.1'
@final @final
*/ */
Ember.VERSION = '1.0.0-rc.6'; Ember.VERSION = '1.0.0-rc.6.1';
/** /**
Standard environmental variables. You can define these in a global `ENV` Standard environmental variables. You can define these in a global `ENV`
@ -4678,6 +4678,9 @@ define("backburner",
run: function(target, method /*, args */) { run: function(target, method /*, args */) {
var ret; var ret;
var t2 = new Date().getTime();
this.begin(); this.begin();
if (!method) { if (!method) {
@ -4703,6 +4706,15 @@ define("backburner",
this.end(); this.end();
} }
} }
var diff = new Date().getTime() - t2;
if ((typeof console !== 'undefined') && console.log && diff > 1) {
console.log("Backburner: " + (new Date() - t2) + "ms");
}
return ret; return ret;
}, },
@ -5970,7 +5982,8 @@ Ember.oneWay = function(obj, to, from) {
(function() { (function() {
/** /**
@module ember-metal @module ember
@submodule ember-metal
*/ */
var Mixin, REQUIRED, Alias, var Mixin, REQUIRED, Alias,
@ -6346,38 +6359,6 @@ Mixin.finishPartial = finishPartial;
Ember.anyUnprocessedMixins = false; Ember.anyUnprocessedMixins = false;
/** /**
Creates an instance of a class. Accepts either no arguments, or an object
containing values to initialize the newly instantiated object with.
```javascript
App.Person = Ember.Object.extend({
helloWorld: function() {
alert("Hi, my name is " + this.get('name'));
}
});
var tom = App.Person.create({
name: 'Tom Dale'
});
tom.helloWorld(); // alerts "Hi, my name is Tom Dale".
```
`create` will call the `init` function if defined during
`Ember.AnyObject.extend`
If no arguments are passed to `create`, it will not set values to the new
instance during initialization:
```javascript
var noName = App.Person.create();
noName.helloWorld(); // alerts undefined
```
NOTE: For performance reasons, you cannot declare methods or computed
properties during `create`. You should instead declare methods and computed
properties when using `extend`.
@method create @method create
@static @static
@param arguments* @param arguments*
@ -8907,7 +8888,6 @@ function iter(key, value) {
@class Enumerable @class Enumerable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Enumerable = Ember.Mixin.create({ Ember.Enumerable = Ember.Mixin.create({
@ -9383,7 +9363,7 @@ Ember.Enumerable = Ember.Mixin.create({
@method some @method some
@param {Function} callback The callback to execute @param {Function} callback The callback to execute
@param {Object} [target] The target object to use @param {Object} [target] The target object to use
@return {Array} A filtered array. @return {Boolean} `true` if the passed function returns `true` for any item
*/ */
some: function(callback, target) { some: function(callback, target) {
return !!this.find(function(x, idx, i) { return !!this.find(function(x, idx, i) {
@ -9398,7 +9378,7 @@ Ember.Enumerable = Ember.Mixin.create({
@method someProperty @method someProperty
@param {String} key the property to test @param {String} key the property to test
@param {String} [value] optional value to test against. @param {String} [value] optional value to test against.
@return {Boolean} `true` @return {Boolean} `true` if the passed function returns `true` for any item
*/ */
someProperty: function(key, value) { someProperty: function(key, value) {
return this.some(iter.apply(this, arguments)); return this.some(iter.apply(this, arguments));
@ -9742,7 +9722,6 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.Enumera
@class Array @class Array
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.Enumerable @uses Ember.Enumerable
@since Ember 0.9.0 @since Ember 0.9.0
*/ */
@ -10140,7 +10119,6 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
@class Comparable @class Comparable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{ Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{
@ -10192,7 +10170,6 @@ var get = Ember.get, set = Ember.set;
@class Copyable @class Copyable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ { Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ {
@ -10297,7 +10274,6 @@ var get = Ember.get, set = Ember.set;
@class Freezable @class Freezable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@since Ember 0.9 @since Ember 0.9
*/ */
Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ { Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ {
@ -10377,7 +10353,6 @@ var forEach = Ember.EnumerableUtils.forEach;
@class MutableEnumerable @class MutableEnumerable
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.Enumerable @uses Ember.Enumerable
*/ */
Ember.MutableEnumerable = Ember.Mixin.create(Ember.Enumerable, { Ember.MutableEnumerable = Ember.Mixin.create(Ember.Enumerable, {
@ -10477,7 +10452,6 @@ var get = Ember.get, set = Ember.set;
@class MutableArray @class MutableArray
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.Array @uses Ember.Array
@uses Ember.MutableEnumerable @uses Ember.MutableEnumerable
*/ */
@ -10831,9 +10805,8 @@ var get = Ember.get, set = Ember.set;
@class Observable @class Observable
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { Ember.Observable = Ember.Mixin.create({
/** /**
Retrieves the value of a property from the object. Retrieves the value of a property from the object.
@ -11213,6 +11186,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
*/ */
incrementProperty: function(keyName, increment) { incrementProperty: function(keyName, increment) {
if (Ember.isNone(increment)) { increment = 1; } if (Ember.isNone(increment)) { increment = 1; }
set(this, keyName, (get(this, keyName) || 0) + increment); set(this, keyName, (get(this, keyName) || 0) + increment);
return get(this, keyName); return get(this, keyName);
}, },
@ -11232,6 +11206,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
*/ */
decrementProperty: function(keyName, decrement) { decrementProperty: function(keyName, decrement) {
if (Ember.isNone(decrement)) { decrement = 1; } if (Ember.isNone(decrement)) { decrement = 1; }
set(this, keyName, (get(this, keyName) || 0) - decrement); set(this, keyName, (get(this, keyName) || 0) - decrement);
return get(this, keyName); return get(this, keyName);
}, },
@ -11449,7 +11424,6 @@ Ember.TargetActionSupport = Ember.Mixin.create({
@class Evented @class Evented
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.Evented = Ember.Mixin.create({ Ember.Evented = Ember.Mixin.create({
@ -11581,7 +11555,6 @@ var get = Ember.get;
/** /**
@class Deferred @class Deferred
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.DeferredMixin = Ember.Mixin.create({ Ember.DeferredMixin = Ember.Mixin.create({
/** /**
@ -11811,6 +11784,10 @@ function makeCtor() {
} }
/**
@class CoreObject
@namespace Ember
*/
var CoreObject = makeCtor(); var CoreObject = makeCtor();
CoreObject.toString = function() { return "Ember.CoreObject"; }; CoreObject.toString = function() { return "Ember.CoreObject"; };
@ -12070,12 +12047,57 @@ var ClassMixin = Mixin.create({
return Class; return Class;
}, },
/**
Equivalent to doing `extend(arguments).create()`.
If possible use the normal `create` method instead.
@method createWithMixins
@static
@param [arguments]*
*/
createWithMixins: function() { createWithMixins: function() {
var C = this; var C = this;
if (arguments.length>0) { this._initMixins(arguments); } if (arguments.length>0) { this._initMixins(arguments); }
return new C(); return new C();
}, },
/**
Creates an instance of a class. Accepts either no arguments, or an object
containing values to initialize the newly instantiated object with.
```javascript
App.Person = Ember.Object.extend({
helloWorld: function() {
alert("Hi, my name is " + this.get('name'));
}
});
var tom = App.Person.create({
name: 'Tom Dale'
});
tom.helloWorld(); // alerts "Hi, my name is Tom Dale".
```
`create` will call the `init` function if defined during
`Ember.AnyObject.extend`
If no arguments are passed to `create`, it will not set values to the new
instance during initialization:
```javascript
var noName = App.Person.create();
noName.helloWorld(); // alerts undefined
```
NOTE: For performance reasons, you cannot declare methods or computed
properties during `create`. You should instead declare methods and computed
properties when using `extend` or use the `createWithMixins` shorthand.
@method create
@static
@param [arguments]*
*/
create: function() { create: function() {
var C = this; var C = this;
if (arguments.length>0) { this._initProperties(arguments); } if (arguments.length>0) { this._initProperties(arguments); }
@ -12174,10 +12196,6 @@ if (Ember.config.overrideClassMixin) {
CoreObject.ClassMixin = ClassMixin; CoreObject.ClassMixin = ClassMixin;
ClassMixin.apply(CoreObject); ClassMixin.apply(CoreObject);
/**
@class CoreObject
@namespace Ember
*/
Ember.CoreObject = CoreObject; Ember.CoreObject = CoreObject;
})(); })();
@ -12950,6 +12968,7 @@ function addObserverForContentKey(content, keyName, proxy, idx, loc) {
while(--loc>=idx) { while(--loc>=idx) {
var item = content.objectAt(loc); var item = content.objectAt(loc);
if (item) { if (item) {
Ember.addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); Ember.addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange');
Ember.addObserver(item, keyName, proxy, 'contentKeyDidChange'); Ember.addObserver(item, keyName, proxy, 'contentKeyDidChange');
@ -13033,7 +13052,7 @@ Ember.EachProxy = Ember.Object.extend({
for(key in keys) { for(key in keys) {
if (!keys.hasOwnProperty(key)) { continue; } if (!keys.hasOwnProperty(key)) { continue; }
if (lim>0) removeObserverForContentKey(content, key, this, idx, lim); if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); }
Ember.propertyWillChange(this, key); Ember.propertyWillChange(this, key);
} }
@ -13043,21 +13062,20 @@ Ember.EachProxy = Ember.Object.extend({
}, },
arrayDidChange: function(content, idx, removedCnt, addedCnt) { arrayDidChange: function(content, idx, removedCnt, addedCnt) {
var keys = this._keys, key, lim; var keys = this._keys, lim;
lim = addedCnt>0 ? idx+addedCnt : -1; lim = addedCnt>0 ? idx+addedCnt : -1;
Ember.beginPropertyChanges(this); Ember.changeProperties(function() {
for(var key in keys) {
for(key in keys) {
if (!keys.hasOwnProperty(key)) { continue; } if (!keys.hasOwnProperty(key)) { continue; }
if (lim>0) addObserverForContentKey(content, key, this, idx, lim); if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); }
Ember.propertyDidChange(this, key); Ember.propertyDidChange(this, key);
} }
Ember.propertyDidChange(this._content, '@each'); Ember.propertyDidChange(this._content, '@each');
Ember.endPropertyChanges(this); }, this);
}, },
// .......................................................... // ..........................................................
@ -13232,7 +13250,6 @@ if (ignore.length>0) {
@class NativeArray @class NativeArray
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.MutableArray @uses Ember.MutableArray
@uses Ember.Observable @uses Ember.Observable
@uses Ember.Copyable @uses Ember.Copyable
@ -13843,7 +13860,6 @@ var get = Ember.get;
@class ControllerMixin @class ControllerMixin
@namespace Ember @namespace Ember
@extends Ember.Mixin
*/ */
Ember.ControllerMixin = Ember.Mixin.create({ Ember.ControllerMixin = Ember.Mixin.create({
/* ducktype as a controller */ /* ducktype as a controller */
@ -13931,7 +13947,6 @@ var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach;
@class SortableMixin @class SortableMixin
@namespace Ember @namespace Ember
@extends Ember.Mixin
@uses Ember.MutableEnumerable @uses Ember.MutableEnumerable
*/ */
Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, { Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, {
@ -19034,7 +19049,7 @@ Ember.CollectionView.CONTAINER_MAP = {
(function() { (function() {
var set = Ember.set; var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
/** /**
@module ember @module ember
@ -19119,12 +19134,66 @@ var set = Ember.set;
@namespace Ember @namespace Ember
@extends Ember.View @extends Ember.View
*/ */
Ember.Component = Ember.View.extend({ Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
init: function() { init: function() {
this._super(); this._super();
set(this, 'context', this); set(this, 'context', this);
set(this, 'controller', this); set(this, 'controller', this);
set(this, 'templateData', {keywords: {}}); set(this, 'templateData', {keywords: {}});
},
targetObject: Ember.computed(function(key) {
var parentView = get(this, '_parentView');
return parentView ? get(parentView, 'controller') : null;
}).property('_parentView'),
/**
Sends an action to component's controller. A component inherits its
controller from the context in which it is used.
By default, calling `sendAction()` will send an action with the name
of the component's `action` property.
For example, if the component had a property `action` with the value
`"addItem"`, calling `sendAction()` would send the `addItem` action
to the component's controller.
If you provide an argument to `sendAction()`, that key will be used to look
up the action name.
For example, if the component had a property `playing` with the value
`didStartPlaying`, calling `sendAction('playing')` would send the
`didStartPlaying` action to the component's controller.
Whether or not you are using the default action or a named action, if
the action name is not defined on the component, calling `sendAction()`
does not have any effect.
For example, if you call `sendAction()` on a component that does not have
an `action` property defined, no action will be sent to the controller,
nor will an exception be raised.
@param [action] {String} the action to trigger
*/
sendAction: function(action) {
var actionName;
// Send the default action
if (action === undefined) {
actionName = get(this, 'action');
} else {
actionName = get(this, action);
}
// If no action name for that action could be found, just abort.
if (actionName === undefined) { return; }
this.triggerAction({
action: actionName
});
} }
}); });
@ -19228,7 +19297,7 @@ define("metamorph",
document = this.document, document = this.document,
// Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges
supportsRange = document && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, supportsRange = false,
// Internet Explorer prior to 9 does not allow setting innerHTML if the first element // 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 // is a "zero-scope" element. This problem can be worked around by making
@ -20211,7 +20280,7 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) {
view = data.view, view = data.view,
currentContext = (options.contexts && options.contexts[0]) || this, currentContext = (options.contexts && options.contexts[0]) || this,
normalized, normalized,
pathRoot, path, pathRoot, path, prefixPathForDependentKeys = '',
loc, hashOption; loc, hashOption;
@ -20260,8 +20329,11 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) {
view.registerObserver(pathRoot, path, bindView, bindView.rerender); view.registerObserver(pathRoot, path, bindView, bindView.rerender);
if(!Ember.isEmpty(path)) {
prefixPathForDependentKeys = path + '.';
}
for (var i=0, l=dependentKeys.length; i<l; i++) { for (var i=0, l=dependentKeys.length; i<l; i++) {
view.registerObserver(pathRoot, path + '.' + dependentKeys[i], bindView, bindView.rerender); view.registerObserver(pathRoot, prefixPathForDependentKeys + dependentKeys[i], bindView, bindView.rerender);
} }
} }
@ -20489,21 +20561,23 @@ var DOMManager = {
view.clearRenderedChildren(); view.clearRenderedChildren();
var buffer = view.renderToBuffer(); var buffer = view.renderToBuffer();
view.invokeRecursively(function(view) { // view.invokeRecursively(function(view) {
view.propertyWillChange('element'); // view.propertyWillChange('element');
}); // });
view.triggerRecursively('willInsertElement'); view.triggerRecursively('willInsertElement');
morph.replaceWith(buffer.string()); morph.replaceWith(buffer.string());
view.transitionTo('inDOM'); view.transitionTo('inDOM');
view.invokeRecursively(function(view) { // view.invokeRecursively(function(view) {
view.propertyDidChange('element'); // view.propertyDidChange('element');
}); // });
view.triggerRecursively('didInsertElement'); view.triggerRecursively('didInsertElement');
notifyMutationListeners(); notifyMutationListeners();
}); });
}, },
empty: function(view) { empty: function(view) {
@ -20518,7 +20592,6 @@ var DOMManager = {
/** /**
@class _Metamorph @class _Metamorph
@namespace Ember @namespace Ember
@extends Ember.Mixin
@private @private
*/ */
Ember._Metamorph = Ember.Mixin.create({ Ember._Metamorph = Ember.Mixin.create({
@ -22594,17 +22667,15 @@ Ember.Handlebars.registerHelper('each', function(path, options) {
Ember.TEMPLATES["my_cool_template"] = Ember.Handlebars.compile('<b>{{user}}</b>'); Ember.TEMPLATES["my_cool_template"] = Ember.Handlebars.compile('<b>{{user}}</b>');
``` ```
@deprecated
@method template @method template
@for Ember.Handlebars.helpers @for Ember.Handlebars.helpers
@param {String} templateName the template to render @param {String} templateName the template to render
*/ */
Ember.Handlebars.registerHelper('template', function(name, options) { Ember.Handlebars.registerHelper('template', function(name, options) {
var view = options.data.view,
template = view.templateForName(name);
return Ember.Handlebars.helpers.partial.apply(this, arguments);
template(this, { data: options.data });
}); });
})(); })();
@ -22655,7 +22726,6 @@ Ember.Handlebars.registerHelper('partial', function(name, options) {
deprecatedTemplate = !template && view.templateForName(name); deprecatedTemplate = !template && view.templateForName(name);
template = template || deprecatedTemplate; template = template || deprecatedTemplate;
template(this, { data: options.data }); template(this, { data: options.data });
@ -22737,6 +22807,7 @@ Ember.Handlebars.registerHelper('yield', function(options) {
var keywords = view._parentView.cloneKeywords(); var keywords = view._parentView.cloneKeywords();
currentView.appendChild(Ember.View, { currentView.appendChild(Ember.View, {
isVirtual: true,
tagName: '', tagName: '',
template: template, template: template,
context: get(view._parentView, 'context'), context: get(view._parentView, 'context'),
@ -22883,7 +22954,6 @@ var get = Ember.get, set = Ember.set;
@class TextSupport @class TextSupport
@namespace Ember @namespace Ember
@extends Ember.Mixin
@private @private
*/ */
Ember.TextSupport = Ember.Mixin.create({ Ember.TextSupport = Ember.Mixin.create({
@ -25091,7 +25161,7 @@ define("router",
} }
function isParam(object) { function isParam(object) {
return object && (typeof object === "string" || object instanceof String || !isNaN(object)); return (typeof object === "string" || object instanceof String || !isNaN(object));
} }
/** /**
@ -25566,8 +25636,7 @@ define("router",
handlerInfo = handlerInfos[index], handlerInfo = handlerInfos[index],
handler = handlerInfo.handler, handler = handlerInfo.handler,
handlerName = handlerInfo.name, handlerName = handlerInfo.name,
seq = transition.sequence, seq = transition.sequence;
errorAlreadyHandled = false;
if (index < matchPoint) { if (index < matchPoint) {
log(router, seq, handlerName + ": using context from already-active handler"); log(router, seq, handlerName + ": using context from already-active handler");
@ -25580,21 +25649,17 @@ define("router",
return RSVP.resolve().then(handleAbort) return RSVP.resolve().then(handleAbort)
.then(beforeModel) .then(beforeModel)
.then(null, handleError)
.then(handleAbort) .then(handleAbort)
.then(model) .then(model)
.then(null, handleError)
.then(handleAbort) .then(handleAbort)
.then(afterModel) .then(afterModel)
.then(null, handleError)
.then(handleAbort) .then(handleAbort)
.then(proceed); .then(proceed)
.then(null, handleError);
function handleAbort(result) { function handleAbort(result) {
if (transition.isAborted) { if (transition.isAborted) {
log(transition.router, transition.sequence, "detected abort."); log(transition.router, transition.sequence, "detected abort.");
errorAlreadyHandled = true;
return RSVP.reject(new Router.TransitionAborted()); return RSVP.reject(new Router.TransitionAborted());
} }
@ -25602,9 +25667,13 @@ define("router",
} }
function handleError(reason) { function handleError(reason) {
if (reason instanceof Router.TransitionAborted) {
// if the transition was aborted and *no additional* error was thrown,
// reject with the Router.TransitionAborted instance
return RSVP.reject(reason);
}
if (errorAlreadyHandled) { return RSVP.reject(reason); } // otherwise, we're here because of a different error
errorAlreadyHandled = true;
transition.abort(); transition.abort();
log(router, seq, handlerName + ": handling error: " + reason); log(router, seq, handlerName + ": handling error: " + reason);
@ -25869,9 +25938,8 @@ var get = Ember.get;
@submodule ember-routing @submodule ember-routing
*/ */
Ember.controllerFor = function(container, controllerName, context, lookupOptions) { Ember.controllerFor = function(container, controllerName, lookupOptions) {
return container.lookup('controller:' + controllerName, lookupOptions) || return container.lookup('controller:' + controllerName, lookupOptions);
Ember.generateController(container, controllerName, context);
}; };
/* /*
Generates a controller automatically if none was provided. Generates a controller automatically if none was provided.
@ -25885,14 +25953,12 @@ Ember.generateController = function(container, controllerName, context) {
if (context && Ember.isArray(context)) { if (context && Ember.isArray(context)) {
DefaultController = container.resolve('controller:array'); DefaultController = container.resolve('controller:array');
controller = DefaultController.extend({ controller = DefaultController.extend({
isGenerated: true, isGenerated: true
content: context
}); });
} else if (context) { } else if (context) {
DefaultController = container.resolve('controller:object'); DefaultController = container.resolve('controller:object');
controller = DefaultController.extend({ controller = DefaultController.extend({
isGenerated: true, isGenerated: true
content: context
}); });
} else { } else {
DefaultController = container.resolve('controller:basic'); DefaultController = container.resolve('controller:basic');
@ -26007,9 +26073,7 @@ Ember.Router = Ember.Object.extend({
}, },
handleURL: function(url) { handleURL: function(url) {
scheduleLoadingStateEntry(this); return doTransition(this, 'handleURL', [url]);
return this.router.handleURL(url).then(transitionCompleted);
}, },
transitionTo: function() { transitionTo: function() {
@ -26174,9 +26238,13 @@ function doTransition(router, method, args) {
} }
scheduleLoadingStateEntry(router);
var transitionPromise = router.router[method].apply(router.router, args); var transitionPromise = router.router[method].apply(router.router, args);
// Don't schedule loading state entry if user has already aborted the transition.
if (router.router.activeTransition) {
scheduleLoadingStateEntry(router);
}
transitionPromise.then(transitionCompleted); transitionPromise.then(transitionCompleted);
// We want to return the configurable promise object // We want to return the configurable promise object
@ -26466,7 +26534,11 @@ Ember.Route = Ember.Object.extend({
@method setup @method setup
*/ */
setup: function(context) { setup: function(context) {
var controller = this.controllerFor(this.controllerName || this.routeName, context); var controllerName = this.controllerName || this.routeName,
controller = this.controllerFor(controllerName, true);
if (!controller) {
controller = this.generateController(controllerName, context);
}
// Assign the route's controller so that it can more easily be // Assign the route's controller so that it can more easily be
// referenced in event handlers // referenced in event handlers
@ -26782,26 +26854,39 @@ Ember.Route = Ember.Object.extend({
}); });
``` ```
By default, the controller for `post` is the shared instance of
`App.PostController`.
@method controllerFor @method controllerFor
@param {String} name the name of the route @param {String} name the name of the route
@param {Object} model the model associated with the route (optional)
@return {Ember.Controller} @return {Ember.Controller}
*/ */
controllerFor: function(name, model) { controllerFor: function(name, _skipAssert) {
var container = this.router.container, var container = this.router.container,
controller = container.lookup('controller:' + name); controller = container.lookup('controller:' + name);
if (!controller) { // NOTE: We're specifically checking that skipAssert is true, because according
// to the old API the second parameter was model. We do not want people who
// passed a model to skip the assertion.
return controller;
},
/**
Generates a controller for a route.
If the optional model is passed then the controller type is determined automatically,
e.g., an ArrayController for arrays.
@method generateController
@param {String} name the name of the controller
@param {Object} model the model to infer the type of the controller (optional)
*/
generateController: function(name, model) {
var container = this.router.container;
model = model || this.modelFor(name); model = model || this.modelFor(name);
controller = Ember.generateController(container, name, model); return Ember.generateController(container, name, model);
}
return controller;
}, },
/** /**
@ -26903,6 +26988,8 @@ Ember.Route = Ember.Object.extend({
render: function(name, options) { render: function(name, options) {
var namePassed = !!name;
if (typeof name === 'object' && !options) { if (typeof name === 'object' && !options) {
options = name; options = name;
name = this.routeName; name = this.routeName;
@ -26915,6 +27002,7 @@ Ember.Route = Ember.Object.extend({
template = container.lookup('template:' + name); template = container.lookup('template:' + name);
if (!view && !template) { if (!view && !template) {
if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) { if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) {
Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name });
} }
@ -27005,16 +27093,15 @@ function parentRoute(route) {
} }
} }
function parentTemplate(route, isRecursive) { function parentTemplate(route) {
var parent = parentRoute(route), template; var parent = parentRoute(route), template;
if (!parent) { return; } if (!parent) { return; }
if (template = parent.lastRenderedTemplate) { if (template = parent.lastRenderedTemplate) {
return template; return template;
} else { } else {
return parentTemplate(parent, true); return parentTemplate(parent);
} }
} }
@ -27233,6 +27320,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
Determines whether the `LinkView` will trigger routing via Determines whether the `LinkView` will trigger routing via
the `replaceWith` routing strategy. the `replaceWith` routing strategy.
@property replace
@type Boolean @type Boolean
@default false @default false
**/ **/
@ -27286,15 +27374,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
} }
var observer = function(object, path) { var observer = function(object, path) {
var notify = true, i;
for(i=0; i < paths.length; i++) {
if (!get(this, paths[i])) {
notify = false;
}
}
if (notify) {
this.notifyPropertyChange('routeArgs'); this.notifyPropertyChange('routeArgs');
}
}; };
for(i=0; i < length; i++) { for(i=0; i < length; i++) {
@ -27385,7 +27465,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
if (get(this, '_isDisabled')) { return false; } if (get(this, '_isDisabled')) { return false; }
if (get(this, 'loading')) { if (get(this, 'loading')) {
Ember.Logger.warn("This linkTo's parameters are either not yet loaded or point to an invalid route."); Ember.Logger.warn("This linkTo is in an inactive loading state because at least one of its parameters' presently has a null/undefined value, or the provided route name is invalid.");
return false; return false;
} }
@ -27819,7 +27899,8 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
controller = container.lookup('controller:' + controllerName, lookupOptions); controller = container.lookup('controller:' + controllerName, lookupOptions);
} else { } else {
controller = Ember.controllerFor(container, name, context, lookupOptions); controller = container.lookup('controller:' + name, lookupOptions) ||
Ember.generateController(container, name, context);
} }
if (controller && context) { if (controller && context) {
@ -30078,8 +30159,7 @@ Ember.ControllerMixin.reopen({
controllerFor: function(controllerName) { controllerFor: function(controllerName) {
var container = get(this, 'container'); return Ember.controllerFor(get(this, 'container'), controllerName);
return container.lookup('controller:' + controllerName);
}, },
controllers: Ember.computed(function() { controllers: Ember.computed(function() {