sproutcore 1.10.1 → 1.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG +13 -0
  3. data/VERSION.yml +1 -1
  4. data/lib/frameworks/sproutcore/CHANGELOG.md +69 -31
  5. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +14 -0
  6. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/object.js +14 -0
  7. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +7 -2
  8. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +13 -9
  9. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +57 -23
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/enabled_states_test.js +24 -6
  11. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +2 -2
  12. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/enabled.js +63 -13
  13. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +3 -3
  14. data/lib/frameworks/sproutcore/frameworks/datastore/models/single_attribute.js +7 -1
  15. data/lib/frameworks/sproutcore/frameworks/datastore/system/many_array.js +28 -5
  16. data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +15 -0
  17. data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +30 -3
  18. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +23 -1
  19. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/many_attribute.js +135 -89
  20. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/single_attribute.js +12 -0
  21. data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +18 -6
  22. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/picker/ui.js +58 -20
  23. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/date_field/methods.js +1 -1
  24. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/select/methods.js +15 -1
  25. data/lib/frameworks/sproutcore/frameworks/desktop/views/button.js +1 -1
  26. data/lib/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +10 -0
  27. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +1 -1
  28. data/lib/frameworks/sproutcore/frameworks/desktop/views/select.js +24 -23
  29. data/lib/frameworks/sproutcore/frameworks/desktop/views/split.js +4 -0
  30. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/views/popup_button.js +10 -0
  31. data/lib/frameworks/sproutcore/frameworks/foundation/delegates/inline_text_field.js +4 -4
  32. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/auto_mixin.js +33 -16
  33. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/content_value_support.js +14 -6
  34. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/control.js +23 -18
  35. data/lib/frameworks/sproutcore/frameworks/foundation/system/user_defaults.js +4 -4
  36. data/lib/frameworks/sproutcore/frameworks/foundation/tests/delegates/inline_text_field/inline_text_field.js +1 -0
  37. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/auto_mixin_tests.js +78 -0
  38. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/auto_resize_test.js +45 -1
  39. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/content_value_support/content.js +112 -58
  40. data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/image_queue.js +2 -2
  41. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/container/transition_test.js +141 -0
  42. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/methods.js +27 -2
  43. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +631 -593
  44. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/swap_fade_color_transition.js +5 -0
  45. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/swap_move_in_transition.js +5 -0
  46. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/swap_reveal_transition.js +68 -1
  47. data/lib/frameworks/sproutcore/frameworks/foundation/views/container.js +128 -49
  48. data/lib/frameworks/sproutcore/frameworks/foundation/views/field.js +33 -8
  49. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +209 -187
  50. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  51. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +7 -0
  52. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +34 -4
  53. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +0 -2
  54. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/binding.js +68 -9
  55. data/lib/frameworks/sproutcore/frameworks/testing/system/runner.js +2 -1
  56. data/lib/sproutcore/rack/builder.rb +45 -25
  57. data/sproutcore.gemspec +1 -0
  58. metadata +17 -2
