contracts-js
Version:
A contract library for JavaScript
1,949 lines (1,546 loc) • 279 kB
JavaScript
/*!
* @overview Ember Data
* @copyright Copyright 2011-2014 Tilde Inc. and contributors.
* Portions Copyright 2011 LivingSocial Inc.
* @license Licensed under MIT license (see license.js)
* @version 1.0.0-beta.5
*/
(function() {
var define, requireModule;
(function() {
var registry = {}, seen = {};
define = function(name, deps, callback) {
registry[name] = { deps: deps, callback: callback };
};
requireModule = function(name) {
if (seen[name]) { return seen[name]; }
seen[name] = {};
var mod, deps, callback, reified , exports;
mod = registry[name];
if (!mod) {
throw new Error("Module '" + name + "' not found.");
}
deps = mod.deps;
callback = mod.callback;
reified = [];
exports;
for (var i=0, l=deps.length; i<l; i++) {
if (deps[i] === 'exports') {
reified.push(exports = {});
} else {
reified.push(requireModule(deps[i]));
}
}
var value = callback.apply(this, reified);
return seen[name] = exports || value;
};
})();
(function() {
/**
@module ember-data
*/
/**
All Ember Data methods and functions are defined inside of this namespace.
@class DS
@static
*/
var DS;
if ('undefined' === typeof DS) {
/**
@property VERSION
@type String
@default '1.0.0-beta.5'
@static
*/
DS = Ember.Namespace.create({
VERSION: '1.0.0-beta.5'
});
if ('undefined' !== typeof window) {
window.DS = DS;
}
if (Ember.libraries) {
Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
}
}
})();
(function() {
var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
// Simple dispatcher to support overriding the aliased
// method in subclasses.
function aliasMethod(methodName) {
return function() {
return this[methodName].apply(this, arguments);
};
}
/**
In Ember Data a Serializer is used to serialize and deserialize
records when they are transfered in and out of an external source.
This process involves normalizing property names, transforming
attribute values and serializeing relationships.
For maximum performance Ember Data recomends you use the
[RESTSerializer](DS.RESTSerializer.html) or one of its subclasses.
`JSONSerializer` is useful for simpler or legacy backends that may
not support the http://jsonapi.org/ spec.
@class JSONSerializer
@namespace DS
*/
DS.JSONSerializer = Ember.Object.extend({
/**
The primaryKey is used when serializing and deserializing
data. Ember Data always uses the `id` propery to store the id of
the record. The external source may not always follow this
convention. In these cases it is usesful to override the
primaryKey property to match the primaryKey of your external
store.
Example
```javascript
App.ApplicationSerializer = DS.JSONSerializer.extend({
primaryKey: '_id'
});
```
@property primaryKey
@type {String}
@default 'id'
*/
primaryKey: 'id',
/**
Given a subclass of `DS.Model` and a JSON object this method will
iterate through each attribute of the `DS.Model` and invoke the
`DS.Transform#deserialize` method on the matching property of the
JSON object. This method is typically called after the
serializer's `normalize` method.
@method applyTransforms
@private
@param {subclass of DS.Model} type
@param {Object} data The data to transform
@return {Object} data The transformed data object
*/
applyTransforms: function(type, data) {
type.eachTransformedAttribute(function(key, type) {
var transform = this.transformFor(type);
data[key] = transform.deserialize(data[key]);
}, this);
return data;
},
/**
Normalizes a part of the JSON payload returned by
the server. You should override this method, munge the hash
and call super if you have generic normalization to do.
It takes the type of the record that is being normalized
(as a DS.Model class), the property where the hash was
originally found, and the hash to normalize.
You can use this method, for example, to normalize underscored keys to camelized
or other general-purpose normalizations.
Example
```javascript
App.ApplicationSerializer = DS.JSONSerializer.extend({
normalize: function(type, hash) {
var fields = Ember.get(type, 'fields');
fields.forEach(function(field) {
var payloadField = Ember.String.underscore(field);
if (field === payloadField) { return; }
hash[field] = hash[payloadField];
delete hash[payloadField];
});
return this._super.apply(this, arguments);
}
});
```
@method normalize
@param {subclass of DS.Model} type
@param {Object} hash
@return {Object}
*/
normalize: function(type, hash) {
if (!hash) { return hash; }
this.applyTransforms(type, hash);
return hash;
},
// SERIALIZE
/**
Called when a record is saved in order to convert the
record into JSON.
By default, it creates a JSON object with a key for
each attribute and belongsTo relationship.
For example, consider this model:
```javascript
App.Comment = DS.Model.extend({
title: DS.attr(),
body: DS.attr(),
author: DS.belongsTo('user')
});
```
The default serialization would create a JSON object like:
```javascript
{
"title": "Rails is unagi",
"body": "Rails? Omakase? O_O",
"author": 12
}
```
By default, attributes are passed through as-is, unless
you specified an attribute type (`DS.attr('date')`). If
you specify a transform, the JavaScript value will be
serialized when inserted into the JSON hash.
By default, belongs-to relationships are converted into
IDs when inserted into the JSON hash.
## IDs
`serialize` takes an options hash with a single option:
`includeId`. If this option is `true`, `serialize` will,
by default include the ID in the JSON object it builds.
The adapter passes in `includeId: true` when serializing
a record for `createRecord`, but not for `updateRecord`.
## Customization
Your server may expect a different JSON format than the
built-in serialization format.
In that case, you can implement `serialize` yourself and
return a JSON hash of your choosing.
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
serialize: function(post, options) {
var json = {
POST_TTL: post.get('title'),
POST_BDY: post.get('body'),
POST_CMS: post.get('comments').mapProperty('id')
}
if (options.includeId) {
json.POST_ID_ = post.get('id');
}
return json;
}
});
```
## Customizing an App-Wide Serializer
If you want to define a serializer for your entire
application, you'll probably want to use `eachAttribute`
and `eachRelationship` on the record.
```javascript
App.ApplicationSerializer = DS.JSONSerializer.extend({
serialize: function(record, options) {
var json = {};
record.eachAttribute(function(name) {
json[serverAttributeName(name)] = record.get(name);
})
record.eachRelationship(function(name, relationship) {
if (relationship.kind === 'hasMany') {
json[serverHasManyName(name)] = record.get(name).mapBy('id');
}
});
if (options.includeId) {
json.ID_ = record.get('id');
}
return json;
}
});
function serverAttributeName(attribute) {
return attribute.underscore().toUpperCase();
}
function serverHasManyName(name) {
return serverAttributeName(name.singularize()) + "_IDS";
}
```
This serializer will generate JSON that looks like this:
```javascript
{
"TITLE": "Rails is omakase",
"BODY": "Yep. Omakase.",
"COMMENT_IDS": [ 1, 2, 3 ]
}
```
## Tweaking the Default JSON
If you just want to do some small tweaks on the default JSON,
you can call super first and make the tweaks on the returned
JSON.
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
serialize: function(record, options) {
var json = this._super.apply(this, arguments);
json.subject = json.title;
delete json.title;
return json;
}
});
```
@method serialize
@param {subclass of DS.Model} record
@param {Object} options
@return {Object} json
*/
serialize: function(record, options) {
var json = {};
if (options && options.includeId) {
var id = get(record, 'id');
if (id) {
json[get(this, 'primaryKey')] = id;
}
}
record.eachAttribute(function(key, attribute) {
this.serializeAttribute(record, json, key, attribute);
}, this);
record.eachRelationship(function(key, relationship) {
if (relationship.kind === 'belongsTo') {
this.serializeBelongsTo(record, json, relationship);
} else if (relationship.kind === 'hasMany') {
this.serializeHasMany(record, json, relationship);
}
}, this);
return json;
},
/**
`serializeAttribute` can be used to customize how `DS.attr`
properties are serialized
For example if you wanted to ensure all you attributes were always
serialized as properties on an `attributes` object you could
write:
```javascript
App.ApplicationSerializer = DS.JSONSerializer.extend({
serializeAttribute: function(record, json, key, attributes) {
json.attributes = json.attributes || {};
this._super(record, json.attributes, key, attributes);
}
});
```
@method serializeAttribute
@param {DS.Model} record
@param {Object} json
@param {String} key
@param {Object} attribute
*/
serializeAttribute: function(record, json, key, attribute) {
var attrs = get(this, 'attrs');
var value = get(record, key), type = attribute.type;
if (type) {
var transform = this.transformFor(type);
value = transform.serialize(value);
}
// if provided, use the mapping provided by `attrs` in
// the serializer
key = attrs && attrs[key] || (this.keyForAttribute ? this.keyForAttribute(key) : key);
json[key] = value;
},
/**
`serializeBelongsTo` can be used to customize how `DS.belongsTo`
properties are serialized.
Example
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
serializeBelongsTo: function(record, json, relationship) {
var key = relationship.key;
var belongsTo = get(record, key);
key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON();
}
});
```
@method serializeBelongsTo
@param {DS.Model} record
@param {Object} json
@param {Object} relationship
*/
serializeBelongsTo: function(record, json, relationship) {
var key = relationship.key;
var belongsTo = get(record, key);
key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
if (isNone(belongsTo)) {
json[key] = belongsTo;
} else {
json[key] = get(belongsTo, 'id');
}
if (relationship.options.polymorphic) {
this.serializePolymorphicType(record, json, relationship);
}
},
/**
`serializeHasMany` can be used to customize how `DS.hasMany`
properties are serialized.
Example
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
serializeHasMany: function(record, json, relationship) {
var key = relationship.key;
if (key === 'comments') {
return;
} else {
this._super.apply(this, arguments);
}
}
});
```
@method serializeHasMany
@param {DS.Model} record
@param {Object} json
@param {Object} relationship
*/
serializeHasMany: function(record, json, relationship) {
var key = relationship.key;
var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') {
json[key] = get(record, key).mapBy('id');
// TODO support for polymorphic manyToNone and manyToMany relationships
}
},
/**
You can use this method to customize how polymorphic objects are
serialized. Objects are considered to be polymorphic if
`{polymorphic: true}` is pass as the second argument to the
`DS.belongsTo` function.
Example
```javascript
App.CommentSerializer = DS.JSONSerializer.extend({
serializePolymorphicType: function(record, json, relationship) {
var key = relationship.key,
belongsTo = get(record, key);
key = this.keyForAttribute ? this.keyForAttribute(key) : key;
json[key + "_type"] = belongsTo.constructor.typeKey;
}
});
```
@method serializePolymorphicType
@param {DS.Model} record
@param {Object} json
@param {Object} relationship
*/
serializePolymorphicType: Ember.K,
// EXTRACT
/**
The `extract` method is used to deserialize payload data from the
server. By default the `JSONSerializer` does not push the records
into the store. However records that subclass `JSONSerializer`
such as the `RESTSerializer` may push records into the store as
part of the extract call.
This method deletegates to a more specific extract method based on
the `requestType`.
Example
```javascript
var get = Ember.get;
socket.on('message', function(message) {
var modelName = message.model;
var data = message.data;
var type = store.modelFor(modelName);
var serializer = store.serializerFor(type.typeKey);
var record = serializer.extract(store, type, data, get(data, 'id'), 'single');
store.push(modelName, record);
});
```
@method extract
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@param {String or Number} id
@param {String} requestType
@return {Object} json The deserialized payload
*/
extract: function(store, type, payload, id, requestType) {
this.extractMeta(store, type, payload);
var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
return this[specificExtract](store, type, payload, id, requestType);
},
/**
`extractFindAll` is a hook into the extract method used when a
call is made to `DS.Store#findAll`. By default this method is an
alias for [extractArray](#method_extractArray).
@method extractFindAll
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Array} array An array of deserialized objects
*/
extractFindAll: aliasMethod('extractArray'),
/**
`extractFindQuery` is a hook into the extract method used when a
call is made to `DS.Store#findQuery`. By default this method is an
alias for [extractArray](#method_extractArray).
@method extractFindQuery
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Array} array An array of deserialized objects
*/
extractFindQuery: aliasMethod('extractArray'),
/**
`extractFindMany` is a hook into the extract method used when a
call is made to `DS.Store#findMany`. By default this method is
alias for [extractArray](#method_extractArray).
@method extractFindMany
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Array} array An array of deserialized objects
*/
extractFindMany: aliasMethod('extractArray'),
/**
`extractFindHasMany` is a hook into the extract method used when a
call is made to `DS.Store#findHasMany`. By default this method is
alias for [extractArray](#method_extractArray).
@method extractFindHasMany
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Array} array An array of deserialized objects
*/
extractFindHasMany: aliasMethod('extractArray'),
/**
`extractCreateRecord` is a hook into the extract method used when a
call is made to `DS.Store#createRecord`. By default this method is
alias for [extractSave](#method_extractSave).
@method extractCreateRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Object} json The deserialized payload
*/
extractCreateRecord: aliasMethod('extractSave'),
/**
`extractUpdateRecord` is a hook into the extract method used when
a call is made to `DS.Store#update`. By default this method is alias
for [extractSave](#method_extractSave).
@method extractUpdateRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Object} json The deserialized payload
*/
extractUpdateRecord: aliasMethod('extractSave'),
/**
`extractDeleteRecord` is a hook into the extract method used when
a call is made to `DS.Store#deleteRecord`. By default this method is
alias for [extractSave](#method_extractSave).
@method extractDeleteRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Object} json The deserialized payload
*/
extractDeleteRecord: aliasMethod('extractSave'),
/**
`extractFind` is a hook into the extract method used when
a call is made to `DS.Store#find`. By default this method is
alias for [extractSingle](#method_extractSingle).
@method extractFind
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Object} json The deserialized payload
*/
extractFind: aliasMethod('extractSingle'),
/**
`extractFindBelongsTo` is a hook into the extract method used when
a call is made to `DS.Store#findBelongsTo`. By default this method is
alias for [extractSingle](#method_extractSingle).
@method extractFindBelongsTo
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Object} json The deserialized payload
*/
extractFindBelongsTo: aliasMethod('extractSingle'),
/**
`extractSave` is a hook into the extract method used when a call
is made to `DS.Model#save`. By default this method is alias
for [extractSingle](#method_extractSingle).
@method extractSave
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Object} json The deserialized payload
*/
extractSave: aliasMethod('extractSingle'),
/**
`extractSingle` is used to deserialize a single record returned
from the adapter.
Example
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
extractSingle: function(store, type, payload) {
payload.comments = payload._embedded.comment;
delete payload._embedded;
return this._super(store, type, payload);
},
});
```
@method extractSingle
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Object} json The deserialized payload
*/
extractSingle: function(store, type, payload) {
return this.normalize(type, payload);
},
/**
`extractArray` is used to deserialize an array of records
returned from the adapter.
Example
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
extractArray: function(store, type, payload) {
return payload.map(function(json) {
return this.extractSingle(json);
}, this);
}
});
```
@method extractArray
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
@return {Array} array An array of deserialized objects
*/
extractArray: function(store, type, payload) {
return this.normalize(type, payload);
},
/**
`extractMeta` is used to deserialize any meta information in the
adapter payload. By default Ember Data expects meta information to
be located on the `meta` property of the payload object.
Example
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
extractMeta: function(store, type, payload) {
if (payload && payload._pagination) {
store.metaForType(type, payload._pagination);
delete payload._pagination;
}
}
});
```
@method extractMeta
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} payload
*/
extractMeta: function(store, type, payload) {
if (payload && payload.meta) {
store.metaForType(type, payload.meta);
delete payload.meta;
}
},
/**
`keyForAttribute` can be used to define rules for how to convert an
attribute name in your model to a key in your JSON.
Example
```javascript
App.ApplicationSerializer = DS.RESTSerializer.extend({
keyForAttribute: function(attr) {
return Ember.String.underscore(attr).toUpperCase();
}
});
```
@method keyForAttribute
@param {String} key
@return {String} normalized key
*/
/**
`keyForRelationship` can be used to define a custom key when
serializeing relationship properties. By default `JSONSerializer`
does not provide an implementation of this method.
Example
```javascript
App.PostSerializer = DS.JSONSerializer.extend({
keyForRelationship: function(key, relationship) {
return 'rel_' + Ember.String.underscore(key);
}
});
```
@method keyForRelationship
@param {String} key
@param {String} relationship type
@return {String} normalized key
*/
// HELPERS
/**
@method transformFor
@private
@param {String} attributeType
@param {Boolean} skipAssertion
@return {DS.Transform} transform
*/
transformFor: function(attributeType, skipAssertion) {
var transform = this.container.lookup('transform:' + attributeType);
Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform);
return transform;
}
});
})();
(function() {
/**
@module ember-data
*/
var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.String.underscore, DS = window.DS ;
/**
Extend `Ember.DataAdapter` with ED specific code.
@class DebugAdapter
@namespace DS
@extends Ember.DataAdapter
@private
*/
DS.DebugAdapter = Ember.DataAdapter.extend({
getFilters: function() {
return [
{ name: 'isNew', desc: 'New' },
{ name: 'isModified', desc: 'Modified' },
{ name: 'isClean', desc: 'Clean' }
];
},
detect: function(klass) {
return klass !== DS.Model && DS.Model.detect(klass);
},
columnsForType: function(type) {
var columns = [{ name: 'id', desc: 'Id' }], count = 0, self = this;
get(type, 'attributes').forEach(function(name, meta) {
if (count++ > self.attributeLimit) { return false; }
var desc = capitalize(underscore(name).replace('_', ' '));
columns.push({ name: name, desc: desc });
});
return columns;
},
getRecords: function(type) {
return this.get('store').all(type);
},
getRecordColumnValues: function(record) {
var self = this, count = 0,
columnValues = { id: get(record, 'id') };
record.eachAttribute(function(key) {
if (count++ > self.attributeLimit) {
return false;
}
var value = get(record, key);
columnValues[key] = value;
});
return columnValues;
},
getRecordKeywords: function(record) {
var keywords = [], keys = Ember.A(['id']);
record.eachAttribute(function(key) {
keys.push(key);
});
keys.forEach(function(key) {
keywords.push(get(record, key));
});
return keywords;
},
getRecordFilterValues: function(record) {
return {
isNew: record.get('isNew'),
isModified: record.get('isDirty') && !record.get('isNew'),
isClean: !record.get('isDirty')
};
},
getRecordColor: function(record) {
var color = 'black';
if (record.get('isNew')) {
color = 'green';
} else if (record.get('isDirty')) {
color = 'blue';
}
return color;
},
observeRecord: function(record, recordUpdated) {
var releaseMethods = Ember.A(), self = this,
keysToObserve = Ember.A(['id', 'isNew', 'isDirty']);
record.eachAttribute(function(key) {
keysToObserve.push(key);
});
keysToObserve.forEach(function(key) {
var handler = function() {
recordUpdated(self.wrapRecord(record));
};
Ember.addObserver(record, key, handler);
releaseMethods.push(function() {
Ember.removeObserver(record, key, handler);
});
});
var release = function() {
releaseMethods.forEach(function(fn) { fn(); } );
};
return release;
}
});
})();
(function() {
/**
The `DS.Transform` class is used to serialize and deserialize model
attributes when they are saved or loaded from an
adapter. Subclassing `DS.Transform` is useful for creating custom
attributes. All subclasses of `DS.Transform` must implement a
`serialize` and a `deserialize` method.
Example
```javascript
App.RawTransform = DS.Transform.extend({
deserialize: function(serialized) {
return serialized;
},
serialize: function(deserialized) {
return deserialized;
}
});
```
Usage
```javascript
var attr = DS.attr;
App.Requirement = DS.Model.extend({
name: attr('string'),
optionsArray: attr('raw')
});
```
@class Transform
@namespace DS
*/
DS.Transform = Ember.Object.extend({
/**
When given a deserialized value from a record attribute this
method must return the serialized value.
Example
```javascript
serialize: function(deserialized) {
return Ember.isEmpty(deserialized) ? null : Number(deserialized);
}
```
@method serialize
@param deserialized The deserialized value
@return The serialized value
*/
serialize: Ember.required(),
/**
When given a serialize value from a JSON object this method must
return the deserialized value for the record attribute.
Example
```javascript
deserialize: function(serialized) {
return empty(serialized) ? null : Number(serialized);
}
```
@method deserialize
@param serialized The serialized value
@return The deserialized value
*/
deserialize: Ember.required()
});
})();
(function() {
/**
The `DS.BooleanTransform` class is used to serialize and deserialize
boolean attributes on Ember Data record objects. This transform is
used when `boolean` is passed as the type parameter to the
[DS.attr](../../data#method_attr) function.
Usage
```javascript
var attr = DS.attr;
App.User = DS.Model.extend({
isAdmin: attr('boolean'),
name: attr('string'),
email: attr('string')
});
```
@class BooleanTransform
@extends DS.Transform
@namespace DS
*/
DS.BooleanTransform = DS.Transform.extend({
deserialize: function(serialized) {
var type = typeof serialized;
if (type === "boolean") {
return serialized;
} else if (type === "string") {
return serialized.match(/^true$|^t$|^1$/i) !== null;
} else if (type === "number") {
return serialized === 1;
} else {
return false;
}
},
serialize: function(deserialized) {
return Boolean(deserialized);
}
});
})();
(function() {
/**
The `DS.DateTransform` class is used to serialize and deserialize
date attributes on Ember Data record objects. This transform is used
when `date` is passed as the type parameter to the
[DS.attr](../../data#method_attr) function.
```javascript
var attr = DS.attr;
App.Score = DS.Model.extend({
value: attr('number'),
player: DS.belongsTo('player'),
date: attr('date')
});
```
@class DateTransform
@extends DS.Transform
@namespace DS
*/
DS.DateTransform = DS.Transform.extend({
deserialize: function(serialized) {
var type = typeof serialized;
if (type === "string") {
return new Date(Ember.Date.parse(serialized));
} else if (type === "number") {
return new Date(serialized);
} else if (serialized === null || serialized === undefined) {
// if the value is not present in the data,
// return undefined, not null.
return serialized;
} else {
return null;
}
},
serialize: function(date) {
if (date instanceof Date) {
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var pad = function(num) {
return num < 10 ? "0"+num : ""+num;
};
var utcYear = date.getUTCFullYear(),
utcMonth = date.getUTCMonth(),
utcDayOfMonth = date.getUTCDate(),
utcDay = date.getUTCDay(),
utcHours = date.getUTCHours(),
utcMinutes = date.getUTCMinutes(),
utcSeconds = date.getUTCSeconds();
var dayOfWeek = days[utcDay];
var dayOfMonth = pad(utcDayOfMonth);
var month = months[utcMonth];
return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
} else {
return null;
}
}
});
})();
(function() {
var empty = Ember.isEmpty;
/**
The `DS.NumberTransform` class is used to serialize and deserialize
numeric attributes on Ember Data record objects. This transform is
used when `number` is passed as the type parameter to the
[DS.attr](../../data#method_attr) function.
Usage
```javascript
var attr = DS.attr;
App.Score = DS.Model.extend({
value: attr('number'),
player: DS.belongsTo('player'),
date: attr('date')
});
```
@class NumberTransform
@extends DS.Transform
@namespace DS
*/
DS.NumberTransform = DS.Transform.extend({
deserialize: function(serialized) {
return empty(serialized) ? null : Number(serialized);
},
serialize: function(deserialized) {
return empty(deserialized) ? null : Number(deserialized);
}
});
})();
(function() {
var none = Ember.isNone;
/**
The `DS.StringTransform` class is used to serialize and deserialize
string attributes on Ember Data record objects. This transform is
used when `string` is passed as the type parameter to the
[DS.attr](../../data#method_attr) function.
Usage
```javascript
var attr = DS.attr;
App.User = DS.Model.extend({
isAdmin: attr('boolean'),
name: attr('string'),
email: attr('string')
});
```
@class StringTransform
@extends DS.Transform
@namespace DS
*/
DS.StringTransform = DS.Transform.extend({
deserialize: function(serialized) {
return none(serialized) ? null : String(serialized);
},
serialize: function(deserialized) {
return none(deserialized) ? null : String(deserialized);
}
});
})();
(function() {
})();
(function() {
/**
@module ember-data
*/
var set = Ember.set;
/*
This code registers an injection for Ember.Application.
If an Ember.js developer defines a subclass of DS.Store on their application,
this code will automatically instantiate it and make it available on the
router.
Additionally, after an application's controllers have been injected, they will
each have the store made available to them.
For example, imagine an Ember.js application with the following classes:
App.Store = DS.Store.extend({
adapter: 'custom'
});
App.PostsController = Ember.ArrayController.extend({
// ...
});
When the application is initialized, `App.Store` will automatically be
instantiated, and the instance of `App.PostsController` will have its `store`
property set to that instance.
Note that this code will only be run if the `ember-application` package is
loaded. If Ember Data is being used in an environment other than a
typical application (e.g., node.js where only `ember-runtime` is available),
this code will be ignored.
*/
Ember.onLoad('Ember.Application', function(Application) {
Application.initializer({
name: "store",
initialize: function(container, application) {
application.register('store:main', application.Store || DS.Store);
application.register('serializer:_default', DS.JSONSerializer);
application.register('serializer:_rest', DS.RESTSerializer);
application.register('adapter:_rest', DS.RESTAdapter);
// Eagerly generate the store so defaultStore is populated.
// TODO: Do this in a finisher hook
container.lookup('store:main');
}
});
Application.initializer({
name: "transforms",
before: "store",
initialize: function(container, application) {
application.register('transform:boolean', DS.BooleanTransform);
application.register('transform:date', DS.DateTransform);
application.register('transform:number', DS.NumberTransform);
application.register('transform:string', DS.StringTransform);
}
});
Application.initializer({
name: "dataAdapter",
before: "store",
initialize: function(container, application) {
application.register('dataAdapter:main', DS.DebugAdapter);
}
});
Application.initializer({
name: "injectStore",
before: "store",
initialize: function(container, application) {
application.inject('controller', 'store', 'store:main');
application.inject('route', 'store', 'store:main');
application.inject('serializer', 'store', 'store:main');
application.inject('dataAdapter', 'store', 'store:main');
}
});
});
})();
(function() {
/**
@module ember-data
*/
/**
Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
© 2011 Colin Snover <http://zetafleet.com>
Released under MIT license.
@class Date
@namespace Ember
@static
*/
Ember.Date = Ember.Date || {};
var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
/**
@method parse
@param date
*/
Ember.Date.parse = function (date) {
var timestamp, struct, minutesOffset = 0;
// ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
// before falling back to any implementation-specific date parsing, so that’s what we do, even if native
// implementations could be faster
// 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
// avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
for (var i = 0, k; (k = numericKeys[i]); ++i) {
struct[k] = +struct[k] || 0;
}
// allow undefined days and months
struct[2] = (+struct[2] || 1) - 1;
struct[3] = +struct[3] || 1;
if (struct[8] !== 'Z' && struct[9] !== undefined) {
minutesOffset = struct[10] * 60 + struct[11];
if (struct[9] === '+') {
minutesOffset = 0 - minutesOffset;
}
}
timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
}
else {
timestamp = origParse ? origParse(date) : NaN;
}
return timestamp;
};
if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) {
Date.parse = Ember.Date.parse;
}
})();
(function() {
})();
(function() {
/**
@module ember-data
*/
var get = Ember.get, set = Ember.set;
/**
A record array is an array that contains records of a certain type. The record
array materializes records as needed when they are retrieved for the first
time. You should not create record arrays yourself. Instead, an instance of
`DS.RecordArray` or its subclasses will be returned by your application's store
in response to queries.
@class RecordArray
@namespace DS
@extends Ember.ArrayProxy
@uses Ember.Evented
*/
DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, {
/**
The model type contained by this record array.
@property type
@type DS.Model
*/
type: null,
/**
The array of client ids backing the record array. When a
record is requested from the record array, the record
for the client id at the same index is materialized, if
necessary, by the store.
@property content
@private
@type Ember.Array
*/
content: null,
/**
The flag to signal a `RecordArray` is currently loading data.
Example
```javascript
var people = store.all(App.Person);
people.get('isLoaded'); // true
```
@property isLoaded
@type Boolean
*/
isLoaded: false,
/**
The flag to signal a `RecordArray` is currently loading data.
Example
```javascript
var people = store.all(App.Person);
people.get('isUpdating'); // false
people.update();
people.get('isUpdating'); // true
```
@property isUpdating
@type Boolean
*/
isUpdating: false,
/**
The store that created this record array.
@property store
@private
@type DS.Store
*/
store: null,
/**
Retrieves an object from the content by index.
@method objectAtContent
@private
@param {Number} index
@return {DS.Model} record
*/
objectAtContent: function(index) {
var content = get(this, 'content');
return content.objectAt(index);
},
/**
Used to get the latest version of all of the records in this array
from the adapter.
Example
```javascript
var people = store.all(App.Person);
people.get('isUpdating'); // false
people.update();
people.get('isUpdating'); // true
```
@method update
*/
update: function() {
if (get(this, 'isUpdating')) { return; }
var store = get(this, 'store'),
type = get(this, 'type');
store.fetchAll(type, this);
},
/**
Adds a record to the `RecordArray`.
@method addRecord
@private
@param {DS.Model} record
*/
addRecord: function(record) {
get(this, 'content').addObject(record);
},
/**
Removes a record to the `RecordArray`.
@method removeRecord
@private
@param {DS.Model} record
*/
removeRecord: function(record) {
get(this, 'content').removeObject(record);
},
/**
Saves all of the records in the `RecordArray`.
Example
```javascript
var messages = store.all(App.Message);
messages.forEach(function(message) {
message.set('hasBeenSeen', true);
});
messages.save();
```
@method save
@return {DS.PromiseArray} promise
*/
save: function() {
var promiseLabel = "DS: RecordArray#save " + get(this, 'type');
var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) {
return Ember.A(array);
}, null, "DS: RecordArray#save apply Ember.NativeArray");
return DS.PromiseArray.create({ promise: promise });
}
});
})();
(function() {
/**
@module ember-data
*/
var get = Ember.get;
/**
Represents a list of records whose membership is determined by the
store. As records are created, loaded, or modified, the store
evaluates them to determine if they should be part of the record
array.
@class FilteredRecordArray
@namespace DS
@extends DS.RecordArray
*/
DS.FilteredRecordArray = DS.RecordArray.extend({
/**
The filterFunction is a function used to test records from the store to
determine if they should be part of the record array.
Example
```javascript
var allPeople = store.all('person');
allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"]
var people = store.filter('person', function(person) {
if (person.get('name').match(/Katz$/)) { return true; }
});
people.mapBy('name'); // ["Yehuda Katz"]
var notKatzFilter = function(person) {
return !person.get('name').match(/Katz$/);
};
people.set('filterFunction', notKatzFilter);
people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"]
```
@method filterFunction
@param {DS.Model} record
@return {Boolean} `true` if the record should be in the array
*/
filterFunction: null,
isLoaded: true,
replace: function() {
var type = get(this, 'type').toString();
throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
},
/**
@method updateFilter
@private
*/
updateFilter: Ember.observer(function() {
var manager = get(this, 'manager');
manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction'));
}, 'filterFunction')
});
})();
(function() {
/**
@module ember-data
*/
var get = Ember.get, set = Ember.set;
/**
Represents an ordered list of records whose order and membership is
determined by the adapter. For example, a query sent to the adapter
may trigger a search on the server, whose results would be loaded
into an instance of the `AdapterPopulatedRecordArray`.
@class AdapterPopulatedRecordArray
@namespace DS
@extends DS.RecordArray
*/
DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({
query: null,
replace: function() {
var type = get(this, 'type').toString();
throw new Error("The result of a server query (on " + type + ") is immutable.");
},
/**
@method load
@private
@param {Array} data
*/
load: function(data) {
var store = get(this, 'store'),
type = get(this, 'type'),
records = store.pushMany(type, data),
meta = store.metadataFor(type);
this.setProperties({
content: Ember.A(records),
isLoaded: true,
meta: meta
});
// TODO: does triggering didLoad event should be the last action of the runLoop?
Ember.run.once(this, 'trigger', 'didLoad');
}
});
})();
(function() {
/**
@module ember-data
*/
var get = Ember.get, set = Ember.set;
var map = Ember.EnumerableUtils.map;
/**
A `ManyArray` is a `RecordArray` that represents the contents of a has-many
relationship.
The `ManyArray` is instantiated lazily the first time the relationship is
requested.
### Inverses
Often, the relationships in Ember Data applications will have
an inverse. For example, imagine the following models are
defined:
```javascript
App.Post = DS.Model.extend({
comments: DS.hasMany('comment')
});
App.Comment = DS.Model.extend({
post: DS.belongsTo('post')
});
```
If you created a new instance of `App.Post` and added
a `App.Comment` record to its `comments` has-many
relationship, you would expect the comment's `post`
property to be set to the post that contained
the has-many.
We call the record to which a relationship belongs the
relationship's _owner_.
@class ManyArray
@namespace DS
@extends DS.RecordArray
*/
DS.ManyArray = DS.RecordArray.extend({
init: function() {
this._super.apply(this, arguments);
this._changesToSync = Ember.OrderedSet.create();
},
/**
The property name of the relationship
@property {String} name
@private
*/
name: null,
/**
The record to which this relationship belongs.
@property {DS.Model} owner
@private
*/
owner: null,
/**
`true` if the relationship is polymorphic, `false` otherwise.
@property {Boolean} isPolymorphic
@private
*/
isPolymorphic: false,
// LOADING STATE
isLoaded: false,
/**
Used for async `hasMany` arrays
to keep track of when they will resolve.
@property {Ember.RSVP.Promise} promise
@private
*/
promise: null,
/**
@method loadingRecordsCount
@param {Number} count
@private
*/
loadingRecordsCount: function(count) {
this.loadingRecordsCount = count;
},
/**
@method loadedRecord
@private
*/
loadedRecord: function() {
this.loadingRecordsCount--;
if (this.loadingRecordsCount === 0) {
set(this, 'isLoaded', true);
this.trigger('didLoad');
}
},
/**
@method fetch
@private
*/
fetch: function() {
var records = get(this, 'content'),
store = get(this, 'store'),
owner = get(this, 'owner'),
resolver = Ember.RSVP.defer("DS: ManyArray#fetch " + get(this, 'type'));
var unloadedRecords = records.filterProperty('isEmpty', true);
store.fetchMany(unloadedRecords, owner, resolver);
},
// Overrides Ember.Array's replace method to implement
replaceContent: function(index, removed, added) {
// Map the array of record objects into an array of client ids.
added = map(added, function(record) {
Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.type.typeKey + "' allowed)", !this.type || record instanceof this.type);
return record;
}, this);
this._super(index, removed, added);
},
arrangedContentDidChange: function() {
Ember.run.once(this, 'fetch');
},
arrayContentWillChange: function(index, removed, added) {
var owner = get(this, 'owner'),
name = get(this, 'name');
if (!owner._suspendedRelationships) {
// This code is the first half of code that continues inside
// of arrayContentDidChange. It gets or creates a change from
// the child object, adds the current owner as the old
// parent if this is the first time the object was removed
// from a ManyArray, and sets `newParent` to null.
//
// Later, if the object is added to another ManyArray,
// the `arrayContentDidChange` will set `newParent` on
// the change.
for (var i=index; i<index+removed; i++) {
var record = get(this, 'content').objectAt(i);
var change = DS.RelationshipChange.createChange(owner, record, get(this, 'store'), {
parentType: owner.constructor,
changeType: "remove",
kind: "hasMany",
key: name
});
this._changesToSync.add(change);
}
}
return this._super.apply(this, arguments);
},
arrayContentDidChange: function(index, removed, added) {
this._super.apply(this, arguments);
var owner = get(this, 'owner'),
name = get(this, 'name'),
store = get(this, 'store');
if (!owner._suspendedRelationships) {
// This code is the second half of code that started in
// `arrayContentWillChange`. It gets or creates a change
// from the child object, and adds the current owner as
// the new parent.
for (var i=index; i<index+added; i++) {
var record = get(this, 'content').objectAt(i);
var change = DS.RelationshipChange.createChange(owner, record, store, {
parentType: owner.constructor,
changeType: "add",
kind:"hasMany",
key: name
});
change.hasManyName = name;
this._changesToSync.add(change);
}
// We wait until the array has finished being
// mutated before syncing the OneToManyChanges created
// in arrayContentWillChange, so that the array
// membership test in the sync() logic operates
// on the final results.
this._changesToSync.forEach(function(change) {
change.sync();
});
this._changesToSync.clear();
}
},
/**
Create a child record within the owner
@method createRecord
@private
@param {Object} hash
@return {DS.Model} record
*/
createRecord: function(hash) {
var owner = get(this, 'owner'),
store = get(owner, 'store'),
type = get(this, 'type'),
record;
Ember.assert("You cannot add '" + type.typeKey + "' records to this polymorphic relationship.", !get(this, 'isPolymorphic'));
record = store.createRecord.call(store, type, hash);
this.pushObject(record);
return record;
}
});
})();
(function() {
/**
@module ember-data
*/
})();
(function() {
/*globals Ember*/
/*jshint eqnull:true*/
/**
@module ember-data
*/
var get = Ember.get, set = Ember.set;
var once = Ember.run.once;
var isNone = Ember.isNone;
var forEach = Ember.EnumerableUtils.forEach;
var indexOf = Ember.EnumerableUtils.indexOf;
var map = Ember.EnumerableUtils.map;
var resolve = Ember.RSVP.resolve;
var copy = Ember.copy;
// Implementors Note:
//
// The variables in this file are consistently named according to the following
// scheme:
//
// * +id+ means an identifier managed by an external source, provided inside
// the data provided by that source. These are always coerced to be strings
// before being used internally.
// * +clientId+ means a transient numerical identifier generated at runtime by
// the data store. It is important primarily because newly created objects may
// not yet have an externally generated id.
// * +reference+ means a record reference object, which holds metadata about a
// record, even if it has not yet been fully materialized.
// * +type+ means a subclass of DS.Model.
// Used by the store to normalize IDs entering the store. Despite the fact
// that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`),
// it is important that internally we use strings, since IDs may be serialized
// and lose type information. For example, Ember's router may put a record's
// ID into the URL, and if we later try to deserialize that URL and find the
// corresponding record, we will not know if it is a string or a number.
var coerceId = function(id) {
r