Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Adding ember-data as a dependency.

  • Loading branch information...
commit e44d16f4433904b9836a2819c5095d92ea97b9af 1 parent 7ee4acf
Joachim Haagen Skeie authored February 05, 2012
1  src/main/webapp/index.html
@@ -12,6 +12,7 @@
12 12
     <script src="scripts/jquery-1.7.1.min.js" type="text/javascript" charset="utf-8"></script>
13 13
     <script src="scripts/jquery.transit.js" type="text/javascript" charset="utf-8"></script>
14 14
     <script src="scripts/ember-0.9.4.js" type="text/javascript" charset="utf-8"></script>
  15
+    <script src="scripts/ember-data-latest.js" type="text/javascript" charset="utf-8"></script>
15 16
     <script src="scripts/ember-touch.js" type="text/javascript" charset="utf-8"></script>
16 17
     <script src="app/app.js" type="text/javascript" charset="utf-8"></script>
17 18
     <script src="app/main.js" type="text/javascript" charset="utf-8"></script>
1,772  src/main/webapp/scripts/ember-data-latest.js
... ...
@@ -0,0 +1,1772 @@
  1
+
  2
+(function(exports) {
  3
+window.DS = Ember.Namespace.create();
  4
+
  5
+})({});
  6
+
  7
+
  8
+(function(exports) {
  9
+DS.Adapter = Ember.Object.extend({
  10
+  commit: function(store, commitDetails) {
  11
+    commitDetails.updated.eachType(function(type, array) {
  12
+      this.updateRecords(store, type, array.slice());
  13
+    }, this);
  14
+
  15
+    commitDetails.created.eachType(function(type, array) {
  16
+      this.createRecords(store, type, array.slice());
  17
+    }, this);
  18
+
  19
+    commitDetails.deleted.eachType(function(type, array) {
  20
+      this.deleteRecords(store, type, array.slice());
  21
+    }, this);
  22
+  },
  23
+
  24
+  createRecords: function(store, type, models) {
  25
+    models.forEach(function(model) {
  26
+      this.createRecord(store, type, model);
  27
+    }, this);
  28
+  },
  29
+
  30
+  updateRecords: function(store, type, models) {
  31
+    models.forEach(function(model) {
  32
+      this.updateRecord(store, type, model);
  33
+    }, this);
  34
+  },
  35
+
  36
+  deleteRecords: function(store, type, models) {
  37
+    models.forEach(function(model) {
  38
+      this.deleteRecord(store, type, model);
  39
+    }, this);
  40
+  },
  41
+
  42
+  findMany: function(store, type, ids) {
  43
+    ids.forEach(function(id) {
  44
+      this.find(store, type, id);
  45
+    }, this);
  46
+  }
  47
+});
  48
+})({});
  49
+
  50
+
  51
+(function(exports) {
  52
+DS.fixtureAdapter = DS.Adapter.create({
  53
+  find: function(store, type, id) {
  54
+    var fixtures = type.FIXTURES;
  55
+
  56
+    ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
  57
+    if (fixtures.hasLoaded) { return; }
  58
+
  59
+    setTimeout(function() {
  60
+      store.loadMany(type, fixtures);
  61
+      fixtures.hasLoaded = true;
  62
+    }, 300);
  63
+  },
  64
+
  65
+  findMany: function() {
  66
+    this.find.apply(this, arguments);
  67
+  },
  68
+
  69
+  findAll: function(store, type) {
  70
+    var fixtures = type.FIXTURES;
  71
+
  72
+    ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
  73
+
  74
+    var ids = fixtures.map(function(item, index, self){ return item.id });
  75
+    store.loadMany(type, ids, fixtures);
  76
+  }
  77
+
  78
+});
  79
+
  80
+})({});
  81
+
  82
+
  83
