ember-rails 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3744 +1,10 @@
1
- (function() {
2
- window.DS = Ember.Namespace.create({
3
- CURRENT_API_REVISION: 4
4
- });
1
+ // ==========================================================================
2
+ // Project: Ember Data
3
+ // Copyright: ©2011 Living Social Inc. and contributors.
4
+ // License: Licensed under MIT license (see license.js)
5
+ // ==========================================================================
5
6
 
6
- })();
7
7
 
8
8
 
9
-
10
- (function() {
11
- var get = Ember.get, set = Ember.set;
12
-
13
- /**
14
- A record array is an array that contains records of a certain type. The record
15
- array materializes records as needed when they are retrieved for the first
16
- time. You should not create record arrays yourself. Instead, an instance of
17
- DS.RecordArray or its subclasses will be returned by your application's store
18
- in response to queries.
19
- */
20
-
21
- DS.RecordArray = Ember.ArrayProxy.extend({
22
-
23
- /**
24
- The model type contained by this record array.
25
-
26
- @type DS.Model
27
- */
28
- type: null,
29
-
30
- // The array of client ids backing the record array. When a
31
- // record is requested from the record array, the record
32
- // for the client id at the same index is materialized, if
33
- // necessary, by the store.
34
- content: null,
35
-
36
- // The store that created this record array.
37
- store: null,
38
-
39
- init: function() {
40
- set(this, 'recordCache', Ember.A([]));
41
- this._super();
42
- },
43
-
44
- arrayDidChange: function(array, index, removed, added) {
45
- var recordCache = get(this, 'recordCache');
46
- recordCache.replace(index, 0, new Array(added));
47
-
48
- this._super(array, index, removed, added);
49
- },
50
-
51
- arrayWillChange: function(array, index, removed, added) {
52
- this._super(array, index, removed, added);
53
-
54
- var recordCache = get(this, 'recordCache');
55
- recordCache.replace(index, removed);
56
- },
57
-
58
- objectAtContent: function(index) {
59
- var recordCache = get(this, 'recordCache');
60
- var record = recordCache.objectAt(index);
61
-
62
- if (!record) {
63
- var store = get(this, 'store');
64
- var content = get(this, 'content');
65
-
66
- var contentObject = content.objectAt(index);
67
-
68
- if (contentObject !== undefined) {
69
- record = store.findByClientId(get(this, 'type'), contentObject);
70
- recordCache.replace(index, 1, [record]);
71
- }
72
- }
73
-
74
- return record;
75
- }
76
- });
77
-
78
- })();
79
-
80
-
81
-
82
- (function() {
83
- var get = Ember.get;
84
-
85
- DS.FilteredRecordArray = DS.RecordArray.extend({
86
- filterFunction: null,
87
-
88
- replace: function() {
89
- var type = get(this, 'type').toString();
90
- throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
91
- },
92
-
93
- updateFilter: Ember.observer(function() {
94
- var store = get(this, 'store');
95
- store.updateRecordArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
96
- }, 'filterFunction')
97
- });
98
-
99
- })();
100
-
101
-
102
-
103
- (function() {
104
- var get = Ember.get, set = Ember.set;
105
-
106
- DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({
107
- query: null,
108
- isLoaded: false,
109
-
110
- replace: function() {
111
- var type = get(this, 'type').toString();
112
- throw new Error("The result of a server query (on " + type + ") is immutable.");
113
- },
114
-
115
- load: function(array) {
116
- var store = get(this, 'store'), type = get(this, 'type');
117
-
118
- var clientIds = store.loadMany(type, array).clientIds;
119
-
120
- this.beginPropertyChanges();
121
- set(this, 'content', Ember.A(clientIds));
122
- set(this, 'isLoaded', true);
123
- this.endPropertyChanges();
124
- }
125
- });
126
-
127
-
128
- })();
129
-
130
-
131
-
132
- (function() {
133
- var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor;
134
-
135
- var Set = function() {
136
- this.hash = {};
137
- this.list = [];
138
- };
139
-
140
- Set.prototype = {
141
- add: function(item) {
142
- var hash = this.hash,
143
- guid = guidFor(item);
144
-
145
- if (hash.hasOwnProperty(guid)) { return; }
146
-
147
- hash[guid] = true;
148
- this.list.push(item);
149
- },
150
-
151
- remove: function(item) {
152
- var hash = this.hash,
153
- guid = guidFor(item);
154
-
155
- if (!hash.hasOwnProperty(guid)) { return; }
156
-
157
- delete hash[guid];
158
- var list = this.list,
159
- index = Ember.ArrayUtils.indexOf(this, item);
160
-
161
- list.splice(index, 1);
162
- },
163
-
164
- isEmpty: function() {
165
- return this.list.length === 0;
166
- }
167
- };
168
-
169
- var ManyArrayState = Ember.State.extend({
170
- recordWasAdded: function(manager, record) {
171
- var dirty = manager.dirty, observer;
172
- dirty.add(record);
173
-
174
- observer = function() {
175
- if (!get(record, 'isDirty')) {
176
- record.removeObserver('isDirty', observer);
177
- manager.send('childWasSaved', record);
178
- }
179
- };
180
-
181
- record.addObserver('isDirty', observer);
182
- },
183
-
184
- recordWasRemoved: function(manager, record) {
185
- var dirty = manager.dirty, observer;
186
- dirty.add(record);
187
-
188
- observer = function() {
189
- record.removeObserver('isDirty', observer);
190
- if (!get(record, 'isDirty')) { manager.send('childWasSaved', record); }
191
- };
192
-
193
- record.addObserver('isDirty', observer);
194
- }
195
- });
196
-
197
- var states = {
198
- clean: ManyArrayState.create({
199
- isDirty: false,
200
-
201
- recordWasAdded: function(manager, record) {
202
- this._super(manager, record);
203
- manager.goToState('dirty');
204
- },
205
-
206
- update: function(manager, clientIds) {
207
- var manyArray = manager.manyArray;
208
- set(manyArray, 'content', clientIds);
209
- }
210
- }),
211
-
212
- dirty: ManyArrayState.create({
213
- isDirty: true,
214
-
215
- childWasSaved: function(manager, child) {
216
- var dirty = manager.dirty;
217
- dirty.remove(child);
218
-
219
- if (dirty.isEmpty()) { manager.send('arrayBecameSaved'); }
220
- },
221
-
222
- arrayBecameSaved: function(manager) {
223
- manager.goToState('clean');
224
- }
225
- })
226
- };
227
-
228
- DS.ManyArrayStateManager = Ember.StateManager.extend({
229
- manyArray: null,
230
- initialState: 'clean',
231
- states: states,
232
-
233
- init: function() {
234
- this._super();
235
- this.dirty = new Set();
236
- }
237
- });
238
-
239
- })();
240
-
241
-
242
-
243
- (function() {
244
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
245
-
246
- DS.ManyArray = DS.RecordArray.extend({
247
- init: function() {
248
- set(this, 'stateManager', DS.ManyArrayStateManager.create({ manyArray: this }));
249
-
250
- return this._super();
251
- },
252
-
253
- parentRecord: null,
254
-
255
- isDirty: Ember.computed(function() {
256
- return getPath(this, 'stateManager.currentState.isDirty');
257
- }).property('stateManager.currentState').cacheable(),
258
-
259
- fetch: function() {
260
- var clientIds = get(this, 'content'),
261
- store = get(this, 'store'),
262
- type = get(this, 'type');
263
-
264
- var ids = clientIds.map(function(clientId) {
265
- return store.clientIdToId[clientId];
266
- });
267
-
268
- store.fetchMany(type, ids);
269
- },
270
-
271
- // Overrides Ember.Array's replace method to implement
272
- replace: function(index, removed, added) {
273
- var parentRecord = get(this, 'parentRecord');
274
- var pendingParent = parentRecord && !get(parentRecord, 'id');
275
- var stateManager = get(this, 'stateManager');
276
-
277
- // Map the array of record objects into an array of client ids.
278
- added = added.map(function(record) {
279
-
280
-
281
- // If the record to which this many array belongs does not yet
282
- // have an id, notify the newly-added record that it must wait
283
- // for the parent to receive an id before the child can be
284
- // saved.
285
- if (pendingParent) {
286
- record.send('waitingOn', parentRecord);
287
- }
288
-
289
- this.assignInverse(record, parentRecord);
290
-
291
- stateManager.send('recordWasAdded', record);
292
-
293
- return record.get('clientId');
294
- }, this);
295
-
296
- var store = this.store;
297
-
298
- var len = index+removed, record;
299
- for (var i = index; i < len; i++) {
300
- // TODO: null out inverse FK
301
- record = this.objectAt(i);
302
- this.assignInverse(record, parentRecord, true);
303
-
304
- // If we put the child record into a pending state because
305
- // we were waiting on the parent record to get an id, we
306
- // can tell the child it no longer needs to wait.
307
- if (pendingParent) {
308
- record.send('doneWaitingOn', parentRecord);
309
- }
310
-
311
- stateManager.send('recordWasAdded', record);
312
- }
313
-
314
- this._super(index, removed, added);
315
- },
316
-
317
- assignInverse: function(record, parentRecord, remove) {
318
- var associationMap = get(record.constructor, 'associations'),
319
- possibleAssociations = associationMap.get(parentRecord.constructor),
320
- possible, actual;
321
-
322
- if (!possibleAssociations) { return; }
323
-
324
- for (var i = 0, l = possibleAssociations.length; i < l; i++) {
325
- possible = possibleAssociations[i];
326
-
327
- if (possible.kind === 'belongsTo') {
328
- actual = possible;
329
- break;
330
- }
331
- }
332
-
333
- if (actual) {
334
- set(record, actual.name, remove ? null : parentRecord);
335
- }
336
- },
337
-
338
- // Create a child record within the parentRecord
339
- createRecord: function(hash, transaction) {
340
- var parentRecord = get(this, 'parentRecord'),
341
- store = get(parentRecord, 'store'),
342
- type = get(this, 'type'),
343
- record;
344
-
345
- transaction = transaction || get(parentRecord, 'transaction');
346
-
347
- record = store.createRecord.call(store, type, hash, transaction);
348
- this.pushObject(record);
349
-
350
- return record;
351
- }
352
- });
353
-
354
- })();
355
-
356
-
357
-
358
- (function() {
359
-
360
- })();
361
-
362
-
363
-
364
- (function() {
365
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
366
-
367
- /**
368
- A transaction allows you to collect multiple records into a unit of work
369
- that can be committed or rolled back as a group.
370
-
371
- For example, if a record has local modifications that have not yet
372
- been saved, calling `commit()` on its transaction will cause those
373
- modifications to be sent to the adapter to be saved. Calling
374
- `rollback()` on its transaction would cause all of the modifications to
375
- be discarded and the record to return to the last known state before
376
- changes were made.
377
-
378
- If a newly created record's transaction is rolled back, it will
379
- immediately transition to the deleted state.
380
-
381
- If you do not explicitly create a transaction, a record is assigned to
382
- an implicit transaction called the default transaction. In these cases,
383
- you can treat your application's instance of `DS.Store` as a transaction
384
- and call the `commit()` and `rollback()` methods on the store itself.
385
-
386
- Once a record has been successfully committed or rolled back, it will
387
- be moved back to the implicit transaction. Because it will now be in
388
- a clean state, it can be moved to a new transaction if you wish.
389
-
390
- ### Creating a Transaction
391
-
392
- To create a new transaction, call the `transaction()` method of your
393
- application's `DS.Store` instance:
394
-
395
- var transaction = App.store.transaction();
396
-
397
- This will return a new instance of `DS.Transaction` with no records
398
- yet assigned to it.
399
-
400
- ### Adding Existing Records
401
-
402
- Add records to a transaction using the `add()` method:
403
-
404
- record = App.store.find(Person, 1);
405
- transaction.add(record);
406
-
407
- Note that only records whose `isDirty` flag is `false` may be added
408
- to a transaction. Once modifications to a record have been made
409
- (its `isDirty` flag is `true`), it is not longer able to be added to
410
- a transaction.
411
-
412
- ### Creating New Records
413
-
414
- Because newly created records are dirty from the time they are created,
415
- and because dirty records can not be added to a transaction, you must
416
- use the `createRecord()` method to assign new records to a transaction.
417
-
418
- For example, instead of this:
419
-
420
- var transaction = store.transaction();
421
- var person = Person.createRecord({ name: "Steve" });
422
-
423
- // won't work because person is dirty
424
- transaction.add(person);
425
-
426
- Call `createRecord()` on the transaction directly:
427
-
428
- var transaction = store.transaction();
429
- transaction.createRecord(Person, { name: "Steve" });
430
-
431
- ### Asynchronous Commits
432
-
433
- Typically, all of the records in a transaction will be committed
434
- together. However, new records that have a dependency on other new
435
- records need to wait for their parent record to be saved and assigned an
436
- ID. In that case, the child record will continue to live in the
437
- transaction until its parent is saved, at which time the transaction will
438
- attempt to commit again.
439
-
440
- For this reason, you should not re-use transactions once you have committed
441
- them. Always make a new transaction and move the desired records to it before
442
- calling commit.
443
- */
444
-
445
- DS.Transaction = Ember.Object.extend({
446
- /**
447
- @private
448
-
449
- Creates the bucket data structure used to segregate records by
450
- type.
451
- */
452
- init: function() {
453
- set(this, 'buckets', {
454
- clean: Ember.Map.create(),
455
- created: Ember.Map.create(),
456
- updated: Ember.Map.create(),
457
- deleted: Ember.Map.create(),
458
- inflight: Ember.Map.create()
459
- });
460
- },
461
-
462
- /**
463
- Creates a new record of the given type and assigns it to the transaction
464
- on which the method was called.
465
-
466
- This is useful as only clean records can be added to a transaction and
467
- new records created using other methods immediately become dirty.
468
-
469
- @param {DS.Model} type the model type to create
470
- @param {Object} hash the data hash to assign the new record
471
- */
472
- createRecord: function(type, hash) {
473
- var store = get(this, 'store');
474
-
475
- return store.createRecord(type, hash, this);
476
- },
477
-
478
- /**
479
- Adds an existing record to this transaction. Only records without
480
- modficiations (i.e., records whose `isDirty` property is `false`)
481
- can be added to a transaction.
482
-
483
- @param {DS.Model} record the record to add to the transaction
484
- */
485
- add: function(record) {
486
- // we could probably make this work if someone has a valid use case. Do you?
487
-
488
-
489
- var recordTransaction = get(record, 'transaction'),
490
- defaultTransaction = getPath(this, 'store.defaultTransaction');
491
-
492
-
493
- this.adoptRecord(record);
494
- },
495
-
496
- /**
497
- Commits the transaction, which causes all of the modified records that
498
- belong to the transaction to be sent to the adapter to be saved.
499
-
500
- Once you call `commit()` on a transaction, you should not re-use it.
501
-
502
- When a record is saved, it will be removed from this transaction and
503
- moved back to the store's default transaction.
504
- */
505
- commit: function() {
506
- var self = this,
507
- iterate;
508
-
509
- iterate = function(bucketType, fn, binding) {
510
- var dirty = self.bucketForType(bucketType);
511
-
512
- dirty.forEach(function(type, records) {
513
- if (records.isEmpty()) { return; }
514
-
515
- var array = [];
516
-
517
- records.forEach(function(record) {
518
- record.send('willCommit');
519
-
520
- if (get(record, 'isPending') === false) {
521
- array.push(record);
522
- }
523
- });
524
-
525
- fn.call(binding, type, array);
526
- });
527
- };
528
-
529
- var commitDetails = {
530
- updated: {
531
- eachType: function(fn, binding) { iterate('updated', fn, binding); }
532
- },
533
-
534
- created: {
535
- eachType: function(fn, binding) { iterate('created', fn, binding); }
536
- },
537
-
538
- deleted: {
539
- eachType: function(fn, binding) { iterate('deleted', fn, binding); }
540
- }
541
- };
542
-
543
- var store = get(this, 'store');
544
- var adapter = get(store, '_adapter');
545
-
546
- this.removeCleanRecords();
547
-
548
- if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
549
- else { throw fmt("Adapter is either null or does not implement `commit` method", this); }
550
- },
551
-
552
- /**
553
- Rolling back a transaction resets the records that belong to
554
- that transaction.
555
-
556
- Updated records have their properties reset to the last known
557
- value from the persistence layer. Deleted records are reverted
558
- to a clean, non-deleted state. Newly created records immediately
559
- become deleted, and are not sent to the adapter to be persisted.
560
-
561
- After the transaction is rolled back, any records that belong
562
- to it will return to the store's default transaction, and the
563
- current transaction should not be used again.
564
- */
565
- rollback: function() {
566
- var store = get(this, 'store'),
567
- dirty;
568
-
569
- // Loop through all of the records in each of the dirty states
570
- // and initiate a rollback on them. As a side effect of telling
571
- // the record to roll back, it should also move itself out of
572
- // the dirty bucket and into the clean bucket.
573
- ['created', 'updated', 'deleted', 'inflight'].forEach(function(bucketType) {
574
- dirty = this.bucketForType(bucketType);
575
-
576
- dirty.forEach(function(type, records) {
577
- records.forEach(function(record) {
578
- record.send('rollback');
579
- });
580
- });
581
- }, this);
582
-
583
- // Now that all records in the transaction are guaranteed to be
584
- // clean, migrate them all to the store's default transaction.
585
- this.removeCleanRecords();
586
- },
587
-
588
- /**
589
- @private
590
-
591
- Removes a record from this transaction and back to the store's
592
- default transaction.
593
-
594
- Note: This method is private for now, but should probably be exposed
595
- in the future once we have stricter error checking (for example, in the
596
- case of the record being dirty).
597
-
598
- @param {DS.Model} record
599
- */
600
- remove: function(record) {
601
- var defaultTransaction = getPath(this, 'store.defaultTransaction');
602
- defaultTransaction.adoptRecord(record);
603
- },
604
-
605
- /**
606
- @private
607
-
608
- Removes all of the records in the transaction's clean bucket.
609
- */
610
- removeCleanRecords: function() {
611
- var clean = this.bucketForType('clean'),
612
- self = this;
613
-
614
- clean.forEach(function(type, records) {
615
- records.forEach(function(record) {
616
- self.remove(record);
617
- });
618
- });
619
- },
620
-
621
- /**
622
- @private
623
-
624
- Returns the bucket for the given bucket type. For example, you might call
625
- `this.bucketForType('updated')` to get the `Ember.Map` that contains all
626
- of the records that have changes pending.
627
-
628
- @param {String} bucketType the type of bucket
629
- @returns Ember.Map
630
- */
631
- bucketForType: function(bucketType) {
632
- var buckets = get(this, 'buckets');
633
-
634
- return get(buckets, bucketType);
635
- },
636
-
637
- /**
638
- @private
639
-
640
- This method moves a record into a different transaction without the normal
641
- checks that ensure that the user is not doing something weird, like moving
642
- a dirty record into a new transaction.
643
-
644
- It is designed for internal use, such as when we are moving a clean record
645
- into a new transaction when the transaction is committed.
646
-
647
- This method must not be called unless the record is clean.
648
-
649
- @param {DS.Model} record
650
- */
651
- adoptRecord: function(record) {
652
- var oldTransaction = get(record, 'transaction');
653
-
654
- if (oldTransaction) {
655
- oldTransaction.removeFromBucket('clean', record);
656
- }
657
-
658
- this.addToBucket('clean', record);
659
- set(record, 'transaction', this);
660
- },
661
-
662
- /**
663
- @private
664
-
665
- Adds a record to the named bucket.
666
-
667
- @param {String} bucketType one of `clean`, `created`, `updated`, or `deleted`
668
- */
669
- addToBucket: function(bucketType, record) {
670
- var bucket = this.bucketForType(bucketType),
671
- type = record.constructor;
672
-
673
- var records = bucket.get(type);
674
-
675
- if (!records) {
676
- records = Ember.OrderedSet.create();
677
- bucket.set(type, records);
678
- }
679
-
680
- records.add(record);
681
- },
682
-
683
- /**
684
- @private
685
-
686
- Removes a record from the named bucket.
687
-
688
- @param {String} bucketType one of `clean`, `created`, `updated`, or `deleted`
689
- */
690
- removeFromBucket: function(bucketType, record) {
691
- var bucket = this.bucketForType(bucketType),
692
- type = record.constructor;
693
-
694
- var records = bucket.get(type);
695
- records.remove(record);
696
- },
697
-
698
- /**
699
- @private
700
-
701
- Called by a record's state manager to indicate that the record has entered
702
- a dirty state. The record will be moved from the `clean` bucket and into
703
- the appropriate dirty bucket.
704
-
705
- @param {String} bucketType one of `created`, `updated`, or `deleted`
706
- */
707
- recordBecameDirty: function(bucketType, record) {
708
- this.removeFromBucket('clean', record);
709
- this.addToBucket(bucketType, record);
710
- },
711
-
712
- /**
713
- @private
714
-
715
- Called by a record's state manager to indicate that the record has entered
716
- inflight state. The record will be moved from its current dirty bucket and into
717
- the `inflight` bucket.
718
-
719
- @param {String} bucketType one of `created`, `updated`, or `deleted`
720
- */
721
- recordBecameInFlight: function(kind, record) {
722
- this.removeFromBucket(kind, record);
723
- this.addToBucket('inflight', record);
724
- },
725
-
726
- /**
727
- @private
728
-
729
- Called by a record's state manager to indicate that the record has entered
730
- a clean state. The record will be moved from its current dirty or inflight bucket and into
731
- the `clean` bucket.
732
-
733
- @param {String} bucketType one of `created`, `updated`, or `deleted`
734
- */
735
- recordBecameClean: function(kind, record) {
736
- this.removeFromBucket(kind, record);
737
-
738
- this.remove(record);
739
- }
740
- });
741
-
742
- })();
743
-
744
-
745
-
746
- (function() {
747
- /*globals Ember*/
748
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
749
-
750
- var DATA_PROXY = {
751
- get: function(name) {
752
- return this.savedData[name];
753
- }
754
- };
755
-
756
- // These values are used in the data cache when clientIds are
757
- // needed but the underlying data has not yet been loaded by
758
- // the server.
759
- var UNLOADED = 'unloaded';
760
- var LOADING = 'loading';
761
-
762
- // Implementors Note:
763
- //
764
- // The variables in this file are consistently named according to the following
765
- // scheme:
766
- //
767
- // * +id+ means an identifier managed by an external source, provided inside the
768
- // data hash provided by that source.
769
- // * +clientId+ means a transient numerical identifier generated at runtime by
770
- // the data store. It is important primarily because newly created objects may
771
- // not yet have an externally generated id.
772
- // * +type+ means a subclass of DS.Model.
773
-
774
- /**
775
- The store contains all of the hashes for records loaded from the server.
776
- It is also responsible for creating instances of DS.Model when you request one
777
- of these data hashes, so that they can be bound to in your Handlebars templates.
778
-
779
- Create a new store like this:
780
-
781
- MyApp.store = DS.Store.create();
782
-
783
- You can retrieve DS.Model instances from the store in several ways. To retrieve
784
- a record for a specific id, use the `find()` method:
785
-
786
- var record = MyApp.store.find(MyApp.Contact, 123);
787
-
788
- By default, the store will talk to your backend using a standard REST mechanism.
789
- You can customize how the store talks to your backend by specifying a custom adapter:
790
-
791
- MyApp.store = DS.Store.create({
792
- adapter: 'MyApp.CustomAdapter'
793
- });
794
-
795
- You can learn more about writing a custom adapter by reading the `DS.Adapter`
796
- documentation.
797
- */
798
- DS.Store = Ember.Object.extend({
799
-
800
- /**
801
- Many methods can be invoked without specifying which store should be used.
802
- In those cases, the first store created will be used as the default. If
803
- an application has multiple stores, it should specify which store to use
804
- when performing actions, such as finding records by id.
805
-
806
- The init method registers this store as the default if none is specified.
807
- */
808
- init: function() {
809
- // Enforce API revisioning. See BREAKING_CHANGES.md for more.
810
- var revision = get(this, 'revision');
811
-
812
- if (revision !== DS.CURRENT_API_REVISION && !Ember.ENV.TESTING) {
813
- throw new Error("Error: The Ember Data library has had breaking API changes since the last time you updated the library. Please review the list of breaking changes at https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md, then update your store's `revision` property to " + DS.CURRENT_API_REVISION);
814
- }
815
-
816
- if (!get(DS, 'defaultStore') || get(this, 'isDefaultStore')) {
817
- set(DS, 'defaultStore', this);
818
- }
819
-
820
- // internal bookkeeping; not observable
821
- this.typeMaps = {};
822
- this.recordCache = [];
823
- this.clientIdToId = {};
824
- this.recordArraysByClientId = {};
825
-
826
- set(this, 'defaultTransaction', this.transaction());
827
-
828
- return this._super();
829
- },
830
-
831
- /**
832
- Returns a new transaction scoped to this store.
833
-
834
- @see {DS.Transaction}
835
- @returns DS.Transaction
836
- */
837
- transaction: function() {
838
- return DS.Transaction.create({ store: this });
839
- },
840
-
841
- /**
842
- @private
843
-
844
- This is used only by the record's DataProxy. Do not use this directly.
845
- */
846
- dataForRecord: function(record) {
847
- var type = record.constructor,
848
- clientId = get(record, 'clientId'),
849
- typeMap = this.typeMapFor(type);
850
-
851
- return typeMap.cidToHash[clientId];
852
- },
853
-
854
- /**
855
- The adapter to use to communicate to a backend server or other persistence layer.
856
-
857
- This can be specified as an instance, a class, or a property path that specifies
858
- where the adapter can be located.
859
-
860
- @property {DS.Adapter|String}
861
- */
862
- adapter: null,
863
-
864
- /**
865
- @private
866
-
867
- This property returns the adapter, after resolving a possible String.
868
-
869
- @returns DS.Adapter
870
- */
871
- _adapter: Ember.computed(function() {
872
- var adapter = get(this, 'adapter');
873
- if (typeof adapter === 'string') {
874
- return getPath(this, adapter, false) || getPath(window, adapter);
875
- }
876
- return adapter;
877
- }).property('adapter').cacheable(),
878
-
879
- // A monotonically increasing number to be used to uniquely identify
880
- // data hashes and records.
881
- clientIdCounter: 1,
882
-
883
- // .....................
884
- // . CREATE NEW RECORD .
885
- // .....................
886
-
887
- /**
888
- Create a new record in the current store. The properties passed
889
- to this method are set on the newly created record.
890
-
891
- @param {subclass of DS.Model} type
892
- @param {Object} properties a hash of properties to set on the
893
- newly created record.
894
- @returns DS.Model
895
- */
896
- createRecord: function(type, properties, transaction) {
897
- properties = properties || {};
898
-
899
- // Create a new instance of the model `type` and put it
900
- // into the specified `transaction`. If no transaction is
901
- // specified, the default transaction will be used.
902
- //
903
- // NOTE: A `transaction` is specified when the
904
- // `transaction.createRecord` API is used.
905
- var record = type._create({
906
- store: this
907
- });
908
-
909
- transaction = transaction || get(this, 'defaultTransaction');
910
- transaction.adoptRecord(record);
911
-
912
- // Extract the primary key from the `properties` hash,
913
- // based on the `primaryKey` for the model type.
914
- var primaryKey = get(record, 'primaryKey'),
915
- id = properties[primaryKey] || null;
916
-
917
- // If the passed properties do not include a primary key,
918
- // give the adapter an opportunity to generate one.
919
- var adapter;
920
- if (Ember.none(id)) {
921
- adapter = get(this, 'adapter');
922
- if (adapter && adapter.generateIdForRecord) {
923
- id = adapter.generateIdForRecord(this, record);
924
- properties.id = id;
925
- }
926
- }
927
-
928
- var hash = {}, clientId;
929
-
930
- // Push the hash into the store. If present, associate the
931
- // extracted `id` with the hash.
932
- clientId = this.pushHash(hash, id, type);
933
-
934
- record.send('didChangeData');
935
-
936
- var recordCache = get(this, 'recordCache');
937
-
938
- // Now that we have a clientId, attach it to the record we
939
- // just created.
940
- set(record, 'clientId', clientId);
941
-
942
- // Store the record we just created in the record cache for
943
- // this clientId.
944
- recordCache[clientId] = record;
945
-
946
- // Set the properties specified on the record.
947
- record.setProperties(properties);
948
-
949
- this.updateRecordArrays(type, clientId, get(record, 'data'));
950
-
951
- return record;
952
- },
953
-
954
- // .................
955
- // . DELETE RECORD .
956
- // .................
957
-
958
- /**
959
- For symmetry, a record can be deleted via the store.
960
-
961
- @param {DS.Model} record
962
- */
963
- deleteRecord: function(record) {
964
- record.send('deleteRecord');
965
- },
966
-
967
- // ................
968
- // . FIND RECORDS .
969
- // ................
970
-
971
- /**
972
- This is the main entry point into finding records. The first
973
- parameter to this method is always a subclass of `DS.Model`.
974
-
975
- You can use the `find` method on a subclass of `DS.Model`
976
- directly if your application only has one store. For
977
- example, instead of `store.find(App.Person, 1)`, you could
978
- say `App.Person.find(1)`.
979
-
980
- ---
981
-
982
- To find a record by ID, pass the `id` as the second parameter:
983
-
984
- store.find(App.Person, 1);
985
- App.Person.find(1);
986
-
987
- If the record with that `id` had not previously been loaded,
988
- the store will return an empty record immediately and ask
989
- the adapter to find the data by calling the adapter's `find`
990
- method.
991
-
992
- The `find` method will always return the same object for a
993
- given type and `id`. To check whether the adapter has populated
994
- a record, you can check its `isLoaded` property.
995
-
996
- ---
997
-
998
- To find all records for a type, call `find` with no additional
999
- parameters:
1000
-
1001
- store.find(App.Person);
1002
- App.Person.find();
1003
-
1004
- This will return a `RecordArray` representing all known records
1005
- for the given type and kick off a request to the adapter's
1006
- `findAll` method to load any additional records for the type.
1007
-
1008
- The `RecordArray` returned by `find()` is live. If any more
1009
- records for the type are added at a later time through any
1010
- mechanism, it will automatically update to reflect the change.
1011
-
1012
- ---
1013
-
1014
- To find a record by a query, call `find` with a hash as the
1015
- second parameter:
1016
-
1017
- store.find(App.Person, { page: 1 });
1018
- App.Person.find({ page: 1 });
1019
-
1020
- This will return a `RecordArray` immediately, but it will always
1021
- be an empty `RecordArray` at first. It will call the adapter's
1022
- `findQuery` method, which will populate the `RecordArray` once
1023
- the server has returned results.
1024
-
1025
- You can check whether a query results `RecordArray` has loaded
1026
- by checking its `isLoaded` property.
1027
- */
1028
- find: function(type, id, query) {
1029
- if (id === undefined) {
1030
- return this.findAll(type);
1031
- }
1032
-
1033
- if (query !== undefined) {
1034
- return this.findMany(type, id, query);
1035
- } else if (Ember.typeOf(id) === 'object') {
1036
- return this.findQuery(type, id);
1037
- }
1038
-
1039
- if (Ember.isArray(id)) {
1040
- return this.findMany(type, id);
1041
- }
1042
-
1043
- var clientId = this.typeMapFor(type).idToCid[id];
1044
-
1045
- return this.findByClientId(type, clientId, id);
1046
- },
1047
-
1048
- findByClientId: function(type, clientId, id) {
1049
- var recordCache = get(this, 'recordCache'),
1050
- dataCache = this.typeMapFor(type).cidToHash,
1051
- record;
1052
-
1053
- // If there is already a clientId assigned for this
1054
- // type/id combination, try to find an existing
1055
- // record for that id and return. Otherwise,
1056
- // materialize a new record and set its data to the
1057
- // value we already have.
1058
- if (clientId !== undefined) {
1059
- record = recordCache[clientId];
1060
-
1061
- if (!record) {
1062
- // create a new instance of the model type in the
1063
- // 'isLoading' state
1064
- record = this.materializeRecord(type, clientId);
1065
-
1066
- if (typeof dataCache[clientId] === 'object') {
1067
- record.send('didChangeData');
1068
- }
1069
- }
1070
- } else {
1071
- clientId = this.pushHash(LOADING, id, type);
1072
-
1073
- // create a new instance of the model type in the
1074
- // 'isLoading' state
1075
- record = this.materializeRecord(type, clientId);
1076
-
1077
- // let the adapter set the data, possibly async
1078
- var adapter = get(this, '_adapter');
1079
- if (adapter && adapter.find) { adapter.find(this, type, id); }
1080
- else { throw fmt("Adapter is either null or does not implement `find` method", this); }
1081
- }
1082
-
1083
- return record;
1084
- },
1085
-
1086
- /**
1087
- @private
1088
-
1089
- Ask the adapter to fetch IDs that are not already loaded.
1090
-
1091
- This method will convert `id`s to `clientId`s, filter out
1092
- `clientId`s that already have a data hash present, and pass
1093
- the remaining `id`s to the adapter.
1094
-
1095
- @param {Class} type A model class
1096
- @param {Array} ids An array of ids
1097
- @param {Object} query
1098
-
1099
- @returns {Array} An Array of all clientIds for the
1100
- specified ids.
1101
- */
1102
- fetchMany: function(type, ids, query) {
1103
- var typeMap = this.typeMapFor(type),
1104
- idToClientIdMap = typeMap.idToCid,
1105
- dataCache = typeMap.cidToHash,
1106
- data = typeMap.cidToHash,
1107
- needed;
1108
-
1109
- var clientIds = Ember.A([]);
1110
-
1111
- if (ids) {
1112
- needed = [];
1113
-
1114
- ids.forEach(function(id) {
1115
- // Get the clientId for the given id
1116
- var clientId = idToClientIdMap[id];
1117
-
1118
- // If there is no `clientId` yet
1119
- if (clientId === undefined) {
1120
- // Create a new `clientId`, marking its data hash
1121
- // as loading. Once the adapter returns the data
1122
- // hash, it will be updated
1123
- clientId = this.pushHash(LOADING, id, type);
1124
- needed.push(id);
1125
-
1126
- // If there is a clientId, but its data hash is
1127
- // marked as unloaded (this happens when a
1128
- // hasMany association creates clientIds for its
1129
- // referenced ids before they were loaded)
1130
- } else if (clientId && data[clientId] === UNLOADED) {
1131
- // change the data hash marker to loading
1132
- dataCache[clientId] = LOADING;
1133
- needed.push(id);
1134
- }
1135
-
1136
- // this method is expected to return a list of
1137
- // all of the clientIds for the specified ids,
1138
- // unconditionally add it.
1139
- clientIds.push(clientId);
1140
- }, this);
1141
- } else {
1142
- needed = null;
1143
- }
1144
-
1145
- // If there are any needed ids, ask the adapter to load them
1146
- if ((needed && get(needed, 'length') > 0) || query) {
1147
- var adapter = get(this, '_adapter');
1148
- if (adapter && adapter.findMany) { adapter.findMany(this, type, needed, query); }
1149
- else { throw fmt("Adapter is either null or does not implement `findMany` method", this); }
1150
- }
1151
-
1152
- return clientIds;
1153
- },
1154
-
1155
- /** @private
1156
- */
1157
- findMany: function(type, ids, query) {
1158
- var clientIds = this.fetchMany(type, ids, query);
1159
-
1160
- return this.createManyArray(type, clientIds);
1161
- },
1162
-
1163
- findQuery: function(type, query) {
1164
- var array = DS.AdapterPopulatedRecordArray.create({ type: type, content: Ember.A([]), store: this });
1165
- var adapter = get(this, '_adapter');
1166
- if (adapter && adapter.findQuery) { adapter.findQuery(this, type, query, array); }
1167
- else { throw fmt("Adapter is either null or does not implement `findQuery` method", this); }
1168
- return array;
1169
- },
1170
-
1171
- findAll: function(type) {
1172
-
1173
- var typeMap = this.typeMapFor(type),
1174
- findAllCache = typeMap.findAllCache;
1175
-
1176
- if (findAllCache) { return findAllCache; }
1177
-
1178
- var array = DS.RecordArray.create({ type: type, content: Ember.A([]), store: this });
1179
- this.registerRecordArray(array, type);
1180
-
1181
- var adapter = get(this, '_adapter');
1182
- if (adapter && adapter.findAll) { adapter.findAll(this, type); }
1183
-
1184
- typeMap.findAllCache = array;
1185
- return array;
1186
- },
1187
-
1188
- filter: function(type, query, filter) {
1189
- // allow an optional server query
1190
- if (arguments.length === 3) {
1191
- this.findQuery(type, query);
1192
- } else if (arguments.length === 2) {
1193
- filter = query;
1194
- }
1195
-
1196
- var array = DS.FilteredRecordArray.create({ type: type, content: Ember.A([]), store: this, filterFunction: filter });
1197
-
1198
- this.registerRecordArray(array, type, filter);
1199
-
1200
- return array;
1201
- },
1202
-
1203
- // ............
1204
- // . UPDATING .
1205
- // ............
1206
-
1207
- hashWasUpdated: function(type, clientId, record) {
1208
- // Because hash updates are invoked at the end of the run loop,
1209
- // it is possible that a record might be deleted after its hash
1210
- // has been modified and this method was scheduled to be called.
1211
- //
1212
- // If that's the case, the record would have already been removed
1213
- // from all record arrays; calling updateRecordArrays would just
1214
- // add it back. If the record is deleted, just bail. It shouldn't
1215
- // give us any more trouble after this.
1216
-
1217
- if (get(record, 'isDeleted')) { return; }
1218
- this.updateRecordArrays(type, clientId, get(record, 'data'));
1219
- },
1220
-
1221
- // ..............
1222
- // . PERSISTING .
1223
- // ..............
1224
-
1225
- commit: function() {
1226
- var defaultTransaction = get(this, 'defaultTransaction');
1227
- set(this, 'defaultTransaction', this.transaction());
1228
-
1229
- defaultTransaction.commit();
1230
- },
1231
-
1232
- didUpdateRecords: function(array, hashes) {
1233
- if (hashes) {
1234
- array.forEach(function(record, idx) {
1235
- this.didUpdateRecord(record, hashes[idx]);
1236
- }, this);
1237
- } else {
1238
- array.forEach(function(record) {
1239
- this.didUpdateRecord(record);
1240
- }, this);
1241
- }
1242
- },
1243
-
1244
- didUpdateRecord: function(record, hash) {
1245
- if (hash) {
1246
- var clientId = get(record, 'clientId'),
1247
- dataCache = this.typeMapFor(record.constructor).cidToHash;
1248
-
1249
- dataCache[clientId] = hash;
1250
- record.send('didChangeData');
1251
- record.hashWasUpdated();
1252
- } else {
1253
- record.send('didSaveData');
1254
- }
1255
-
1256
- record.send('didCommit');
1257
- },
1258
-
1259
- didDeleteRecords: function(array) {
1260
- array.forEach(function(record) {
1261
- record.send('didCommit');
1262
- });
1263
- },
1264
-
1265
- didDeleteRecord: function(record) {
1266
- record.send('didCommit');
1267
- },
1268
-
1269
- _didCreateRecord: function(record, hash, typeMap, clientId, primaryKey) {
1270
- var recordData = get(record, 'data'), id, changes;
1271
-
1272
- if (hash) {
1273
- typeMap.cidToHash[clientId] = hash;
1274
-
1275
- // If the server returns a hash, we assume that the server's version
1276
- // of the data supercedes the local changes.
1277
- record.beginPropertyChanges();
1278
- record.send('didChangeData');
1279
- recordData.adapterDidUpdate();
1280
- record.hashWasUpdated();
1281
- record.endPropertyChanges();
1282
-
1283
- id = hash[primaryKey];
1284
-
1285
- typeMap.idToCid[id] = clientId;
1286
- this.clientIdToId[clientId] = id;
1287
- } else {
1288
- recordData.commit();
1289
- }
1290
-
1291
- record.send('didCommit');
1292
- },
1293
-
1294
-
1295
- didCreateRecords: function(type, array, hashes) {
1296
- var primaryKey = type.proto().primaryKey,
1297
- typeMap = this.typeMapFor(type),
1298
- clientId;
1299
-
1300
- for (var i=0, l=get(array, 'length'); i<l; i++) {
1301
- var record = array[i], hash = hashes[i];
1302
- clientId = get(record, 'clientId');
1303
-
1304
- this._didCreateRecord(record, hash, typeMap, clientId, primaryKey);
1305
- }
1306
- },
1307
-
1308
- didCreateRecord: function(record, hash) {
1309
- var type = record.constructor,
1310
- typeMap = this.typeMapFor(type),
1311
- clientId, primaryKey;
1312
-
1313
- // The hash is optional, but if it is not provided, the client must have
1314
- // provided a primary key.
1315
-
1316
- primaryKey = type.proto().primaryKey;
1317
-
1318
- // TODO: Make Ember.assert more flexible
1319
- if (hash) {
1320
-
1321
- } else {
1322
-
1323
- }
1324
-
1325
- clientId = get(record, 'clientId');
1326
-
1327
- this._didCreateRecord(record, hash, typeMap, clientId, primaryKey);
1328
- },
1329
-
1330
- recordWasInvalid: function(record, errors) {
1331
- record.send('becameInvalid', errors);
1332
- },
1333
-
1334
- // .................
1335
- // . RECORD ARRAYS .
1336
- // .................
1337
-
1338
- registerRecordArray: function(array, type, filter) {
1339
- var recordArrays = this.typeMapFor(type).recordArrays;
1340
-
1341
- recordArrays.push(array);
1342
-
1343
- this.updateRecordArrayFilter(array, type, filter);
1344
- },
1345
-
1346
- createManyArray: function(type, clientIds) {
1347
- var array = DS.ManyArray.create({ type: type, content: clientIds, store: this });
1348
-
1349
- clientIds.forEach(function(clientId) {
1350
- var recordArrays = this.recordArraysForClientId(clientId);
1351
- recordArrays.add(array);
1352
- }, this);
1353
-
1354
- return array;
1355
- },
1356
-
1357
- updateRecordArrayFilter: function(array, type, filter) {
1358
- var typeMap = this.typeMapFor(type),
1359
- dataCache = typeMap.cidToHash,
1360
- clientIds = typeMap.clientIds,
1361
- clientId, hash, proxy;
1362
-
1363
- var recordCache = get(this, 'recordCache'), record;
1364
-
1365
- for (var i=0, l=clientIds.length; i<l; i++) {
1366
- clientId = clientIds[i];
1367
-
1368
- hash = dataCache[clientId];
1369
- if (typeof hash === 'object') {
1370
- if (record = recordCache[clientId]) {
1371
- proxy = get(record, 'data');
1372
- } else {
1373
- DATA_PROXY.savedData = hash;
1374
- proxy = DATA_PROXY;
1375
- }
1376
-
1377
- this.updateRecordArray(array, filter, type, clientId, proxy);
1378
- }
1379
- }
1380
- },
1381
-
1382
- updateRecordArrays: function(type, clientId, dataProxy) {
1383
- var recordArrays = this.typeMapFor(type).recordArrays,
1384
- filter;
1385
-
1386
- recordArrays.forEach(function(array) {
1387
- filter = get(array, 'filterFunction');
1388
- this.updateRecordArray(array, filter, type, clientId, dataProxy);
1389
- }, this);
1390
- },
1391
-
1392
- updateRecordArray: function(array, filter, type, clientId, dataProxy) {
1393
- var shouldBeInArray;
1394
-
1395
- if (!filter) {
1396
- shouldBeInArray = true;
1397
- } else {
1398
- shouldBeInArray = filter(dataProxy);
1399
- }
1400
-
1401
- var content = get(array, 'content');
1402
- var alreadyInArray = content.indexOf(clientId) !== -1;
1403
-
1404
- var recordArrays = this.recordArraysForClientId(clientId);
1405
-
1406
- if (shouldBeInArray && !alreadyInArray) {
1407
- recordArrays.add(array);
1408
- content.pushObject(clientId);
1409
- } else if (!shouldBeInArray && alreadyInArray) {
1410
- recordArrays.remove(array);
1411
- content.removeObject(clientId);
1412
- }
1413
- },
1414
-
1415
- removeFromRecordArrays: function(record) {
1416
- var clientId = get(record, 'clientId');
1417
- var recordArrays = this.recordArraysForClientId(clientId);
1418
-
1419
- recordArrays.forEach(function(array) {
1420
- var content = get(array, 'content');
1421
- content.removeObject(clientId);
1422
- });
1423
- },
1424
-
1425
- // ............
1426
- // . INDEXING .
1427
- // ............
1428
-
1429
- recordArraysForClientId: function(clientId) {
1430
- var recordArrays = get(this, 'recordArraysByClientId');
1431
- var ret = recordArrays[clientId];
1432
-
1433
- if (!ret) {
1434
- ret = recordArrays[clientId] = Ember.OrderedSet.create();
1435
- }
1436
-
1437
- return ret;
1438
- },
1439
-
1440
- typeMapFor: function(type) {
1441
- var typeMaps = get(this, 'typeMaps');
1442
- var guidForType = Ember.guidFor(type);
1443
-
1444
- var typeMap = typeMaps[guidForType];
1445
-
1446
- if (typeMap) {
1447
- return typeMap;
1448
- } else {
1449
- return (typeMaps[guidForType] =
1450
- {
1451
- idToCid: {},
1452
- clientIds: [],
1453
- cidToHash: {},
1454
- recordArrays: []
1455
- });
1456
- }
1457
- },
1458
-
1459
- /** @private
1460
-
1461
- For a given type and id combination, returns the client id used by the store.
1462
- If no client id has been assigned yet, one will be created and returned.
1463
-
1464
- @param {DS.Model} type
1465
- @param {String|Number} id
1466
- */
1467
- clientIdForId: function(type, id) {
1468
- var clientId = this.typeMapFor(type).idToCid[id];
1469
-
1470
- if (clientId !== undefined) { return clientId; }
1471
-
1472
- return this.pushHash(UNLOADED, id, type);
1473
- },
1474
-
1475
- // ................
1476
- // . LOADING DATA .
1477
- // ................
1478
-
1479
- /**
1480
- Load a new data hash into the store for a given id and type combination.
1481
- If data for that record had been loaded previously, the new information
1482
- overwrites the old.
1483
-
1484
- If the record you are loading data for has outstanding changes that have not
1485
- yet been saved, an exception will be thrown.
1486
-
1487
- @param {DS.Model} type
1488
- @param {String|Number} id
1489
- @param {Object} hash the data hash to load
1490
- */
1491
- load: function(type, id, hash) {
1492
- if (hash === undefined) {
1493
- hash = id;
1494
- var primaryKey = type.proto().primaryKey;
1495
-
1496
- id = hash[primaryKey];
1497
- }
1498
-
1499
- var typeMap = this.typeMapFor(type),
1500
- dataCache = typeMap.cidToHash,
1501
- clientId = typeMap.idToCid[id],
1502
- recordCache = get(this, 'recordCache');
1503
-
1504
- if (clientId !== undefined) {
1505
- dataCache[clientId] = hash;
1506
-
1507
- var record = recordCache[clientId];
1508
- if (record) {
1509
- record.send('didChangeData');
1510
- }
1511
- } else {
1512
- clientId = this.pushHash(hash, id, type);
1513
- }
1514
-
1515
- DATA_PROXY.savedData = hash;
1516
- this.updateRecordArrays(type, clientId, DATA_PROXY);
1517
-
1518
- return { id: id, clientId: clientId };
1519
- },
1520
-
1521
- loadMany: function(type, ids, hashes) {
1522
- var clientIds = Ember.A([]);
1523
-
1524
- if (hashes === undefined) {
1525
- hashes = ids;
1526
- ids = [];
1527
- var primaryKey = type.proto().primaryKey;
1528
-
1529
- ids = Ember.ArrayUtils.map(hashes, function(hash) {
1530
- return hash[primaryKey];
1531
- });
1532
- }
1533
-
1534
- for (var i=0, l=get(ids, 'length'); i<l; i++) {
1535
- var loaded = this.load(type, ids[i], hashes[i]);
1536
- clientIds.pushObject(loaded.clientId);
1537
- }
1538
-
1539
- return { clientIds: clientIds, ids: ids };
1540
- },
1541
-
1542
- /** @private
1543
-
1544
- Stores a data hash for the specified type and id combination and returns
1545
- the client id.
1546
-
1547
- @param {Object} hash
1548
- @param {String|Number} id
1549
- @param {DS.Model} type
1550
- @returns {Number}
1551
- */
1552
- pushHash: function(hash, id, type) {
1553
- var typeMap = this.typeMapFor(type);
1554
-
1555
- var idToClientIdMap = typeMap.idToCid,
1556
- clientIdToIdMap = this.clientIdToId,
1557
- clientIds = typeMap.clientIds,
1558
- dataCache = typeMap.cidToHash;
1559
-
1560
- var clientId = ++this.clientIdCounter;
1561
-
1562
- dataCache[clientId] = hash;
1563
-
1564
- // if we're creating an item, this process will be done
1565
- // later, once the object has been persisted.
1566
- if (id) {
1567
- idToClientIdMap[id] = clientId;
1568
- clientIdToIdMap[clientId] = id;
1569
- }
1570
-
1571
- clientIds.push(clientId);
1572
-
1573
- return clientId;
1574
- },
1575
-
1576
- // ..........................
1577
- // . RECORD MATERIALIZATION .
1578
- // ..........................
1579
-
1580
- materializeRecord: function(type, clientId) {
1581
- var record;
1582
-
1583
- get(this, 'recordCache')[clientId] = record = type._create({
1584
- store: this,
1585
- clientId: clientId
1586
- });
1587
-
1588
- get(this, 'defaultTransaction').adoptRecord(record);
1589
-
1590
- record.send('loadingData');
1591
- return record;
1592
- },
1593
-
1594
- destroy: function() {
1595
- if (get(DS, 'defaultStore') === this) {
1596
- set(DS, 'defaultStore', null);
1597
- }
1598
-
1599
- return this._super();
1600
- }
1601
- });
1602
-
1603
- })();
1604
-
1605
-
1606
-
1607
- (function() {
1608
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, guidFor = Ember.guidFor;
1609
-
1610
- /**
1611
- This file encapsulates the various states that a record can transition
1612
- through during its lifecycle.
1613
-
1614
- ### State Manager
1615
-
1616
- A record's state manager explicitly tracks what state a record is in
1617
- at any given time. For instance, if a record is newly created and has
1618
- not yet been sent to the adapter to be saved, it would be in the
1619
- `created.uncommitted` state. If a record has had local modifications
1620
- made to it that are in the process of being saved, the record would be
1621
- in the `updated.inFlight` state. (These state paths will be explained
1622
- in more detail below.)
1623
-
1624
- Events are sent by the record or its store to the record's state manager.
1625
- How the state manager reacts to these events is dependent on which state
1626
- it is in. In some states, certain events will be invalid and will cause
1627
- an exception to be raised.
1628
-
1629
- States are hierarchical. For example, a record can be in the
1630
- `deleted.start` state, then transition into the `deleted.inFlight` state.
1631
- If a child state does not implement an event handler, the state manager
1632
- will attempt to invoke the event on all parent states until the root state is
1633
- reached. The state hierarchy of a record is described in terms of a path
1634
- string. You can determine a record's current state by getting its manager's
1635
- current state path:
1636
-
1637
- record.getPath('stateManager.currentState.path');
1638
- //=> "created.uncommitted"
1639
-
1640
- The `DS.Model` states are themselves stateless. What we mean is that,
1641
- though each instance of a record also has a unique instance of a
1642
- `DS.StateManager`, the hierarchical states that each of *those* points
1643
- to is a shared data structure. For performance reasons, instead of each
1644
- record getting its own copy of the hierarchy of states, each state
1645
- manager points to this global, immutable shared instance. How does a
1646
- state know which record it should be acting on? We pass a reference to
1647
- the current state manager as the first parameter to every method invoked
1648
- on a state.
1649
-
1650
- The state manager passed as the first parameter is where you should stash
1651
- state about the record if needed; you should never store data on the state
1652
- object itself. If you need access to the record being acted on, you can
1653
- retrieve the state manager's `record` property. For example, if you had
1654
- an event handler `myEvent`:
1655
-
1656
- myEvent: function(manager) {
1657
- var record = manager.get('record');
1658
- record.doSomething();
1659
- }
1660
-
1661
- For more information about state managers in general, see the Ember.js
1662
- documentation on `Ember.StateManager`.
1663
-
1664
- ### Events, Flags, and Transitions
1665
-
1666
- A state may implement zero or more events, flags, or transitions.
1667
-
1668
- #### Events
1669
-
1670
- Events are named functions that are invoked when sent to a record. The
1671
- state manager will first look for a method with the given name on the
1672
- current state. If no method is found, it will search the current state's
1673
- parent, and then its grandparent, and so on until reaching the top of
1674
- the hierarchy. If the root is reached without an event handler being found,
1675
- an exception will be raised. This can be very helpful when debugging new
1676
- features.
1677
-
1678
- Here's an example implementation of a state with a `myEvent` event handler:
1679
-
1680
- aState: DS.State.create({
1681
- myEvent: function(manager, param) {
1682
- console.log("Received myEvent with "+param);
1683
- }
1684
- })
1685
-
1686
- To trigger this event:
1687
-
1688
- record.send('myEvent', 'foo');
1689
- //=> "Received myEvent with foo"
1690
-
1691
- Note that an optional parameter can be sent to a record's `send()` method,
1692
- which will be passed as the second parameter to the event handler.
1693
-
1694
- Events should transition to a different state if appropriate. This can be
1695
- done by calling the state manager's `goToState()` method with a path to the
1696
- desired state. The state manager will attempt to resolve the state path
1697
- relative to the current state. If no state is found at that path, it will
1698
- attempt to resolve it relative to the current state's parent, and then its
1699
- parent, and so on until the root is reached. For example, imagine a hierarchy
1700
- like this:
1701
-
1702
- * created
1703
- * start <-- currentState
1704
- * inFlight
1705
- * updated
1706
- * inFlight
1707
-
1708
- If we are currently in the `start` state, calling
1709
- `goToState('inFlight')` would transition to the `created.inFlight` state,
1710
- while calling `goToState('updated.inFlight')` would transition to
1711
- the `updated.inFlight` state.
1712
-
1713
- Remember that *only events* should ever cause a state transition. You should
1714
- never call `goToState()` from outside a state's event handler. If you are
1715
- tempted to do so, create a new event and send that to the state manager.
1716
-
1717
- #### Flags
1718
-
1719
- Flags are Boolean values that can be used to introspect a record's current
1720
- state in a more user-friendly way than examining its state path. For example,
1721
- instead of doing this:
1722
-
1723
- var statePath = record.getPath('stateManager.currentState.path');
1724
- if (statePath === 'created.inFlight') {
1725
- doSomething();
1726
- }
1727
-
1728
- You can say:
1729
-
1730
- if (record.get('isNew') && record.get('isSaving')) {
1731
- doSomething();
1732
- }
1733
-
1734
- If your state does not set a value for a given flag, the value will
1735
- be inherited from its parent (or the first place in the state hierarchy
1736
- where it is defined).
1737
-
1738
- The current set of flags are defined below. If you want to add a new flag,
1739
- in addition to the area below, you will also need to declare it in the
1740
- `DS.Model` class.
1741
-
1742
- #### Transitions
1743
-
1744
- Transitions are like event handlers but are called automatically upon
1745
- entering or exiting a state. To implement a transition, just call a method
1746
- either `enter` or `exit`:
1747
-
1748
- myState: DS.State.create({
1749
- // Gets called automatically when entering
1750
- // this state.
1751
- enter: function(manager) {
1752
- console.log("Entered myState");
1753
- }
1754
- })
1755
-
1756
- Note that enter and exit events are called once per transition. If the
1757
- current state changes, but changes to another child state of the parent,
1758
- the transition event on the parent will not be triggered.
1759
- */
1760
-
1761
- var stateProperty = Ember.computed(function(key) {
1762
- var parent = get(this, 'parentState');
1763
- if (parent) {
1764
- return get(parent, key);
1765
- }
1766
- }).property();
1767
-
1768
- var isEmptyObject = function(object) {
1769
- for (var name in object) {
1770
- if (object.hasOwnProperty(name)) { return false; }
1771
- }
1772
-
1773
- return true;
1774
- };
1775
-
1776
- var hasDefinedProperties = function(object) {
1777
- for (var name in object) {
1778
- if (object.hasOwnProperty(name) && object[name]) { return true; }
1779
- }
1780
-
1781
- return false;
1782
- };
1783
-
1784
- DS.State = Ember.State.extend({
1785
- isLoaded: stateProperty,
1786
- isDirty: stateProperty,
1787
- isSaving: stateProperty,
1788
- isDeleted: stateProperty,
1789
- isError: stateProperty,
1790
- isNew: stateProperty,
1791
- isValid: stateProperty,
1792
- isPending: stateProperty,
1793
-
1794
- // For states that are substates of a
1795
- // DirtyState (updated or created), it is
1796
- // useful to be able to determine which
1797
- // type of dirty state it is.
1798
- dirtyType: stateProperty
1799
- });
1800
-
1801
- var setProperty = function(manager, context) {
1802
- var key = context.key, value = context.value;
1803
-
1804
- var record = get(manager, 'record'),
1805
- data = get(record, 'data');
1806
-
1807
- set(data, key, value);
1808
- };
1809
-
1810
- var setAssociation = function(manager, context) {
1811
- var key = context.key, value = context.value;
1812
-
1813
- var record = get(manager, 'record'),
1814
- data = get(record, 'data');
1815
-
1816
- data.setAssociation(key, value);
1817
- };
1818
-
1819
- var didChangeData = function(manager) {
1820
- var record = get(manager, 'record'),
1821
- data = get(record, 'data');
1822
-
1823
- data._savedData = null;
1824
- record.notifyPropertyChange('data');
1825
- };
1826
-
1827
- // The waitingOn event shares common functionality
1828
- // between the different dirty states, but each is
1829
- // treated slightly differently. This method is exposed
1830
- // so that each implementation can invoke the common
1831
- // behavior, and then implement the behavior specific
1832
- // to the state.
1833
- var waitingOn = function(manager, object) {
1834
- var record = get(manager, 'record'),
1835
- pendingQueue = get(record, 'pendingQueue'),
1836
- objectGuid = guidFor(object);
1837
-
1838
- var observer = function() {
1839
- if (get(object, 'id')) {
1840
- manager.send('doneWaitingOn', object);
1841
- Ember.removeObserver(object, 'id', observer);
1842
- }
1843
- };
1844
-
1845
- pendingQueue[objectGuid] = [object, observer];
1846
- Ember.addObserver(object, 'id', observer);
1847
- };
1848
-
1849
- // Implementation notes:
1850
- //
1851
- // Each state has a boolean value for all of the following flags:
1852
- //
1853
- // * isLoaded: The record has a populated `data` property. When a
1854
- // record is loaded via `store.find`, `isLoaded` is false
1855
- // until the adapter sets it. When a record is created locally,
1856
- // its `isLoaded` property is always true.
1857
- // * isDirty: The record has local changes that have not yet been
1858
- // saved by the adapter. This includes records that have been
1859
- // created (but not yet saved) or deleted.
1860
- // * isSaving: The record's transaction has been committed, but
1861
- // the adapter has not yet acknowledged that the changes have
1862
- // been persisted to the backend.
1863
- // * isDeleted: The record was marked for deletion. When `isDeleted`
1864
- // is true and `isDirty` is true, the record is deleted locally
1865
- // but the deletion was not yet persisted. When `isSaving` is
1866
- // true, the change is in-flight. When both `isDirty` and
1867
- // `isSaving` are false, the change has persisted.
1868
- // * isError: The adapter reported that it was unable to save
1869
- // local changes to the backend. This may also result in the
1870
- // record having its `isValid` property become false if the
1871
- // adapter reported that server-side validations failed.
1872
- // * isNew: The record was created on the client and the adapter
1873
- // did not yet report that it was successfully saved.
1874
- // * isValid: No client-side validations have failed and the
1875
- // adapter did not report any server-side validation failures.
1876
- // * isPending: A record `isPending` when it belongs to an
1877
- // association on another record and that record has not been
1878
- // saved. A record in this state cannot be saved because it
1879
- // lacks a "foreign key" that will be supplied by its parent
1880
- // association when the parent record has been created. When
1881
- // the adapter reports that the parent has saved, the
1882
- // `isPending` property on all children will become `false`
1883
- // and the transaction will try to commit the records.
1884
-
1885
- // This mixin is mixed into various uncommitted states. Make
1886
- // sure to mix it in *after* the class definition, so its
1887
- // super points to the class definition.
1888
- var Uncommitted = Ember.Mixin.create({
1889
- setProperty: setProperty,
1890
- setAssociation: setAssociation,
1891
- });
1892
-
1893
- // These mixins are mixed into substates of the concrete
1894
- // subclasses of DirtyState.
1895
-
1896
- var CreatedUncommitted = Ember.Mixin.create({
1897
- deleteRecord: function(manager) {
1898
- var record = get(manager, 'record');
1899
- this._super(manager);
1900
-
1901
- record.withTransaction(function(t) {
1902
- t.recordBecameClean('created', record);
1903
- });
1904
- manager.goToState('deleted.saved');
1905
- }
1906
- });
1907
-
1908
- var UpdatedUncommitted = Ember.Mixin.create({
1909
- deleteRecord: function(manager) {
1910
- this._super(manager);
1911
-
1912
- var record = get(manager, 'record');
1913
-
1914
- record.withTransaction(function(t) {
1915
- t.recordBecameClean('updated', record);
1916
- });
1917
-
1918
- manager.goToState('deleted');
1919
- }
1920
- });
1921
-
1922
- // The dirty state is a abstract state whose functionality is
1923
- // shared between the `created` and `updated` states.
1924
- //
1925
- // The deleted state shares the `isDirty` flag with the
1926
- // subclasses of `DirtyState`, but with a very different
1927
- // implementation.
1928
- var DirtyState = DS.State.extend({
1929
- initialState: 'uncommitted',
1930
-
1931
- // FLAGS
1932
- isDirty: true,
1933
-
1934
- // SUBSTATES
1935
-
1936
- // When a record first becomes dirty, it is `uncommitted`.
1937
- // This means that there are local pending changes,
1938
- // but they have not yet begun to be saved.
1939
- uncommitted: DS.State.extend({
1940
- // TRANSITIONS
1941
- enter: function(manager) {
1942
- var dirtyType = get(this, 'dirtyType'),
1943
- record = get(manager, 'record');
1944
-
1945
- record.withTransaction(function (t) {
1946
- t.recordBecameDirty(dirtyType, record);
1947
- });
1948
- },
1949
-
1950
- // EVENTS
1951
- deleteRecord: Ember.K,
1952
-
1953
- waitingOn: function(manager, object) {
1954
- waitingOn(manager, object);
1955
- manager.goToState('pending');
1956
- },
1957
-
1958
- willCommit: function(manager) {
1959
- manager.goToState('inFlight');
1960
- },
1961
-
1962
- becameInvalid: function(manager) {
1963
- var dirtyType = get(this, 'dirtyType'),
1964
- record = get(manager, 'record');
1965
-
1966
- record.withTransaction(function (t) {
1967
- t.recordBecameInFlight(dirtyType, record);
1968
- });
1969
-
1970
- manager.goToState('invalid');
1971
- },
1972
-
1973
- rollback: function(manager) {
1974
- var record = get(manager, 'record'),
1975
- dirtyType = get(this, 'dirtyType'),
1976
- data = get(record, 'data');
1977
-
1978
- data.rollback();
1979
-
1980
- record.withTransaction(function(t) {
1981
- t.recordBecameClean(dirtyType, record);
1982
- });
1983
-
1984
- manager.goToState('loaded');
1985
- }
1986
- }, Uncommitted),
1987
-
1988
- // Once a record has been handed off to the adapter to be
1989
- // saved, it is in the 'in flight' state. Changes to the
1990
- // record cannot be made during this window.
1991
- inFlight: DS.State.extend({
1992
- // FLAGS
1993
- isSaving: true,
1994
-
1995
- // TRANSITIONS
1996
- enter: function(manager) {
1997
- var dirtyType = get(this, 'dirtyType'),
1998
- record = get(manager, 'record');
1999
-
2000
- record.withTransaction(function (t) {
2001
- t.recordBecameInFlight(dirtyType, record);
2002
- });
2003
- },
2004
-
2005
- // EVENTS
2006
- didCommit: function(manager) {
2007
- var dirtyType = get(this, 'dirtyType'),
2008
- record = get(manager, 'record');
2009
-
2010
- record.withTransaction(function(t) {
2011
- t.recordBecameClean('inflight', record);
2012
- });
2013
-
2014
- manager.goToState('loaded');
2015
- manager.send('invokeLifecycleCallbacks', dirtyType);
2016
- },
2017
-
2018
- becameInvalid: function(manager, errors) {
2019
- var record = get(manager, 'record');
2020
-
2021
- set(record, 'errors', errors);
2022
-
2023
- manager.goToState('invalid');
2024
- manager.send('invokeLifecycleCallbacks');
2025
- },
2026
-
2027
- becameError: function(manager) {
2028
- manager.goToState('error');
2029
- manager.send('invokeLifecycleCallbacks');
2030
- },
2031
-
2032
- didChangeData: didChangeData
2033
- }),
2034
-
2035
- // If a record becomes associated with a newly created
2036
- // parent record, it will be `pending` until the parent
2037
- // record has successfully persisted. Once this happens,
2038
- // this record can use the parent's primary key as its
2039
- // foreign key.
2040
- //
2041
- // If the record's transaction had already started to
2042
- // commit, the record will transition to the `inFlight`
2043
- // state. If it had not, the record will transition to
2044
- // the `uncommitted` state.
2045
- pending: DS.State.extend({
2046
- initialState: 'uncommitted',
2047
-
2048
- // FLAGS
2049
- isPending: true,
2050
-
2051
- // SUBSTATES
2052
-
2053
- // A pending record whose transaction has not yet
2054
- // started to commit is in this state.
2055
- uncommitted: DS.State.extend({
2056
- // EVENTS
2057
- deleteRecord: function(manager) {
2058
- var record = get(manager, 'record'),
2059
- pendingQueue = get(record, 'pendingQueue'),
2060
- tuple;
2061
-
2062
- // since we are leaving the pending state, remove any
2063
- // observers we have registered on other records.
2064
- for (var prop in pendingQueue) {
2065
- if (!pendingQueue.hasOwnProperty(prop)) { continue; }
2066
-
2067
- tuple = pendingQueue[prop];
2068
- Ember.removeObserver(tuple[0], 'id', tuple[1]);
2069
- }
2070
- },
2071
-
2072
- willCommit: function(manager) {
2073
- manager.goToState('committing');
2074
- },
2075
-
2076
- doneWaitingOn: function(manager, object) {
2077
- var record = get(manager, 'record'),
2078
- pendingQueue = get(record, 'pendingQueue'),
2079
- objectGuid = guidFor(object);
2080
-
2081
- delete pendingQueue[objectGuid];
2082
-
2083
- if (isEmptyObject(pendingQueue)) {
2084
- manager.send('doneWaiting');
2085
- }
2086
- },
2087
-
2088
- doneWaiting: function(manager) {
2089
- var dirtyType = get(this, 'dirtyType');
2090
- manager.goToState(dirtyType + '.uncommitted');
2091
- }
2092
- }, Uncommitted),
2093
-
2094
- // A pending record whose transaction has started
2095
- // to commit is in this state. Since it has not yet
2096
- // been sent to the adapter, it is not `inFlight`
2097
- // until all of its dependencies have been committed.
2098
- committing: DS.State.extend({
2099
- // FLAGS
2100
- isSaving: true,
2101
-
2102
- // EVENTS
2103
- doneWaitingOn: function(manager, object) {
2104
- var record = get(manager, 'record'),
2105
- pendingQueue = get(record, 'pendingQueue'),
2106
- objectGuid = guidFor(object);
2107
-
2108
- delete pendingQueue[objectGuid];
2109
-
2110
- if (isEmptyObject(pendingQueue)) {
2111
- manager.send('doneWaiting');
2112
- }
2113
- },
2114
-
2115
- doneWaiting: function(manager) {
2116
- var record = get(manager, 'record'),
2117
- transaction = get(record, 'transaction');
2118
-
2119
- // Now that the record is no longer pending, schedule
2120
- // the transaction to commit.
2121
- Ember.run.once(transaction, transaction.commit);
2122
- },
2123
-
2124
- willCommit: function(manager) {
2125
- var record = get(manager, 'record'),
2126
- pendingQueue = get(record, 'pendingQueue');
2127
-
2128
- if (isEmptyObject(pendingQueue)) {
2129
- var dirtyType = get(this, 'dirtyType');
2130
- manager.goToState(dirtyType + '.inFlight');
2131
- }
2132
- }
2133
- })
2134
- }),
2135
-
2136
- // A record is in the `invalid` state when its client-side
2137
- // invalidations have failed, or if the adapter has indicated
2138
- // the the record failed server-side invalidations.
2139
- invalid: DS.State.extend({
2140
- // FLAGS
2141
- isValid: false,
2142
-
2143
- exit: function(manager) {
2144
- var record = get(manager, 'record');
2145
-
2146
- record.withTransaction(function (t) {
2147
- t.recordBecameClean('inflight', record);
2148
- });
2149
- },
2150
-
2151
- // EVENTS
2152
- deleteRecord: function(manager) {
2153
- manager.goToState('deleted');
2154
- },
2155
-
2156
- setAssociation: setAssociation,
2157
-
2158
- setProperty: function(manager, context) {
2159
- setProperty(manager, context);
2160
-
2161
- var record = get(manager, 'record'),
2162
- errors = get(record, 'errors'),
2163
- key = context.key;
2164
-
2165
- delete errors[key];
2166
-
2167
- if (!hasDefinedProperties(errors)) {
2168
- manager.send('becameValid');
2169
- }
2170
- },
2171
-
2172
- rollback: function(manager) {
2173
- manager.send('becameValid');
2174
- manager.send('rollback');
2175
- },
2176
-
2177
- becameValid: function(manager) {
2178
- manager.goToState('uncommitted');
2179
- },
2180
-
2181
- invokeLifecycleCallbacks: function(manager) {
2182
- var record = get(manager, 'record');
2183
- record.fire('becameInvalid', record);
2184
- }
2185
- })
2186
- });
2187
-
2188
- // The created and updated states are created outside the state
2189
- // chart so we can reopen their substates and add mixins as
2190
- // necessary.
2191
-
2192
- var createdState = DirtyState.create({
2193
- dirtyType: 'created',
2194
-
2195
- // FLAGS
2196
- isNew: true
2197
- });
2198
-
2199
- var updatedState = DirtyState.create({
2200
- dirtyType: 'updated'
2201
- });
2202
-
2203
- // The created.uncommitted state and created.pending.uncommitted share
2204
- // some logic defined in CreatedUncommitted.
2205
- createdState.states.uncommitted.reopen(CreatedUncommitted);
2206
- createdState.states.pending.states.uncommitted.reopen(CreatedUncommitted);
2207
-
2208
- // The created.uncommitted state needs to immediately transition to the
2209
- // deleted state if it is rolled back.
2210
- createdState.states.uncommitted.reopen({
2211
- rollback: function(manager) {
2212
- this._super(manager);
2213
- manager.goToState('deleted.saved');
2214
- }
2215
- });
2216
-
2217
- // The updated.uncommitted state and updated.pending.uncommitted share
2218
- // some logic defined in UpdatedUncommitted.
2219
- updatedState.states.uncommitted.reopen(UpdatedUncommitted);
2220
- updatedState.states.pending.states.uncommitted.reopen(UpdatedUncommitted);
2221
- updatedState.states.inFlight.reopen({
2222
- didSaveData: function(manager) {
2223
- var record = get(manager, 'record'),
2224
- data = get(record, 'data');
2225
-
2226
- data.saveData();
2227
- data.adapterDidUpdate();
2228
- }
2229
- });
2230
-
2231
- var states = {
2232
- rootState: Ember.State.create({
2233
- // FLAGS
2234
- isLoaded: false,
2235
- isDirty: false,
2236
- isSaving: false,
2237
- isDeleted: false,
2238
- isError: false,
2239
- isNew: false,
2240
- isValid: true,
2241
- isPending: false,
2242
-
2243
- // SUBSTATES
2244
-
2245
- // A record begins its lifecycle in the `empty` state.
2246
- // If its data will come from the adapter, it will
2247
- // transition into the `loading` state. Otherwise, if
2248
- // the record is being created on the client, it will
2249
- // transition into the `created` state.
2250
- empty: DS.State.create({
2251
- // EVENTS
2252
- loadingData: function(manager) {
2253
- manager.goToState('loading');
2254
- },
2255
-
2256
- didChangeData: function(manager) {
2257
- didChangeData(manager);
2258
-
2259
- manager.goToState('loaded.created');
2260
- }
2261
- }),
2262
-
2263
- // A record enters this state when the store askes
2264
- // the adapter for its data. It remains in this state
2265
- // until the adapter provides the requested data.
2266
- //
2267
- // Usually, this process is asynchronous, using an
2268
- // XHR to retrieve the data.
2269
- loading: DS.State.create({
2270
- // TRANSITIONS
2271
- exit: function(manager) {
2272
- var record = get(manager, 'record');
2273
- record.fire('didLoad');
2274
- },
2275
-
2276
- // EVENTS
2277
- didChangeData: function(manager, data) {
2278
- didChangeData(manager);
2279
- manager.send('loadedData');
2280
- },
2281
-
2282
- loadedData: function(manager) {
2283
- manager.goToState('loaded');
2284
- }
2285
- }),
2286
-
2287
- // A record enters this state when its data is populated.
2288
- // Most of a record's lifecycle is spent inside substates
2289
- // of the `loaded` state.
2290
- loaded: DS.State.create({
2291
- initialState: 'saved',
2292
-
2293
- // FLAGS
2294
- isLoaded: true,
2295
-
2296
- // SUBSTATES
2297
-
2298
- // If there are no local changes to a record, it remains
2299
- // in the `saved` state.
2300
- saved: DS.State.create({
2301
-
2302
- // EVENTS
2303
- setProperty: function(manager, context) {
2304
- setProperty(manager, context);
2305
- manager.goToState('updated');
2306
- },
2307
-
2308
- setAssociation: function(manager, context) {
2309
- setAssociation(manager, context);
2310
- manager.goToState('updated');
2311
- },
2312
-
2313
- didChangeData: didChangeData,
2314
-
2315
- deleteRecord: function(manager) {
2316
- manager.goToState('deleted');
2317
- },
2318
-
2319
- waitingOn: function(manager, object) {
2320
- waitingOn(manager, object);
2321
- manager.goToState('updated.pending');
2322
- },
2323
-
2324
- invokeLifecycleCallbacks: function(manager, dirtyType) {
2325
- var record = get(manager, 'record');
2326
- if (dirtyType === 'created') {
2327
- record.fire('didCreate', record);
2328
- } else {
2329
- record.fire('didUpdate', record);
2330
- }
2331
- }
2332
- }),
2333
-
2334
- // A record is in this state after it has been locally
2335
- // created but before the adapter has indicated that
2336
- // it has been saved.
2337
- created: createdState,
2338
-
2339
- // A record is in this state if it has already been
2340
- // saved to the server, but there are new local changes
2341
- // that have not yet been saved.
2342
- updated: updatedState
2343
- }),
2344
-
2345
- // A record is in this state if it was deleted from the store.
2346
- deleted: DS.State.create({
2347
- // FLAGS
2348
- isDeleted: true,
2349
- isLoaded: true,
2350
- isDirty: true,
2351
-
2352
- // TRANSITIONS
2353
- enter: function(manager) {
2354
- var record = get(manager, 'record'),
2355
- store = get(record, 'store');
2356
-
2357
- store.removeFromRecordArrays(record);
2358
- },
2359
-
2360
- // SUBSTATES
2361
-
2362
- // When a record is deleted, it enters the `start`
2363
- // state. It will exit this state when the record's
2364
- // transaction starts to commit.
2365
- start: DS.State.create({
2366
- // TRANSITIONS
2367
- enter: function(manager) {
2368
- var record = get(manager, 'record');
2369
-
2370
- record.withTransaction(function(t) {
2371
- t.recordBecameDirty('deleted', record);
2372
- });
2373
- },
2374
-
2375
- // EVENTS
2376
- willCommit: function(manager) {
2377
- manager.goToState('inFlight');
2378
- },
2379
-
2380
- rollback: function(manager) {
2381
- var record = get(manager, 'record'),
2382
- data = get(record, 'data');
2383
-
2384
- data.rollback();
2385
- record.withTransaction(function(t) {
2386
- t.recordBecameClean('deleted', record);
2387
- });
2388
- manager.goToState('loaded');
2389
- }
2390
- }),
2391
-
2392
- // After a record's transaction is committing, but
2393
- // before the adapter indicates that the deletion
2394
- // has saved to the server, a record is in the
2395
- // `inFlight` substate of `deleted`.
2396
- inFlight: DS.State.create({
2397
- // FLAGS
2398
- isSaving: true,
2399
-
2400
- // TRANSITIONS
2401
- enter: function(manager) {
2402
- var record = get(manager, 'record');
2403
-
2404
- record.withTransaction(function (t) {
2405
- t.recordBecameInFlight('deleted', record);
2406
- });
2407
- },
2408
-
2409
- // EVENTS
2410
- didCommit: function(manager) {
2411
- var record = get(manager, 'record');
2412
-
2413
- record.withTransaction(function(t) {
2414
- t.recordBecameClean('inflight', record);
2415
- });
2416
-
2417
- manager.goToState('saved');
2418
-
2419
- manager.send('invokeLifecycleCallbacks');
2420
- }
2421
- }),
2422
-
2423
- // Once the adapter indicates that the deletion has
2424
- // been saved, the record enters the `saved` substate
2425
- // of `deleted`.
2426
- saved: DS.State.create({
2427
- // FLAGS
2428
- isDirty: false,
2429
-
2430
- invokeLifecycleCallbacks: function(manager) {
2431
- var record = get(manager, 'record');
2432
- record.fire('didDelete', record);
2433
- }
2434
- })
2435
- }),
2436
-
2437
- // If the adapter indicates that there was an unknown
2438
- // error saving a record, the record enters the `error`
2439
- // state.
2440
- error: DS.State.create({
2441
- isError: true,
2442
-
2443
- // EVENTS
2444
-
2445
- invokeLifecycleCallbacks: function(manager) {
2446
- var record = get(manager, 'record');
2447
- record.fire('becameError', record);
2448
- }
2449
- })
2450
- })
2451
- };
2452
-
2453
- DS.StateManager = Ember.StateManager.extend({
2454
- record: null,
2455
- initialState: 'rootState',
2456
- states: states
2457
- });
2458
-
2459
- })();
2460
-
2461
-
2462
-
2463
- (function() {
2464
- var get = Ember.get, set = Ember.set;
2465
-
2466
- // When a record is changed on the client, it is considered "dirty"--there are
2467
- // pending changes that need to be saved to a persistence layer, such as a
2468
- // server.
2469
- //
2470
- // If the record is rolled back, it re-enters a clean state, any changes are
2471
- // discarded, and its attributes are reset back to the last known good copy
2472
- // of the data that came from the server.
2473
- //
2474
- // If the record is committed, the changes are sent to the server to be saved,
2475
- // and once the server confirms that they are valid, the record's "canonical"
2476
- // data becomes the original canonical data plus the changes merged in.
2477
- //
2478
- // A DataProxy is an object that encapsulates this change tracking. It
2479
- // contains three buckets:
2480
- //
2481
- // * `savedData` - the last-known copy of the data from the server
2482
- // * `unsavedData` - a hash that contains any changes that have not yet
2483
- // been committed
2484
- // * `associations` - this is similar to `savedData`, but holds the client
2485
- // ids of associated records
2486
- //
2487
- // When setting a property on the object, the value is placed into the
2488
- // `unsavedData` bucket:
2489
- //
2490
- // proxy.set('key', 'value');
2491
- //
2492
- // // unsavedData:
2493
- // {
2494
- // key: "value"
2495
- // }
2496
- //
2497
- // When retrieving a property from the object, it first looks to see
2498
- // if that value exists in the `unsavedData` bucket, and returns it if so.
2499
- // Otherwise, it returns the value from the `savedData` bucket.
2500
- //
2501
- // When the adapter notifies a record that it has been saved, it merges the
2502
- // `unsavedData` bucket into the `savedData` bucket. If the record's
2503
- // transaction is rolled back, the `unsavedData` hash is simply discarded.
2504
- //
2505
- // This object is a regular JS object for performance. It is only
2506
- // used internally for bookkeeping purposes.
2507
-
2508
- var DataProxy = DS._DataProxy = function(record) {
2509
- this.record = record;
2510
-
2511
- this.unsavedData = {};
2512
-
2513
- this.associations = {};
2514
- };
2515
-
2516
- DataProxy.prototype = {
2517
- get: function(key) { return Ember.get(this, key); },
2518
- set: function(key, value) { return Ember.set(this, key, value); },
2519
-
2520
- setAssociation: function(key, value) {
2521
- this.associations[key] = value;
2522
- },
2523
-
2524
- savedData: function() {
2525
- var savedData = this._savedData;
2526
- if (savedData) { return savedData; }
2527
-
2528
- var record = this.record,
2529
- clientId = get(record, 'clientId'),
2530
- store = get(record, 'store');
2531
-
2532
- if (store) {
2533
- savedData = store.dataForRecord(record);
2534
- this._savedData = savedData;
2535
- return savedData;
2536
- }
2537
- },
2538
-
2539
- unknownProperty: function(key) {
2540
- var unsavedData = this.unsavedData,
2541
- associations = this.associations,
2542
- savedData = this.savedData(),
2543
- store;
2544
-
2545
- var value = unsavedData[key], association;
2546
-
2547
- // if this is a belongsTo association, this will
2548
- // be a clientId.
2549
- association = associations[key];
2550
-
2551
- if (association !== undefined) {
2552
- store = get(this.record, 'store');
2553
- return store.clientIdToId[association];
2554
- }
2555
-
2556
- if (savedData && value === undefined) {
2557
- value = savedData[key];
2558
- }
2559
-
2560
- return value;
2561
- },
2562
-
2563
- setUnknownProperty: function(key, value) {
2564
- var record = this.record,
2565
- unsavedData = this.unsavedData;
2566
-
2567
- unsavedData[key] = value;
2568
-
2569
- record.hashWasUpdated();
2570
-
2571
- return value;
2572
- },
2573
-
2574
- commit: function() {
2575
- this.saveData();
2576
-
2577
- this.record.notifyPropertyChange('data');
2578
- },
2579
-
2580
- rollback: function() {
2581
- this.unsavedData = {};
2582
-
2583
- this.record.notifyPropertyChange('data');
2584
- },
2585
-
2586
- saveData: function() {
2587
- var record = this.record;
2588
-
2589
- var unsavedData = this.unsavedData;
2590
- var savedData = this.savedData();
2591
-
2592
- for (var prop in unsavedData) {
2593
- if (unsavedData.hasOwnProperty(prop)) {
2594
- savedData[prop] = unsavedData[prop];
2595
- delete unsavedData[prop];
2596
- }
2597
- }
2598
- },
2599
-
2600
- adapterDidUpdate: function() {
2601
- this.unsavedData = {};
2602
- }
2603
- };
2604
-
2605
- })();
2606
-
2607
-
2608
-
2609
- (function() {
2610
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, none = Ember.none;
2611
-
2612
- var retrieveFromCurrentState = Ember.computed(function(key) {
2613
- return get(getPath(this, 'stateManager.currentState'), key);
2614
- }).property('stateManager.currentState').cacheable();
2615
-
2616
- DS.Model = Ember.Object.extend(Ember.Evented, {
2617
- isLoaded: retrieveFromCurrentState,
2618
- isDirty: retrieveFromCurrentState,
2619
- isSaving: retrieveFromCurrentState,
2620
- isDeleted: retrieveFromCurrentState,
2621
- isError: retrieveFromCurrentState,
2622
- isNew: retrieveFromCurrentState,
2623
- isPending: retrieveFromCurrentState,
2624
- isValid: retrieveFromCurrentState,
2625
-
2626
- clientId: null,
2627
- transaction: null,
2628
- stateManager: null,
2629
- pendingQueue: null,
2630
- errors: null,
2631
-
2632
- // because unknownProperty is used, any internal property
2633
- // must be initialized here.
2634
- primaryKey: 'id',
2635
- id: Ember.computed(function(key, value) {
2636
- var primaryKey = get(this, 'primaryKey'),
2637
- data = get(this, 'data');
2638
-
2639
- if (arguments.length === 2) {
2640
- set(data, primaryKey, value);
2641
- return value;
2642
- }
2643
-
2644
- return data && get(data, primaryKey);
2645
- }).property('primaryKey', 'data'),
2646
-
2647
- // The following methods are callbacks invoked by `toJSON`. You
2648
- // can override one of the callbacks to override specific behavior,
2649
- // or toJSON itself.
2650
- //
2651
- // If you override toJSON, you can invoke these callbacks manually
2652
- // to get the default behavior.
2653
-
2654
- /**
2655
- Add the record's primary key to the JSON hash.
2656
-
2657
- The default implementation uses the record's specified `primaryKey`
2658
- and the `id` computed property, which are passed in as parameters.
2659
-
2660
- @param {Object} json the JSON hash being built
2661
- @param {Number|String} id the record's id
2662
- @param {String} key the primaryKey for the record
2663
- */
2664
- addIdToJSON: function(json, id, key) {
2665
- if (id) { json[key] = id; }
2666
- },
2667
-
2668
- /**
2669
- Add the attributes' current values to the JSON hash.
2670
-
2671
- The default implementation gets the current value of each
2672
- attribute from the `data`, and uses a `defaultValue` if
2673
- specified in the `DS.attr` definition.
2674
-
2675
- @param {Object} json the JSON hash being build
2676
- @param {Ember.Map} attributes a Map of attributes
2677
- @param {DataProxy} data the record's data, accessed with `get` and `set`.
2678
- */
2679
- addAttributesToJSON: function(json, attributes, data) {
2680
- attributes.forEach(function(name, meta) {
2681
- var key = meta.key(this.constructor),
2682
- value = get(data, key);
2683
-
2684
- if (value === undefined) {
2685
- value = meta.options.defaultValue;
2686
- }
2687
-
2688
- json[key] = value;
2689
- }, this);
2690
- },
2691
-
2692
- /**
2693
- Add the value of a `hasMany` association to the JSON hash.
2694
-
2695
- The default implementation honors the `embedded` option
2696
- passed to `DS.hasMany`. If embedded, `toJSON` is recursively
2697
- called on the child records. If not, the `id` of each
2698
- record is added.
2699
-
2700
- Note that if a record is not embedded and does not
2701
- yet have an `id` (usually provided by the server), it
2702
- will not be included in the output.
2703
-
2704
- @param {Object} json the JSON hash being built
2705
- @param {DataProxy} data the record's data, accessed with `get` and `set`.
2706
- @param {Object} meta information about the association
2707
- @param {Object} options options passed to `toJSON`
2708
- */
2709
- addHasManyToJSON: function(json, data, meta, options) {
2710
- var key = meta.key,
2711
- manyArray = get(this, key),
2712
- records = [], i, l,
2713
- clientId, id;
2714
-
2715
- if (meta.options.embedded) {
2716
- // TODO: Avoid materializing embedded hashes if possible
2717
- manyArray.forEach(function(record) {
2718
- records.push(record.toJSON(options));
2719
- });
2720
- } else {
2721
- var clientIds = get(manyArray, 'content');
2722
-
2723
- for (i=0, l=clientIds.length; i<l; i++) {
2724
- clientId = clientIds[i];
2725
- id = get(this, 'store').clientIdToId[clientId];
2726
-
2727
- if (id !== undefined) {
2728
- records.push(id);
2729
- }
2730
- }
2731
- }
2732
-
2733
- key = meta.options.key || get(this, 'namingConvention').keyToJSONKey(key);
2734
- json[key] = records;
2735
- },
2736
-
2737
- /**
2738
- Add the value of a `belongsTo` association to the JSON hash.
2739
-
2740
- The default implementation always includes the `id`.
2741
-
2742
- @param {Object} json the JSON hash being built
2743
- @param {DataProxy} data the record's data, accessed with `get` and `set`.
2744
- @param {Object} meta information about the association
2745
- @param {Object} options options passed to `toJSON`
2746
- */
2747
- addBelongsToToJSON: function(json, data, meta, options) {
2748
- var key = meta.key, value, id;
2749
-
2750
- if (meta.options.embedded) {
2751
- key = meta.options.key || get(this, 'namingConvention').keyToJSONKey(key);
2752
- value = get(data.record, key);
2753
- json[key] = value ? value.toJSON(options) : null;
2754
- } else {
2755
- key = meta.options.key || get(this, 'namingConvention').foreignKey(key);
2756
- id = data.get(key);
2757
- json[key] = none(id) ? null : id;
2758
- }
2759
- },
2760
- /**
2761
- Create a JSON representation of the record, including its `id`,
2762
- attributes and associations. Honor any settings defined on the
2763
- attributes or associations (such as `embedded` or `key`).
2764
- */
2765
- toJSON: function(options) {
2766
- var data = get(this, 'data'),
2767
- result = {},
2768
- type = this.constructor,
2769
- attributes = get(type, 'attributes'),
2770
- primaryKey = get(this, 'primaryKey'),
2771
- id = get(this, 'id'),
2772
- store = get(this, 'store'),
2773
- associations;
2774
-
2775
- options = options || {};
2776
-
2777
- // delegate to `addIdToJSON` callback
2778
- this.addIdToJSON(result, id, primaryKey);
2779
-
2780
- // delegate to `addAttributesToJSON` callback
2781
- this.addAttributesToJSON(result, attributes, data);
2782
-
2783
- associations = get(type, 'associationsByName');
2784
-
2785
- // add associations, delegating to `addHasManyToJSON` and
2786
- // `addBelongsToToJSON`.
2787
- associations.forEach(function(key, meta) {
2788
- if (options.associations && meta.kind === 'hasMany') {
2789
- this.addHasManyToJSON(result, data, meta, options);
2790
- } else if (meta.kind === 'belongsTo') {
2791
- this.addBelongsToToJSON(result, data, meta, options);
2792
- }
2793
- }, this);
2794
-
2795
- return result;
2796
- },
2797
-
2798
- data: Ember.computed(function() {
2799
- return new DS._DataProxy(this);
2800
- }).cacheable(),
2801
-
2802
- didLoad: Ember.K,
2803
- didUpdate: Ember.K,
2804
- didCreate: Ember.K,
2805
- didDelete: Ember.K,
2806
- becameInvalid: Ember.K,
2807
- becameError: Ember.K,
2808
-
2809
- init: function() {
2810
- var stateManager = DS.StateManager.create({
2811
- record: this
2812
- });
2813
-
2814
- set(this, 'pendingQueue', {});
2815
-
2816
- set(this, 'stateManager', stateManager);
2817
- stateManager.goToState('empty');
2818
- },
2819
-
2820
- destroy: function() {
2821
- if (!get(this, 'isDeleted')) {
2822
- this.deleteRecord();
2823
- }
2824
- this._super();
2825
- },
2826
-
2827
- send: function(name, context) {
2828
- return get(this, 'stateManager').send(name, context);
2829
- },
2830
-
2831
- withTransaction: function(fn) {
2832
- var transaction = get(this, 'transaction');
2833
- if (transaction) { fn(transaction); }
2834
- },
2835
-
2836
- setProperty: function(key, value) {
2837
- this.send('setProperty', { key: key, value: value });
2838
- },
2839
-
2840
- deleteRecord: function() {
2841
- this.send('deleteRecord');
2842
- },
2843
-
2844
- waitingOn: function(record) {
2845
- this.send('waitingOn', record);
2846
- },
2847
-
2848
- notifyHashWasUpdated: function() {
2849
- var store = get(this, 'store');
2850
- if (store) {
2851
- store.hashWasUpdated(this.constructor, get(this, 'clientId'), this);
2852
- }
2853
- },
2854
-
2855
- unknownProperty: function(key) {
2856
- var data = get(this, 'data');
2857
-
2858
- if (data && key in data) {
2859
-
2860
- }
2861
- },
2862
-
2863
- setUnknownProperty: function(key, value) {
2864
- var data = get(this, 'data');
2865
-
2866
- if (data && key in data) {
2867
-
2868
- } else {
2869
- return this._super(key, value);
2870
- }
2871
- },
2872
-
2873
- namingConvention: {
2874
- keyToJSONKey: function(key) {
2875
- // TODO: Strip off `is` from the front. Example: `isHipster` becomes `hipster`
2876
- return Ember.String.decamelize(key);
2877
- },
2878
-
2879
- foreignKey: function(key) {
2880
- return Ember.String.decamelize(key) + '_id';
2881
- }
2882
- },
2883
-
2884
- /** @private */
2885
- hashWasUpdated: function() {
2886
- // At the end of the run loop, notify record arrays that
2887
- // this record has changed so they can re-evaluate its contents
2888
- // to determine membership.
2889
- Ember.run.once(this, this.notifyHashWasUpdated);
2890
- },
2891
-
2892
- dataDidChange: Ember.observer(function() {
2893
- var associations = get(this.constructor, 'associationsByName'),
2894
- data = get(this, 'data'), store = get(this, 'store'),
2895
- idToClientId = store.idToClientId,
2896
- cachedValue;
2897
-
2898
- associations.forEach(function(name, association) {
2899
- if (association.kind === 'hasMany') {
2900
- cachedValue = this.cacheFor(name);
2901
-
2902
- if (cachedValue) {
2903
- var key = association.options.key || name,
2904
- ids = data.get(key) || [];
2905
- var clientIds = Ember.ArrayUtils.map(ids, function(id) {
2906
- return store.clientIdForId(association.type, id);
2907
- });
2908
-
2909
- set(cachedValue, 'content', Ember.A(clientIds));
2910
- cachedValue.fetch();
2911
- }
2912
- }
2913
- }, this);
2914
- }, 'data'),
2915
-
2916
- /**
2917
- @private
2918
-
2919
- Override the default event firing from Ember.Evented to
2920
- also call methods with the given name.
2921
- */
2922
- fire: function(name) {
2923
- this[name].apply(this, [].slice.call(arguments, 1));
2924
- this._super.apply(this, arguments);
2925
- }
2926
- });
2927
-
2928
- // Helper function to generate store aliases.
2929
- // This returns a function that invokes the named alias
2930
- // on the default store, but injects the class as the
2931
- // first parameter.
2932
- var storeAlias = function(methodName) {
2933
- return function() {
2934
- var store = get(DS, 'defaultStore'),
2935
- args = [].slice.call(arguments);
2936
-
2937
- args.unshift(this);
2938
- return store[methodName].apply(store, args);
2939
- };
2940
- };
2941
-
2942
- DS.Model.reopenClass({
2943
- find: storeAlias('find'),
2944
- filter: storeAlias('filter'),
2945
-
2946
- _create: DS.Model.create,
2947
-
2948
- create: function() {
2949
- throw new Ember.Error("You should not call `create` on a model. Instead, call `createRecord` with the attributes you would like to set.");
2950
- },
2951
-
2952
- createRecord: storeAlias('createRecord')
2953
- });
2954
-
2955
- })();
2956
-
2957
-
2958
-
2959
- (function() {
2960
- var get = Ember.get, getPath = Ember.getPath;
2961
- DS.Model.reopenClass({
2962
- attributes: Ember.computed(function() {
2963
- var map = Ember.Map.create();
2964
-
2965
- this.eachComputedProperty(function(name, meta) {
2966
- if (meta.isAttribute) { map.set(name, meta); }
2967
- });
2968
-
2969
- return map;
2970
- }).cacheable(),
2971
-
2972
- processAttributeKeys: function() {
2973
- if (this.processedAttributeKeys) { return; }
2974
-
2975
- var namingConvention = this.proto().namingConvention;
2976
-
2977
- this.eachComputedProperty(function(name, meta) {
2978
- if (meta.isAttribute && !meta.options.key) {
2979
- meta.options.key = namingConvention.keyToJSONKey(name, this);
2980
- }
2981
- }, this);
2982
- }
2983
- });
2984
-
2985
- DS.attr = function(type, options) {
2986
- var transform = DS.attr.transforms[type];
2987
-
2988
-
2989
- var transformFrom = transform.from;
2990
- var transformTo = transform.to;
2991
-
2992
- options = options || {};
2993
-
2994
- var meta = {
2995
- type: type,
2996
- isAttribute: true,
2997
- options: options,
2998
-
2999
- // this will ensure that the key always takes naming
3000
- // conventions into consideration.
3001
- key: function(recordType) {
3002
- recordType.processAttributeKeys();
3003
- return options.key;
3004
- }
3005
- };
3006
-
3007
- return Ember.computed(function(key, value) {
3008
- var data;
3009
-
3010
- key = meta.key(this.constructor);
3011
-
3012
- if (arguments.length === 2) {
3013
- value = transformTo(value);
3014
- this.setProperty(key, value);
3015
- } else {
3016
- data = get(this, 'data');
3017
- value = get(data, key);
3018
-
3019
- if (value === undefined) {
3020
- value = options.defaultValue;
3021
- }
3022
- }
3023
-
3024
- return transformFrom(value);
3025
- // `data` is never set directly. However, it may be
3026
- // invalidated from the state manager's setData
3027
- // event.
3028
- }).property('data').cacheable().meta(meta);
3029
- };
3030
-
3031
- DS.attr.transforms = {
3032
- string: {
3033
- from: function(serialized) {
3034
- return Ember.none(serialized) ? null : String(serialized);
3035
- },
3036
-
3037
- to: function(deserialized) {
3038
- return Ember.none(deserialized) ? null : String(deserialized);
3039
- }
3040
- },
3041
-
3042
- number: {
3043
- from: function(serialized) {
3044
- return Ember.none(serialized) ? null : Number(serialized);
3045
- },
3046
-
3047
- to: function(deserialized) {
3048
- return Ember.none(deserialized) ? null : Number(deserialized);
3049
- }
3050
- },
3051
-
3052
- 'boolean': {
3053
- from: function(serialized) {
3054
- return Boolean(serialized);
3055
- },
3056
-
3057
- to: function(deserialized) {
3058
- return Boolean(deserialized);
3059
- }
3060
- },
3061
-
3062
- date: {
3063
- from: function(serialized) {
3064
- var type = typeof serialized;
3065
-
3066
- if (type === "string" || type === "number") {
3067
- return new Date(serialized);
3068
- } else if (serialized === null || serialized === undefined) {
3069
- // if the value is not present in the data,
3070
- // return undefined, not null.
3071
- return serialized;
3072
- } else {
3073
- return null;
3074
- }
3075
- },
3076
-
3077
- to: function(date) {
3078
- if (date instanceof Date) {
3079
- var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
3080
- var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
3081
-
3082
- var pad = function(num) {
3083
- return num < 10 ? "0"+num : ""+num;
3084
- };
3085
-
3086
- var utcYear = date.getUTCFullYear(),
3087
- utcMonth = date.getUTCMonth(),
3088
- utcDayOfMonth = date.getUTCDate(),
3089
- utcDay = date.getUTCDay(),
3090
- utcHours = date.getUTCHours(),
3091
- utcMinutes = date.getUTCMinutes(),
3092
- utcSeconds = date.getUTCSeconds();
3093
-
3094
-
3095
- var dayOfWeek = days[utcDay];
3096
- var dayOfMonth = pad(utcDayOfMonth);
3097
- var month = months[utcMonth];
3098
-
3099
- return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
3100
- pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
3101
- } else if (date === undefined) {
3102
- return undefined;
3103
- } else {
3104
- return null;
3105
- }
3106
- }
3107
- }
3108
- };
3109
-
3110
-
3111
- })();
3112
-
3113
-
3114
-
3115
- (function() {
3116
-
3117
- })();
3118
-
3119
-
3120
-
3121
- (function() {
3122
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath,
3123
- none = Ember.none;
3124
-
3125
- var embeddedFindRecord = function(store, type, data, key, one) {
3126
- var association = get(data, key);
3127
- return none(association) ? undefined : store.load(type, association).id;
3128
- };
3129
-
3130
- var referencedFindRecord = function(store, type, data, key, one) {
3131
- return get(data, key);
3132
- };
3133
-
3134
- var hasAssociation = function(type, options, one) {
3135
- options = options || {};
3136
-
3137
- var embedded = options.embedded,
3138
- findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
3139
-
3140
- var meta = { type: type, isAssociation: true, options: options, kind: 'belongsTo' };
3141
-
3142
- return Ember.computed(function(key, value) {
3143
- var data = get(this, 'data'), ids, id, association,
3144
- store = get(this, 'store');
3145
-
3146
- if (typeof type === 'string') {
3147
- type = getPath(this, type, false) || getPath(window, type);
3148
- }
3149
-
3150
- if (arguments.length === 2) {
3151
- key = options.key || get(this, 'namingConvention').foreignKey(key);
3152
- this.send('setAssociation', { key: key, value: value === null ? null : get(value, 'clientId') });
3153
- //data.setAssociation(key, get(value, 'clientId'));
3154
- // put the client id in `key` in the data hash
3155
- return value;
3156
- } else {
3157
- // Embedded belongsTo associations should not look for
3158
- // a foreign key.
3159
- if (embedded) {
3160
- key = options.key || get(this, 'namingConvention').keyToJSONKey(key);
3161
-
3162
- // Non-embedded associations should look for a foreign key.
3163
- // For example, instead of person, we might look for person_id
3164
- } else {
3165
- key = options.key || get(this, 'namingConvention').foreignKey(key);
3166
- }
3167
- id = findRecord(store, type, data, key, true);
3168
- association = id ? store.find(type, id) : null;
3169
- }
3170
-
3171
- return association;
3172
- }).property('data').cacheable().meta(meta);
3173
- };
3174
-
3175
- DS.belongsTo = function(type, options) {
3176
-
3177
- return hasAssociation(type, options);
3178
- };
3179
-
3180
- })();
3181
-
3182
-
3183
-
3184
- (function() {
3185
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
3186
- var embeddedFindRecord = function(store, type, data, key) {
3187
- var association = get(data, key);
3188
- return association ? store.loadMany(type, association).ids : [];
3189
- };
3190
-
3191
- var referencedFindRecord = function(store, type, data, key, one) {
3192
- return get(data, key);
3193
- };
3194
-
3195
- var hasAssociation = function(type, options) {
3196
- options = options || {};
3197
-
3198
- var embedded = options.embedded,
3199
- findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
3200
-
3201
- var meta = { type: type, isAssociation: true, options: options, kind: 'hasMany' };
3202
-
3203
- return Ember.computed(function(key, value) {
3204
- var data = get(this, 'data'),
3205
- store = get(this, 'store'),
3206
- ids, id, association;
3207
-
3208
- if (typeof type === 'string') {
3209
- type = getPath(this, type, false) || getPath(window, type);
3210
- }
3211
-
3212
- key = options.key || get(this, 'namingConvention').keyToJSONKey(key);
3213
- ids = findRecord(store, type, data, key);
3214
- association = store.findMany(type, ids);
3215
- set(association, 'parentRecord', this);
3216
-
3217
- return association;
3218
- }).property().cacheable().meta(meta);
3219
- };
3220
-
3221
- DS.hasMany = function(type, options) {
3222
-
3223
- return hasAssociation(type, options);
3224
- };
3225
-
3226
- })();
3227
-
3228
-
3229
-
3230
- (function() {
3231
- var get = Ember.get, getPath = Ember.getPath;
3232
-
3233
- DS.Model.reopenClass({
3234
- typeForAssociation: function(name) {
3235
- var association = get(this, 'associationsByName').get(name);
3236
- return association && association.type;
3237
- },
3238
-
3239
- associations: Ember.computed(function() {
3240
- var map = Ember.Map.create();
3241
-
3242
- this.eachComputedProperty(function(name, meta) {
3243
- if (meta.isAssociation) {
3244
- var type = meta.type,
3245
- typeList = map.get(type);
3246
-
3247
- if (typeof type === 'string') {
3248
- type = getPath(this, type, false) || getPath(window, type);
3249
- meta.type = type;
3250
- }
3251
-
3252
- if (!typeList) {
3253
- typeList = [];
3254
- map.set(type, typeList);
3255
- }
3256
-
3257
- typeList.push({ name: name, kind: meta.kind });
3258
- }
3259
- });
3260
-
3261
- return map;
3262
- }).cacheable(),
3263
-
3264
- associationsByName: Ember.computed(function() {
3265
- var map = Ember.Map.create(), type;
3266
-
3267
- this.eachComputedProperty(function(name, meta) {
3268
- if (meta.isAssociation) {
3269
- meta.key = name;
3270
- type = meta.type;
3271
-
3272
- if (typeof type === 'string') {
3273
- type = getPath(this, type, false) || getPath(window, type);
3274
- meta.type = type;
3275
- }
3276
-
3277
- map.set(name, meta);
3278
- }
3279
- });
3280
-
3281
- return map;
3282
- }).cacheable()
3283
- });
3284
-
3285
- })();
3286
-
3287
-
3288
-
3289
- (function() {
3290
-
3291
- })();
3292
-
3293
-
3294
-
3295
- (function() {
3296
- /**
3297
- An adapter is an object that receives requests from a store and
3298
- translates them into the appropriate action to take against your
3299
- persistence layer. The persistence layer is usually an HTTP API, but may
3300
- be anything, such as the browser's local storage.
3301
-
3302
- ### Creating an Adapter
3303
-
3304
- First, create a new subclass of `DS.Adapter`:
3305
-
3306
- App.MyAdapter = DS.Adapter.extend({
3307
- // ...your code here
3308
- });
3309
-
3310
- To tell your store which adapter to use, set its `adapter` property:
3311
-
3312
- App.store = DS.Store.create({
3313
- revision: 3,
3314
- adapter: App.MyAdapter.create()
3315
- });
3316
-
3317
- `DS.Adapter` is an abstract base class that you should override in your
3318
- application to customize it for your backend. The minimum set of methods
3319
- that you should implement is:
3320
-
3321
- * `find()`
3322
- * `createRecord()`
3323
- * `updateRecord()`
3324
- * `deleteRecord()`
3325
-
3326
- To improve the network performance of your application, you can optimize
3327
- your adapter by overriding these lower-level methods:
3328
-
3329
- * `findMany()`
3330
- * `createRecords()`
3331
- * `updateRecords()`
3332
- * `deleteRecords()`
3333
- * `commit()`
3334
-
3335
- For more information about the adapter API, please see `README.md`.
3336
- */
3337
-
3338
- DS.Adapter = Ember.Object.extend({
3339
- /**
3340
- The `find()` method is invoked when the store is asked for a record that
3341
- has not previously been loaded. In response to `find()` being called, you
3342
- should query your persistence layer for a record with the given ID. Once
3343
- found, you can asynchronously call the store's `load()` method to load
3344
- the record.
3345
-
3346
- Here is an example `find` implementation:
3347
-
3348
- find: function(store, type, id) {
3349
- var url = type.url;
3350
- url = url.fmt(id);
3351
-
3352
- jQuery.getJSON(url, function(data) {
3353
- // data is a Hash of key/value pairs. If your server returns a
3354
- // root, simply do something like:
3355
- // store.load(type, id, data.person)
3356
- store.load(type, id, data);
3357
- });
3358
- }
3359
- */
3360
- find: null,
3361
-
3362
- /**
3363
- If the globally unique IDs for your records should be generated on the client,
3364
- implement the `generateIdForRecord()` method. This method will be invoked
3365
- each time you create a new record, and the value returned from it will be
3366
- assigned to the record's `primaryKey`.
3367
-
3368
- Most traditional REST-like HTTP APIs will not use this method. Instead, the ID
3369
- of the record will be set by the server, and your adapter will update the store
3370
- with the new ID when it calls `didCreateRecord()`. Only implement this method if
3371
- you intend to generate record IDs on the client-side.
3372
-
3373
- The `generateIdForRecord()` method will be invoked with the requesting store as
3374
- the first parameter and the newly created record as the second parameter:
3375
-
3376
- generateIdForRecord: function(store, record) {
3377
- var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision();
3378
- return uuid;
3379
- }
3380
- */
3381
- generateIdForRecord: null,
3382
-
3383
- commit: function(store, commitDetails) {
3384
- commitDetails.updated.eachType(function(type, array) {
3385
- this.updateRecords(store, type, array.slice());
3386
- }, this);
3387
-
3388
- commitDetails.created.eachType(function(type, array) {
3389
- this.createRecords(store, type, array.slice());
3390
- }, this);
3391
-
3392
- commitDetails.deleted.eachType(function(type, array) {
3393
- this.deleteRecords(store, type, array.slice());
3394
- }, this);
3395
- },
3396
-
3397
- createRecords: function(store, type, records) {
3398
- records.forEach(function(record) {
3399
- this.createRecord(store, type, record);
3400
- }, this);
3401
- },
3402
-
3403
- updateRecords: function(store, type, records) {
3404
- records.forEach(function(record) {
3405
- this.updateRecord(store, type, record);
3406
- }, this);
3407
- },
3408
-
3409
- deleteRecords: function(store, type, records) {
3410
- records.forEach(function(record) {
3411
- this.deleteRecord(store, type, record);
3412
- }, this);
3413
- },
3414
-
3415
- findMany: function(store, type, ids) {
3416
- ids.forEach(function(id) {
3417
- this.find(store, type, id);
3418
- }, this);
3419
- }
3420
- });
3421
-
3422
- })();
3423
-
3424
-
3425
-
3426
- (function() {
3427
- var set = Ember.set;
3428
-
3429
- Ember.onLoad('application', function(app) {
3430
- app.registerInjection(function(app, stateManager, property) {
3431
- if (property === 'Store') {
3432
- set(stateManager, 'store', app[property].create());
3433
- }
3434
- });
3435
- });
3436
-
3437
- })();
3438
-
3439
-
3440
-
3441
- (function() {
3442
- DS.fixtureAdapter = DS.Adapter.create({
3443
- find: function(store, type, id) {
3444
- var fixtures = type.FIXTURES;
3445
-
3446
- if (fixtures.hasLoaded) { return; }
3447
-
3448
- setTimeout(function() {
3449
- store.loadMany(type, fixtures);
3450
- fixtures.hasLoaded = true;
3451
- }, 300);
3452
- },
3453
-
3454
- findMany: function() {
3455
- this.find.apply(this, arguments);
3456
- },
3457
-
3458
- findAll: function(store, type) {
3459
- var fixtures = type.FIXTURES;
3460
-
3461
-
3462
- var ids = fixtures.map(function(item, index, self){ return item.id; });
3463
- store.loadMany(type, ids, fixtures);
3464
- }
3465
-
3466
- });
3467
-
3468
- })();
3469
-
3470
-
3471
-
3472
- (function() {
3473
- /*global jQuery*/
3474
-
3475
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
3476
-
3477
- DS.RESTAdapter = DS.Adapter.extend({
3478
- bulkCommit: false,
3479
-
3480
- createRecord: function(store, type, record) {
3481
- var root = this.rootForType(type);
3482
-
3483
- var data = {};
3484
- data[root] = record.toJSON();
3485
-
3486
- this.ajax(this.buildURL(root), "POST", {
3487
- data: data,
3488
- success: function(json) {
3489
- this.sideload(store, type, json, root);
3490
- store.didCreateRecord(record, json[root]);
3491
- }
3492
- });
3493
- },
3494
-
3495
- createRecords: function(store, type, records) {
3496
- if (get(this, 'bulkCommit') === false) {
3497
- return this._super(store, type, records);
3498
- }
3499
-
3500
- var root = this.rootForType(type),
3501
- plural = this.pluralize(root);
3502
-
3503
- var data = {};
3504
- data[plural] = records.map(function(record) {
3505
- return record.toJSON();
3506
- });
3507
-
3508
- this.ajax(this.buildURL(root), "POST", {
3509
- data: data,
3510
-
3511
- success: function(json) {
3512
- this.sideload(store, type, json, plural);
3513
- store.didCreateRecords(type, records, json[plural]);
3514
- }
3515
- });
3516
- },
3517
-
3518
- updateRecord: function(store, type, record) {
3519
- var id = get(record, 'id');
3520
- var root = this.rootForType(type);
3521
-
3522
- var data = {};
3523
- data[root] = record.toJSON();
3524
-
3525
- this.ajax(this.buildURL(root, id), "PUT", {
3526
- data: data,
3527
- success: function(json) {
3528
- this.sideload(store, type, json, root);
3529
- store.didUpdateRecord(record, json && json[root]);
3530
- }
3531
- });
3532
- },
3533
-
3534
- updateRecords: function(store, type, records) {
3535
- if (get(this, 'bulkCommit') === false) {
3536
- return this._super(store, type, records);
3537
- }
3538
-
3539
- var root = this.rootForType(type),
3540
- plural = this.pluralize(root);
3541
-
3542
- var data = {};
3543
- data[plural] = records.map(function(record) {
3544
- return record.toJSON();
3545
- });
3546
-
3547
- this.ajax(this.buildURL(root, "bulk"), "PUT", {
3548
- data: data,
3549
- success: function(json) {
3550
- this.sideload(store, type, json, plural);
3551
- store.didUpdateRecords(records, json[plural]);
3552
- }
3553
- });
3554
- },
3555
-
3556
- deleteRecord: function(store, type, record) {
3557
- var id = get(record, 'id');
3558
- var root = this.rootForType(type);
3559
-
3560
- this.ajax(this.buildURL(root, id), "DELETE", {
3561
- success: function(json) {
3562
- if (json) { this.sideload(store, type, json); }
3563
- store.didDeleteRecord(record);
3564
- }
3565
- });
3566
- },
3567
-
3568
- deleteRecords: function(store, type, records) {
3569
- if (get(this, 'bulkCommit') === false) {
3570
- return this._super(store, type, records);
3571
- }
3572
-
3573
- var root = this.rootForType(type),
3574
- plural = this.pluralize(root);
3575
-
3576
- var data = {};
3577
- data[plural] = records.map(function(record) {
3578
- return get(record, 'id');
3579
- });
3580
-
3581
- this.ajax(this.buildURL(root, 'bulk'), "DELETE", {
3582
- data: data,
3583
- success: function(json) {
3584
- if (json) { this.sideload(store, type, json); }
3585
- store.didDeleteRecords(records);
3586
- }
3587
- });
3588
- },
3589
-
3590
- find: function(store, type, id) {
3591
- var root = this.rootForType(type);
3592
-
3593
- this.ajax(this.buildURL(root, id), "GET", {
3594
- success: function(json) {
3595
- store.load(type, json[root]);
3596
- this.sideload(store, type, json, root);
3597
- }
3598
- });
3599
- },
3600
-
3601
- findMany: function(store, type, ids) {
3602
- var root = this.rootForType(type), plural = this.pluralize(root);
3603
-
3604
- this.ajax(this.buildURL(root), "GET", {
3605
- data: { ids: ids },
3606
- success: function(json) {
3607
- store.loadMany(type, json[plural]);
3608
- this.sideload(store, type, json, plural);
3609
- }
3610
- });
3611
- },
3612
-
3613
- findAll: function(store, type) {
3614
- var root = this.rootForType(type), plural = this.pluralize(root);
3615
-
3616
- this.ajax(this.buildURL(root), "GET", {
3617
- success: function(json) {
3618
- store.loadMany(type, json[plural]);
3619
- this.sideload(store, type, json, plural);
3620
- }
3621
- });
3622
- },
3623
-
3624
- findQuery: function(store, type, query, recordArray) {
3625
- var root = this.rootForType(type), plural = this.pluralize(root);
3626
-
3627
- this.ajax(this.buildURL(root), "GET", {
3628
- data: query,
3629
- success: function(json) {
3630
- recordArray.load(json[plural]);
3631
- this.sideload(store, type, json, plural);
3632
- }
3633
- });
3634
- },
3635
-
3636
- // HELPERS
3637
-
3638
- plurals: {},
3639
-
3640
- // define a plurals hash in your subclass to define
3641
- // special-case pluralization
3642
- pluralize: function(name) {
3643
- return this.plurals[name] || name + "s";
3644
- },
3645
-
3646
- rootForType: function(type) {
3647
- if (type.url) { return type.url; }
3648
-
3649
- // use the last part of the name as the URL
3650
- var parts = type.toString().split(".");
3651
- var name = parts[parts.length - 1];
3652
- return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
3653
- },
3654
-
3655
- ajax: function(url, type, hash) {
3656
- hash.url = url;
3657
- hash.type = type;
3658
- hash.dataType = 'json';
3659
- hash.contentType = 'application/json; charset=utf-8';
3660
- hash.context = this;
3661
-
3662
- if (hash.data && type !== 'GET') {
3663
- hash.data = JSON.stringify(hash.data);
3664
- }
3665
-
3666
- jQuery.ajax(hash);
3667
- },
3668
-
3669
- sideload: function(store, type, json, root) {
3670
- var sideloadedType, mappings;
3671
-
3672
- for (var prop in json) {
3673
- if (!json.hasOwnProperty(prop)) { continue; }
3674
- if (prop === root) { continue; }
3675
-
3676
- sideloadedType = type.typeForAssociation(prop);
3677
-
3678
- if (!sideloadedType) {
3679
- mappings = get(this, 'mappings');
3680
-
3681
-
3682
- sideloadedType = get(mappings, prop);
3683
-
3684
- }
3685
-
3686
- this.loadValue(store, sideloadedType, json[prop]);
3687
- }
3688
- },
3689
-
3690
- loadValue: function(store, type, value) {
3691
- if (value instanceof Array) {
3692
- store.loadMany(type, value);
3693
- } else {
3694
- store.load(type, value);
3695
- }
3696
- },
3697
-
3698
- buildURL: function(record, suffix) {
3699
- var url = [""];
3700
-
3701
-
3702
-
3703
-
3704
- if (this.namespace !== undefined) {
3705
- url.push(this.namespace);
3706
- }
3707
-
3708
- url.push(this.pluralize(record));
3709
- if (suffix !== undefined) {
3710
- url.push(suffix);
3711
- }
3712
-
3713
- return url.join("/");
3714
- }
3715
- });
3716
-
3717
-
3718
- })();
3719
-
3720
-
3721
-
3722
- (function() {
3723
- //Copyright (C) 2011 by Living Social, Inc.
3724
-
3725
- //Permission is hereby granted, free of charge, to any person obtaining a copy of
3726
- //this software and associated documentation files (the "Software"), to deal in
3727
- //the Software without restriction, including without limitation the rights to
3728
- //use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
3729
- //of the Software, and to permit persons to whom the Software is furnished to do
3730
- //so, subject to the following conditions:
3731
-
3732
- //The above copyright notice and this permission notice shall be included in all
3733
- //copies or substantial portions of the Software.
3734
-
3735
- //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3736
- //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3737
- //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3738
- //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3739
- //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3740
- //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3741
- //SOFTWARE.
3742
-
3743
- })();
3744
-
9
+ (function(){window.DS=Ember.Namespace.create({CURRENT_API_REVISION:4})})(),function(){var a=Ember.get,b=Ember.set;DS.RecordArray=Ember.ArrayProxy.extend({type:null,content:null,store:null,init:function(){b(this,"recordCache",Ember.A([])),this._super()},arrayDidChange:function(b,c,d,e){var f=a(this,"recordCache");f.replace(c,0,new Array(e)),this._super(b,c,d,e)},arrayWillChange:function(b,c,d,e){this._super(b,c,d,e);var f=a(this,"recordCache");f.replace(c,d)},objectAtContent:function(b){var c=a(this,"recordCache"),d=c.objectAt(b);if(!d){var e=a(this,"store"),f=a(this,"content"),g=f.objectAt(b);g!==undefined&&(d=e.findByClientId(a(this,"type"),g),c.replace(b,1,[d]))}return d}})}(),function(){var a=Ember.get;DS.FilteredRecordArray=DS.RecordArray.extend({filterFunction:null,replace:function(){var b=a(this,"type").toString();throw new Error("The result of a client-side filter (on "+b+") is immutable.")},updateFilter:Ember.observer(function(){var b=a(this,"store");b.updateRecordArrayFilter(this,a(this,"type"),a(this,"filterFunction"))},"filterFunction")})}(),function(){var a=Ember.get,b=Ember.set;DS.AdapterPopulatedRecordArray=DS.RecordArray.extend({query:null,isLoaded:!1,replace:function(){var b=a(this,"type").toString();throw new Error("The result of a server query (on "+b+") is immutable.")},load:function(c){var d=a(this,"store"),e=a(this,"type"),f=d.loadMany(e,c).clientIds;this.beginPropertyChanges(),b(this,"content",Ember.A(f)),b(this,"isLoaded",!0),this.endPropertyChanges()}})}(),function(){var a=Ember.get,b=Ember.set,c=Ember.guidFor,d=function(){this.hash={},this.list=[]};d.prototype={add:function(a){var b=this.hash,d=c(a);if(b.hasOwnProperty(d))return;b[d]=!0,this.list.push(a)},remove:function(a){var b=this.hash,d=c(a);if(!b.hasOwnProperty(d))return;delete b[d];var e=this.list,f=Ember.ArrayUtils.indexOf(this,a);e.splice(f,1)},isEmpty:function(){return this.list.length===0}};var e=Ember.State.extend({recordWasAdded:function(b,c){var d=b.dirty,e;d.add(c),e=function(){a(c,"isDirty")||(c.removeObserver("isDirty",e),b.send("childWasSaved",c))},c.addObserver("isDirty",e)},recordWasRemoved:function(b,c){var d=b.dirty,e;d.add(c),e=function(){c.removeObserver("isDirty",e),a(c,"isDirty")||b.send("childWasSaved",c)},c.addObserver("isDirty",e)}}),f={clean:e.create({isDirty:!1,recordWasAdded:function(a,b){this._super(a,b),a.goToState("dirty")},update:function(a,c){var d=a.manyArray;b(d,"content",c)}}),dirty:e.create({isDirty:!0,childWasSaved:function(a,b){var c=a.dirty;c.remove(b),c.isEmpty()&&a.send("arrayBecameSaved")},arrayBecameSaved:function(a){a.goToState("clean")}})};DS.ManyArrayStateManager=Ember.StateManager.extend({manyArray:null,initialState:"clean",states:f,init:function(){this._super(),this.dirty=new d}})}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath;DS.ManyArray=DS.RecordArray.extend({init:function(){return b(this,"stateManager",DS.ManyArrayStateManager.create({manyArray:this})),this._super()},parentRecord:null,isDirty:Ember.computed(function(){return c(this,"stateManager.currentState.isDirty")}).property("stateManager.currentState").cacheable(),fetch:function(){var b=a(this,"content"),c=a(this,"store"),d=a(this,"type"),e=b.map(function(a){return c.clientIdToId[a]});c.fetchMany(d,e)},replace:function(b,c,d){var e=a(this,"parentRecord"),f=e&&!a(e,"id"),g=a(this,"stateManager");d=d.map(function(a){return f&&a.send("waitingOn",e),this.assignInverse(a,e),g.send("recordWasAdded",a),a.get("clientId")},this);var h=this.store,i=b+c,j;for(var k=b;k<i;k++)j=this.objectAt(k),this.assignInverse(j,e,!0),f&&j.send("doneWaitingOn",e),g.send("recordWasAdded",j);this._super(b,c,d)},assignInverse:function(c,d,e){var f=a(c.constructor,"associations"),g=f.get(d.constructor),h,i;if(!g)return;for(var j=0,k=g.length;j<k;j++){h=g[j];if(h.kind==="belongsTo"){i=h;break}}i&&b(c,i.name,e?null:d)},createRecord:function(b,c){var d=a(this,"parentRecord"),e=a(d,"store"),f=a(this,"type"),g;return c=c||a(d,"transaction"),g=e.createRecord.call(e,f,b,c),this.pushObject(g),g}})}(),function(){}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath,d=Ember.String.fmt;DS.Transaction=Ember.Object.extend({init:function(){b(this,"buckets",{clean:Ember.Map.create(),created:Ember.Map.create(),updated:Ember.Map.create(),deleted:Ember.Map.create(),inflight:Ember.Map.create()})},createRecord:function(b,c){var d=a(this,"store");return d.createRecord(b,c,this)},add:function(b){var d=a(b,"transaction"),e=c(this,"store.defaultTransaction");this.adoptRecord(b)},commit:function(){var b=this,c;c=function(c,d,e){var f=b.bucketForType(c);f.forEach(function(b,c){if(c.isEmpty())return;var f=[];c.forEach(function(b){b.send("willCommit"),a(b,"isPending")===!1&&f.push(b)}),d.call(e,b,f)})};var e={updated:{eachType:function(a,b){c("updated",a,b)}},created:{eachType:function(a,b){c("created",a,b)}},deleted:{eachType:function(a,b){c("deleted",a,b)}}},f=a(this,"store"),g=a(f,"_adapter");this.removeCleanRecords();if(g&&g.commit)g.commit(f,e);else throw d("Adapter is either null or does not implement `commit` method",this)},rollback:function(){var b=a(this,"store"),c;["created","updated","deleted","inflight"].forEach(function(a){c=this.bucketForType(a),c.forEach(function(a,b){b.forEach(function(a){a.send("rollback")})})},this),this.removeCleanRecords()},remove:function(a){var b=c(this,"store.defaultTransaction");b.adoptRecord(a)},removeCleanRecords:function(){var a=this.bucketForType("clean"),b=this;a.forEach(function(a,c){c.forEach(function(a){b.remove(a)})})},bucketForType:function(b){var c=a(this,"buckets");return a(c,b)},adoptRecord:function(c){var d=a(c,"transaction");d&&d.removeFromBucket("clean",c),this.addToBucket("clean",c),b(c,"transaction",this)},addToBucket:function(a,b){var c=this.bucketForType(a),d=b.constructor,e=c.get(d);e||(e=Ember.OrderedSet.create(),c.set(d,e)),e.add(b)},removeFromBucket:function(a,b){var c=this.bucketForType(a),d=b.constructor,e=c.get(d);e.remove(b)},recordBecameDirty:function(a,b){this.removeFromBucket("clean",b),this.addToBucket(a,b)},recordBecameInFlight:function(a,b){this.removeFromBucket(a,b),this.addToBucket("inflight",b)},recordBecameClean:function(a,b){this.removeFromBucket(a,b),this.remove(b)}})}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath,d=Ember.String.fmt,e={get:function(a){return this.savedData[a]}},f="unloaded",g="loading";DS.Store=Ember.Object.extend({init:function(){var c=a(this,"revision");if(c!==DS.CURRENT_API_REVISION&&!Ember.ENV.TESTING)throw new Error("Error: The Ember Data library has had breaking API changes since the last time you updated the library. Please review the list of breaking changes at https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md, then update your store's `revision` property to "+DS.CURRENT_API_REVISION);return(!a(DS,"defaultStore")||a(this,"isDefaultStore"))&&b(DS,"defaultStore",this),this.typeMaps={},this.recordCache=[],this.clientIdToId={},this.recordArraysByClientId={},b(this,"defaultTransaction",this.transaction()),this._super()},transaction:function(){return DS.Transaction.create({store:this})},dataForRecord:function(b){var c=b.constructor,d=a(b,"clientId"),e=this.typeMapFor(c);return e.cidToHash[d]},adapter:null,_adapter:Ember.computed(function(){var b=a(this,"adapter");return typeof b=="string"?c(this,b,!1)||c(window,b):b}).property("adapter").cacheable(),clientIdCounter:1,createRecord:function(c,d,e){d=d||{};var f=c._create({store:this});e=e||a(this,"defaultTransaction"),e.adoptRecord(f);var g=a(f,"primaryKey"),h=d[g]||null,i;Ember.none(h)&&(i=a(this,"adapter"),i&&i.generateIdForRecord&&(h=i.generateIdForRecord(this,f),d.id=h));var j={},k;k=this.pushHash(j,h,c),f.send("didChangeData");var l=a(this,"recordCache");return b(f,"clientId",k),l[k]=f,f.setProperties(d),this.updateRecordArrays(c,k,a(f,"data")),f},deleteRecord:function(a){a.send("deleteRecord")},find:function(a,b,c){if(b===undefined)return this.findAll(a);if(c!==undefined)return this.findMany(a,b,c);if(Ember.typeOf(b)==="object")return this.findQuery(a,b);if(Ember.isArray(b))return this.findMany(a,b);var d=this.typeMapFor(a).idToCid[b];return this.findByClientId(a,d,b)},findByClientId:function(b,c,e){var f=a(this,"recordCache"),h=this.typeMapFor(b).cidToHash,i;if(c!==undefined)i=f[c],i||(i=this.materializeRecord(b,c),typeof h[c]=="object"&&i.send("didChangeData"));else{c=this.pushHash(g,e,b),i=this.materializeRecord(b,c,e);var j=a(this,"_adapter");if(j&&j.find)j.find(this,b,e);else throw d("Adapter is either null or does not implement `find` method",this)}return i},fetchMany:function(b,c,e){var h=this.typeMapFor(b),i=h.idToCid,j=h.cidToHash,k=h.cidToHash,l,m=Ember.A([]);c?(l=[],c.forEach(function(a){var c=i[a];c===undefined?(c=this.pushHash(g,a,b),l.push(a)):c&&k[c]===f&&(j[c]=g,l.push(a)),m.push(c)},this)):l=null;if(l&&a(l,"length")>0||e){var n=a(this,"_adapter");if(n&&n.findMany)n.findMany(this,b,l,e);else throw d("Adapter is either null or does not implement `findMany` method",this)}return m},findMany:function(a,b,c){var d=this.fetchMany(a,b,c);return this.createManyArray(a,d)},findQuery:function(b,c){var e=DS.AdapterPopulatedRecordArray.create({type:b,content:Ember.A([]),store:this}),f=a(this,"_adapter");if(f&&f.findQuery)f.findQuery(this,b,c,e);else throw d("Adapter is either null or does not implement `findQuery` method",this);return e},findAll:function(b){var c=this.typeMapFor(b),d=c.findAllCache;if(d)return d;var e=DS.RecordArray.create({type:b,content:Ember.A([]),store:this});this.registerRecordArray(e,b);var f=a(this,"_adapter");return f&&f.findAll&&f.findAll(this,b),c.findAllCache=e,e},filter:function(a,b,c){arguments.length===3?this.findQuery(a,b):arguments.length===2&&(c=b);var d=DS.FilteredRecordArray.create({type:a,content:Ember.A([]),store:this,filterFunction:c});return this.registerRecordArray(d,a,c),d},hashWasUpdated:function(b,c,d){if(a(d,"isDeleted"))return;this.updateRecordArrays(b,c,a(d,"data"))},commit:function(){var c=a(this,"defaultTransaction");b(this,"defaultTransaction",this.transaction()),c.commit()},didUpdateRecords:function(a,b){b?a.forEach(function(a,c){this.didUpdateRecord(a,b[c])},this):a.forEach(function(a){this.didUpdateRecord(a)},this)},didUpdateRecord:function(b,c){if(c){var d=a(b,"clientId"),e=this.typeMapFor(b.constructor).cidToHash;e[d]=c,b.send("didChangeData"),b.hashWasUpdated()}else b.send("didSaveData");b.send("didCommit")},didDeleteRecords:function(a){a.forEach(function(a){a.send("didCommit")})},didDeleteRecord:function(a){a.send("didCommit")},_didCreateRecord:function(b,c,d,e,f){var g=a(b,"data"),h,i;c?(d.cidToHash[e]=c,b.beginPropertyChanges(),b.send("didChangeData"),g.adapterDidUpdate(),b.hashWasUpdated(),b.endPropertyChanges(),h=c[f],d.idToCid[h]=e,this.clientIdToId[e]=h):g.commit(),b.send("didCommit")},didCreateRecords:function(b,c,d){var e=b.proto().primaryKey,f=this.typeMapFor(b),g;for(var h=0,i=a(c,"length");h<i;h++){var j=c[h],k=d[h];g=a(j,"clientId"),this._didCreateRecord(j,k,f,g,e)}},didCreateRecord:function(b,c){var d=b.constructor,e=this.typeMapFor(d),f,g;g=d.proto().primaryKey,!c,f=a(b,"clientId"),this._didCreateRecord(b,c,e,f,g)},recordWasInvalid:function(a,b){a.send("becameInvalid",b)},registerRecordArray:function(a,b,c){var d=this.typeMapFor(b).recordArrays;d.push(a),this.updateRecordArrayFilter(a,b,c)},createManyArray:function(a,b){var c=DS.ManyArray.create({type:a,content:b,store:this});return b.forEach(function(a){var b=this.recordArraysForClientId(a);b.add(c)},this),c},updateRecordArrayFilter:function(b,c,d){var f=this.typeMapFor(c),g=f.cidToHash,h=f.clientIds,i,j,k,l=a(this,"recordCache"),m;for(var n=0,o=h.length;n<o;n++)i=h[n],j=g[i],typeof j=="object"&&((m=l[i])?k=a(m,"data"):(e.savedData=j,k=e),this.updateRecordArray(b,d,c,i,k))},updateRecordArrays:function(b,c,d){var e=this.typeMapFor(b).recordArrays,f;e.forEach(function(e){f=a(e,"filterFunction"),this.updateRecordArray(e,f,b,c,d)},this)},updateRecordArray:function(b,c,d,e,f){var g;c?g=c(f):g=!0;var h=a(b,"content"),i=h.indexOf(e)!==-1,j=this.recordArraysForClientId(e);g&&!i?(j.add(b),h.pushObject(e)):!g&&i&&(j.remove(b),h.removeObject(e))},removeFromRecordArrays:function(b){var c=a(b,"clientId"),d=this.recordArraysForClientId(c);d.forEach(function(b){var d=a(b,"content");d.removeObject(c)})},recordArraysForClientId:function(b){var c=a(this,"recordArraysByClientId"),d=c[b];return d||(d=c[b]=Ember.OrderedSet.create()),d},typeMapFor:function(b){var c=a(this,"typeMaps"),d=Ember.guidFor(b),e=c[d];return e?e:c[d]={idToCid:{},clientIds:[],cidToHash:{},recordArrays:[]}},clientIdForId:function(a,b){var c=this.typeMapFor(a).idToCid[b];return c!==undefined?c:this.pushHash(f,b,a)},load:function(b,c,d){if(d===undefined){d=c;var f=b.proto().primaryKey;c=d[f]}var g=this.typeMapFor(b),h=g.cidToHash,i=g.idToCid[c],j=a(this,"recordCache");if(i!==undefined){h[i]=d;var k=j[i];k&&k.send("didChangeData")}else i=this.pushHash(d,c,b);return e.savedData=d,this.updateRecordArrays(b,i,e),{id:c,clientId:i}},loadMany:function(b,c,d){var e=Ember.A([]);if(d===undefined){d=c,c=[];var f=b.proto().primaryKey;c=Ember.ArrayUtils.map(d,function(a){return a[f]})}for(var g=0,h=a(c,"length");g<h;g++){var i=this.load(b,c[g],d[g]);e.pushObject(i.clientId)}return{clientIds:e,ids:c}},pushHash:function(a,b,c){var d=this.typeMapFor(c),e=d.idToCid,f=this.clientIdToId,g=d.clientIds,h=d.cidToHash,i=++this.clientIdCounter;return h[i]=a,b&&(e[b]=i,f[i]=b),g.push(i),i},materializeRecord:function(b,c,d){var e;return a(this,"recordCache")[c]=e=b._create({store:this,clientId:c,_id:d}),a(this,"defaultTransaction").adoptRecord(e),e.send("loadingData"),e},destroy:function(){return a(DS,"defaultStore")===this&&b(DS,"defaultStore",null),this._super()}})}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath,d=Ember.guidFor,e=Ember.computed(function(b){var c=a(this,"parentState");if(c)return a(c,b)}).property(),f=function(a){for(var b in a)if(a.hasOwnProperty(b))return!1;return!0},g=function(a){for(var b in a)if(a.hasOwnProperty(b)&&a[b])return!0;return!1};DS.State=Ember.State.extend({isLoaded:e,isDirty:e,isSaving:e,isDeleted:e,isError:e,isNew:e,isValid:e,isPending:e,dirtyType:e});var h=function(c,d){var e=d.key,f=d.value,g=a(c,"record"),h=a(g,"data");b(h,e,f)},i=function(b,c){var d=c.key,e=c.value,f=a(b,"record"),g=a(f,"data");g.setAssociation(d,e)},j=function(b){var c=a(b,"record"),d=a(c,"data");d._savedData=null,c.notifyPropertyChange("data")},k=function(b,c){var e=a(b,"record"),f=a(e,"pendingQueue"),g=d(c),h=function(){a(c,"id")&&(b.send("doneWaitingOn",c),Ember.removeObserver(c,"id",h))};f[g]=[c,h],Ember.addObserver(c,"id",h)},l=Ember.Mixin.create({setProperty:h,setAssociation:i}),m=Ember.Mixin.create({deleteRecord:function(b){var c=a(b,"record");this._super(b),c.withTransaction(function(a){a.recordBecameClean("created",c)}),b.goToState("deleted.saved")}}),n=Ember.Mixin.create({deleteRecord:function(b){this._super(b);var c=a(b,"record");c.withTransaction(function(a){a.recordBecameClean("updated",c)}),b.goToState("deleted")}}),o=DS.State.extend({initialState:"uncommitted",isDirty:!0,uncommitted:DS.State.extend({enter:function(b){var c=a(this,"dirtyType"),d=a(b,"record");d.withTransaction(function(a){a.recordBecameDirty(c,d)})},deleteRecord:Ember.K,waitingOn:function(a,b){k(a,b),a.goToState("pending")},willCommit:function(a){a.goToState("inFlight")},becameInvalid:function(b){var c=a(this,"dirtyType"),d=a(b,"record");d.withTransaction(function(a){a.recordBecameInFlight(c,d)}),b.goToState("invalid")},rollback:function(b){var c=a(b,"record"),d=a(this,"dirtyType"),e=a(c,"data");e.rollback(),c.withTransaction(function(a){a.recordBecameClean(d,c)}),b.goToState("loaded")}},l),inFlight:DS.State.extend({isSaving:!0,enter:function(b){var c=a(this,"dirtyType"),d=a(b,"record");d.withTransaction(function(a){a.recordBecameInFlight(c,d)})},didCommit:function(b){var c=a(this,"dirtyType"),d=a(b,"record");d.withTransaction(function(a){a.recordBecameClean("inflight",d)}),b.goToState("loaded"),b.send("invokeLifecycleCallbacks",c)},becameInvalid:function(c,d){var e=a(c,"record");b(e,"errors",d),c.goToState("invalid"),c.send("invokeLifecycleCallbacks")},becameError:function(a){a.goToState("error"),a.send("invokeLifecycleCallbacks")},didChangeData:j}),pending:DS.State.extend({initialState:"uncommitted",isPending:!0,uncommitted:DS.State.extend({deleteRecord:function(b){var c=a(b,"record"),d=a(c,"pendingQueue"),e;for(var f in d){if(!d.hasOwnProperty(f))continue;e=d[f],Ember.removeObserver(e[0],"id",e[1])}},willCommit:function(a){a.goToState("committing")},doneWaitingOn:function(b,c){var e=a(b,"record"),g=a(e,"pendingQueue"),h=d(c);delete g[h],f(g)&&b.send("doneWaiting")},doneWaiting:function(b){var c=a(this,"dirtyType");b.goToState(c+".uncommitted")}},l),committing:DS.State.extend({isSaving:!0,doneWaitingOn:function(b,c){var e=a(b,"record"),g=a(e,"pendingQueue"),h=d(c);delete g[h],f(g)&&b.send("doneWaiting")},doneWaiting:function(b){var c=a(b,"record"),d=a(c,"transaction");Ember.run.once(d,d.commit)},willCommit:function(b){var c=a(b,"record"),d=a(c,"pendingQueue");if(f(d)){var e=a(this,"dirtyType");b.goToState(e+".inFlight")}}})}),invalid:DS.State.extend({isValid:!1,exit:function(b){var c=a(b,"record");c.withTransaction(function(a){a.recordBecameClean("inflight",c)})},deleteRecord:function(a){a.goToState("deleted")},setAssociation:i,setProperty:function(b,c){h(b,c);var d=a(b,"record"),e=a(d,"errors"),f=c.key;delete e[f],g(e)||b.send("becameValid")},rollback:function(a){a.send("becameValid"),a.send("rollback")},becameValid:function(a){a.goToState("uncommitted")},invokeLifecycleCallbacks:function(b){var c=a(b,"record");c.fire("becameInvalid",c)}})}),p=o.create({dirtyType:"created",isNew:!0}),q=o.create({dirtyType:"updated"});p.states.uncommitted.reopen(m),p.states.pending.states.uncommitted.reopen(m),p.states.uncommitted.reopen({rollback:function(a){this._super(a),a.goToState("deleted.saved")}}),q.states.uncommitted.reopen(n),q.states.pending.states.uncommitted.reopen(n),q.states.inFlight.reopen({didSaveData:function(b){var c=a(b,"record"),d=a(c,"data");d.saveData(),d.adapterDidUpdate()}});var r={rootState:Ember.State.create({isLoaded:!1,isDirty:!1,isSaving:!1,isDeleted:!1,isError:!1,isNew:!1,isValid:!0,isPending:!1,empty:DS.State.create({loadingData:function(a){a.goToState("loading")},didChangeData:function(a){j(a),a.goToState("loaded.created")}}),loading:DS.State.create({exit:function(b){var c=a(b,"record");c.fire("didLoad")},didChangeData:function(a,b){j(a),a.send("loadedData")},loadedData:function(a){a.goToState("loaded")}}),loaded:DS.State.create({initialState:"saved",isLoaded:!0,saved:DS.State.create({setProperty:function(a,b){h(a,b),a.goToState("updated")},setAssociation:function(a,b){i(a,b),a.goToState("updated")},didChangeData:j,deleteRecord:function(a){a.goToState("deleted")},waitingOn:function(a,b){k(a,b),a.goToState("updated.pending")},invokeLifecycleCallbacks:function(b,c){var d=a(b,"record");c==="created"?d.fire("didCreate",d):d.fire("didUpdate",d)}}),created:p,updated:q}),deleted:DS.State.create({isDeleted:!0,isLoaded:!0,isDirty:!0,enter:function(b){var c=a(b,"record"),d=a(c,"store");d.removeFromRecordArrays(c)},start:DS.State.create({enter:function(b){var c=a(b,"record");c.withTransaction(function(a){a.recordBecameDirty("deleted",c)})},willCommit:function(a){a.goToState("inFlight")},rollback:function(b){var c=a(b,"record"),d=a(c,"data");d.rollback(),c.withTransaction(function(a){a.recordBecameClean("deleted",c)}),b.goToState("loaded")}}),inFlight:DS.State.create({isSaving:!0,enter:function(b){var c=a(b,"record");c.withTransaction(function(a){a.recordBecameInFlight("deleted",c)})},didCommit:function(b){var c=a(b,"record");c.withTransaction(function(a){a.recordBecameClean("inflight",c)}),b.goToState("saved"),b.send("invokeLifecycleCallbacks")}}),saved:DS.State.create({isDirty:!1,invokeLifecycleCallbacks:function(b){var c=a(b,"record");c.fire("didDelete",c)}})}),error:DS.State.create({isError:!0,invokeLifecycleCallbacks:function(b){var c=a(b,"record");c.fire("becameError",c)}})})};DS.StateManager=Ember.StateManager.extend({record:null,initialState:"rootState",states:r})}(),function(){var a=Ember.get,b=Ember.set,c=DS._DataProxy=function(a){this.record=a,this.unsavedData={},this.associations={}};c.prototype={get:function(a){return Ember.get(this,a)},set:function(a,b){return Ember.set(this,a,b)},setAssociation:function(a,b){this.associations[a]=b},savedData:function(){var b=this._savedData;if(b)return b;var c=this.record,d=a(c,"clientId"),e=a(c,"store");if(e)return b=e.dataForRecord(c),this._savedData=b,b},unknownProperty:function(b){var c=this.unsavedData,d=this.associations,e=this.savedData(),f,g=c[b],h;return h=d[b],h!==undefined?(f=a(this.record,"store"),f.clientIdToId[h]):(e&&g===undefined&&(g=e[b]),g)},setUnknownProperty:function(a,b){var c=this.record,d=this.unsavedData;return d[a]=b,c.hashWasUpdated(),b},commit:function(){this.saveData(),this.record.notifyPropertyChange("data")},rollback:function(){this.unsavedData={},this.record.notifyPropertyChange("data")},saveData:function(){var a=this.record,b=this.unsavedData,c=this.savedData();for(var d in b)b.hasOwnProperty(d)&&(c[d]=b[d],delete b[d])},adapterDidUpdate:function(){this.unsavedData={}}}}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath,d=Ember.none,e=Ember.computed(function(b){return a(c(this,"stateManager.currentState"),b)}).property("stateManager.currentState").cacheable();DS.Model=Ember.Object.extend(Ember.Evented,{isLoaded:e,isDirty:e,isSaving:e,isDeleted:e,isError:e,isNew:e,isPending:e,isValid:e,clientId:null,transaction:null,stateManager:null,pendingQueue:null,errors:null,primaryKey:"id",id:Ember.computed(function(c,d){var e=a(this,"primaryKey"),f=a(this,"data");if(arguments.length===2)return b(f,e,d),d;var g=a(f,e);return g?g:this._id}).property("primaryKey","data"),addIdToJSON:function(a,b,c){b&&(a[c]=b)},addAttributesToJSON:function(b,c,d){c.forEach(function(c,e){var f=e.key(this.constructor),g=a(d,f);g===undefined&&(g=e.options.defaultValue),b[f]=g},this)},addHasManyToJSON:function(b,c,d,e){var f=d.key,g=a(this,f),h=[],i,j,k,l;if(d.options.embedded)g.forEach(function(a){h.push(a.toJSON(e))});else{var m=a(g,"content");for(i=0,j=m.length;i<j;i++)k=m[i],l=a(this,"store").clientIdToId[k],l!==undefined&&h.push(l)}f=d.options.key||a(this,"namingConvention").keyToJSONKey(f),b[f]=h},addBelongsToToJSON:function(b,c,e,f){var g=e.key,h,i;e.options.embedded?(g=e.options.key||a(this,"namingConvention").keyToJSONKey(g),h=a(c.record,g),b[g]=h?h.toJSON(f):null):(g=e.options.key||a(this,"namingConvention").foreignKey(g),i=c.get(g),b[g]=d(i)?null:i)},toJSON:function(b){var c=a(this,"data"),d={},e=this.constructor,f=a(e,"attributes"),g=a(this,"primaryKey"),h=a(this,"id"),i=a(this,"store"),j;return b=b||{},this.addIdToJSON(d,h,g),this.addAttributesToJSON(d,f,c),j=a(e,"associationsByName"),j.forEach(function(a,e){b.associations&&e.kind==="hasMany"?this.addHasManyToJSON(d,c,e,b):e.kind==="belongsTo"&&this.addBelongsToToJSON(d,c,e,b)},this),d},data:Ember.computed(function(){return new DS._DataProxy(this)}).cacheable(),didLoad:Ember.K,didUpdate:Ember.K,didCreate:Ember.K,didDelete:Ember.K,becameInvalid:Ember.K,becameError:Ember.K,init:function(){var a=DS.StateManager.create({record:this});b(this,"pendingQueue",{}),b(this,"stateManager",a),a.goToState("empty")},destroy:function(){a(this,"isDeleted")||this.deleteRecord(),this._super()},send:function(b,c){return a(this,"stateManager").send(b,c)},withTransaction:function(b){var c=a(this,"transaction");c&&b(c)},setProperty:function(a,b){this.send("setProperty",{key:a,value:b})},deleteRecord:function(){this.send("deleteRecord")},waitingOn:function(a){this.send("waitingOn",a)},notifyHashWasUpdated:function(){var b=a(this,"store");b&&b.hashWasUpdated(this.constructor,a(this,"clientId"),this)},unknownProperty:function(b){var c=a(this,"data");!(c&&b in c)},setUnknownProperty:function(b,c){var d=a(this,"data");if(!(d&&b in d))return this._super(b,c)},namingConvention:{keyToJSONKey:function(a){return Ember.String.decamelize(a)},foreignKey:function(a){return Ember.String.decamelize(a)+"_id"}},hashWasUpdated:function(){Ember.run.once(this,this.notifyHashWasUpdated)},dataDidChange:Ember.observer(function(){var c=a(this.constructor,"associationsByName"),d=a(this,"data"),e=a(this,"store"),f=e.idToClientId,g;c.forEach(function(a,c){if(c.kind==="hasMany"){g=this.cacheFor(a);if(g){var f=c.options.key||a,h=d.get(f)||[],i=Ember.ArrayUtils.map(h,function(a){return e.clientIdForId(c.type,a)});b(g,"content",Ember.A(i)),g.fetch()}}},this)},"data"),fire:function(a){this[a].apply(this,[].slice.call(arguments,1)),this._super.apply(this,arguments)}});var f=function(b){return function(){var c=a(DS,"defaultStore"),d=[].slice.call(arguments);return d.unshift(this),c[b].apply(c,d)}};DS.Model.reopenClass({find:f("find"),filter:f("filter"),_create:DS.Model.create,create:function(){throw new Ember.Error("You should not call `create` on a model. Instead, call `createRecord` with the attributes you would like to set.")},createRecord:f("createRecord")})}(),function(){var a=Ember.get,b=Ember.getPath;DS.Model.reopenClass({attributes:Ember.computed(function(){var a=Ember.Map.create();return this.eachComputedProperty(function(b,c){c.isAttribute&&a.set(b,c)}),a}).cacheable(),processAttributeKeys:function(){if(this.processedAttributeKeys)return;var a=this.proto().namingConvention;this.eachComputedProperty(function(b,c){c.isAttribute&&!c.options.key&&(c.options.key=a.keyToJSONKey(b,this))},this)}}),DS.attr=function(b,c){var d=DS.attr.transforms[b],e=d.from,f=d.to;c=c||{};var g={type:b,isAttribute:!0,options:c,key:function(a){return a.processAttributeKeys(),c.key}};return Ember.computed(function(b,d){var h;return b=g.key(this.constructor),arguments.length===2?(d=f(d),this.setProperty(b,d)):(h=a(this,"data"),d=a(h,b),d===undefined&&(d=c.defaultValue)),e(d)}).property("data").cacheable().meta(g)},DS.attr.transforms={string:{from:function(a){return Ember.none(a)?null:String(a)},to:function(a){return Ember.none(a)?null:String(a)}},number:{from:function(a){return Ember.none(a)?null:Number(a)},to:function(a){return Ember.none(a)?null:Number(a)}},"boolean":{from:function(a){return Boolean(a)},to:function(a){return Boolean(a)}},date:{from:function(a){var b=typeof a;return b==="string"||b==="number"?new Date(a):a===null||a===undefined?a:null},to:function(a){if(a instanceof Date){var b=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],c=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],d=function(a){return a<10?"0"+a:""+a},e=a.getUTCFullYear(),f=a.getUTCMonth(),g=a.getUTCDate(),h=a.getUTCDay(),i=a.getUTCHours(),j=a.getUTCMinutes(),k=a.getUTCSeconds(),l=b[h],m=d(g),n=c[f];return l+", "+m+" "+n+" "+e+" "+d(i)+":"+d(j)+":"+d(k)+" GMT"}return a===undefined?undefined:null}}}}(),function(){}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath,d=Ember.none,e=function(b,c,e,f,g){var h=a(e,f);return d(h)?undefined:b.load(c,h).id},f=function(b,c,d,e,f){return a(d,e)},g=function(b,d,g){d=d||{};var h=d.embedded,i=h?e:f,j={type:b,isAssociation:!0,options:d,kind:"belongsTo"};return Ember.computed(function(e,f){var g=a(this,"data"),j,k,l,m=a(this,"store");return typeof b=="string"&&(b=c(this,b,!1)||c(window,b)),arguments.length===2?(e=d.key||a(this,"namingConvention").foreignKey(e),this.send("setAssociation",{key:e,value:f===null?null:a(f,"clientId")}),f):(h?e=d.key||a(this,"namingConvention").keyToJSONKey(e):e=d.key||a(this,"namingConvention").foreignKey(e),k=i(m,b,g,e,!0),l=k?m.find(b,k):null,l)}).property("data").cacheable().meta(j)};DS.belongsTo=function(a,b){return g(a,b)}}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath,d=function(b,c,d,e){var f=a(d,e);return f?b.loadMany(c,f).ids:[]},e=function(b,c,d,e,f){return a(d,e)},f=function(f,g){g=g||{};var h=g.embedded,i=h?d:e,j={type:f,isAssociation:!0,options:g,kind:"hasMany"};return Ember.computed(function(d,e){var h=a(this,"data"),j=a(this,"store"),k,l,m;return typeof f=="string"&&(f=c(this,f,!1)||c(window,f)),d=g.key||a(this,"namingConvention").keyToJSONKey(d),k=i(j,f,h,d),m=j.findMany(f,k),b(m,"parentRecord",this),m}).property().cacheable().meta(j)};DS.hasMany=function(a,b){return f(a,b)}}(),function(){var a=Ember.get,b=Ember.getPath;DS.Model.reopenClass({typeForAssociation:function(b){var c=a(this,"associationsByName").get(b);return c&&c.type},associations:Ember.computed(function(){var a=Ember.Map.create();return this.eachComputedProperty(function(c,d){if(d.isAssociation){var e=d.type,f=a.get(e);typeof e=="string"&&(e=b(this,e,!1)||b(window,e),d.type=e),f||(f=[],a.set(e,f)),f.push({name:c,kind:d.kind})}}),a}).cacheable(),associationsByName:Ember.computed(function(){var a=Ember.Map.create(),c;return this.eachComputedProperty(function(d,e){e.isAssociation&&(e.key=d,c=e.type,typeof c=="string"&&(c=b(this,c,!1)||b(window,c),e.type=c),a.set(d,e))}),a}).cacheable()})}(),function(){}(),function(){DS.Adapter=Ember.Object.extend({find:null,generateIdForRecord:null,commit:function(a,b){b.updated.eachType(function(b,c){this.updateRecords(a,b,c.slice())},this),b.created.eachType(function(b,c){this.createRecords(a,b,c.slice())},this),b.deleted.eachType(function(b,c){this.deleteRecords(a,b,c.slice())},this)},createRecords:function(a,b,c){c.forEach(function(c){this.createRecord(a,b,c)},this)},updateRecords:function(a,b,c){c.forEach(function(c){this.updateRecord(a,b,c)},this)},deleteRecords:function(a,b,c){c.forEach(function(c){this.deleteRecord(a,b,c)},this)},findMany:function(a,b,c){c.forEach(function(c){this.find(a,b,c)},this)}})}(),function(){var a=Ember.set;Ember.onLoad("application",function(b){b.registerInjection(function(b,c,d){d==="Store"&&a(c,"store",b[d].create())})})}(),function(){DS.fixtureAdapter=DS.Adapter.create({find:function(a,b,c){var d=b.FIXTURES;if(d.hasLoaded)return;setTimeout(function(){a.loadMany(b,d),d.hasLoaded=!0},300)},findMany:function(){this.find.apply(this,arguments)},findAll:function(a,b){var c=b.FIXTURES,d=c.map(function(a,b,c){return a.id});a.loadMany(b,d,c)}})}(),function(){var a=Ember.get,b=Ember.set,c=Ember.getPath;DS.RESTAdapter=DS.Adapter.extend({bulkCommit:!1,createRecord:function(a,b,c){var d=this.rootForType(b),e={};e[d]=c.toJSON(),this.ajax(this.buildURL(d),"POST",{data:e,success:function(e){this.sideload(a,b,e,d),a.didCreateRecord(c,e[d])}})},createRecords:function(b,c,d){if(a(this,"bulkCommit")===!1)return this._super(b,c,d);var e=this.rootForType(c),f=this.pluralize(e),g={};g[f]=d.map(function(a){return a.toJSON()}),this.ajax(this.buildURL(e),"POST",{data:g,success:function(a){this.sideload(b,c,a,f),b.didCreateRecords(c,d,a[f])}})},updateRecord:function(b,c,d){var e=a(d,"id"),f=this.rootForType(c),g={};g[f]=d.toJSON(),this.ajax(this.buildURL(f,e),"PUT",{data:g,success:function(a){this.sideload(b,c,a,f),b.didUpdateRecord(d,a&&a[f])}})},updateRecords:function(b,c,d){if(a(this,"bulkCommit")===!1)return this._super(b,c,d);var e=this.rootForType(c),f=this.pluralize(e),g={};g[f]=d.map(function(a){return a.toJSON()}),this.ajax(this.buildURL(e,"bulk"),"PUT",{data:g,success:function(a){this.sideload(b,c,a,f),b.didUpdateRecords(d,a[f])}})},deleteRecord:function(b,c,d){var e=a(d,"id"),f=this.rootForType(c);this.ajax(this.buildURL(f,e),"DELETE",{success:function(a){a&&this.sideload(b,c,a),b.didDeleteRecord(d)}})},deleteRecords:function(b,c,d){if(a(this,"bulkCommit")===!1)return this._super(b,c,d);var e=this.rootForType(c),f=this.pluralize(e),g={};g[f]=d.map(function(b){return a(b,"id")}),this.ajax(this.buildURL(e,"bulk"),"DELETE",{data:g,success:function(a){a&&this.sideload(b,c,a),b.didDeleteRecords(d)}})},find:function(a,b,c){var d=this.rootForType(b);this.ajax(this.buildURL(d,c),"GET",{success:function(c){a.load(b,c[d]),this.sideload(a,b,c,d)}})},findMany:function(a,b,c){var d=this.rootForType(b),e=this.pluralize(d);this.ajax(this.buildURL(d),"GET",{data:{ids:c},success:function(c){a.loadMany(b,c[e]),this.sideload(a,b,c,e)}})},findAll:function(a,b){var c=this.rootForType(b),d=this.pluralize(c);this.ajax(this.buildURL(c),"GET",{success:function(c){a.loadMany(b,c[d]),this.sideload(a,b,c,d)}})},findQuery:function(a,b,c,d){var e=this.rootForType(b),f=this.pluralize(e);this.ajax(this.buildURL(e),"GET",{data:c,success:function(c){d.load(c[f]),this.sideload(a,b,c,f)}})},plurals:{},pluralize:function(a){return this.plurals[a]||a+"s"},rootForType:function(a){if(a.url)return a.url;var b=a.toString().split("."),c=b[b.length-1];return c.replace(/([A-Z])/g,"_$1").toLowerCase().slice(1)},ajax:function(a,b,c){c.url=a,c.type=b,c.dataType="json",c.contentType="application/json; charset=utf-8",c.context=this,c.data&&b!=="GET"&&(c.data=JSON.stringify(c.data)),jQuery.ajax(c)},sideload:function(b,c,d,e){var f,g;for(var h in d){if(!d.hasOwnProperty
10
+ (h))continue;if(h===e)continue;f=c.typeForAssociation(h),f||(g=a(this,"mappings"),f=a(g,h)),this.loadValue(b,f,d[h])}},loadValue:function(a,b,c){c instanceof Array?a.loadMany(b,c):a.load(b,c)},buildURL:function(a,b){var c=[""];return this.namespace!==undefined&&c.push(this.namespace),c.push(this.pluralize(a)),b!==undefined&&c.push(b),c.join("/")}})}(),function(){}()