Update ember and ember-data

This commit is contained in:
Piotr Sarnacki 2013-03-08 16:53:59 +01:00
parent 017b69d10a
commit 01cdbc1d89
2 changed files with 570 additions and 179 deletions

View File

@ -1,10 +1,10 @@
// Last commit: 6d9eeaf (2013-02-20 22:27:54 +0100)
// Last commit: 0c516e4 (2013-03-08 15:59:48 +0100)
(function() {
window.DS = Ember.Namespace.create({
// this one goes to 11
CURRENT_API_REVISION: 11
// this one goes past 11
CURRENT_API_REVISION: 12
});
})();
@ -849,7 +849,7 @@ DS._Mappable = Ember.Mixin.create({
instanceMap.set(transformedKey, newValue);
}
},
}
});
@ -2271,7 +2271,6 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
}
var content = get(array, 'content');
var alreadyInArray = content.indexOf(clientId) !== -1;
var recordArrays = this.recordArraysForClientId(clientId);
var reference = this.referenceForClientId(clientId);
@ -2416,7 +2415,6 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
if (prematerialized && prematerialized.id) {
id = prematerialized.id;
} else if (id === undefined) {
var adapter = this.adapterForType(type);
id = this.preprocessData(type, data);
}
@ -2495,6 +2493,8 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
clientIds = typeMap.clientIds,
cidToData = this.clientIdToData;
Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToClientIdMap[id]);
var clientId = ++this.clientIdCounter;
cidToData[clientId] = data;
@ -2521,7 +2521,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
this.recordCache[clientId] = record = type._create({
store: this,
clientId: clientId,
clientId: clientId
});
set(record, 'id', id);
@ -3836,6 +3836,7 @@ var storeAlias = function(methodName) {
args = [].slice.call(arguments);
args.unshift(this);
Ember.assert("Your application does not have a 'Store' property defined. Attempts to call '" + methodName + "' on model classes will fail. Please provide one as with 'YourAppName.Store = DS.Store.extend()'", !!store);
return store[methodName].apply(store, args);
};
};
@ -3844,6 +3845,7 @@ DS.Model.reopenClass({
isLoaded: storeAlias('recordIsLoaded'),
find: storeAlias('find'),
all: storeAlias('all'),
query: storeAlias('findQuery'),
filter: storeAlias('filter'),
_create: DS.Model.create,
@ -4193,7 +4195,6 @@ DS.Model.reopenClass({
App.Blog = DS.Model.extend({
users: DS.hasMany(App.User),
owner: DS.belongsTo(App.User),
posts: DS.hasMany(App.Post)
});
@ -4269,6 +4270,51 @@ DS.Model.reopenClass({
return names;
}),
/**
An array of types directly related to a model. Each type will be
included once, regardless of the number of relationships it has with
the model.
For example, given a model with this definition:
App.Blog = DS.Model.extend({
users: DS.hasMany(App.User),
owner: DS.belongsTo(App.User),
posts: DS.hasMany(App.Post)
});
This property would contain the following:
var relatedTypes = Ember.get(App.Blog, 'relatedTypes');
//=> [ App.User, App.Post ]
@type Ember.Array
@readOnly
*/
relatedTypes: Ember.computed(function() {
var type,
types = Ember.A([]);
// Loop through each computed property on the class,
// and create an array of the unique types involved
// in relationships
this.eachComputedProperty(function(name, meta) {
if (meta.isRelationship) {
type = meta.type;
if (typeof type === 'string') {
type = get(this, type, false) || get(Ember.lookup, type);
}
if (!types.contains(type)) {
types.push(type);
}
}
});
return types;
}),
/**
A map whose keys are the relationships of a model and whose values are
relationship descriptors.
@ -4370,6 +4416,21 @@ DS.Model.reopenClass({
get(this, 'relationshipsByName').forEach(function(name, relationship) {
callback.call(binding, name, relationship);
});
},
/**
Given a callback, iterates over each of the types related to a model,
invoking the callback with the related type's class. Each type will be
returned just once, regardless of how many different relationships it has
with a model.
@param {Function} callback the callback to invoke
@param {any} binding the value to which the callback's `this` should be bound
*/
eachRelatedType: function(callback, binding) {
get(this, 'relatedTypes').forEach(function(type) {
callback.call(binding, type);
});
}
});
@ -5635,7 +5696,7 @@ DS.Serializer = Ember.Object.extend({
primaryKey: function(type) {
// If the type is `BlogPost`, this will return
// `blog_post_id`.
var typeString = type.toString.split(".")[1].underscore();
var typeString = type.toString().split(".")[1].underscore();
return typeString + "_id";
}
});
@ -6310,6 +6371,13 @@ DS.JSONSerializer = DS.Serializer.extend({
if (sideloadAs) {
this.sideloadMapping.set(sideloadAs, type);
// Set a flag indicating that mappings may need to be normalized
// (i.e. converted from strings -> types) before sideloading.
// We can't do this conversion immediately here, because `configure`
// may be called before certain types have been defined.
this.sideloadMapping.normalized = false;
delete configuration.sideloadAs;
}
@ -6471,47 +6539,88 @@ DS.JSONSerializer = DS.Serializer.extend({
}
},
sideload: function(loader, type, json, root) {
var sideloadedType, mappings, loaded = {};
/**
@private
loaded[root] = true;
Iterates over the `json` payload and attempts to load any data
included alongside `root`.
The keys expected for sideloaded data are based upon the types related
to the root model. Recursion is used to ensure that types related to
related types can be loaded as well. Any custom keys specified by
`sideloadAs` mappings will also be respected.
@param {DS.Store subclass} loader
@param {DS.Model subclass} type
@param {Object} json
@param {String} root
*/
sideload: function(loader, type, json, root) {
var sideloadedType;
this.normalizeSideloadMappings();
this.configureSideloadMappingForType(type);
for (var prop in json) {
if (!json.hasOwnProperty(prop)) { continue; }
if (prop === root) { continue; }
if (prop === this.configOption(type, 'meta')) { continue; }
sideloadedType = type.typeForRelationship(prop);
if (!sideloadedType) {
sideloadedType = this.sideloadMapping.get(prop);
if (typeof sideloadedType === 'string') {
sideloadedType = get(Ember.lookup, sideloadedType);
}
Ember.assert("Your server returned a hash with the key " + prop + " but you have no mapping for it", !!sideloadedType);
if (!json.hasOwnProperty(prop) ||
prop === root ||
prop === this.configOption(type, 'meta')) {
continue;
}
this.sideloadRelationships(loader, sideloadedType, json, prop, loaded);
sideloadedType = this.sideloadMapping.get(prop);
Ember.assert("Your server returned a hash with the key " + prop +
" but you have no mapping for it",
!!sideloadedType);
this.loadValue(loader, sideloadedType, json[prop]);
}
},
sideloadRelationships: function(loader, type, json, prop, loaded) {
if (loaded[prop]) { return; }
loaded[prop] = true;
/**
@private
get(type, 'relationshipsByName').forEach(function(key, meta) {
key = meta.key || key;
if (meta.kind === 'belongsTo') {
key = this.pluralize(key);
}
if (json[key]) {
this.sideloadRelationships(loader, meta.type, json, key, loaded);
Iterates over all the `sideloadAs` mappings and converts any that are
strings to their equivalent types.
This is an optimization used to avoid performing lookups for every
call to `sideload`.
*/
normalizeSideloadMappings: function() {
if (! this.sideloadMapping.normalized) {
this.sideloadMapping.forEach(function(key, value) {
if (typeof value === 'string') {
this.sideloadMapping.set(key, get(Ember.lookup, value));
}
}, this);
this.sideloadMapping.normalized = true;
}
},
/**
@private
Configures possible sideload mappings for the types related to a
particular model. This recursive method ensures that sideloading
works for related models as well.
@param {DS.Model subclass} type
@param {Ember.A} configured an array of types that have already been configured
*/
configureSideloadMappingForType: function(type, configured) {
if (!configured) {configured = Ember.A([]);}
configured.pushObject(type);
type.eachRelatedType(function(relatedType) {
if (!configured.contains(relatedType)) {
var root = this.sideloadMappingForType(relatedType);
if (!root) {
root = this.defaultSideloadRootForType(relatedType);
this.sideloadMapping.set(root, relatedType);
}
this.configureSideloadMappingForType(relatedType, configured);
}
}, this);
this.loadValue(loader, type, json[prop]);
},
loadValue: function(loader, type, value) {
@ -6551,6 +6660,18 @@ DS.JSONSerializer = DS.Serializer.extend({
}
},
/**
@private
Determines the singular root name for a particular type.
This is an underscored, lowercase version of the model name.
For example, the type `App.UserGroup` will have the root
`user_group`.
@param {DS.Model subclass} type
@returns {String} name of the root element
*/
rootForType: function(type) {
var typeString = type.toString();
@ -6560,6 +6681,34 @@ DS.JSONSerializer = DS.Serializer.extend({
var parts = typeString.split(".");
var name = parts[parts.length - 1];
return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
},
/**
@private
Determines the root name mapped to a particular sideloaded type.
@param {DS.Model subclass} type
@returns {String} name of the root element, if any is registered
*/
sideloadMappingForType: function(type) {
this.sideloadMapping.forEach(function(key, value) {
if (type === value) {
return key;
}
});
},
/**
@private
The default root name for a particular sideloaded type.
@param {DS.Model subclass} type
@returns {String} name of the root element
*/
defaultSideloadRootForType: function(type) {
return this.pluralize(this.rootForType(type));
}
});
@ -6731,7 +6880,6 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
if (payload) {
var loader = DS.loaderFor(store);
var serializer = get(this, 'serializer');
loader.load = function(type, data, prematerialized) {
store.updateId(record, data);
@ -7293,6 +7441,27 @@ DS.FixtureSerializer = DS.Serializer.extend({
return value;
},
addId: function(data, key, id) {
data[key] = id;
},
addAttribute: function(hash, key, value) {
hash[key] = value;
},
addBelongsTo: function(hash, record, key, relationship) {
var id = get(record, relationship.key+'.id');
if (!Ember.isNone(id)) { hash[key] = id; }
},
addHasMany: function(hash, record, key, relationship) {
var ids = get(record, relationship.key).map(function(item) {
return item.get('id');
});
hash[relationship.key] = ids;
},
/**
@private
@ -7356,7 +7525,7 @@ DS.FixtureSerializer = DS.Serializer.extend({
(function() {
var get = Ember.get;
var get = Ember.get, fmt = Ember.String.fmt;
/**
`DS.FixtureAdapter` is an adapter that loads records from memory.
@ -7384,7 +7553,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
var fixtures = Ember.A(type.FIXTURES);
return fixtures.map(function(fixture){
if(!fixture.id){
throw new Error('the id property must be defined for fixture %@'.fmt(fixture));
throw new Error(fmt('the id property must be defined for fixture %@', [fixture]));
}
fixture.id = fixture.id + '';
return fixture;
@ -7397,7 +7566,19 @@ DS.FixtureAdapter = DS.Adapter.extend({
Implement this method in order to query fixtures data
*/
queryFixtures: function(fixtures, query, type) {
return fixtures;
Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.');
},
updateFixtures: function(type, fixture) {
if(!type.FIXTURES) {
type.FIXTURES = [];
}
var fixtures = type.FIXTURES;
this.deleteLoadedFixture(type, fixture);
fixtures.push(fixture);
},
/*
@ -7418,7 +7599,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
var fixtures = this.fixturesForType(type),
fixture;
Ember.assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
Ember.warn("Unable to find fixtures for model type " + type.toString(), fixtures);
if (fixtures) {
fixture = Ember.A(fixtures).findProperty('id', id);
@ -7476,7 +7657,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
createRecord: function(store, type, record) {
var fixture = this.mockJSON(type, record);
fixture.id = this.generateIdForRecord(store, record);
this.updateFixtures(type, fixture);
this.simulateRemoteCall(function() {
this.didCreateRecord(store, type, record, fixture);
@ -7486,12 +7667,18 @@ DS.FixtureAdapter = DS.Adapter.extend({
updateRecord: function(store, type, record) {
var fixture = this.mockJSON(type, record);
this.updateFixtures(type, fixture);
this.simulateRemoteCall(function() {
this.didUpdateRecord(store, type, record, fixture);
}, this);
},
deleteRecord: function(store, type, record) {
var fixture = this.mockJSON(type, record);
this.deleteLoadedFixture(type, fixture);
this.simulateRemoteCall(function() {
this.didDeleteRecord(store, type, record);
}, this);
@ -7500,15 +7687,44 @@ DS.FixtureAdapter = DS.Adapter.extend({
/*
@private
*/
simulateRemoteCall: function(callback, context) {
function response() {
Ember.run(context, callback);
}
deleteLoadedFixture: function(type, record) {
var id = this.extractId(type, record);
var existingFixture = this.findExistingFixture(type, record);
if(existingFixture) {
var index = type.FIXTURES.indexOf(existingFixture);
type.FIXTURES.splice(index, 1);
return true;
}
},
findExistingFixture: function(type, record) {
var fixtures = this.fixturesForType(type);
var id = this.extractId(type, record);
return this.findFixtureById(fixtures, id);
},
findFixtureById: function(fixtures, id) {
var adapter = this;
return Ember.A(fixtures).find(function(r) {
if(''+get(r, 'id') === ''+id) {
return true;
} else {
return false;
}
});
},
simulateRemoteCall: function(callback, context) {
if (get(this, 'simulateRemoteResponse')) {
setTimeout(response, get(this, 'latency'));
// Schedule with setTimeout
Ember.run.later(context, callback, get(this, 'latency'));
} else {
response();
// Asynchronous, but at the of the runloop with zero latency
Ember.run.once(context, callback);
}
}
});
@ -7919,6 +8135,168 @@ DS.RESTAdapter = DS.Adapter.extend({
(function() {
var camelize = Ember.String.camelize,
get = Ember.get,
registeredTransforms;
var passthruTransform = {
serialize: function(value) { return value; },
deserialize: function(value) { return value; }
};
var defaultTransforms = {
string: passthruTransform,
boolean: passthruTransform,
number: passthruTransform
};
function camelizeKeys(json) {
var value;
for (var prop in json) {
value = json[prop];
delete json[prop];
json[camelize(prop)] = value;
}
}
function munge(json, callback) {
callback(json);
}
function applyTransforms(json, type, transformType) {
var transforms = registeredTransforms[transformType];
Ember.assert("You are trying to apply the '" + transformType + "' transforms, but you didn't register any transforms with that name", transforms);
get(type, 'attributes').forEach(function(name, attribute) {
var attributeType = attribute.type,
value = json[name];
var transform = transforms[attributeType] || defaultTransforms[attributeType];
Ember.assert("Your model specified the '" + attributeType + "' type for the '" + name + "' attribute, but no transform for that type was registered", transform);
json[name] = transform.deserialize(value);
});
}
function ObjectProcessor(json, type, store) {
this.json = json;
this.type = type;
this.store = store;
}
ObjectProcessor.prototype = {
load: function() {
this.store.load(this.type, {}, this.json);
},
camelizeKeys: function() {
camelizeKeys(this.json);
return this;
},
munge: function(callback) {
munge(this.json, callback);
return this;
},
applyTransforms: function(transformType) {
applyTransforms(this.json, this.type, transformType);
return this;
}
};
function processorFactory(store, type) {
return function(json) {
return new ObjectProcessor(json, type, store);
};
}
function ArrayProcessor(json, type, array, store) {
this.json = json;
this.type = type;
this.array = array;
this.store = store;
}
ArrayProcessor.prototype = {
load: function() {
var store = this.store,
type = this.type;
var references = this.json.map(function(object) {
return store.load(type, {}, object);
});
this.array.load(references);
},
camelizeKeys: function() {
this.json.forEach(camelizeKeys);
return this;
},
munge: function(callback) {
this.json.forEach(function(object) {
munge(object, callback);
});
return this;
},
applyTransforms: function(transformType) {
var type = this.type;
this.json.forEach(function(object) {
applyTransforms(object, type, transformType);
});
return this;
}
};
function arrayProcessorFactory(store, type, array) {
return function(json) {
return new ArrayProcessor(json, type, array, store);
};
}
DS.BasicAdapter = DS.Adapter.extend({
find: function(store, type, id) {
var sync = type.sync;
Ember.assert("You are trying to use the BasicAdapter to find id '" + id + "' of " + type + " but " + type + ".sync was not found", sync);
Ember.assert("The sync code on " + type + " does not implement find(), but you are trying to find id '" + id + "'.", sync.find);
sync.find(id, processorFactory(store, type));
},
findQuery: function(store, type, query, recordArray) {
var sync = type.sync;
Ember.assert("You are trying to use the BasicAdapter to query " + type + " but " + type + ".sync was not found", sync);
Ember.assert("The sync code on " + type + " does not implement query(), but you are trying to query " + type + ".", sync.query);
sync.query(query, arrayProcessorFactory(store, type, recordArray));
}
});
DS.registerTransforms = function(kind, object) {
registeredTransforms[kind] = object;
};
DS.clearTransforms = function() {
registeredTransforms = {};
};
DS.clearTransforms();
})();
(function() {
})();

View File

@ -1,5 +1,5 @@
// Version: v1.0.0-rc.1-110-g1446ad7
// Last commit: 1446ad7 (2013-03-07 19:07:55 +0100)
// Version: v1.0.0-rc.1-134-gf13ef4c
// Last commit: f13ef4c (2013-03-08 16:46:38 +0100)
(function() {
@ -150,8 +150,8 @@ Ember.deprecateFunc = function(message, func) {
})();
// Version: v1.0.0-rc.1-110-g1446ad7
// Last commit: 1446ad7 (2013-03-07 19:07:55 +0100)
// Version: v1.0.0-rc.1-134-gf13ef4c
// Last commit: f13ef4c (2013-03-08 16:46:38 +0100)
(function() {
@ -418,6 +418,58 @@ Ember.merge = function(original, updates) {
}
};
/**
Returns true if the passed value is null or undefined. This avoids errors
from JSLint complaining about use of ==, which can be technically
confusing.
```javascript
Ember.isNone(); // true
Ember.isNone(null); // true
Ember.isNone(undefined); // true
Ember.isNone(''); // false
Ember.isNone([]); // false
Ember.isNone(function(){}); // false
```
@method isNone
@for Ember
@param {Object} obj Value to test
@return {Boolean}
*/
Ember.isNone = function(obj) {
return obj === null || obj === undefined;
};
Ember.none = Ember.deprecateFunc("Ember.none is deprecated. Please use Ember.isNone instead.", Ember.isNone);
/**
Verifies that a value is `null` or an empty string, empty array,
or empty function.
Constrains the rules on `Ember.isNone` by returning false for empty
string and empty arrays.
```javascript
Ember.isEmpty(); // true
Ember.isEmpty(null); // true
Ember.isEmpty(undefined); // true
Ember.isEmpty(''); // true
Ember.isEmpty([]); // true
Ember.isEmpty('Adam Hawkins'); // false
Ember.isEmpty([0,1,2]); // false
```
@method isEmpty
@for Ember
@param {Object} obj Value to test
@return {Boolean}
*/
Ember.isEmpty = function(obj) {
return obj === null || obj === undefined || (obj.length === 0 && typeof obj !== 'function') || (typeof obj === 'object' && Ember.get(obj, 'length') === 0);
};
Ember.empty = Ember.deprecateFunc("Ember.empty is deprecated. Please use Ember.isEmpty instead.", Ember.isEmpty) ;
})();
@ -3615,6 +3667,18 @@ Ember.computed.not = function(dependentKey) {
});
};
/**
@method computed.none
@for Ember
@param {String} dependentKey
*/
Ember.computed.none = function(dependentKey) {
return Ember.computed(dependentKey, function(key) {
var val = get(this, dependentKey);
return Ember.isNone(val);
});
};
/**
@method computed.empty
@for Ember
@ -3623,7 +3687,7 @@ Ember.computed.not = function(dependentKey) {
Ember.computed.empty = function(dependentKey) {
return Ember.computed(dependentKey, function(key) {
var val = get(this, dependentKey);
return val === undefined || val === null || val === '' || (Ember.isArray(val) && get(val, 'length') === 0);
return Ember.isEmpty(val);
});
};
@ -4273,7 +4337,7 @@ var run = Ember.run;
/**
Begins a new RunLoop. Any deferred actions invoked after the begin will
be buffered until you invoke a matching call to `Ember.run.end()`. This is
an lower-level way to use a RunLoop instead of using `Ember.run()`.
a lower-level way to use a RunLoop instead of using `Ember.run()`.
```javascript
Ember.run.begin();
@ -4319,9 +4383,9 @@ Ember.run.end = function() {
@property queues
@type Array
@default ['sync', 'actions', 'destroy', 'timers']
@default ['sync', 'actions', 'destroy']
*/
Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
Ember.run.queues = ['sync', 'actions', 'destroy'];
/**
Adds the passed target/method and any optional arguments to the named
@ -4334,19 +4398,19 @@ Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
the `run.queues` property.
```javascript
Ember.run.schedule('timers', this, function(){
// this will be executed at the end of the RunLoop, when timers are run
console.log("scheduled on timers queue");
Ember.run.schedule('sync', this, function(){
// this will be executed in the first RunLoop queue, when bindings are synced
console.log("scheduled on sync queue");
});
Ember.run.schedule('sync', this, function(){
// this will be executed at the end of the RunLoop, when bindings are synced
console.log("scheduled on sync queue");
Ember.run.schedule('actions', this, function(){
// this will be executed in the 'actions' queue, after bindings have synced.
console.log("scheduled on actions queue");
});
// Note the functions will be run in order based on the run queues order. Output would be:
// scheduled on sync queue
// scheduled on timers queue
// scheduled on actions queue
```
@method schedule
@ -4372,7 +4436,7 @@ function autorun() {
// Used by global test teardown
Ember.run.hasScheduledTimers = function() {
return !!(scheduledAutorun || scheduledLater || scheduledNext);
return !!(scheduledAutorun || scheduledLater);
};
// Used by global test teardown
@ -4385,10 +4449,6 @@ Ember.run.cancelTimers = function () {
clearTimeout(scheduledLater);
scheduledLater = null;
}
if (scheduledNext) {
clearTimeout(scheduledNext);
scheduledNext = null;
}
timers = {};
};
@ -4423,7 +4483,8 @@ Ember.run.autorun = function() {
bindings in the application to sync.
You should call this method anytime you need any changed state to propagate
throughout the app immediately without repainting the UI.
throughout the app immediately without repainting the UI (which happens
in the later 'render' queue added by the `ember-views` package).
```javascript
Ember.run.sync();
@ -4443,25 +4504,30 @@ Ember.run.sync = function() {
var timers = {}; // active timers...
var scheduledLater;
var scheduledLater, scheduledLaterExpires;
function invokeLaterTimers() {
scheduledLater = null;
var now = (+ new Date()), earliest = -1;
for (var key in timers) {
if (!timers.hasOwnProperty(key)) { continue; }
var timer = timers[key];
if (timer && timer.expires) {
if (now >= timer.expires) {
delete timers[key];
invoke(timer.target, timer.method, timer.args, 2);
} else {
if (earliest<0 || (timer.expires < earliest)) earliest=timer.expires;
run(function() {
var now = (+ new Date()), earliest = -1;
for (var key in timers) {
if (!timers.hasOwnProperty(key)) { continue; }
var timer = timers[key];
if (timer && timer.expires) {
if (now >= timer.expires) {
delete timers[key];
invoke(timer.target, timer.method, timer.args, 2);
} else {
if (earliest < 0 || (timer.expires < earliest)) { earliest = timer.expires; }
}
}
}
}
// schedule next timeout to fire...
if (earliest > 0) { scheduledLater = setTimeout(invokeLaterTimers, earliest-(+ new Date())); }
// schedule next timeout to fire when the earliest timer expires
if (earliest > 0) {
scheduledLater = setTimeout(invokeLaterTimers, earliest - now);
scheduledLaterExpires = earliest;
}
});
}
/**
@ -4509,7 +4575,19 @@ Ember.run.later = function(target, method) {
timer = { target: target, method: method, expires: expires, args: args };
guid = Ember.guidFor(timer);
timers[guid] = timer;
run.once(timers, invokeLaterTimers);
if(scheduledLater && expires < scheduledLaterExpires) {
// Cancel later timer (then reschedule earlier timer below)
clearTimeout(scheduledLater);
scheduledLater = null;
}
if (!scheduledLater) {
// Schedule later timers to be run.
scheduledLater = setTimeout(invokeLaterTimers, wait);
scheduledLaterExpires = expires;
}
return guid;
};
@ -4596,22 +4674,9 @@ Ember.run.scheduleOnce = function(queue, target, method, args) {
return scheduleOnce(queue, target, method, slice.call(arguments, 3));
};
var scheduledNext;
function invokeNextTimers() {
scheduledNext = null;
for(var key in timers) {
if (!timers.hasOwnProperty(key)) { continue; }
var timer = timers[key];
if (timer.next) {
delete timers[key];
invoke(timer.target, timer.method, timer.args, 2);
}
}
}
/**
Schedules an item to run after control has been returned to the system.
This is often equivalent to calling `setTimeout(function() {}, 1)`.
This is equivalent to calling `Ember.run.later` with a wait time of 1ms.
```javascript
Ember.run.next(myContext, function(){
@ -4627,20 +4692,10 @@ function invokeNextTimers() {
@param {Object} [args*] Optional arguments to pass to the timeout.
@return {Object} timer
*/
Ember.run.next = function(target, method) {
var guid,
timer = {
target: target,
method: method,
args: slice.call(arguments),
next: true
};
guid = Ember.guidFor(timer);
timers[guid] = timer;
if (!scheduledNext) { scheduledNext = setTimeout(invokeNextTimers, 1); }
return guid;
Ember.run.next = function() {
var args = slice.call(arguments);
args.push(1); // 1 millisecond wait
return run.later.apply(this, args);
};
/**
@ -6465,57 +6520,6 @@ Ember.typeOf = function(item) {
return ret;
};
/**
Returns true if the passed value is null or undefined. This avoids errors
from JSLint complaining about use of ==, which can be technically
confusing.
```javascript
Ember.isNone(); // true
Ember.isNone(null); // true
Ember.isNone(undefined); // true
Ember.isNone(''); // false
Ember.isNone([]); // false
Ember.isNone(function(){}); // false
```
@method isNone
@for Ember
@param {Object} obj Value to test
@return {Boolean}
*/
Ember.isNone = function(obj) {
return obj === null || obj === undefined;
};
Ember.none = Ember.deprecateFunc("Ember.none is deprecated. Please use Ember.isNone instead.", Ember.isNone);
/**
Verifies that a value is `null` or an empty string, empty array,
or empty function.
Constrains the rules on `Ember.isNone` by returning false for empty
string and empty arrays.
```javascript
Ember.isEmpty(); // true
Ember.isEmpty(null); // true
Ember.isEmpty(undefined); // true
Ember.isEmpty(''); // true
Ember.isEmpty([]); // true
Ember.isEmpty('Adam Hawkins'); // false
Ember.isEmpty([0,1,2]); // false
```
@method isEmpty
@for Ember
@param {Object} obj Value to test
@return {Boolean}
*/
Ember.isEmpty = function(obj) {
return obj === null || obj === undefined || (obj.length === 0 && typeof obj !== 'function') || (typeof obj === 'object' && Ember.get(obj, 'length') === 0);
};
Ember.empty = Ember.deprecateFunc("Ember.empty is deprecated. Please use Ember.isEmpty instead.", Ember.isEmpty) ;
/**
This will compare two javascript values of possibly different types.
It will tell you which one is greater than the other by returning:
@ -10555,7 +10559,7 @@ Ember.CoreObject = CoreObject;
@submodule ember-runtime
*/
var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, none = Ember.isNone;
var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, none = Ember.isNone, fmt = Ember.String.fmt;
/**
An unordered collection of objects.
@ -10997,7 +11001,7 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
for(idx = 0; idx < len; idx++) {
array[idx] = this[idx];
}
return "Ember.Set<%@>".fmt(array.join(','));
return fmt("Ember.Set<%@>", [array.join(',')]);
}
});
@ -15097,6 +15101,10 @@ Ember.View = Ember.CoreView.extend(
Appends the view's element to the document body. If the view does
not have an HTML representation yet, `createElement()` will be called
automatically.
If your application uses the `rootElement` property, you must append
the view within that element. Rendering views outside of the `rootElement`
is not supported.
Note that this method just schedules the view to be appended; the DOM
element will not be appended to the document body until all bindings have
@ -16005,6 +16013,9 @@ Ember.View.applyAttributeBindings = function(elem, name, value) {
elem.attr(name, value);
}
} else if (name === 'value' || type === 'boolean') {
// We can't set properties to undefined
if (value === undefined) { value = null; }
if (value !== elem.prop(name)) {
// value and booleans should always be properties
elem.prop(name, value);
@ -17616,7 +17627,7 @@ if(!Handlebars && typeof require === 'function') {
Handlebars = require('handlebars');
}
Ember.assert("Ember Handlebars requires Handlebars 1.0.rc.3 or greater", Handlebars && Handlebars.VERSION.match(/^1\.0(\.0)?(\.|-)rc\.[23456789]+/));
Ember.assert("Ember Handlebars requires Handlebars 1.0.0-rc.3 or greater", Handlebars && Handlebars.VERSION.match(/^1\.0\.[0-9](\.rc\.[23456789]+)?/));
/**
Prepares the Handlebars templating library for use inside Ember's view
@ -23369,7 +23380,7 @@ Ember.Route = Ember.Object.extend({
namespace = this.router.namespace,
modelClass = namespace[className];
Ember.assert("You used the dynamic segment " + name + "_id in your router, but " + namespace + "." + className + " did not exist and you did not override your state's `model` hook.", modelClass);
Ember.assert("You used the dynamic segment " + name + "_id in your router, but " + namespace + "." + className + " did not exist and you did not override your route's `model` hook.", modelClass);
return modelClass.find(value);
},
@ -24736,7 +24747,7 @@ Ember.Location.registerImplementation('hash', Ember.HashLocation);
*/
var get = Ember.get, set = Ember.set;
var popstateReady = false;
var popstateFired = false;
/**
Ember.HistoryLocation implements the location API using the browser's
@ -24750,6 +24761,7 @@ Ember.HistoryLocation = Ember.Object.extend({
init: function() {
set(this, 'location', get(this, 'location') || window.location);
this._initialUrl = this.getURL();
this.initState();
},
@ -24802,7 +24814,6 @@ Ember.HistoryLocation = Ember.Object.extend({
path = this.formatURL(path);
if (this.getState() && this.getState().path !== path) {
popstateReady = true;
this.pushState(path);
}
},
@ -24820,7 +24831,6 @@ Ember.HistoryLocation = Ember.Object.extend({
path = this.formatURL(path);
if (this.getState() && this.getState().path !== path) {
popstateReady = true;
this.replaceState(path);
}
},
@ -24874,8 +24884,10 @@ Ember.HistoryLocation = Ember.Object.extend({
self = this;
Ember.$(window).bind('popstate.ember-location-'+guid, function(e) {
if(!popstateReady) {
return;
// Ignore initial page load popstate event in Chrome
if(!popstateFired) {
popstateFired = true;
if (self.getURL() === self._initialUrl) { return; }
}
callback(self.getURL());
});
@ -25276,6 +25288,7 @@ var Application = Ember.Application = Ember.Namespace.extend({
}
if ( Ember.LOG_VERSION ) {
Ember.LOG_VERSION = false; // we only need to see this once per Application#init
Ember.debug('-------------------------------');
Ember.debug('Ember.VERSION : ' + Ember.VERSION);
Ember.debug('Handlebars.VERSION : ' + Ember.Handlebars.VERSION);
@ -25722,11 +25735,11 @@ function normalize(fullName) {
var result = name;
if (result.indexOf('.') > -1) {
result = result.replace(/\.(.)/g, function(m) { return m[1].toUpperCase(); });
result = result.replace(/\.(.)/g, function(m) { return m.charAt(1).toUpperCase(); });
}
if (name.indexOf('_') > -1) {
result = result.replace(/_(.)/g, function(m) { return m[1].toUpperCase(); });
result = result.replace(/_(.)/g, function(m) { return m.charAt(1).toUpperCase(); });
}
return type + ':' + result;
@ -27082,8 +27095,8 @@ Ember States
})();
// Version: v1.0.0-rc.1-110-g1446ad7
// Last commit: 1446ad7 (2013-03-07 19:07:55 +0100)
// Version: v1.0.0-rc.1-134-gf13ef4c
// Last commit: f13ef4c (2013-03-08 16:46:38 +0100)
(function() {