todomvc
Version:
> Helping you select an MV\* framework
1,681 lines (1,372 loc) • 341 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.7+canary.b45e23ba
*/
(function(global) {
var define, requireModule, require, requirejs;
(function() {
var registry = {}, seen = {};
define = function(name, deps, callback) {
registry[name] = { deps: deps, callback: callback };
};
requirejs = require = requireModule = function(name) {
requirejs._eak_seen = registry;
if (seen[name]) { return seen[name]; }
seen[name] = {};
if (!registry[name]) {
throw new Error("Could not find module " + name);
}
var mod = registry[name],
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(resolve(deps[i])));
}
}
var value = callback.apply(this, reified);
return seen[name] = exports || value;
function resolve(child) {
if (child.charAt(0) !== '.') { return child; }
var parts = child.split("/");
var parentBase = name.split("/").slice(0, -1);
for (var i=0, l=parts.length; i<l; i++) {
var part = parts[i];
if (part === '..') { parentBase.pop(); }
else if (part === '.') { continue; }
else { parentBase.push(part); }
}
return parentBase.join("/");
}
};
})();
define("activemodel-adapter/lib/initializers",
["../../ember-data/lib/system/container_proxy","./system/active_model_serializer","./system/active_model_adapter"],
function(__dependency1__, __dependency2__, __dependency3__) {
"use strict";
var ContainerProxy = __dependency1__["default"];
var ActiveModelSerializer = __dependency2__["default"];
var ActiveModelAdapter = __dependency3__["default"];
Ember.onLoad('Ember.Application', function(Application) {
Application.initializer({
name: "activeModelAdapter",
initialize: function(container, application) {
var proxy = new ContainerProxy(container);
proxy.registerDeprecations([
{deprecated: 'serializer:_ams', valid: 'serializer:-active-model'},
{deprecated: 'adapter:_ams', valid: 'adapter:-active-model'}
]);
application.register('serializer:-active-model', ActiveModelSerializer);
application.register('adapter:-active-model', ActiveModelAdapter);
}
});
});
});
define("activemodel-adapter/lib/main",
["./system","./initializers","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var ActiveModelAdapter = __dependency1__.ActiveModelAdapter;
var ActiveModelSerializer = __dependency1__.ActiveModelSerializer;
var EmbeddedRecordsMixin = __dependency1__.EmbeddedRecordsMixin;
__exports__.ActiveModelAdapter = ActiveModelAdapter;
__exports__.ActiveModelSerializer = ActiveModelSerializer;
__exports__.EmbeddedRecordsMixin = EmbeddedRecordsMixin;
});
define("activemodel-adapter/lib/system",
["./system/embedded_records_mixin","./system/active_model_adapter","./system/active_model_serializer","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
"use strict";
var EmbeddedRecordsMixin = __dependency1__["default"];
var ActiveModelAdapter = __dependency2__["default"];
var ActiveModelSerializer = __dependency3__["default"];
__exports__.EmbeddedRecordsMixin = EmbeddedRecordsMixin;
__exports__.ActiveModelAdapter = ActiveModelAdapter;
__exports__.ActiveModelSerializer = ActiveModelSerializer;
});
define("activemodel-adapter/lib/system/active_model_adapter",
["../../../ember-data/lib/adapters","../../../ember-data/lib/system/adapter","../../../ember-inflector/lib/main","./active_model_serializer","./embedded_records_mixin","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
"use strict";
var RESTAdapter = __dependency1__.RESTAdapter;
var InvalidError = __dependency2__.InvalidError;
var pluralize = __dependency3__.pluralize;
var ActiveModelSerializer = __dependency4__["default"];
var EmbeddedRecordsMixin = __dependency5__["default"];
/**
@module ember-data
*/
var forEach = Ember.EnumerableUtils.forEach;
var decamelize = Ember.String.decamelize,
underscore = Ember.String.underscore;
/**
The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
with a JSON API that uses an underscored naming convention instead of camelcasing.
It has been designed to work out of the box with the
[active_model_serializers](http://github.com/rails-api/active_model_serializers)
Ruby gem.
This adapter extends the DS.RESTAdapter by making consistent use of the camelization,
decamelization and pluralization methods to normalize the serialized JSON into a
format that is compatible with a conventional Rails backend and Ember Data.
## JSON Structure
The ActiveModelAdapter expects the JSON returned from your server to follow
the REST adapter conventions substituting underscored keys for camelcased ones.
### Conventional Names
Attribute names in your JSON payload should be the underscored versions of
the attributes in your Ember.js models.
For example, if you have a `Person` model:
```js
App.FamousPerson = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
occupation: DS.attr('string')
});
```
The JSON returned should look like this:
```js
{
"famous_person": {
"first_name": "Barack",
"last_name": "Obama",
"occupation": "President"
}
}
```
@class ActiveModelAdapter
@constructor
@namespace DS
@extends DS.Adapter
**/
var ActiveModelAdapter = RESTAdapter.extend({
defaultSerializer: '-active-model',
/**
The ActiveModelAdapter overrides the `pathForType` method to build
underscored URLs by decamelizing and pluralizing the object type name.
```js
this.pathForType("famousPerson");
//=> "famous_people"
```
@method pathForType
@param {String} type
@returns String
*/
pathForType: function(type) {
var decamelized = decamelize(type);
var underscored = underscore(decamelized);
return pluralize(underscored);
},
/**
The ActiveModelAdapter overrides the `ajaxError` method
to return a DS.InvalidError for all 422 Unprocessable Entity
responses.
A 422 HTTP response from the server generally implies that the request
was well formed but the API was unable to process it because the
content was not semantically correct or meaningful per the API.
For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918
https://tools.ietf.org/html/rfc4918#section-11.2
@method ajaxError
@param jqXHR
@returns error
*/
ajaxError: function(jqXHR) {
var error = this._super(jqXHR);
if (jqXHR && jqXHR.status === 422) {
var response = Ember.$.parseJSON(jqXHR.responseText),
errors = {};
if (response.errors !== undefined) {
var jsonErrors = response.errors;
forEach(Ember.keys(jsonErrors), function(key) {
errors[Ember.String.camelize(key)] = jsonErrors[key];
});
}
return new InvalidError(errors);
} else {
return error;
}
}
});
__exports__["default"] = ActiveModelAdapter;
});
define("activemodel-adapter/lib/system/active_model_serializer",
["../../../ember-inflector/lib/main","../../../ember-data/lib/serializers/rest_serializer","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var singularize = __dependency1__.singularize;
var RESTSerializer = __dependency2__["default"];
/**
@module ember-data
*/
var get = Ember.get,
forEach = Ember.EnumerableUtils.forEach,
camelize = Ember.String.camelize,
capitalize = Ember.String.capitalize,
decamelize = Ember.String.decamelize,
underscore = Ember.String.underscore;
var ActiveModelSerializer = RESTSerializer.extend({
// SERIALIZE
/**
Converts camelcased attributes to underscored when serializing.
@method keyForAttribute
@param {String} attribute
@returns String
*/
keyForAttribute: function(attr) {
return decamelize(attr);
},
/**
Underscores relationship names and appends "_id" or "_ids" when serializing
relationship keys.
@method keyForRelationship
@param {String} key
@param {String} kind
@returns String
*/
keyForRelationship: function(key, kind) {
key = decamelize(key);
if (kind === "belongsTo") {
return key + "_id";
} else if (kind === "hasMany") {
return singularize(key) + "_ids";
} else {
return key;
}
},
/**
Does not serialize hasMany relationships by default.
*/
serializeHasMany: Ember.K,
/**
Underscores the JSON root keys when serializing.
@method serializeIntoHash
@param {Object} hash
@param {subclass of DS.Model} type
@param {DS.Model} record
@param {Object} options
*/
serializeIntoHash: function(data, type, record, options) {
var root = underscore(decamelize(type.typeKey));
data[root] = this.serialize(record, options);
},
/**
Serializes a polymorphic type as a fully capitalized model name.
@method serializePolymorphicType
@param {DS.Model} record
@param {Object} json
@param relationship
*/
serializePolymorphicType: function(record, json, relationship) {
var key = relationship.key,
belongsTo = get(record, key);
key = this.keyForAttribute(key);
json[key + "_type"] = capitalize(camelize(belongsTo.constructor.typeKey));
},
// EXTRACT
/**
Extracts the model typeKey from underscored root objects.
@method typeForRoot
@param {String} root
@returns String the model's typeKey
*/
typeForRoot: function(root) {
var camelized = camelize(root);
return singularize(camelized);
},
/**
Add extra step to `DS.RESTSerializer.normalize` so links are
normalized.
If your payload looks like this
```js
{
"post": {
"id": 1,
"title": "Rails is omakase",
"links": { "flagged_comments": "api/comments/flagged" }
}
}
```
The normalized version would look like this
```js
{
"post": {
"id": 1,
"title": "Rails is omakase",
"links": { "flaggedComments": "api/comments/flagged" }
}
}
```
@method normalize
@param {subclass of DS.Model} type
@param {Object} hash
@param {String} prop
@returns Object
*/
normalize: function(type, hash, prop) {
this.normalizeLinks(hash);
return this._super(type, hash, prop);
},
/**
Convert `snake_cased` links to `camelCase`
@method normalizeLinks
@param {Object} hash
*/
normalizeLinks: function(data){
if (data.links) {
var links = data.links;
for (var link in links) {
var camelizedLink = camelize(link);
if (camelizedLink !== link) {
links[camelizedLink] = links[link];
delete links[link];
}
}
}
},
/**
Normalize the polymorphic type from the JSON.
Normalize:
```js
{
id: "1"
minion: { type: "evil_minion", id: "12"}
}
```
To:
```js
{
id: "1"
minion: { type: "evilMinion", id: "12"}
}
```
@method normalizeRelationships
@private
*/
normalizeRelationships: function(type, hash) {
var payloadKey, payload;
if (this.keyForRelationship) {
type.eachRelationship(function(key, relationship) {
if (relationship.options.polymorphic) {
payloadKey = this.keyForAttribute(key);
payload = hash[payloadKey];
if (payload && payload.type) {
payload.type = this.typeForRoot(payload.type);
} else if (payload && relationship.kind === "hasMany") {
var self = this;
forEach(payload, function(single) {
single.type = self.typeForRoot(single.type);
});
}
} else {
payloadKey = this.keyForRelationship(key, relationship.kind);
payload = hash[payloadKey];
}
hash[key] = payload;
if (key !== payloadKey) {
delete hash[payloadKey];
}
}, this);
}
}
});
__exports__["default"] = ActiveModelSerializer;
});
define("activemodel-adapter/lib/system/embedded_records_mixin",
["../../../ember-inflector/lib/main","exports"],
function(__dependency1__, __exports__) {
"use strict";
var get = Ember.get;
var forEach = Ember.EnumerableUtils.forEach;
var pluralize = __dependency1__.pluralize;
/**
The EmbeddedRecordsMixin allows you to add embedded record support to your
serializers.
To set up embedded records, you include the mixin into the serializer and then
define your embedded relations.
```js
App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
comments: {embedded: 'always'}
}
})
```
Currently only `{embedded: 'always'}` records are supported.
@class EmbeddedRecordsMixin
@namespace DS
*/
var EmbeddedRecordsMixin = Ember.Mixin.create({
/**
Serialize has-may relationship when it is configured as embedded objects.
@method serializeHasMany
*/
serializeHasMany: function(record, json, relationship) {
var key = relationship.key,
attrs = get(this, 'attrs'),
embed = attrs && attrs[key] && attrs[key].embedded === 'always';
if (embed) {
json[this.keyForAttribute(key)] = get(record, key).map(function(relation) {
var data = relation.serialize(),
primaryKey = get(this, 'primaryKey');
data[primaryKey] = get(relation, primaryKey);
return data;
}, this);
}
},
/**
Extract embedded objects out of the payload for a single object
and add them as sideloaded objects instead.
@method extractSingle
*/
extractSingle: function(store, primaryType, payload, recordId, requestType) {
var root = this.keyForAttribute(primaryType.typeKey),
partial = payload[root];
updatePayloadWithEmbedded(store, this, primaryType, partial, payload);
return this._super(store, primaryType, payload, recordId, requestType);
},
/**
Extract embedded objects out of a standard payload
and add them as sideloaded objects instead.
@method extractArray
*/
extractArray: function(store, type, payload) {
var root = this.keyForAttribute(type.typeKey),
partials = payload[pluralize(root)];
forEach(partials, function(partial) {
updatePayloadWithEmbedded(store, this, type, partial, payload);
}, this);
return this._super(store, type, payload);
}
});
function updatePayloadWithEmbedded(store, serializer, type, partial, payload) {
var attrs = get(serializer, 'attrs');
if (!attrs) {
return;
}
type.eachRelationship(function(key, relationship) {
var expandedKey, embeddedTypeKey, attribute, ids,
config = attrs[key],
serializer = store.serializerFor(relationship.type.typeKey),
primaryKey = get(serializer, "primaryKey");
if (relationship.kind !== "hasMany") {
return;
}
if (config && (config.embedded === 'always' || config.embedded === 'load')) {
// underscore forces the embedded records to be side loaded.
// it is needed when main type === relationship.type
embeddedTypeKey = '_' + Ember.String.pluralize(relationship.type.typeKey);
expandedKey = this.keyForRelationship(key, relationship.kind);
attribute = this.keyForAttribute(key);
ids = [];
if (!partial[attribute]) {
return;
}
payload[embeddedTypeKey] = payload[embeddedTypeKey] || [];
forEach(partial[attribute], function(data) {
var embeddedType = store.modelFor(relationship.type.typeKey);
updatePayloadWithEmbedded(store, serializer, embeddedType, data, payload);
ids.push(data[primaryKey]);
payload[embeddedTypeKey].push(data);
});
partial[expandedKey] = ids;
delete partial[attribute];
}
}, serializer);
}
__exports__["default"] = EmbeddedRecordsMixin;
});
define("ember-data/lib/adapters",
["./adapters/fixture_adapter","./adapters/rest_adapter","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
/**
@module ember-data
*/
var FixtureAdapter = __dependency1__["default"];
var RESTAdapter = __dependency2__["default"];
__exports__.RESTAdapter = RESTAdapter;
__exports__.FixtureAdapter = FixtureAdapter;
});
define("ember-data/lib/adapters/fixture_adapter",
["../system/adapter","exports"],
function(__dependency1__, __exports__) {
"use strict";
/**
@module ember-data
*/
var get = Ember.get, fmt = Ember.String.fmt,
indexOf = Ember.EnumerableUtils.indexOf;
var counter = 0;
var Adapter = __dependency1__["default"];
/**
`DS.FixtureAdapter` is an adapter that loads records from memory.
Its primarily used for development and testing. You can also use
`DS.FixtureAdapter` while working on the API but are not ready to
integrate yet. It is a fully functioning adapter. All CRUD methods
are implemented. You can also implement query logic that a remote
system would do. Its possible to do develop your entire application
with `DS.FixtureAdapter`.
For information on how to use the `FixtureAdapter` in your
application please see the [FixtureAdapter
guide](/guides/models/the-fixture-adapter/).
@class FixtureAdapter
@namespace DS
@extends DS.Adapter
*/
var FixtureAdapter = Adapter.extend({
// by default, fixtures are already in normalized form
serializer: null,
/**
If `simulateRemoteResponse` is `true` the `FixtureAdapter` will
wait a number of milliseconds before resolving promises with the
fixture values. The wait time can be configured via the `latency`
property.
@property simulateRemoteResponse
@type {Boolean}
@default true
*/
simulateRemoteResponse: true,
/**
By default the `FixtureAdapter` will simulate a wait of the
`latency` milliseconds before resolving promises with the fixture
values. This behavior can be turned off via the
`simulateRemoteResponse` property.
@property latency
@type {Number}
@default 50
*/
latency: 50,
/**
Implement this method in order to provide data associated with a type
@method fixturesForType
@param {Subclass of DS.Model} type
@return {Array}
*/
fixturesForType: function(type) {
if (type.FIXTURES) {
var fixtures = Ember.A(type.FIXTURES);
return fixtures.map(function(fixture){
var fixtureIdType = typeof fixture.id;
if(fixtureIdType !== "number" && fixtureIdType !== "string"){
throw new Error(fmt('the id property must be defined as a number or string for fixture %@', [fixture]));
}
fixture.id = fixture.id + '';
return fixture;
});
}
return null;
},
/**
Implement this method in order to query fixtures data
@method queryFixtures
@param {Array} fixture
@param {Object} query
@param {Subclass of DS.Model} type
@return {Promise|Array}
*/
queryFixtures: function(fixtures, query, type) {
Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.');
},
/**
@method updateFixtures
@param {Subclass of DS.Model} type
@param {Array} fixture
*/
updateFixtures: function(type, fixture) {
if(!type.FIXTURES) {
type.FIXTURES = [];
}
var fixtures = type.FIXTURES;
this.deleteLoadedFixture(type, fixture);
fixtures.push(fixture);
},
/**
Implement this method in order to provide json for CRUD methods
@method mockJSON
@param {Subclass of DS.Model} type
@param {DS.Model} record
*/
mockJSON: function(store, type, record) {
return store.serializerFor(type).serialize(record, { includeId: true });
},
/**
@method generateIdForRecord
@param {DS.Store} store
@param {DS.Model} record
@return {String} id
*/
generateIdForRecord: function(store) {
return "fixture-" + counter++;
},
/**
@method find
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {String} id
@return {Promise} promise
*/
find: function(store, type, id) {
var fixtures = this.fixturesForType(type),
fixture;
Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
if (fixtures) {
fixture = Ember.A(fixtures).findProperty('id', id);
}
if (fixture) {
return this.simulateRemoteCall(function() {
return fixture;
}, this);
}
},
/**
@method findMany
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Array} ids
@return {Promise} promise
*/
findMany: function(store, type, ids) {
var fixtures = this.fixturesForType(type);
Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
if (fixtures) {
fixtures = fixtures.filter(function(item) {
return indexOf(ids, item.id) !== -1;
});
}
if (fixtures) {
return this.simulateRemoteCall(function() {
return fixtures;
}, this);
}
},
/**
@private
@method findAll
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {String} sinceToken
@return {Promise} promise
*/
findAll: function(store, type) {
var fixtures = this.fixturesForType(type);
Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
return this.simulateRemoteCall(function() {
return fixtures;
}, this);
},
/**
@private
@method findQuery
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} query
@param {DS.AdapterPopulatedRecordArray} recordArray
@return {Promise} promise
*/
findQuery: function(store, type, query, array) {
var fixtures = this.fixturesForType(type);
Ember.assert("Unable to find fixtures for model type " + type.toString(), fixtures);
fixtures = this.queryFixtures(fixtures, query, type);
if (fixtures) {
return this.simulateRemoteCall(function() {
return fixtures;
}, this);
}
},
/**
@method createRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@return {Promise} promise
*/
createRecord: function(store, type, record) {
var fixture = this.mockJSON(store, type, record);
this.updateFixtures(type, fixture);
return this.simulateRemoteCall(function() {
return fixture;
}, this);
},
/**
@method updateRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@return {Promise} promise
*/
updateRecord: function(store, type, record) {
var fixture = this.mockJSON(store, type, record);
this.updateFixtures(type, fixture);
return this.simulateRemoteCall(function() {
return fixture;
}, this);
},
/**
@method deleteRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@return {Promise} promise
*/
deleteRecord: function(store, type, record) {
var fixture = this.mockJSON(store, type, record);
this.deleteLoadedFixture(type, fixture);
return this.simulateRemoteCall(function() {
// no payload in a deletion
return null;
});
},
/*
@method deleteLoadedFixture
@private
@param type
@param record
*/
deleteLoadedFixture: function(type, record) {
var existingFixture = this.findExistingFixture(type, record);
if(existingFixture) {
var index = indexOf(type.FIXTURES, existingFixture);
type.FIXTURES.splice(index, 1);
return true;
}
},
/*
@method findExistingFixture
@private
@param type
@param record
*/
findExistingFixture: function(type, record) {
var fixtures = this.fixturesForType(type);
var id = get(record, 'id');
return this.findFixtureById(fixtures, id);
},
/*
@method findFixtureById
@private
@param fixtures
@param id
*/
findFixtureById: function(fixtures, id) {
return Ember.A(fixtures).find(function(r) {
if(''+get(r, 'id') === ''+id) {
return true;
} else {
return false;
}
});
},
/*
@method simulateRemoteCall
@private
@param callback
@param context
*/
simulateRemoteCall: function(callback, context) {
var adapter = this;
return new Ember.RSVP.Promise(function(resolve) {
if (get(adapter, 'simulateRemoteResponse')) {
// Schedule with setTimeout
Ember.run.later(function() {
resolve(callback.call(context));
}, get(adapter, 'latency'));
} else {
// Asynchronous, but at the of the runloop with zero latency
Ember.run.schedule('actions', null, function() {
resolve(callback.call(context));
});
}
}, "DS: FixtureAdapter#simulateRemoteCall");
}
});
__exports__["default"] = FixtureAdapter;
});
define("ember-data/lib/adapters/rest_adapter",
["../system/adapter","exports"],
function(__dependency1__, __exports__) {
"use strict";
/**
@module ember-data
*/
var Adapter = __dependency1__["default"];
var get = Ember.get, set = Ember.set;
var forEach = Ember.ArrayPolyfills.forEach;
/**
The REST adapter allows your store to communicate with an HTTP server by
transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
should use the REST adapter.
This adapter is designed around the idea that the JSON exchanged with
the server should be conventional.
## JSON Structure
The REST adapter expects the JSON returned from your server to follow
these conventions.
### Object Root
The JSON payload should be an object that contains the record inside a
root property. For example, in response to a `GET` request for
`/posts/1`, the JSON should look like this:
```js
{
"post": {
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
}
}
```
### Conventional Names
Attribute names in your JSON payload should be the camelCased versions of
the attributes in your Ember.js models.
For example, if you have a `Person` model:
```js
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
occupation: DS.attr('string')
});
```
The JSON returned should look like this:
```js
{
"person": {
"firstName": "Barack",
"lastName": "Obama",
"occupation": "President"
}
}
```
## Customization
### Endpoint path customization
Endpoint paths can be prefixed with a `namespace` by setting the namespace
property on the adapter:
```js
DS.RESTAdapter.reopen({
namespace: 'api/1'
});
```
Requests for `App.Person` would now target `/api/1/people/1`.
### Host customization
An adapter can target other hosts by setting the `host` property.
```js
DS.RESTAdapter.reopen({
host: 'https://api.example.com'
});
```
### Headers customization
Some APIs require HTTP headers, e.g. to provide an API key. An array of
headers can be added to the adapter which are passed with every request:
```js
DS.RESTAdapter.reopen({
headers: {
"API_KEY": "secret key",
"ANOTHER_HEADER": "Some header value"
}
});
```
@class RESTAdapter
@constructor
@namespace DS
@extends DS.Adapter
*/
var RESTAdapter = Adapter.extend({
defaultSerializer: '-rest',
/**
Endpoint paths can be prefixed with a `namespace` by setting the namespace
property on the adapter:
```javascript
DS.RESTAdapter.reopen({
namespace: 'api/1'
});
```
Requests for `App.Post` would now target `/api/1/post/`.
@property namespace
@type {String}
*/
/**
An adapter can target other hosts by setting the `host` property.
```javascript
DS.RESTAdapter.reopen({
host: 'https://api.example.com'
});
```
Requests for `App.Post` would now target `https://api.example.com/post/`.
@property host
@type {String}
*/
/**
Some APIs require HTTP headers, e.g. to provide an API key. An array of
headers can be added to the adapter which are passed with every request:
```javascript
DS.RESTAdapter.reopen({
headers: {
"API_KEY": "secret key",
"ANOTHER_HEADER": "Some header value"
}
});
```
@property headers
@type {Object}
*/
/**
Called by the store in order to fetch the JSON for a given
type and ID.
The `find` method makes an Ajax request to a URL computed by `buildURL`, and returns a
promise for the resulting payload.
This method performs an HTTP `GET` request with the id provided as part of the query string.
@method find
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {String} id
@returns {Promise} promise
*/
find: function(store, type, id) {
return this.ajax(this.buildURL(type.typeKey, id), 'GET');
},
/**
Called by the store in order to fetch a JSON array for all
of the records for a given type.
The `findAll` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
promise for the resulting payload.
@private
@method findAll
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {String} sinceToken
@returns {Promise} promise
*/
findAll: function(store, type, sinceToken) {
var query;
if (sinceToken) {
query = { since: sinceToken };
}
return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
},
/**
Called by the store in order to fetch a JSON array for
the records that match a particular query.
The `findQuery` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
promise for the resulting payload.
The `query` argument is a simple JavaScript object that will be passed directly
to the server as parameters.
@private
@method findQuery
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Object} query
@returns {Promise} promise
*/
findQuery: function(store, type, query) {
return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
},
/**
Called by the store in order to fetch a JSON array for
the unloaded records in a has-many relationship that were originally
specified as IDs.
For example, if the original payload looks like:
```js
{
"id": 1,
"title": "Rails is omakase",
"comments": [ 1, 2, 3 ]
}
```
The IDs will be passed as a URL-encoded Array of IDs, in this form:
```
ids[]=1&ids[]=2&ids[]=3
```
Many servers, such as Rails and PHP, will automatically convert this URL-encoded array
into an Array for you on the server-side. If you want to encode the
IDs, differently, just override this (one-line) method.
The `findMany` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
promise for the resulting payload.
@method findMany
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {Array} ids
@returns {Promise} promise
*/
findMany: function(store, type, ids) {
return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } });
},
/**
Called by the store in order to fetch a JSON array for
the unloaded records in a has-many relationship that were originally
specified as a URL (inside of `links`).
For example, if your original payload looks like this:
```js
{
"post": {
"id": 1,
"title": "Rails is omakase",
"links": { "comments": "/posts/1/comments" }
}
}
```
This method will be called with the parent record and `/posts/1/comments`.
The `findHasMany` method will make an Ajax (HTTP GET) request to the originally specified URL.
If the URL is host-relative (starting with a single slash), the
request will use the host specified on the adapter (if any).
@method findHasMany
@param {DS.Store} store
@param {DS.Model} record
@param {String} url
@returns {Promise} promise
*/
findHasMany: function(store, record, url) {
var host = get(this, 'host'),
id = get(record, 'id'),
type = record.constructor.typeKey;
if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') {
url = host + url;
}
return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
},
/**
Called by the store in order to fetch a JSON array for
the unloaded records in a belongs-to relationship that were originally
specified as a URL (inside of `links`).
For example, if your original payload looks like this:
```js
{
"person": {
"id": 1,
"name": "Tom Dale",
"links": { "group": "/people/1/group" }
}
}
```
This method will be called with the parent record and `/people/1/group`.
The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL.
@method findBelongsTo
@param {DS.Store} store
@param {DS.Model} record
@param {String} url
@returns {Promise} promise
*/
findBelongsTo: function(store, record, url) {
var id = get(record, 'id'),
type = record.constructor.typeKey;
return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
},
/**
Called by the store when a newly created record is
saved via the `save` method on a model record instance.
The `createRecord` method serializes the record and makes an Ajax (HTTP POST) request
to a URL computed by `buildURL`.
See `serialize` for information on how to customize the serialized form
of a record.
@method createRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@returns {Promise} promise
*/
createRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record, { includeId: true });
return this.ajax(this.buildURL(type.typeKey), "POST", { data: data });
},
/**
Called by the store when an existing record is saved
via the `save` method on a model record instance.
The `updateRecord` method serializes the record and makes an Ajax (HTTP PUT) request
to a URL computed by `buildURL`.
See `serialize` for information on how to customize the serialized form
of a record.
@method updateRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@returns {Promise} promise
*/
updateRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record);
var id = get(record, 'id');
return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data });
},
/**
Called by the store when a record is deleted.
The `deleteRecord` method makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`.
@method deleteRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@returns {Promise} promise
*/
deleteRecord: function(store, type, record) {
var id = get(record, 'id');
return this.ajax(this.buildURL(type.typeKey, id), "DELETE");
},
/**
Builds a URL for a given type and optional ID.
By default, it pluralizes the type's name (for example, 'post'
becomes 'posts' and 'person' becomes 'people'). To override the
pluralization see [pathForType](#method_pathForType).
If an ID is specified, it adds the ID to the path generated
for the type, separated by a `/`.
@method buildURL
@param {String} type
@param {String} id
@returns {String} url
*/
buildURL: function(type, id) {
var url = [],
host = get(this, 'host'),
prefix = this.urlPrefix();
if (type) { url.push(this.pathForType(type)); }
if (id) { url.push(id); }
if (prefix) { url.unshift(prefix); }
url = url.join('/');
if (!host && url) { url = '/' + url; }
return url;
},
/**
@method urlPrefix
@private
@param {String} path
@param {String} parentUrl
@return {String} urlPrefix
*/
urlPrefix: function(path, parentURL) {
var host = get(this, 'host'),
namespace = get(this, 'namespace'),
url = [];
if (path) {
// Absolute path
if (path.charAt(0) === '/') {
if (host) {
path = path.slice(1);
url.push(host);
}
// Relative path
} else if (!/^http(s)?:\/\//.test(path)) {
url.push(parentURL);
}
} else {
if (host) { url.push(host); }
if (namespace) { url.push(namespace); }
}
if (path) {
url.push(path);
}
return url.join('/');
},
/**
Determines the pathname for a given type.
By default, it pluralizes the type's name (for example,
'post' becomes 'posts' and 'person' becomes 'people').
### Pathname customization
For example if you have an object LineItem with an
endpoint of "/line_items/".
```js
DS.RESTAdapter.reopen({
pathForType: function(type) {
var decamelized = Ember.String.decamelize(type);
return Ember.String.pluralize(decamelized);
};
});
```
@method pathForType
@param {String} type
@returns {String} path
**/
pathForType: function(type) {
var camelized = Ember.String.camelize(type);
return Ember.String.pluralize(camelized);
},
/**
Takes an ajax response, and returns a relevant error.
Returning a `DS.InvalidError` from this method will cause the
record to transition into the `invalid` state and make the
`errors` object available on the record.
```javascript
App.ApplicationAdapter = DS.RESTAdapter.extend({
ajaxError: function(jqXHR) {
var error = this._super(jqXHR);
if (jqXHR && jqXHR.status === 422) {
var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
return new DS.InvalidError(jsonErrors);
} else {
return error;
}
}
});
```
Note: As a correctness optimization, the default implementation of
the `ajaxError` method strips out the `then` method from jquery's
ajax response (jqXHR). This is important because the jqXHR's
`then` method fulfills the promise with itself resulting in a
circular "thenable" chain which may cause problems for some
promise libraries.
@method ajaxError
@param {Object} jqXHR
@return {Object} jqXHR
*/
ajaxError: function(jqXHR) {
if (jqXHR) {
jqXHR.then = null;
}
return jqXHR;
},
/**
Takes a URL, an HTTP method and a hash of data, and makes an
HTTP request.
When the server responds with a payload, Ember Data will call into `extractSingle`
or `extractArray` (depending on whether the original query was for one record or
many records).
By default, `ajax` method has the following behavior:
* It sets the response `dataType` to `"json"`
* If the HTTP method is not `"GET"`, it sets the `Content-Type` to be
`application/json; charset=utf-8`
* If the HTTP method is not `"GET"`, it stringifies the data passed in. The
data is the serialized record in the case of a save.
* Registers success and failure handlers.
@method ajax
@private
@param {String} url
@param {String} type The request type GET, POST, PUT, DELETE etc.
@param {Object} hash
@return {Promise} promise
*/
ajax: function(url, type, hash) {
var adapter = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
hash = adapter.ajaxOptions(url, type, hash);
hash.success = function(json) {
Ember.run(null, resolve, json);
};
hash.error = function(jqXHR, textStatus, errorThrown) {
Ember.run(null, reject, adapter.ajaxError(jqXHR));
};
Ember.$.ajax(hash);
}, "DS: RestAdapter#ajax " + type + " to " + url);
},
/**
@method ajaxOptions
@private
@param {String} url
@param {String} type The request type GET, POST, PUT, DELETE etc.
@param {Object} hash
@return {Object} hash
*/
ajaxOptions: function(url, type, hash) {
hash = hash || {};
hash.url = url;
hash.type = type;
hash.dataType = 'json';
hash.context = this;
if (hash.data && type !== 'GET') {
hash.contentType = 'application/json; charset=utf-8';
hash.data = JSON.stringify(hash.data);
}
if (this.headers !== undefined) {
var headers = this.headers;
hash.beforeSend = function (xhr) {
forEach.call(Ember.keys(headers), function(key) {
xhr.setRequestHeader(key, headers[key]);
});
};
}
return hash;
}
});
__exports__["default"] = RESTAdapter;
});
define("ember-data/lib/core",
["exports"],
function(__exports__) {
"use strict";
/**
@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.7+canary.b45e23ba'
@static
*/
DS = Ember.Namespace.create({
VERSION: '1.0.0-beta.7+canary.b45e23ba'
});
if ('undefined' !== typeof window) {
window.DS = DS;
}
if (Ember.libraries) {
Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
}
}
__exports__["default"] = DS;
});
define("ember-data/lib/ext/date",
[],
function() {
"use strict";
/**
@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;
}
});
define("ember-data/lib/initializers",
["./system/store","./serializers","./adapters","./system/debug/debug_adapter","./system/container_proxy","./transforms"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) {
"use strict";
var Store = __dependency1__["default"];
var JSONSerializer = __dependency2__.JSONSerializer;
var RESTSerializer = __dependency2__.RESTSerializer;
var RESTAdapter = __dependency3__.RESTAdapter;
var DebugAdapter = __dependency4