@@ -15,23 +15,23 @@ module("SC.ManyAttribute core methods", {
15
15
  MyApp = SC.Object.create({
16
16
  store: SC.Store.create()
17
17
  });
18
-
18
+
19
19
  MyApp.Foo = SC.Record.extend({
20
-
20
+
21
21
  // test simple reading of a pass-through prop
22
22
  firstName: SC.Record.attr(String),
23
23
 
24
24
  // test mapping to another internal key
25
25
  otherName: SC.Record.attr(String, { key: "firstName" }),
26
-
26
+
27
27
  // test mapping Date
28
28
  date: SC.Record.attr(Date),
29
-
29
+
30
30
  // used to test default value
31
31
  defaultValue: SC.Record.attr(String, {
32
32
  defaultValue: "default"
33
33
  }),
34
-
34
+
35
35
  // test toMany relationships
36
36
  fooMany: SC.Record.toMany('MyApp.Foo'),
37
37
 
@@ -44,85 +44,85 @@ module("SC.ManyAttribute core methods", {
44
44
  barToMany: SC.Record.toMany('MyApp.Bar', {
45
45
  inverse: 'fooToMany', isMaster: YES, orderBy: 'name'
46
46
  }),
47
-
47
+
48
48
  // test many-to-one relationships with inverse
49
49
  barToOne: SC.Record.toMany('MyApp.Bar', {
50
50
  inverse: 'fooToOne', isMaster: NO
51
51
  })
52
-
52
+
53
53
  });
54
-
54
+
55
55
  MyApp.Bar = SC.Record.extend({
56
-
56
+
57
57
  // test many-to-many
58
58
  fooToMany: SC.Record.toMany('MyApp.Foo', {
59
59
  inverse: 'barToMany', isMaster: NO
60
60
  }),
61
-
61
+
62
62
  // test many-to-one
63
63
  fooToOne: SC.Record.toOne('MyApp.Foo', {
64
64
  inverse: 'barToOne', isMaster: YES
65
65
  })
66
66
  });
67
-
67
+
68
68
  storeKeys = MyApp.store.loadRecords(MyApp.Foo, [
69
- { guid: 1,
70
- firstName: "John",
69
+ { guid: 1,
70
+ firstName: "John",
71
71
  lastName: "Doe",
72
72
  barToMany: ['bar1'],
73
- barToOne: ['bar1', 'bar2']
73
+ barToOne: ['bar1', 'bar2']
74
74
  },
75
-
76
- { guid: 2,
77
- firstName: "Jane",
75
+
76
+ { guid: 2,
77
+ firstName: "Jane",
78
78
  lastName: "Doe",
79
79
  barToMany: ['bar1', 'bar2'],
80
- barToOne: []
80
+ barToOne: []
81
81
  },
82
-
83
- { guid: 3,
84
- firstName: "Emily",
85
- lastName: "Parker",
82
+
83
+ { guid: 3,
84
+ firstName: "Emily",
85
+ lastName: "Parker",
86
86
  fooMany: [1,2],
87
87
  barToMany: ['bar2'],
88
- barToOne: []
88
+ barToOne: []
89
89
  },
90
-
90
+
91
91
  { guid: 4,
92
92
  firstName: "Johnny",
93
93
  lastName: "Cash",
94
94
  fooIds: [1,2]
95
95
  }
96
96
  ]);
97
-
97
+
98
98
  MyApp.store.loadRecords(MyApp.Bar, [
99
99
  { guid: "bar1", name: "A", fooToMany: [1,2], fooToOne: 1 },
100
100
  { guid: "bar2", name: "Z", fooToMany: [2,3], fooToOne: 1 },
101
101
  { guid: "bar3", name: "C" }
102
102
  ]);
103
-
103
+
104
104
  foo1 = rec = MyApp.store.find(MyApp.Foo, 1);
105
105
  foo2 = rec2 = MyApp.store.find(MyApp.Foo, 2);
106
106
  foo3 = rec3 = MyApp.store.find(MyApp.Foo, 3);
107
107
  rec4 = MyApp.store.find(MyApp.Foo, 4);
108
108
  equals(rec.storeKey, storeKeys[0], 'should find record');
109
-
109
+
110
110
  bar1 = MyApp.store.find(MyApp.Bar, "bar1");
111
111
  bar2 = MyApp.store.find(MyApp.Bar, 'bar2');
112
112
  bar3 = MyApp.store.find(MyApp.Bar, 'bar3');
113
-
113
+
114
114
  SC.RunLoop.end();
115
115
  },
116
-
116
+
117
117
  teardown: function() {
118
- MyApp = rec = rec2 = rec3 =
118
+ MyApp = rec = rec2 = rec3 =
119
119
  foo1 = foo2 = foo3 = bar1 = bar2 = null;
120
120
  }
121
121
  });
122
122
 
123
123
  // ..........................................................
124
124
  // READING
125
- //
125
+ //
126
126
 
127
127
  test("pass-through should return builtin value" ,function() {
128
128
  equals(rec.get('firstName'), 'John', 'reading prop should get attr value');
@@ -141,10 +141,10 @@ test("getting toMany relationship should map guid to real records when using dif
141
141
  equals(rec4.get('fooManyKeyed').objectAt(0), rec, 'should get rec1 instance for rec4.fooManyKeyed');
142
142
  equals(rec4.get('fooManyKeyed').objectAt(1), rec2, 'should get rec2 instance for rec4.fooManyKeyed');
143
143
  });
144
-
144
+
145
145
  test("getting toMany relation should not change record state", function() {
146
146
  equals(rec3.get('status'), SC.Record.READY_CLEAN, 'precond - status should be READY_CLEAN');
147
-
147
+
148
148
  var recs = rec3.get('fooMany');
149
149
  ok(recs, 'rec3.get(fooMany) should return records');
150
150
  equals(rec3.get('status'), SC.Record.READY_CLEAN, 'getting toMany should not change state');
@@ -152,41 +152,41 @@ test("getting toMany relation should not change record state", function() {
152
152
 
153
153
  test("reading toMany in chained store", function() {
154
154
  var recs1, recs2, store, rec3a;
155
-
155
+
156
156
  recs1 = rec3.get('fooMany');
157
157
  store = MyApp.store.chain();
158
-
158
+
159
159
  rec3a = store.find(rec3);
160
160
  recs2 = rec3a.get('fooMany');
161
-
161
+
162
162
  same(recs2.getEach('storeKey'), recs1.getEach('storeKey'), 'returns arrays from chained and parent should be same');
163
163
  ok(recs2 !== recs1, 'returned arrays should not be same instance');
164
-
164
+
165
165
  });
166
166
 
167
167
  test("reading a null relation", function() {
168
-
168
+
169
169
  // note: rec1 hash has NO array
170
170
  equals(rec.readAttribute('fooMany'), null, 'rec1.fooMany attr should be null');
171
-
171
+
172
172
  var ret = rec.get('fooMany');
173
- equals(ret.get('length'), 0, 'rec1.get(fooMany).length should be 0');
173
+ equals(ret.get('length'), 0, 'rec1.get(fooMany).length should be 0');
174
174
  same(ret.getEach('storeKey'), [], 'rec1.get(fooMany) should return empty array');
175
175
  });
176
176
 
177
177
  // ..........................................................
178
178
  // WRITING
179
- //
179
+ //
180
180
 
181
181
  test("writing to a to-many relationship should update set guids", function() {
182
182
  var rec3 = MyApp.store.find(MyApp.Foo, 3);
183
183
  equals(rec3.get('id'), 3, 'precond - should find record 3');
184
184
  equals(rec3.get('fooMany').objectAt(0), rec, 'should get rec1 instance for rec3.fooMany');
185
-
185
+
186
186
  SC.RunLoop.begin();
187
187
  rec3.set('fooMany', [rec2, rec4]);
188
188
  SC.RunLoop.end();
189
-
189
+
190
190
  equals(rec3.get('fooMany').objectAt(0), rec2, 'should get rec2 instance for rec3.fooMany');
191
191
  equals(rec3.get('fooMany').objectAt(1), rec4, 'should get rec4 instance for rec3.fooMany');
192
192
  });
@@ -207,25 +207,27 @@ test("pushing an object to a to-many relationship attribute should update set gu
207
207
  var rec3 = MyApp.store.find(MyApp.Foo, 3);
208
208
  equals(rec3.get('id'), 3, 'precond - should find record 3');
209
209
  equals(rec3.get('fooMany').length(), 2, 'should be 2 foo instances related');
210
-
211
- rec3.get('fooMany').pushObject(rec4);
212
-
210
+
211
+ SC.run(function () {
212
+ rec3.get('fooMany').pushObject(rec4);
213
+ });
214
+
213
215
  equals(rec3.get('fooMany').length(), 3, 'should be 3 foo instances related');
214
-
216
+
215
217
  equals(rec3.get('fooMany').objectAt(0), rec, 'should get rec instance for rec3.fooMany');
216
218
  equals(rec3.get('fooMany').objectAt(1), rec2, 'should get rec2 instance for rec3.fooMany');
217
219
  equals(rec3.get('fooMany').objectAt(2), rec4, 'should get rec4 instance for rec3.fooMany');
218
220
  });
219
-
221
+
220
222
  test("modifying a toMany array should mark the record as changed", function() {
221
223
  var recs = rec3.get('fooMany');
222
224
  equals(rec3.get('status'), SC.Record.READY_CLEAN, 'precond - rec3.status should be READY_CLEAN');
223
225
  ok(!!rec4, 'precond - rec4 should be defined');
224
-
226
+
225
227
  SC.RunLoop.begin();
226
228
  recs.pushObject(rec4);
227
229
  SC.RunLoop.end();
228
-
230
+
229
231
  equals(rec3.get('status'), SC.Record.READY_DIRTY, 'record status should have changed to dirty');
230
232
 
231
233
  });
@@ -233,9 +235,11 @@ test("modifying a toMany array should mark the record as changed", function() {
233
235
  test("Modifying a toMany array using replace", function() {
234
236
  var recs = rec.get('barToOne'),
235
237
  objectForRemoval = recs.objectAt(1);
236
-
237
- recs.replace(1, 1, null); // the object should be removed
238
-
238
+
239
+ SC.run(function () {
240
+ recs.replace(1, 1, null); // the object should be removed
241
+ });
242
+
239
243
  ok(objectForRemoval !== recs.objectAt(1), "record should not be present after a replace");
240
244
  equals(bar2.get('fooToOne'), null, "record should have notified attribute of change");
241
245
  });
@@ -245,49 +249,75 @@ test("modifying a toMany array within a nested store", function() {
245
249
 
246
250
  var child = MyApp.store.chain() ; // get a chained store
247
251
  var parentFooMany = rec3.get('fooMany'); // base foo many
248
-
249
- var childRec3 = child.find(rec3);
252
+
253
+ var childRec3 = child.find(rec3);
250
254
  var childFooMany = childRec3.get('fooMany'); // get the nested fooMany
251
-
255
+
252
256
  // save store keys before modifying for easy testing
253
257
  var expected = parentFooMany.getEach('storeKey');
254
-
258
+
255
259
  // now trying modifying...
256
260
  var childRec4 = child.find(rec4);
257
261
  equals(childFooMany.get('length'), 2, 'precond - childFooMany should be like parent');
258
- childFooMany.pushObject(childRec4);
262
+
263
+ SC.run(function () {
264
+ childFooMany.pushObject(childRec4);
265
+ });
259
266
  equals(childFooMany.get('length'), 3, 'childFooMany should have 1 more item');
260
-
267
+
261
268
  SC.RunLoop.end(); // allow notifications to process, if there were any...
262
-
269
+
263
270
  same(parentFooMany.getEach('storeKey'), expected, 'parent.fooMany should not have changed yet');
264
271
  equals(rec3.get('status'), SC.Record.READY_CLEAN, 'parent rec3 should still be READY_CLEAN');
265
-
272
+
266
273
  expected = childFooMany.getEach('storeKey'); // update for after commit
267
274
 
268
275
  SC.RunLoop.begin();
269
276
  child.commitChanges();
270
277
  SC.RunLoop.end();
271
-
278
+
272
279
  // NOTE: not getting fooMany from parent again also tests changing an array
273
280
  // underneath. Does it clear caches, etc?
274
281
  equals(parentFooMany.get('length'), 3, 'parent.fooMany length should have changed');
275
282
  same(parentFooMany.getEach('storeKey'), expected, 'parent.fooMany should now have changed form child store');
276
283
  equals(rec3.get('status'), SC.Record.READY_DIRTY, 'parent rec3 should now be READY_DIRTY');
277
-
284
+
278
285
  });
279
286
 
280
287
  test("should be able to modify an initially empty record", function() {
281
-
288
+
282
289
  same(rec.get('fooMany').getEach('storeKey'), [], 'precond - fooMany should be empty');
283
- rec.get('fooMany').pushObject(rec4);
290
+ SC.run(function () {
291
+ rec.get('fooMany').pushObject(rec4);
292
+ });
284
293
  same(rec.get('fooMany').getEach('storeKey'), [rec4.get('storeKey')], 'after edit should have new array');
285
294
  });
286
295
 
287
296
 
297
+ test("Adding an unsaved record should throw an Error", function() {
298
+ var foo;
299
+
300
+ SC.run(function () {
301
+ foo = MyApp.store.createRecord(MyApp.Foo, {
302
+ firstName: "John",
303
+ lastName: "Doe",
304
+ barToMany: ['bar1']
305
+ });
306
+ });
307
+
308
+ try {
309
+ SC.run(function () {
310
+ bar1.get('fooToMany').pushObject(foo);
311
+ });
312
+ ok(false, "Attempting to assign an unsaved record resulted in an error.");
313
+ } catch (x) {
314
+ ok(true, "Attempting to assign an unsaved record resulted in an error.");
315
+ }
316
+ });
317
+
288
318
  // ..........................................................
289
319
  // MANY-TO-MANY RELATIONSHIPS
290
- //
320
+ //
291
321
 
292
322
  function checkAllClean() {
293
323
  SC.A(arguments).forEach(function(r) {
@@ -299,38 +329,44 @@ test("removing a record from a many-to-many", function() {
299
329
  ok(foo1.get('barToMany').indexOf(bar1) >= 0, 'PRECOND - foo1.barToMany should contain bar1');
300
330
  ok(bar1.get('fooToMany').indexOf(foo1) >= 0, 'PRECOND - bar1.fooToMany should contain foo1');
301
331
  checkAllClean(foo1, bar1);
302
-
303
- foo1.get('barToMany').removeObject(bar1);
332
+
333
+ SC.run(function () {
334
+ foo1.get('barToMany').removeObject(bar1);
335
+ });
304
336
 
305
337
  ok(foo1.get('barToMany').indexOf(bar1) < 0, 'foo1.barToMany should NOT contain bar1');
306
338
  ok(bar1.get('fooToMany').indexOf(foo1) < 0, 'bar1.fooToMany should NOT contain foo1');
307
339
 
308
340
  equals(foo1.get('status'), SC.Record.READY_DIRTY, 'foo1.status should be READY_DIRTY');
309
341
  equals(bar1.get('status'), SC.Record.READY_CLEAN, 'bar1.status should be READY_CLEAN');
310
-
342
+
311
343
  });
312
344
 
313
345
  test("removing a record from a many-to-many; other side", function() {
314
346
  ok(foo1.get('barToMany').indexOf(bar1) >= 0, 'PRECOND - foo1.barToMany should contain bar1');
315
347
  ok(bar1.get('fooToMany').indexOf(foo1) >= 0, 'PRECOND - bar1.fooToMany should contain foo1');
316
348
  checkAllClean(foo1, bar1);
317
-
318
- bar1.get('fooToMany').removeObject(foo1);
349
+
350
+ SC.run(function () {
351
+ bar1.get('fooToMany').removeObject(foo1);
352
+ });
319
353
 
320
354
  ok(foo1.get('barToMany').indexOf(bar1) < 0, 'foo1.barToMany should NOT contain bar1');
321
355
  ok(bar1.get('fooToMany').indexOf(foo1) < 0, 'bar1.fooToMany should NOT contain foo1');
322
356
 
323
357
  equals(foo1.get('status'), SC.Record.READY_DIRTY, 'foo1.status should be READY_DIRTY');
324
358
  equals(bar1.get('status'), SC.Record.READY_CLEAN, 'bar1.status should be READY_CLEAN');
325
-
359
+
326
360
  });
327
361
 
328
362
  test("adding a record to a many-to-many; bar side", function() {
329
363
  ok(foo2.get('barToMany').indexOf(bar3) < 0, 'PRECOND - foo1.barToMany should NOT contain bar1');
330
364
  ok(bar3.get('fooToMany').indexOf(foo2) < 0, 'PRECOND - bar3.fooToMany should NOT contain foo1');
331
365
  checkAllClean(foo2, bar3);
332
-
333
- bar3.get('fooToMany').pushObject(foo2);
366
+
367
+ SC.run(function () {
368
+ bar3.get('fooToMany').pushObject(foo2);
369
+ });
334
370
 
335
371
  // v-- since bar3 is added through inverse, it should follow orderBy
336
372
  equals(foo2.get('barToMany').indexOf(bar3), 1, 'foo1.barToMany should contain bar1');
@@ -345,8 +381,10 @@ test("adding a record to a many-to-many; foo side", function() {
345
381
  ok(foo2.get('barToMany').indexOf(bar3) < 0, 'PRECOND - foo1.barToMany should NOT contain bar3');
346
382
  ok(bar3.get('fooToMany').indexOf(foo2) < 0, 'PRECOND - bar3.fooToMany should NOT contain foo1');
347
383
  checkAllClean(foo2, bar3);
348
-
349
- foo2.get('barToMany').pushObject(bar3);
384
+
385
+ SC.run(function () {
386
+ foo2.get('barToMany').pushObject(bar3);
387
+ });
350
388
 
351
389
  ok(foo2.get('barToMany').indexOf(bar3) >= 0, 'foo1.barToMany should contain bar3');
352
390
  ok(bar3.get('fooToMany').indexOf(foo2) >= 0, 'bar1.fooToMany should contain foo3');
@@ -357,21 +395,23 @@ test("adding a record to a many-to-many; foo side", function() {
357
395
 
358
396
  // ..........................................................
359
397
  // ONE-TO-MANY RELATIONSHIPS
360
- //
398
+ //
361
399
 
362
400
  test("removing a record from a one-to-many", function() {
363
401
  ok(foo1.get('barToOne').indexOf(bar1) >= 0, 'PRECOND - foo1.barToOne should contain bar1');
364
402
  equals(bar1.get('fooToOne'), foo1, 'PRECOND - bar1.fooToOne should eq foo1');
365
403
  checkAllClean(foo1, bar1);
366
-
367
- foo1.get('barToOne').removeObject(bar1);
404
+
405
+ SC.run(function () {
406
+ foo1.get('barToOne').removeObject(bar1);
407
+ });
368
408
 
369
409
  ok(foo1.get('barToOne').indexOf(bar1) < 0, 'foo1.barToOne should NOT contain bar1');
370
410
  equals(bar1.get('fooToOne'), null, 'bar1.fooToOne should eq null');
371
411
 
372
412
  equals(foo1.get('status'), SC.Record.READY_CLEAN, 'foo1.status should be READY_CLEAN');
373
413
  equals(bar1.get('status'), SC.Record.READY_DIRTY, 'bar1.status should be READY_DIRTY');
374
-
414
+
375
415
  });
376
416
 
377
417
 
@@ -379,15 +419,17 @@ test("removing a record from a one-to-many; other-side", function() {
379
419
  ok(foo1.get('barToOne').indexOf(bar1) >= 0, 'PRECOND - foo1.barToOne should contain bar1');
380
420
  equals(bar1.get('fooToOne'), foo1, 'PRECOND - bar1.fooToOne should eq foo1');
381
421
  checkAllClean(foo1, bar1);
382
-
383
- bar1.set('fooToOne', null);
422
+
423
+ SC.run(function () {
424
+ bar1.set('fooToOne', null);
425
+ });
384
426
 
385
427
  ok(foo1.get('barToOne').indexOf(bar1) < 0, 'foo1.barToOne should NOT contain bar1');
386
428
  equals(bar1.get('fooToOne'), null, 'bar1.fooToOne should eq null');
387
429
 
388
430
  equals(foo1.get('status'), SC.Record.READY_CLEAN, 'foo1.status should be READY_CLEAN');
389
431
  equals(bar1.get('status'), SC.Record.READY_DIRTY, 'bar1.status should be READY_DIRTY');
390
-
432
+
391
433
  });
392
434
 
393
435
 
@@ -395,15 +437,17 @@ test("add a record to a one-to-many; many-side", function() {
395
437
  ok(foo1.get('barToOne').indexOf(bar3) < 0, 'PRECOND - foo1.barToOne should NOT contain bar3');
396
438
  equals(bar3.get('fooToOne'), null, 'PRECOND - bar3.fooToOne should eq null');
397
439
  checkAllClean(foo1, bar1);
398
-
399
- foo1.get('barToOne').pushObject(bar3);
440
+
441
+ SC.run(function () {
442
+ foo1.get('barToOne').pushObject(bar3);
443
+ });
400
444
 
401
445
  ok(foo1.get('barToOne').indexOf(bar3) >= 0, 'foo1.barToOne should contain bar3');
402
446
  equals(bar3.get('fooToOne'), foo1, 'bar3.fooToOne should eq foo1');
403
447
 
404
448
  equals(foo1.get('status'), SC.Record.READY_CLEAN, 'foo1.status should be READY_CLEAN');
405
449
  equals(bar3.get('status'), SC.Record.READY_DIRTY, 'bar3.status should be READY_DIRTY');
406
-
450
+
407
451
  });
408
452
 
409
453
 
@@ -411,13 +455,15 @@ test("add a record to a one-to-many; one-side", function() {
411
455
  ok(foo1.get('barToOne').indexOf(bar3) < 0, 'PRECOND - foo1.barToOne should NOT contain bar3');
412
456
  equals(bar3.get('fooToOne'), null, 'PRECOND - bar3.fooToOne should eq null');
413
457
  checkAllClean(foo1, bar1);
414
-
415
- bar3.set('fooToOne', foo1);
458
+
459
+ SC.run(function () {
460
+ bar3.set('fooToOne', foo1);
461
+ });
416
462
 
417
463
  ok(foo1.get('barToOne').indexOf(bar3) >= 0, 'foo1.barToOne should contain bar3');
418
464
  equals(bar3.get('fooToOne'), foo1, 'bar3.fooToOne should eq foo1');
419
465
 
420
466
  equals(foo1.get('status'), SC.Record.READY_CLEAN, 'foo1.status should be READY_CLEAN');
421
467
  equals(bar3.get('status'), SC.Record.READY_DIRTY, 'bar3.status should be READY_DIRTY');
422
-
468
+
423
469
  });
@@ -297,6 +297,18 @@ test("adding toOne pointing to non existing class should throw error", function(
297
297
  same(message, 'Attempted to create toOne attribute with undefined recordType. Did you forget to sc_require a dependency?');
298
298
  });
299
299
 
300
+ test("Adding an unsaved record should throw an Error", function() {
301
+ var bar1 = MyApp.store.find(MyApp.Bar, 'bar1'),
302
+ foo = MyApp.store.createRecord(MyApp.Foo, {});
303
+
304
+ try {
305
+ bar1.set('foo', foo);
306
+ ok(false, "Attempting to assign an unsaved record resulted in an error.");
307
+ } catch (x) {
308
+ ok(true, "Attempting to assign an unsaved record resulted in an error.");
309
+ }
310
+ });
311
+
300
312
  test("adding toMany pointing to non existing class should throw error", function() {
301
313
  var message;
302
314
  try {
@@ -405,6 +405,11 @@ SC.PickerPane = SC.PalettePane.extend(
405
405
  */
406
406
  windowPadding: null,
407
407
 
408
+ /* @private Observe the frame for changes so that we can reposition if necessary. */
409
+ borderFrameDidChange: function () {
410
+ this.positionPane(true);
411
+ },
412
+
408
413
  /**
409
414
  Displays a new picker pane.
410
415
 
@@ -430,6 +435,10 @@ SC.PickerPane = SC.PalettePane.extend(
430
435
  this.endPropertyChanges();
431
436
  this.positionPane();
432
437
  this._hideOverflow();
438
+
439
+ // Start observing the frame for changes.
440
+ this.addObserver('borderFrame', this.borderFrameDidChange);
441
+
433
442
  return this.append();
434
443
  },
435
444
 
@@ -443,11 +452,11 @@ SC.PickerPane = SC.PalettePane.extend(
443
452
  useAnchorCached = useAnchorCached && this.get('anchorCached');
444
453
 
445
454
  var anchor = useAnchorCached ? this.get('anchorCached') : this.get('anchorElement'),
446
- frame = this.get('borderFrame'),
447
- preferType = this.get('preferType'),
448
- preferMatrix = this.get('preferMatrix'),
449
- layout = this.get('layout'),
450
- origin;
455
+ frame = this.get('borderFrame'),
456
+ preferType = this.get('preferType'),
457
+ preferMatrix = this.get('preferMatrix'),
458
+ layout = this.get('layout'),
459
+ origin;
451
460
 
452
461
  // usually an anchorElement will be passed. The ideal position is just
453
462
  // below the anchor + default or custom offset according to preferType.
@@ -492,7 +501,6 @@ SC.PickerPane = SC.PalettePane.extend(
492
501
  frame.halfHeight = parseInt(frame.height * 0.5, 0);
493
502
 
494
503
  origin = this.fitPositionToScreen(origin, frame, anchor);
495
-
496
504
  this.adjust({
497
505
  width: origin.width,
498
506
  height: origin.height,
@@ -1083,6 +1091,10 @@ SC.PickerPane = SC.PalettePane.extend(
1083
1091
  this._withdrawOverflowRequest();
1084
1092
  }
1085
1093
  this._removeScrollObservers();
1094
+
1095
+ // Stop observing the frame for changes.
1096
+ this.removeObserver('borderFrame', this.borderFrameDidChange);
1097
+
1086
1098
  return sc_super();
1087
1099
  },
1088
1100