+(function(exports) {
  84
+var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
  85
+
  86
+DS.RESTAdapter = DS.Adapter.extend({
  87
+  createRecord: function(store, type, model) {
  88
+    var root = this.rootForType(type);
  89
+
  90
+    var data = {};
  91
+    data[root] = get(model, 'data');
  92
+
  93
+    this.ajax("/" + this.pluralize(root), "POST", {
  94
+      data: data,
  95
+      success: function(json) {
  96
+        store.didCreateRecord(model, json[root]);
  97
+      }
  98
+    });
  99
+  },
  100
+
  101
+  createRecords: function(store, type, models) {
  102
+    if (get(this, 'bulkCommit') === false) {
  103
+      return this._super(store, type, models);
  104
+    }
  105
+
  106
+    var root = this.rootForType(type),
  107
+        plural = this.pluralize(root);
  108
+
  109
+    var data = {};
  110
+    data[plural] = models.map(function(model) {
  111
+      return get(model, 'data');
  112
+    });
  113
+
  114
+    this.ajax("/" + this.pluralize(root), "POST", {
  115
+      data: data,
  116
+      success: function(json) {
  117
+        store.didCreateRecords(type, models, json[plural]);
  118
+      }
  119
+    });
  120
+  },
  121
+
  122
+  updateRecord: function(store, type, model) {
  123
+    var primaryKey = getPath(type, 'proto.primaryKey'),
  124
+        id = get(model, primaryKey);
  125
+    var root = this.rootForType(type);
  126
+
  127
+    var data = {};
  128
+    data[root] = get(model, 'data');
  129
+
  130
+    var url = ["", this.pluralize(root), id].join("/");
  131
+
  132
+    this.ajax(url, "PUT", {
  133
+      data: data,
  134
+      success: function(json) {
  135
+        store.didUpdateRecord(model, json[root]);
  136
+      }
  137
+    });
  138
+  },
  139
+
  140
+  updateRecords: function(store, type, models) {
  141
+    if (get(this, 'bulkCommit') === false) {
  142
+      return this._super(store, type, models);
  143
+    }
  144
+
  145
+    var root = this.rootForType(type),
  146
+        plural = this.pluralize(root);
  147
+
  148
+    var data = {};
  149
+    data[plural] = models.map(function(model) {
  150
+      return get(model, 'data');
  151
+    });
  152
+
  153
+    this.ajax("/" + this.pluralize(root), "POST", {
  154
+      data: data,
  155
+      success: function(json) {
  156
+        store.didUpdateRecords(models, json[plural]);
  157
+      }
  158
+    });
  159
+  },
  160
+
  161
+  deleteRecord: function(store, type, model) {
  162
+    var primaryKey = getPath(type, 'proto.primaryKey'),
  163
+        id = get(model, primaryKey);
  164
+    var root = this.rootForType(type);
  165
+
  166
+    var url = ["", this.pluralize(root), id].join("/");
  167
+
  168
+    this.ajax(url, "DELETE", {
  169
+      success: function(json) {
  170
+        store.didDeleteRecord(model);
  171
+      }
  172
+    });
  173
+  },
  174
+
  175
+  deleteRecords: function(store, type, models) {
  176
+    if (get(this, 'bulkCommit') === false) {
  177
+      return this._super(store, type, models);
  178
+    }
  179
+
  180
+    var root = this.rootForType(type),
  181
+        plural = this.pluralize(root),
  182
+        primaryKey = getPath(type, 'proto.primaryKey');
  183
+
  184
+    var data = {};
  185
+    data[plural] = models.map(function(model) {
  186
+      return get(model, primaryKey);
  187
+    });
  188
+
  189
+    this.ajax("/" + this.pluralize(root) + "/delete", "POST", {
  190
+      data: data,
  191
+      success: function(json) {
  192
+        store.didDeleteRecords(models);
  193
+      }
  194
+    });
  195
+  },
  196
+
  197
+  find: function(store, type, id) {
  198
+    var root = this.rootForType(type);
  199
+
  200
+    var url = ["", this.pluralize(root), id].join("/");
  201
+
  202
+    this.ajax(url, "GET", {
  203
+      success: function(json) {
  204
+        store.load(type, json[root]);
  205
+      }
  206
+    });
  207
+  },
  208
+
  209
+  findMany: function(store, type, ids) {
  210
+    var root = this.rootForType(type), plural = this.pluralize(root);
  211
+
  212
+    this.ajax("/" + plural, "GET", {
  213
+      data: { ids: ids },
  214
+      success: function(json) {
  215
+        store.loadMany(type, ids, json[plural]);
  216
+      }
  217
+    });
  218
+    var url = "/" + plural;
  219
+  },
  220
+
  221
+  findAll: function(store, type) {
  222
+    var root = this.rootForType(type), plural = this.pluralize(root);
  223
+
  224
+    this.ajax("/" + plural, "GET", {
  225
+      success: function(json) {
  226
+        store.loadMany(type, json[plural]);
  227
+      }
  228
+    });
  229
+  },
  230
+
  231
+  findQuery: function(store, type, query, modelArray) {
  232
+    var root = this.rootForType(type), plural = this.pluralize(root);
  233
+
  234
+    this.ajax("/" + plural, "GET", {
  235
+      data: query,
  236
+      success: function(json) {
  237
+        modelArray.load(json[plural]);
  238
+      }
  239
+    });
  240
+  },
  241
+
  242
+  // HELPERS
  243
+
  244
+  plurals: {},
  245
+
  246
+  // define a plurals hash in your subclass to define
  247
+  // special-case pluralization
  248
+  pluralize: function(name) {
  249
+    return this.plurals[name] || name + "s";
  250
+  },
  251
+
  252
+  rootForType: function(type) {
  253
+    if (type.url) { return type.url; }
  254
+
  255
+    // use the last part of the name as the URL
  256
+    var parts = type.toString().split(".");
  257
+    var name = parts[parts.length - 1];
  258
+    return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
  259
+  },
  260
+
  261
+  ajax: function(url, type, hash) {
  262
+    hash.url = url;
  263
+    hash.type = type;
  264
+    hash.dataType = "json";
  265
+
  266
+    jQuery.ajax(hash);
  267
+  }
  268
+});
  269
+
  270
+
  271
+})({});
  272
