sproutcore 1.10.1 → 1.10.2

Sign up to get free protection for your applications and to get access to all the features.
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