+
  273
+
  274
+(function(exports) {
  275
+var get = Ember.get, set = Ember.set;
  276
+
  277
+DS.ModelArray = Ember.ArrayProxy.extend({
  278
+  type: null,
  279
+  content: null,
  280
+  store: null,
  281
+
  282
+  init: function() {
  283
+    set(this, 'modelCache', Ember.A([]));
  284
+    this._super();
  285
+  },
  286
+
  287
+  arrayDidChange: function(array, index, removed, added) {
  288
+    var modelCache = get(this, 'modelCache');
  289
+    modelCache.replace(index, 0, Array(added));
  290
+
  291
+    this._super(array, index, removed, added);
  292
+  },
  293
+
  294
+  arrayWillChange: function(array, index, removed, added) {
  295
+    this._super(array, index, removed, added);
  296
+
  297
+    var modelCache = get(this, 'modelCache');
  298
+    modelCache.replace(index, removed);
  299
+  },
  300
+
  301
+  objectAtContent: function(index) {
  302
+    var modelCache = get(this, 'modelCache');
  303
+    var model = modelCache.objectAt(index);
  304
+
  305
+    if (!model) {
  306
+      var store = get(this, 'store');
  307
+      var content = get(this, 'content');
  308
+
  309
+      var contentObject = content.objectAt(index);
  310
+
  311
+      if (contentObject !== undefined) {
  312
+        model = store.findByClientId(get(this, 'type'), contentObject);
  313
+        modelCache.replace(index, 1, [model]);
  314
+      }
  315
+    }
  316
+
  317
+    return model;
  318
+  }
  319
+});
  320
+
  321
+DS.FilteredModelArray = DS.ModelArray.extend({
  322
+  filterFunction: null,
  323
+
  324
+  updateFilter: Ember.observer(function() {
  325
+    var store = get(this, 'store');
  326
+    store.updateModelArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
  327
+  }, 'filterFunction')
  328
+});
  329
+
  330
+DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
  331
+  query: null,
  332
+  isLoaded: false,
  333
+
  334
+  load: function(array) {
  335
+    var store = get(this, 'store'), type = get(this, 'type');
  336
+
  337
+    var clientIds = store.loadMany(type, array).clientIds;
  338
+
  339
+    this.beginPropertyChanges();
  340
+    set(this, 'content', Ember.A(clientIds));
  341
+    set(this, 'isLoaded', true);
  342
+    this.endPropertyChanges();
  343
+  }
  344
+});
  345
+
  346
+})({});
  347
+
  348
+
  349
+(function(exports) {
  350
+var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
  351
+
  352
+var OrderedSet = Ember.Object.extend({
  353
+  init: function() {
  354
+    this.clear();
  355
+  },
  356
+
  357
+  clear: function() {
  358
+    this.set('presenceSet', {});
  359
+    this.set('list', Ember.NativeArray.apply([]));
  360
+  },
  361
+
  362
+  add: function(obj) {
  363
+    var guid = Ember.guidFor(obj),
  364
+        presenceSet = get(this, 'presenceSet'),
  365
+        list = get(this, 'list');
  366
+
  367
+    if (guid in presenceSet) { return; }
  368
+
  369
+    presenceSet[guid] = true;
  370
+    list.pushObject(obj);
  371
+  },
  372
+
  373
+  remove: function(obj) {
  374
+    var guid = Ember.guidFor(obj),
  375
+        presenceSet = get(this, 'presenceSet'),
  376
+        list = get(this, 'list');
  377
+
  378
+    delete presenceSet[guid];
  379
+    list.removeObject(obj);
  380
+  },
  381
+
  382
+  isEmpty: function() {
  383
+    return getPath(this, 'list.length') === 0;
  384
+  },
  385
+
  386
+  forEach: function(fn, self) {
  387
+    get(this, 'list').forEach(function(item) {
  388
+      fn.call(self, item);
  389
+    });
  390
+  },
  391
+
  392
+  toArray: function() {
  393
+    return get(this, 'list').slice();
  394
+  }
  395
+});
  396
+
  397
+/**
  398
+  A Hash stores values indexed by keys. Unlike JavaScript's
  399
+  default Objects, the keys of a Hash can be any JavaScript
  400
+  object.
  401
+
  402
+  Internally, a Hash has two data structures:
  403
+
  404
+    `keys`: an OrderedSet of all of the existing keys
  405
+    `values`: a JavaScript Object indexed by the
  406
+      Ember.guidFor(key)
  407
+
  408
+  When a key/value pair is added for the first time, we
  409
+  add the key to the `keys` OrderedSet, and create or
  410
+  replace an entry in `values`. When an entry is deleted,
  411
+  we delete its entry in `keys` and `values`.
  412
+*/
  413
+
  414
+var Hash = Ember.Object.extend({
  415
+  init: function() {
  416
+    set(this, 'keys', OrderedSet.create());
  417
+    set(this, 'values', {});
  418
+  },
  419
+
  420
+  add: function(key, value) {
  421
+    var keys = get(this, 'keys'), values = get(this, 'values');
  422
+    var guid = Ember.guidFor(key);
  423
+
  424
+    keys.add(key);
  425
+    values[guid] = value;
  426
+
  427
+    return value;
  428
+  },
  429
+
  430
+  remove: function(key) {
  431
+    var keys = get(this, 'keys'), values = get(this, 'values');
  432
+    var guid = Ember.guidFor(key), value;
  433
+
  434
+    keys.remove(key);
  435
+
  436
+    value = values[guid];
  437
+    delete values[guid];
  438
+
  439
+    return value;
  440
+  },
  441
+
  442
+  fetch: function(key) {
  443
+    var values = get(this, 'values');
  444
+    var guid = Ember.guidFor(key);
  445
+
  446
+    return values[guid];
  447
+  },
  448
+
  449
+  forEach: function(fn, binding) {
  450
+    var keys = get(this, 'keys'), values = get(this, 'values');
  451
+
  452
+    keys.forEach(function(key) {
  453
+      var guid = Ember.guidFor(key);
  454
+      fn.call(binding, key, values[guid]);
  455
+    });
  456
+  }
  457
+});
  458
+
  459
+DS.Transaction = Ember.Object.extend({
  460
+  init: function() {
  461
+    set(this, 'dirty', {
  462
+      created: Hash.create(),
  463
+      updated: Hash.create(),
  464
+      deleted: Hash.create()
  465
+    });
  466
+  },
  467
+
  468
+  createRecord: function(type, hash) {
  469
+    var store = get(this, 'store');
  470
+
  471
+    return store.createRecord(type, hash, this);
  472
+  },
  473
+
  474
+  add: function(model) {
  475
+    var modelTransaction = get(model, 'transaction');
  476
+    ember_assert("Models cannot belong to more than one transaction at a time.", !modelTransaction);
  477
+
  478
+    set(model, 'transaction', this);
  479
+  },
  480
+
  481
+  modelBecameDirty: function(kind, model) {
  482
+    var dirty = get(get(this, 'dirty'), kind),
  483
+        type = model.constructor;
  484
+
  485
+    var models = dirty.fetch(type);
  486
+
  487
+    models = models || dirty.add(type, OrderedSet.create());
  488
+    models.add(model);
  489
+  },
  490
+
  491
+  modelBecameClean: function(kind, model) {
  492
+    var dirty = get(get(this, 'dirty'), kind),
  493
+        type = model.constructor;
  494
+
  495
+    var models = dirty.fetch(type);
  496
+    models.remove(model);
  497
+
  498
+    set(model, 'transaction', null);
  499
+  },
  500
+
  501
+  commit: function() {
  502
+    var dirtyMap = get(this, 'dirty');
  503
+
  504
+    var iterate = function(kind, fn, binding) {
  505
+      var dirty = get(dirtyMap, kind);
  506
+
  507
+      dirty.forEach(function(type, models) {
  508
+        if (models.isEmpty()) { return; }
  509
+
  510
+        models.forEach(function(model) { model.willCommit(); });
  511
+        fn.call(binding, type, models.toArray());
  512
+      });
  513
+    };
  514
+
  515
+    var commitDetails = {
  516
+      updated: {
  517
+        eachType: function(fn, binding) { iterate('updated', fn, binding); }
  518
+      },
  519
+
  520
+      created: {
  521
+        eachType: function(fn, binding) { iterate('created', fn, binding); }
  522
+      },
  523
+
  524
+      deleted: {
  525
+        eachType: function(fn, binding) { iterate('deleted', fn, binding); }
  526
+      }
  527
+    };
  528
+
  529
+    var store = get(this, 'store');
  530
+    var adapter = get(store, '_adapter');
  531
+    if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
  532
+    else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
  533
+  }
  534
+});
  535
+
  536
+})({});
  537
+
  538
+
  539
+(function(exports) {
  540
+var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
  541
+
  542
+var OrderedSet = Ember.Object.extend({
  543
+  init: function() {
  544
+    this.clear();
  545
+  },
  546
+
  547
+  clear: function() {
  548
+    this.set('presenceSet', {});
  549
+    this.set('list', Ember.NativeArray.apply([]));
  550
+  },
  551
+
  552
+  add: function(obj) {
  553
+    var guid = Ember.guidFor(obj),
  554
+        presenceSet = get(this, 'presenceSet'),
  555
+        list = get(this, 'list');
  556
+
  557
+    if (guid in presenceSet) { return; }
  558
+
  559
+    presenceSet[guid] = true;
  560
+    list.pushObject(obj);
  561
+  },
  562
+
  563
+  remove: function(obj) {
  564
+    var guid = Ember.guidFor(obj),
  565
+        presenceSet = get(this, 'presenceSet'),
  566
+        list = get(this, 'list');
  567
+
  568
+    delete presenceSet[guid];
  569
+    list.removeObject(obj);
  570
+  },
  571
+
  572
+  isEmpty: function() {
  573
+    return getPath(this, 'list.length') === 0;
  574
+  },
  575
+
  576
+  forEach: function(fn, self) {
  577
+    get(this, 'list').forEach(function(item) {
  578
+      fn.call(self, item);
  579
+    });
  580
+  }
  581
+});
  582
+
  583
+// Implementors Note:
  584
+//
  585
+//   The variables in this file are consistently named according to the following
  586
+//   scheme:
  587
+//
  588
+//   * +id+ means an identifier managed by an external source, provided inside the
  589
+//     data hash provided by that source.
  590
+//   * +clientId+ means a transient numerical identifier generated at runtime by
  591
+//     the data store. It is important primarily because newly created objects may
  592
+//     not yet have an externally generated id.
  593
+//   * +type+ means a subclass of DS.Model.
  594
+
  595
+/**
  596
+  The store contains all of the hashes for data models loaded from the server.
  597
+  It is also responsible for creating instances of DS.Model when you request one
  598
+  of these data hashes, so that they can be bound to in your Handlebars templates.
  599
+
  600
+  Create a new store like this:
  601
+
  602
+       MyApp.store = DS.Store.create();
  603
+
  604
+  You can retrieve DS.Model instances from the store in several ways. To retrieve
  605
+  a model for a specific id, use the `find()` method:
  606
+
  607
+       var model = MyApp.store.find(MyApp.Contact, 123);
  608
+
  609
+   By default, the store will talk to your backend using a standard REST mechanism.
  610
+   You can customize how the store talks to your backend by specifying a custom adapter:
  611
+
  612
+       MyApp.store = DS.Store.create({
  613
+         adapter: 'MyApp.CustomAdapter'
  614
+       });
  615
+
  616
+    You can learn more about writing a custom adapter by reading the `DS.Adapter`
  617
+    documentation.
  618
+*/
  619
+DS.Store = Ember.Object.extend({
  620
+
  621
+  /**
  622
+    Many methods can be invoked without specifying which store should be used.
  623
+    In those cases, the first store created will be used as the default. If
  624
+    an application has multiple stores, it should specify which store to use
  625
+    when performing actions, such as finding records by id.
  626
+
  627
+    The init method registers this store as the default if none is specified.
  628
+  */
  629
+  init: function() {
  630
+    if (!get(DS, 'defaultStore') || get(this, 'isDefaultStore')) {
  631
+      set(DS, 'defaultStore', this);
  632
+    }
  633
+
  634
+    set(this, 'data', []);
  635
+    set(this, '_typeMap', {});
  636
+    set(this, 'models', []);
  637
+    set(this, 'modelArrays', []);
  638
+    set(this, 'modelArraysByClientId', {});
  639
+    set(this, 'defaultTransaction', DS.Transaction.create({ store: this }));
  640
+
  641
+    return this._super();
  642
+  },
  643
+
  644
+  transaction: function() {
  645
+    return DS.Transaction.create({ store: this });
  646
+  },
  647
+
  648
+  modelArraysForClientId: function(clientId) {
  649
+    var modelArrays = get(this, 'modelArraysByClientId');
  650
+    var ret = modelArrays[clientId];
  651
+
  652
+    if (!ret) {
  653
+      ret = modelArrays[clientId] = OrderedSet.create();
  654
+    }
  655
+
  656
+    return ret;
  657
+  },
  658
+
  659
+  /**
  660
+    The adapter to use to communicate to a backend server or other persistence layer.
  661
+
  662
+    This can be specified as an instance, a class, or a property path that specifies
  663
+    where the adapter can be located.
  664
+
  665
+    @property {DS.Adapter|String}
  666
+  */
  667
+  adapter: null,
  668
+
  669
+  _adapter: Ember.computed(function() {
  670
+    var adapter = get(this, 'adapter');
  671
+    if (typeof adapter === 'string') {
  672
+      return getPath(this, adapter, false) || getPath(window, adapter);
  673
+    }
  674
+    return adapter;
  675
+  }).property('adapter').cacheable(),
  676
+
  677
+  clientIdCounter: -1,
  678
+
  679
+  // ....................
  680
+  // . CREATE NEW MODEL .
  681
+  // ....................
  682
+
  683
+  createRecord: function(type, hash, transaction) {
  684
+    hash = hash || {};
  685
+
  686
+    var id = hash[getPath(type, 'proto.primaryKey')] || null;
  687
+
  688
+    var model = type.create({
  689
+      data: hash || {},
  690
+      store: this,
  691
+      transaction: transaction
  692
+    });
  693
+
  694
+    model.adapterDidCreate();
  695
+
  696
+    var data = this.clientIdToHashMap(type);
  697
+    var models = get(this, 'models');
  698
+
  699
+    var clientId = this.pushHash(hash, id, type);
  700
+
  701
+    set(model, 'clientId', clientId);
  702
+
  703
+    models[clientId] = model;
  704
+
  705
+    this.updateModelArrays(type, clientId, hash);
  706
+
  707
+    return model;
  708
+  },
  709
+
  710
+  // ................
  711
+  // . DELETE MODEL .
  712
+  // ................
  713
+
  714
+  deleteRecord: function(model) {
  715
+    model.deleteRecord();
  716
+  },
  717
+
  718
+  // ...............
  719
+  // . FIND MODELS .
  720
+  // ...............
  721
+
  722
+  /**
  723
+    Finds a model by its id. If the data for that model has already been
  724
+    loaded, an instance of DS.Model with that data will be returned
  725
+    immediately. Otherwise, an empty DS.Model instance will be returned in
  726
+    the loading state. As soon as the requested data is available, the model
  727
+    will be moved into the loaded state and all of the information will be
  728
+    available.
  729
+
  730
+    Note that only one DS.Model instance is ever created per unique id for a
  731
+    given type.
  732
+
  733
+    Example:
  734
+
  735
+        var record = MyApp.store.find(MyApp.Person, 1234);
  736
+
  737
+    @param {DS.Model} type
  738
+    @param {String|Number} id
  739
+  */
  740
+  find: function(type, id, query) {
  741
+    if (id === undefined) {
  742
+      return this.findMany(type, null, null);
  743
+    }
  744
+
  745
+    if (query !== undefined) {
  746
+      return this.findMany(type, id, query);
  747
+    } else if (Ember.typeOf(id) === 'object') {
  748
+      return this.findQuery(type, id);
  749
+    }
  750
+
  751
+    if (Ember.isArray(id)) {
  752
+      return this.findMany(type, id);
  753
+    }
  754
+
  755
+    var clientId = this.clientIdForId(type, id);
  756
+
  757
+    return this.findByClientId(type, clientId, id);
  758
+  },
  759
+
  760
+  findByClientId: function(type, clientId, id) {
  761
+    var model;
  762
+
  763
+    var models = get(this, 'models');
  764
+    var data = this.clientIdToHashMap(type);
  765
+
  766
+    // If there is already a clientId assigned for this
  767
+    // type/id combination, try to find an existing
  768
+    // model for that id and return. Otherwise,
  769
+    // materialize a new model and set its data to the
  770
+    // value we already have.
  771
+    if (clientId !== undefined) {
  772
+      model = models[clientId];
  773
+
  774
+      if (!model) {
  775
+        // create a new instance of the model in the
  776
+        // 'isLoading' state
  777
+        model = this.createModel(type, clientId);
  778
+
  779
+        // immediately set its data
  780
+        model.setData(data[clientId] || null);
  781
+      }
  782
+    } else {
  783
+      clientId = this.pushHash(null, id, type);
  784
+
  785
+      // create a new instance of the model in the
  786
+      // 'isLoading' state
  787
+      model = this.createModel(type, clientId);
  788
+
  789
+      // let the adapter set the data, possibly async
  790
+      var adapter = get(this, '_adapter');
  791
+      if (adapter && adapter.find) { adapter.find(this, type, id); }
  792
+      else { throw fmt("Adapter is either null or do not implement `find` method", this); }
  793
+    }
  794
+
  795
+    return model;
  796
+  },
  797
+
  798
+  /** @private
  799
+  */
  800
+  findMany: function(type, ids, query) {
  801
+    var idToClientIdMap = this.idToClientIdMap(type);
  802
+    var data = this.clientIdToHashMap(type), needed;
  803
+
  804
+    var clientIds = Ember.A([]);
  805
+
  806
+    if (ids) {
  807
+      needed = [];
  808
+
  809
+      ids.forEach(function(id) {
  810
+        var clientId = idToClientIdMap[id];
  811
+        if (clientId === undefined || data[clientId] === undefined) {
  812
+          clientId = this.pushHash(null, id, type);
  813
+          needed.push(id);
  814
+        }
  815
+
  816
+        clientIds.push(clientId);
  817
+      }, this);
  818
+    } else {
  819
+      needed = null;
  820
+    }
  821
+
  822
+    if ((needed && get(needed, 'length') > 0) || query) {
  823
+      var adapter = get(this, '_adapter');
  824
+      if (adapter && adapter.findMany) { adapter.findMany(this, type, needed, query); }
  825
+      else { throw fmt("Adapter is either null or do not implement `findMany` method", this); }
  826
+    }
  827
+
  828
+    return this.createModelArray(type, clientIds);
  829
+  },
  830
+
  831
+  findQuery: function(type, query) {
  832
+    var array = DS.AdapterPopulatedModelArray.create({ type: type, content: Ember.A([]), store: this });
  833
+    var adapter = get(this, '_adapter');
  834
+    if (adapter && adapter.findQuery) { adapter.findQuery(this, type, query, array); }
  835
+    else { throw fmt("Adapter is either null or do not implement `findQuery` method", this); }
  836
+    return array;
  837
+  },
  838
+
  839
+  findAll: function(type) {
  840
+
  841
+    var typeMap = this.typeMapFor(type),
  842
+        findAllCache = typeMap.findAllCache;
  843
+
  844
+    if (findAllCache) { return findAllCache; }
  845
+
  846
+    var array = DS.ModelArray.create({ type: type, content: Ember.A([]), store: this });
  847
+    this.registerModelArray(array, type);
  848
+
  849
+    var adapter = get(this, '_adapter');
  850
+    if (adapter && adapter.findAll) { adapter.findAll(this, type); }
  851
+
  852
+    typeMap.findAllCache = array;
  853
+    return array;
  854
+  },
  855
+
  856
+  filter: function(type, filter) {
  857
+    var array = DS.FilteredModelArray.create({ type: type, content: Ember.A([]), store: this, filterFunction: filter });
  858
+
  859
+    this.registerModelArray(array, type, filter);
  860
+
  861
+    return array;
  862
+  },
  863
+
  864
+  // ............
  865
+  // . UPDATING .
  866
+  // ............
  867
+
  868
+  hashWasUpdated: function(type, clientId) {
  869
+    var clientIdToHashMap = this.clientIdToHashMap(type);
  870
+    var hash = clientIdToHashMap[clientId];
  871
+
  872
+    this.updateModelArrays(type, clientId, hash);
  873
+  },
  874
+
  875
+  // ..............
  876
+  // . PERSISTING .
  877
+  // ..............
  878
+
  879
+  commit: function() {
  880
+    get(this, 'defaultTransaction').commit();
  881
+  },
  882
+
  883
+  didUpdateRecords: function(array, hashes) {
  884
+    if (arguments.length === 2) {
  885
+      array.forEach(function(model, idx) {
  886
+        this.didUpdateRecord(model, hashes[idx]);
  887
+      }, this);
  888
+    } else {
  889
+      array.forEach(function(model) {
  890
+        this.didUpdateRecord(model);
  891
+      }, this);
  892
+    }
  893
+  },
  894
+
  895
+  didUpdateRecord: function(model, hash) {
  896
+    if (arguments.length === 2) {
  897
+      var clientId = get(model, 'clientId');
  898
+      var data = this.clientIdToHashMap(model.constructor);
  899
+
  900
+      data[clientId] = hash;
  901
+      model.set('data', hash);
  902
+    }
  903
+
  904
+    model.adapterDidUpdate();
  905
+  },
  906
+
  907
+  didDeleteRecords: function(array) {
  908
+    array.forEach(function(model) {
  909
+      model.adapterDidDelete();
  910
+    });
  911
+  },
  912
+
  913
+  didDeleteRecord: function(model) {
  914
+    model.adapterDidDelete();
  915
+  },
  916
+
  917
+  didCreateRecords: function(type, array, hashes) {
  918
+    var id, clientId, primaryKey = getPath(type, 'proto.primaryKey');
  919
+
  920
+    var idToClientIdMap = this.idToClientIdMap(type);
  921
+    var data = this.clientIdToHashMap(type);
  922
+    var idList = this.idList(type);
  923
+
  924
+    for (var i=0, l=get(array, 'length'); i<l; i++) {
  925
+      var model = array[i], hash = hashes[i];
  926
+      id = hash[primaryKey];
  927
+      clientId = get(model, 'clientId');
  928
+
  929
+      data[clientId] = hash;
  930
+      set(model, 'data', hash);
  931
+
  932
+      idToClientIdMap[id] = clientId;
  933
+      idList.push(id);
  934
+
  935
+      model.adapterDidUpdate();
  936
+    }
  937
+  },
  938
+
  939
+  didCreateRecord: function(model, hash) {
  940
+    var type = model.constructor;
  941
+
  942
+    var id, clientId, primaryKey = getPath(type, 'proto.primaryKey');
  943
+
  944
+    var idToClientIdMap = this.idToClientIdMap(type);
  945
+    var data = this.clientIdToHashMap(type);
  946
+    var idList = this.idList(type);
  947
+
  948
+    id = hash[primaryKey];
  949
+
  950
+    clientId = get(model, 'clientId');
  951
+    data[clientId] = hash;
  952
+    set(model, 'data', hash);
  953
+
  954
+    idToClientIdMap[id] = clientId;
  955
+    idList.push(id);
  956
+
  957
+    model.adapterDidUpdate();
  958
+  },
  959
+
  960
+  recordWasInvalid: function(record, errors) {
  961
+    record.wasInvalid(errors);
  962
+  },
  963
+
  964
+  // ................
  965
+  // . MODEL ARRAYS .
  966
+  // ................
  967
+
  968
+  registerModelArray: function(array, type, filter) {
  969
+    var modelArrays = get(this, 'modelArrays');
  970
+    var idToClientIdMap = this.idToClientIdMap(type);
  971
+
  972
+    modelArrays.push(array);
  973
+
  974
+    this.updateModelArrayFilter(array, type, filter);
  975
+  },
  976
+
  977
+  createModelArray: function(type, clientIds) {
  978
+    var array = DS.ModelArray.create({ type: type, content: clientIds, store: this });
  979
+
  980
+    clientIds.forEach(function(clientId) {
  981
+      var modelArrays = this.modelArraysForClientId(clientId);
  982
+      modelArrays.add(array);
  983
+    }, this);
  984
+
  985
+    return array;
  986
+  },
  987
+
  988
+  updateModelArrayFilter: function(array, type, filter) {
  989
+    var data = this.clientIdToHashMap(type);
  990
+    var allClientIds = this.clientIdList(type);
  991
+
  992
+    for (var i=0, l=allClientIds.length; i<l; i++) {
  993
+      clientId = allClientIds[i];
  994
+
  995
+      hash = data[clientId];
  996
+
  997
+      if (hash) {
  998
+        this.updateModelArray(array, filter, type, clientId, hash);
  999
+      }
  1000
+    }
  1001
+  },
  1002
+
  1003
+  updateModelArrays: function(type, clientId, hash) {
  1004
+    var modelArrays = get(this, 'modelArrays');
  1005
+
  1006
+    modelArrays.forEach(function(array) {
  1007
+          modelArrayType = get(array, 'type');
  1008
+          filter = get(array, 'filterFunction');
  1009
+
  1010
+      if (type !== modelArrayType) { return; }
  1011
+
  1012
+      this.updateModelArray(array, filter, type, clientId, hash);
  1013
+    }, this);
  1014
+  },
  1015
+
  1016
+  updateModelArray: function(array, filter, type, clientId, hash) {
  1017
+    var shouldBeInArray;
  1018
+
  1019
+    if (!filter) {
  1020
+      shouldBeInArray = true;
  1021
+    } else {
  1022
+      shouldBeInArray = filter(hash);
  1023
+    }
  1024
+
  1025
+    var content = get(array, 'content');
  1026
+    var alreadyInArray = content.indexOf(clientId) !== -1;
  1027
+
  1028
+    var modelArrays = this.modelArraysForClientId(clientId);
  1029
+
  1030
+    if (shouldBeInArray && !alreadyInArray) {
  1031
+      modelArrays.add(array);
  1032
+      content.pushObject(clientId);
  1033
+    } else if (!shouldBeInArray && alreadyInArray) {
  1034
+      modelArrays.remove(array);
  1035
+      content.removeObject(clientId);
  1036
+    }
  1037
+  },
  1038
+
  1039
+  removeFromModelArrays: function(model) {
  1040
+    var clientId = get(model, 'clientId');
  1041
+    var modelArrays = this.modelArraysForClientId(clientId);
  1042
+
  1043
+    modelArrays.forEach(function(array) {
  1044
+      var content = get(array, 'content');
  1045
+      content.removeObject(clientId);
  1046
+    });
  1047
+  },
  1048
+
  1049
+  // ............
  1050
+  // . TYPE MAP .
  1051
+  // ............
  1052
+
  1053
+  typeMapFor: function(type) {
  1054
+    var ids = get(this, '_typeMap');
  1055
+    var guidForType = Ember.guidFor(type);
  1056
+
  1057
+    var typeMap = ids[guidForType];
  1058
+
  1059
+    if (typeMap) {
  1060
+      return typeMap;
  1061
+    } else {
  1062
+      return (ids[guidForType] =
  1063
+        {
  1064
+          idToCid: {},
  1065
+          idList: [],
  1066
+          cidList: [],
  1067
+          cidToHash: {}
  1068
+      });
  1069
+    }
  1070
+  },
  1071
+
  1072
+  idToClientIdMap: function(type) {
  1073
+    return this.typeMapFor(type).idToCid;
  1074
+  },
  1075
+
  1076
+  idList: function(type) {
  1077
+    return this.typeMapFor(type).idList;
  1078
+  },
  1079
+
  1080
+  clientIdList: function(type) {
  1081
+    return this.typeMapFor(type).cidList;
  1082
+  },
  1083
+
  1084
+  clientIdToHashMap: function(type) {
  1085
+    return this.typeMapFor(type).cidToHash;
  1086
+  },
  1087
+
  1088
+  /** @private
  1089
+
  1090
+    For a given type and id combination, returns the client id used by the store.
  1091
+    If no client id has been assigned yet, `undefined` is returned.
  1092
+
  1093
+    @param {DS.Model} type