sproutcore 1.10.0.rc.2 → 1.10.0.rc.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +8 -8
  2. data/VERSION.yml +1 -1
  3. data/lib/frameworks/sproutcore/CHANGELOG.md +14 -0
  4. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +9 -1
  5. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane_statechart.js +7 -0
  6. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/controllers/array/array_case.js +14 -0
  7. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layout.js +103 -0
  8. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutChildViews.js +4 -4
  9. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutDidChange.js +4 -1
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/viewDidResize.js +5 -5
  11. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout.js +17 -6
  12. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/statechart.js +62 -8
  13. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +14 -2
  14. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +26 -5
  15. data/lib/frameworks/sproutcore/frameworks/datastore/tests/integration/many_array.js +9 -1
  16. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/data_store.js +6 -2
  17. data/lib/frameworks/sproutcore/frameworks/desktop/mixins/split_child.js +1 -1
  18. data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +0 -2
  19. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/render.js +56 -54
  20. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/methods.js +221 -171
  21. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/split/methods.js +261 -315
  22. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/split/split_child.js +137 -122
  23. data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +1 -1
  24. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +10 -7
  25. data/lib/frameworks/sproutcore/frameworks/desktop/views/split.js +5 -4
  26. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/content_display.js +14 -14
  27. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/content_value_support.js +123 -98
  28. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/content_display.js +18 -6
  29. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/inline_text_field/api.js +9 -11
  30. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/container/transition_test.js +2 -2
  31. data/lib/frameworks/sproutcore/frameworks/foundation/views/container.js +15 -16
  32. data/lib/frameworks/sproutcore/frameworks/foundation/views/inline_text_field.js +11 -0
  33. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/enumerable.js +1 -1
  34. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +3 -3
  35. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/binding.js +170 -153
  36. data/lib/frameworks/sproutcore/frameworks/table/views/table.js +105 -101
  37. data/lib/frameworks/sproutcore/frameworks/table/views/table_head.js +0 -7
  38. data/lib/frameworks/sproutcore/frameworks/table/views/table_header.js +46 -56
  39. data/lib/frameworks/sproutcore/frameworks/table/views/table_row.js +0 -6
  40. data/lib/frameworks/sproutcore/frameworks/template_view/tests/views/template/collection.js +12 -4
  41. data/lib/frameworks/sproutcore/frameworks/template_view/views/bindable_span.js +3 -2
  42. data/lib/frameworks/sproutcore/frameworks/template_view/views/template_collection.js +11 -8
  43. data/lib/frameworks/sproutcore/tests/phantomjs_runner.phantomjs +0 -1
  44. metadata +3 -3
  45. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/inline_text_field/beginEditing.js +0 -235
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDg1MjQwYWRlZjUzZGFmNjFkMGUyYjFjYmFhN2NjNGM3MzM1M2IyZg==
4
+ ZmUxOTcxMGViYzE0OWVlYmJiMzlhNTUwMzk1M2I5ZDM1NzFiYWMzOA==
5
5
  data.tar.gz: !binary |-
6
- NWEzMzgxNjhlYTJjOTBlMDQxNTgzYTYxOTNmYzE4MTU4ZmViOGQ2MA==
6
+ M2VkMDZmODFjODExNDE4ZDY1NzQzYzVlNTU3NzdlZmE0MTU3NmQzMw==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- M2VkY2E5ZTBkNTY3MTdjZTk0YmY1YmU4YjAyYzFkYzc5Nzg1YzQxN2ViZmEz
10
- MDlkMDBkOTZmMWY1YzNkMTgwNWNiNTcyNjcyOGJmMjY5YzViYTkyYmRiY2Uz
11
- Mzk3OWI0ZjZhMTI4YjYyZTZhNzRmZDkxYjIwMjZkZGYwN2NmOTI=
9
+ OWM1OGJhNDk5OWFiNmVlOWM4YjZjMzk3OWExYzU1NjliODUxMDZmZjYxMjUz
10
+ NDE2ODZiN2M5ZGQ3NzdmYTg1YWU5NjEwY2NkMWYzYjdkNDcyM2MyZGY5ODg3
11
+ NDEwZGFmMjMyMGQzMTNkMGFiNDMyYzBhNDBlNWQwYzAxZWFiZGY=
12
12
  data.tar.gz: !binary |-
13
- OWVlMzRiOGMxNGRkMWVmYWRjMmU5MTRjYjFjYTExZTYyZjRmYmU5NDZmZDBj
14
- ZjRiMTA4YmNlZWJjZjMwMzZlNjFjZDYwNjgzZmY2YWZjZTYzOGRiNzYyNjg1
15
- NzFhNDQ1ZGY4NjU0N2I4MWE3OTJjMzRhMDNlNTM4ZTA1YzBlZTE=
13
+ NjYxYzdhZmYyMDI4N2FiN2UyZDJkNTlhNTBkMjJmYmM5MjI4ZmJkNDg3OTdh
14
+ ZjhhYzcwNzg0MzgzOGI4M2FjNGY4NzNhZTBiOTA2YTJkMjA0Y2YzOTMxOTE4
15
+ ZmQxMTU2ZDg0YzI3NjkyMTFjMDFkZGFkOGM0M2Q3NjU2NTMyOGQ=
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 1
3
3
  :minor: 10
4
- :patch: 0.rc.2
4
+ :patch: 0.rc.3
@@ -7,6 +7,8 @@ CHANGE LOG
7
7
  ### CHANGES & FEATURES
8
8
 
9
9
  * Improves and adds hundreds of lines of documentation.
10
+ * Removes a developer warning when animating with a duration of 0, which can be valid if the duration is calculated. In any case, animating a duration of 0 has always been supported by SC.View.prototype.animate.
11
+ * Improves the regular expression used by SC.RenderContext to escape strings so that HTML entities like ' or à are preserved.
10
12
  * Set SC.DEFAULT_CURSOR to 'default' instead of 'auto'.
11
13
  * Improves the ability of the SC.browser experimental name testing to succeed by adding a test value that can be used.
12
14
  * Masks all debug statements from SC.Binding using @if(debug).
@@ -332,6 +334,18 @@ Also added better documentation for using dataSourceDidFetchQuery.
332
334
 
333
335
  ### BUG FIXES
334
336
 
337
+ * Fixes a problem which kept firstObject and lastObject referenced from an array controller from updating on replace. Also fixes a problem updating lastObject on an enumerable when replacing the last items and shrinking the collection.
338
+ * Fixes a problem when using an SC.SplitView as a split child.
339
+ * Fixes a bug that updating the parent record data hash failed to update the nested record hashes as well.
340
+ * Fixes a problem with SC.ScrollView that required an additional frame change notification to work previously.
341
+ * Fixes a problem where childViews incorrectly called viewDidResize one extra time when appended to the DOM. The views will already have run viewDidResize once on being adopted by a parentView.
342
+ * Fixes a miscalculation of isFixedPosition that resulted in incorrect values when short form layouts were used (ex. specifying { width: 10, height: 10 } only which implies top: 0 and left: 0). This would have caused such views to return false for hasAcceleratedLayer even though they had set wantsAcceleratedLayer to true.
343
+ * Prevents needless view updates when displayDidChange is called on an unrendered view. Normally, the actual update code handles the check to see if the view is rendered and can be updated, but if displayDidChange comes before a render, we would end up rendering and then updating. Now it checks to see if the view is rendered before attempting to update it at all.
344
+ * Fixes a potentially huge memory leak when views that mix in SC.ContentValueSupport (or via SC.Control) will not remove all the content value observers from the content object when the view is destroyed. One result is that every time you scrolled a list of SC.ListItemViews back and forth it was appending more and more observers to the content.
345
+ * Gives SC.InlineTextFieldView a default layout size of 0x0 so that it doesn't alter the scrollTop/scrollLeft of a parent view layer that allows overflow.
346
+ * Fixed position of picker panes when the anchor has a frame origin of 0,0.
347
+ * Fixes the initialization of SC.ContainerView when the contentView is already set.
348
+ * Fixes typo in SC.Binding.transform causing transforms to be added to parent.
335
349
  * Fixes inability to specify the window padding on a PickerPane to a value other than the default.
336
350
  * Fixes escaped overflowTitle of SC.SegmentedView that showed '»' and invalid text of overflowToolTip that showed 'More…'. Also removes unnecessary escaping of `title` attributes on buttons and labels, because the browser doesn't render HTML inside of a `title` and removes invalid `alt` attribute on button divs.
337
351
  * Fixes SC.ArrayController so that firstObject, firstSelectableObject and lastObject update properly when swapping out the content. This also ensures that when allowsEmptySelection is false, that the selection changes to the new first object when the content is swapped.
@@ -428,6 +428,15 @@ SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
428
428
  _scac_arrayContentDidChange: function (start, removed, added) {
429
429
  this._scac_cached = NO;
430
430
  this.arrayContentDidChange(start, removed, added);
431
+
432
+ // If the start & length are provided, we can also indicate if the firstObject
433
+ // or lastObject properties changed, thus making them independently observable.
434
+ if (!SC.none(start)) {
435
+ if (start === 0) this.notifyPropertyChange('firstObject');
436
+ var length = added + removed;
437
+ if (!SC.none(length) && start + length >= this.get('length') - 1) this.notifyPropertyChange('lastObject');
438
+ }
439
+
431
440
  if (this._kvo_enumerable_property_chains) {
432
441
  var addedObjects = this.slice(start, start + added);
433
442
  this.setupEnumerablePropertyChains(addedObjects);
@@ -441,7 +450,6 @@ SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
441
450
  */
442
451
  _scac_contentDidChange: function () {
443
452
  this._scac_cached = NO; // invalidate observable content
444
-
445
453
  var content = this.get('content'),
446
454
  lastContent = this._scac_content,
447
455
  didChange = this._scac_arrayContentDidChange,
@@ -28,6 +28,13 @@ SC.Pane.reopen({
28
28
  // handle intercept if needed
29
29
  this._addIntercept();
30
30
 
31
+ // If the layout is flexible (dependent on the window size), then the view
32
+ // will resize when appended.
33
+ if (!this.get('isFixedSize')) {
34
+ // We call viewDidResize so that it calls parentViewDidResize on all child views.
35
+ this.viewDidResize();
36
+ }
37
+
31
38
  sc_super();
32
39
  },
33
40
 
@@ -183,6 +183,20 @@ test("The computed properties firstObject, firstSelectableObject & lastObject sh
183
183
  equals(controller.get('lastObject'), newObject, 'lastObject should be the new last object in content');
184
184
  });
185
185
 
186
+ test("The computed properties firstObject, firstSelectableObject & lastObject should update when content items change.", function(){
187
+ equals(controller.get('firstObject'), content[0], 'first object should be the first object in content');
188
+ equals(controller.get('firstSelectableObject'), content[0], 'first selectable object should be the first object in content');
189
+ equals(controller.get('lastObject'), content[4], 'lastObject should be the last object in content');
190
+
191
+ // Change the items.
192
+ var newObject = TestObject.create({ title: "BLAH" });
193
+ controller.replace(0, 5, [newObject]);
194
+
195
+ equals(controller.get('firstObject'), newObject, 'first object should be the new first object in content');
196
+ equals(controller.get('firstSelectableObject'), newObject, 'first selectable object should be the new first object in content');
197
+ equals(controller.get('lastObject'), newObject, 'lastObject should be the new last object in content');
198
+ });
199
+
186
200
  test("array orderBy using String", function(){
187
201
  var testController = SC.ArrayController.create({
188
202
  content: content,
@@ -0,0 +1,103 @@
1
+ // ==========================================================================
2
+ // Project: SproutCore - JavaScript Application Framework
3
+ // Copyright: ©2006-2011 Strobe Inc. and contributors.
4
+ // ©2008-2011 Apple Inc. All rights reserved.
5
+ // License: Licensed under MIT license (see license.js)
6
+ // ==========================================================================
7
+
8
+ /*global module, test, equals, ok */
9
+
10
+ var view;
11
+
12
+ /** Test isFixedLayout via isFixedSize and isFixedPosition properties. */
13
+ module("SC.View.prototype.isFixedLayout", {
14
+
15
+ setup: function () {
16
+ // Create a basic view.
17
+ view = SC.View.create({});
18
+ },
19
+
20
+ teardown: function () {
21
+ // Clean up.
22
+ view.destroy();
23
+ view = null;
24
+ }
25
+
26
+ });
27
+
28
+ test("Test isFixedSize for various layouts.", function () {
29
+ ok(!view.get('isFixedSize'), "The default layout doesn't correspond to a fixed size.");
30
+
31
+ SC.run(function () { view.set('layout', { width: 100 }); });
32
+ ok(!view.get('isFixedSize'), "A width alone doesn't correspond to a fixed size.");
33
+
34
+ SC.run(function () { view.set('layout', { height: 100 }); });
35
+ ok(!view.get('isFixedSize'), "A height alone doesn't correspond to a fixed size.");
36
+
37
+ SC.run(function () { view.set('layout', { width: 100, height: 100 }); });
38
+ ok(view.get('isFixedSize'), "A width & height corresponds to a fixed size.");
39
+ });
40
+
41
+ test("Test isFixedPosition for various layouts.", function () {
42
+ ok(view.get('isFixedPosition'), "The default layout corresponds to a fixed position.");
43
+
44
+ SC.run(function () { view.set('layout', { left: 0 }); });
45
+ ok(view.get('isFixedPosition'), "A left: 0 (implied top, bottom, right) corresponds to a fixed position.");
46
+
47
+ SC.run(function () { view.set('layout', { top: 0 }); });
48
+ ok(view.get('isFixedPosition'), "A top: 0 (implied left, bottom, right) corresponds to a fixed position.");
49
+
50
+ SC.run(function () { view.set('layout', { left: 0, top: 0 }); });
51
+ ok(view.get('isFixedPosition'), "A left: 0, top: 0 corresponds to a fixed position.");
52
+
53
+ SC.run(function () { view.set('layout', { left: 50 }); });
54
+ ok(view.get('isFixedPosition'), "A left: 50 corresponds to a fixed position.");
55
+
56
+ SC.run(function () { view.set('layout', { top: 50 }); });
57
+ ok(view.get('isFixedPosition'), "A top: 50 corresponds to a fixed position.");
58
+
59
+ SC.run(function () { view.set('layout', { left: 50, top: 50 }); });
60
+ ok(view.get('isFixedPosition'), "A left: 50, top: 50 corresponds to a fixed position.");
61
+
62
+ SC.run(function () { view.set('layout', { right: 0 }); });
63
+ ok(view.get('isFixedPosition'), "A right: 0 (implied left) corresponds to a fixed position.");
64
+
65
+ SC.run(function () { view.set('layout', { bottom: 0 }); });
66
+ ok(view.get('isFixedPosition'), "A bottom: 0 (implied top) corresponds to a fixed position.");
67
+
68
+ SC.run(function () { view.set('layout', { right: 50 }); });
69
+ ok(view.get('isFixedPosition'), "A right: 50 (implied left) corresponds to a fixed position.");
70
+
71
+ SC.run(function () { view.set('layout', { bottom: 50 }); });
72
+ ok(view.get('isFixedPosition'), "A bottom: 50 (implied top) corresponds to a fixed position.");
73
+
74
+ SC.run(function () { view.set('layout', { width: 100 }); });
75
+ ok(view.get('isFixedPosition'), "A width: 100 (implied left) corresponds to a fixed position.");
76
+
77
+ SC.run(function () { view.set('layout', { height: 100 }); });
78
+ ok(view.get('isFixedPosition'), "A height: 100 (implied top) corresponds to a fixed position.");
79
+
80
+ SC.run(function () { view.set('layout', { right: 0, width: 100 }); });
81
+ ok(!view.get('isFixedPosition'), "A right: 0, width: 100 (overridden left) doesn't correspond to a fixed position.");
82
+
83
+ SC.run(function () { view.set('layout', { bottom: 0, height: 100 }); });
84
+ ok(!view.get('isFixedPosition'), "A bottom: 0, height: 100 (overridden top) doesn't correspond to a fixed position.");
85
+
86
+ SC.run(function () { view.set('layout', { centerX: 0, width: 100 }); });
87
+ ok(!view.get('isFixedPosition'), "A centerX: 0, width: 100 (overridden left) doesn't correspond to a fixed position.");
88
+
89
+ SC.run(function () { view.set('layout', { centerY: 0, height: 100 }); });
90
+ ok(!view.get('isFixedPosition'), "A centerY: 0, height: 100 (overridden top) doesn't correspond to a fixed position.");
91
+
92
+ SC.run(function () { view.set('layout', { left: 0.2 }); });
93
+ ok(!view.get('isFixedPosition'), "A left: 0.2 (percentage left) doesn't correspond to a fixed position.");
94
+
95
+ SC.run(function () { view.set('layout', { top: 0.2 }); });
96
+ ok(!view.get('isFixedPosition'), "A top: 0.2 (percentage top) doesn't correspond to a fixed position.");
97
+
98
+ SC.run(function () { view.set('layout', { left: SC.LAYOUT_AUTO }); });
99
+ ok(!view.get('isFixedPosition'), "A left: SC.LAYOUT_AUTO (auto left) doesn't correspond to a fixed position.");
100
+
101
+ SC.run(function () { view.set('layout', { top: SC.LAYOUT_AUTO }); });
102
+ ok(!view.get('isFixedPosition'), "A top: SC.LAYOUT_AUTO (auto top) doesn't correspond to a fixed position.");
103
+ });
@@ -41,7 +41,7 @@ test("calls renderLayout() on child views on views that need layout if they have
41
41
  //
42
42
  module("SC.View#updateLayout");
43
43
 
44
- test("if view has layout, calls _executeDoUpdateLayout", function() {
44
+ test("if view has layout, calls _doUpdateLayoutStyle", function() {
45
45
 
46
46
  // NOTE: renderLayout() is also called when a view's
47
47
  // layer is first created. We use isTesting below to
@@ -49,7 +49,7 @@ test("if view has layout, calls _executeDoUpdateLayout", function() {
49
49
  // are actually doing layout.
50
50
  var callCount = 0, isTesting = NO ;
51
51
  var view = SC.View.create({
52
- _executeDoUpdateLayout: function() {
52
+ _doUpdateLayoutStyle: function() {
53
53
  callCount++;
54
54
  }
55
55
  });
@@ -58,10 +58,10 @@ test("if view has layout, calls _executeDoUpdateLayout", function() {
58
58
  ok(view.get('layer'), 'precond - should have a layer');
59
59
 
60
60
  view.updateLayout();
61
- equals(callCount, 0, 'should not call _executeDoUpdateLayout() because the view isn\'t shown');
61
+ equals(callCount, 0, 'should not call _doUpdateLayoutStyle() because the view isn\'t shown');
62
62
 
63
63
  view.updateLayout(true);
64
- equals(callCount, 1, 'should call _executeDoUpdateLayout() because we force it');
64
+ equals(callCount, 1, 'should call _doUpdateLayoutStyle() because we force it');
65
65
 
66
66
  // Clean up.
67
67
  view.destroy();
@@ -25,7 +25,9 @@ test("notifies layoutStyle & frame change", function () {
25
25
  equals(layoutStyleCallCount, 0, 'should not trigger observers for layoutStyle');
26
26
 
27
27
  // Attach to the document.
28
- view.createLayer()._doAttach(document.body);
28
+ var parent = SC.Pane.create();
29
+ parent.append();
30
+ parent.appendChild(view);
29
31
 
30
32
  equals(frameCallCount, 2, 'should trigger observers for frame when attached to the document');
31
33
  equals(layoutStyleCallCount, 0, 'should still not trigger observers for layoutStyle');
@@ -39,6 +41,7 @@ test("notifies layoutStyle & frame change", function () {
39
41
 
40
42
  // Clean up.
41
43
  view.destroy();
44
+ parent.destroy();
42
45
  });
43
46
 
44
47
  test("invokes layoutDidChangeFor() on layoutView each time it is called", function () {
@@ -166,28 +166,28 @@ test("When parentViewDidResize is called on a view, it should only notify on fra
166
166
  view.viewDidResize.reset(); view.frameCallCount = 0;
167
167
  parentView.adjust({ width: 60, height: 60 });
168
168
  view.viewDidResize.expect(0);
169
- equals(view.frameCallCount, 1, 'should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
169
+ equals(view.frameCallCount, 1, 'right align: should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
170
170
 
171
171
  // try with bottom align
172
- view.set('layout', { top: 10, bottom: 10, height: 10, width: 10 });
172
+ view.set('layout', { left: 10, bottom: 10, height: 10, width: 10 });
173
173
  view.viewDidResize.reset(); view.frameCallCount = 0;
174
174
  parentView.adjust({ width: 50, height: 50 });
175
175
  view.viewDidResize.expect(0);
176
- equals(view.frameCallCount, 1, 'should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
176
+ equals(view.frameCallCount, 1, 'bottom align: should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
177
177
 
178
178
  // try with center horizontal align
179
179
  view.set('layout', { centerX: 10, top: 10, height: 10, width: 10 });
180
180
  view.viewDidResize.reset(); view.frameCallCount = 0;
181
181
  parentView.adjust({ width: 40, height: 40 });
182
182
  view.viewDidResize.expect(0);
183
- equals(view.frameCallCount, 1, 'should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
183
+ equals(view.frameCallCount, 1, 'centerX: should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
184
184
 
185
185
  // try with center vertical align
186
186
  view.set('layout', { left: 10, centerY: 10, height: 10, width: 10 });
187
187
  view.viewDidResize.reset(); view.frameCallCount = 0;
188
188
  parentView.adjust({ width: 30, height: 30 });
189
189
  view.viewDidResize.expect(0);
190
- equals(view.frameCallCount, 1, 'should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
190
+ equals(view.frameCallCount, 1, 'centerY: should notify frame changed when isFixedPosition: %@ and isFixedSize: %@'.fmt(view.get('isFixedPosition'), view.get('isFixedSize')));
191
191
  });
192
192
 
193
193
  // ..........................................................
@@ -276,13 +276,24 @@ SC.View.reopen(
276
276
  */
277
277
  isFixedPosition: function () {
278
278
  var layout = this.get('layout'),
279
- ret;
279
+ hasLeft,
280
+ hasTop,
281
+ ret;
280
282
 
281
283
  // Position is fixed if it has left + top !== SC.LAYOUT_AUTO
282
- ret = (
283
- ((layout.left !== undefined) && (layout.top !== undefined)) &&
284
- ((layout.left !== SC.LAYOUT_AUTO) && (layout.top !== SC.LAYOUT_AUTO))
285
- );
284
+ hasLeft = layout.left !== undefined;
285
+ if (!hasLeft) {
286
+ // Check for implied left. If there is a width, then there can't be a right or centerX.
287
+ hasLeft = layout.width === undefined || (layout.width !== undefined && layout.right === undefined && layout.centerX === undefined);
288
+ }
289
+
290
+ hasTop = layout.top !== undefined;
291
+ if (!hasTop) {
292
+ // Check for implied top. If there is a height, then there can't be a bottom or centerY.
293
+ hasTop = layout.height === undefined || (layout.height !== undefined && layout.bottom === undefined && layout.centerY === undefined);
294
+ }
295
+
296
+ ret = (hasLeft && hasTop && layout.left !== SC.LAYOUT_AUTO && layout.top !== SC.LAYOUT_AUTO);
286
297
 
287
298
  // The position may appear fixed, but only if none of the values are percentages.
288
299
  if (ret) {
@@ -1220,7 +1231,7 @@ SC.View.reopen(
1220
1231
  /** @private Override: Notify on attached (avoids notify of frame changed). */
1221
1232
  _notifyAttached: function () {
1222
1233
  // If we are using static layout then we don't know the frame until appended to the document.
1223
- if (this.get('useStaticLayout') || !this.get('isFixedLayout')) {
1234
+ if (this.get('useStaticLayout')) {
1224
1235
  // We call viewDidResize so that it calls parentViewDidResize on all child views.
1225
1236
  this.viewDidResize();
1226
1237
  }
@@ -1,5 +1,10 @@
1
1
  sc_require("views/view/base");
2
2
 
3
+ // When in debug mode, core developers can log the view state.
4
+ //@if (debug)
5
+ SC.LOG_VIEW_STATES = false;
6
+ //@endif
7
+
3
8
 
4
9
  SC.CoreView.mixin(
5
10
  /** @scope SC.CoreView */ {
@@ -260,6 +265,12 @@ SC.CoreView.reopen(
260
265
  var curParentView = this.get('parentView'),
261
266
  handled = true;
262
267
 
268
+ //@if (debug)
269
+ if (SC.LOG_VIEW_STATES) {
270
+ SC.Logger.log('%@:%@ — _doAdopt(%@, %@)'.fmt(this, this.get('viewState'), parentView, beforeView));
271
+ }
272
+ //@endif
273
+
263
274
  if (curParentView && curParentView !== parentView) {
264
275
  //@if(debug)
265
276
  // This should be avoided, because using the same view instance without explicitly orphaning it first is a dangerous practice.
@@ -295,7 +306,6 @@ SC.CoreView.reopen(
295
306
  // Notify adopted (on self and all child views).
296
307
  this._adopted();
297
308
 
298
-
299
309
  switch (this.get('viewState')) {
300
310
  case SC.CoreView.UNRENDERED:
301
311
  switch (parentViewState) {
@@ -353,6 +363,12 @@ SC.CoreView.reopen(
353
363
  transitionIn = this.get('transitionIn'),
354
364
  parentView;
355
365
 
366
+ //@if (debug)
367
+ if (SC.LOG_VIEW_STATES) {
368
+ SC.Logger.log('%@:%@ — _doAttach(%@, %@)'.fmt(this, state, parentNode, nextNode));
369
+ }
370
+ //@endif
371
+
356
372
  switch (state) {
357
373
  case SC.CoreView.ATTACHED_HIDING: // FAST PATH!
358
374
  case SC.CoreView.ATTACHED_HIDDEN: // FAST PATH!
@@ -434,6 +450,12 @@ SC.CoreView.reopen(
434
450
  _doDestroyLayer: function () {
435
451
  var handled = true;
436
452
 
453
+ //@if (debug)
454
+ if (SC.LOG_VIEW_STATES) {
455
+ SC.Logger.log('%@:%@ — _doDestroyLayer()'.fmt(this, this.get('viewState')));
456
+ }
457
+ //@endif
458
+
437
459
  if (this.get('_isRendered') && !this.get('isAttached')) {
438
460
  // Remove our reference to the layer (our self and all our child views).
439
461
  this._executeDoDestroyLayer();
@@ -450,6 +472,12 @@ SC.CoreView.reopen(
450
472
  var state = this.get('viewState'),
451
473
  transitionOut = this.get('transitionOut');
452
474
 
475
+ //@if (debug)
476
+ if (SC.LOG_VIEW_STATES) {
477
+ SC.Logger.log('%@:%@ — _doDetach()'.fmt(this, state));
478
+ }
479
+ //@endif
480
+
453
481
  switch (state) {
454
482
  case SC.CoreView.UNRENDERED: // FAST PATH!
455
483
  case SC.CoreView.UNATTACHED: // FAST PATH!
@@ -540,6 +568,12 @@ SC.CoreView.reopen(
540
568
  var state = this.get('viewState'),
541
569
  transitionHide = this.get('transitionHide');
542
570
 
571
+ //@if (debug)
572
+ if (SC.LOG_VIEW_STATES) {
573
+ SC.Logger.log('%@:%@ — _doHide()'.fmt(this, state));
574
+ }
575
+ //@endif
576
+
543
577
  switch (state) {
544
578
  case SC.CoreView.UNRENDERED: // FAST PATH!
545
579
  case SC.CoreView.ATTACHED_HIDDEN: // FAST PATH!
@@ -603,6 +637,12 @@ SC.CoreView.reopen(
603
637
  var parentView = this.get('parentView'),
604
638
  handled = true;
605
639
 
640
+ //@if (debug)
641
+ if (SC.LOG_VIEW_STATES) {
642
+ SC.Logger.log('%@:%@ — _doOrphan()'.fmt(this, this.get('viewState')));
643
+ }
644
+ //@endif
645
+
606
646
  if (parentView) {
607
647
  var childViews = parentView.get('childViews'),
608
648
  idx = childViews.indexOf(this);
@@ -623,10 +663,15 @@ SC.CoreView.reopen(
623
663
  },
624
664
 
625
665
  /** @private Render this view action. */
626
-
627
666
  _doRender: function () {
628
667
  var state = this.get('viewState');
629
668
 
669
+ //@if (debug)
670
+ if (SC.LOG_VIEW_STATES) {
671
+ SC.Logger.log('%@:%@ — _doRender()'.fmt(this, state));
672
+ }
673
+ //@endif
674
+
630
675
  switch (state) {
631
676
  case SC.CoreView.ATTACHED_SHOWING: // FAST PATHS!
632
677
  case SC.CoreView.ATTACHED_SHOWN:
@@ -646,7 +691,7 @@ SC.CoreView.reopen(
646
691
  this.renderToContext(context);
647
692
  this.set('layer', context.element());
648
693
 
649
- // Route.
694
+ // Route first.
650
695
  this._gotoUnattachedState();
651
696
 
652
697
  // Notify rendered (on self and all child views).
@@ -679,6 +724,12 @@ SC.CoreView.reopen(
679
724
  isParentShown = parentView ? parentView.get('viewState') & SC.CoreView.IS_SHOWN : true,
680
725
  transitionShow = this.get('transitionShow');
681
726
 
727
+ //@if (debug)
728
+ if (SC.LOG_VIEW_STATES) {
729
+ SC.Logger.log('%@:%@ — _doShow()'.fmt(this, state));
730
+ }
731
+ //@endif
732
+
682
733
  switch (state) {
683
734
  case SC.CoreView.ATTACHED_SHOWN: // FAST PATH!
684
735
  case SC.CoreView.ATTACHED_SHOWING: // FAST PATH!
@@ -744,8 +795,11 @@ SC.CoreView.reopen(
744
795
  var isVisibleInWindow = this.get('isVisibleInWindow'),
745
796
  handled = true;
746
797
 
747
- // Legacy.
748
- this.set('layerNeedsUpdate', true);
798
+ //@if (debug)
799
+ if (SC.LOG_VIEW_STATES) {
800
+ SC.Logger.log('%@:%@ — _doUpdateContent(%@)'.fmt(this, this.get('viewState'), force));
801
+ }
802
+ //@endif
749
803
 
750
804
  if (this.get('_isRendered')) {
751
805
  if (isVisibleInWindow || force) {
@@ -1188,11 +1242,11 @@ SC.CoreView.reopen(
1188
1242
 
1189
1243
  /** @private Updates according to parent did render. */
1190
1244
  _parentDidRender: function () {
1245
+ // Route first.
1246
+ this._gotoUnattachedByParentState();
1247
+
1191
1248
  // Notify rendered.
1192
1249
  this._rendered();
1193
-
1194
- // Route
1195
- this._gotoUnattachedByParentState();
1196
1250
  },
1197
1251
 
1198
1252
  /** @private Starts building out view if appropriate. */
@@ -281,8 +281,20 @@ SC.CoreView.reopen(
281
281
  @returns {SC.View} receiver
282
282
  */
283
283
  displayDidChange: function () {
284
- // Filter the input channel.
285
- this.invokeOnce(this._doUpdateContent);
284
+ //@if (debug)
285
+ if (SC.LOG_VIEW_STATES) {
286
+ SC.Logger.log('%@:%@ — displayDidChange()'.fmt(this, this.get('viewState')));
287
+ }
288
+ //@endif
289
+
290
+ // Don't run _doUpdateContent needlessly, because the view may render
291
+ // before it is invoked, which would result in a needless update.
292
+ if (this.get('_isRendered')) {
293
+ // Legacy.
294
+ this.set('layerNeedsUpdate', true);
295
+
296
+ this.invokeOnce(this._doUpdateContent);
297
+ }
286
298
 
287
299
  return this;
288
300
  },
@@ -393,12 +393,33 @@ SC.Store = SC.Object.extend( /** @scope SC.Store.prototype */ {
393
393
  if (!editables) editables = this.editables = [];
394
394
  editables[storeKey] = 1 ; // use number for dense array support
395
395
 
396
- var that = this;
397
- this._propagateToChildren(storeKey, function(storeKey){
398
- that.writeDataHash(storeKey, null, status);
399
- });
396
+ // Update the child record hashes in place.
397
+ if (!SC.none(this.parentRecords) ) {
398
+ var children = this.parentRecords[storeKey] || {},
399
+ childHash;
400
+
401
+ for (var key in children) {
402
+
403
+ if (children.hasOwnProperty(key)) {
404
+
405
+ if (hash) {
406
+ var childPath = children[key];
407
+ childPath = childPath.split('.');
408
+ if (childPath.length > 1) {
409
+ childHash = hash[childPath[0]][childPath[1]];
410
+ } else {
411
+ childHash = hash[childPath[0]];
412
+ }
400
413
 
401
- return this ;
414
+ this.writeDataHash(key, childHash, status);
415
+ } else {
416
+ this.writeDataHash(key, null, status);
417
+ }
418
+ }
419
+ }
420
+ }
421
+
422
+ return this;
402
423
  },
403
424
 
404
425
  /**
@@ -41,7 +41,14 @@ module("SC.Record.toMany array with data source", {
41
41
  }
42
42
  });
43
43
 
44
- test("when retrieving records with toMany association, it should call retrieveRecords once instead of calling retrieveRecord multiple times", function() {
44
+ test("when retrieving records with toMany association, it should call retrieveRecords once instead of calling retrieveRecord multiple times");
45
+ /** WON'T FIX AT THIS TIME.
46
+
47
+ Yes, iterating a toMany attribute will call retrieveRecord for each individual index, but we cannot change this. If we tried to buffer all calls to retrieveRecord then we will break the existing contract that retrieveRecord and retrieveRecords will return immediately with storeKeys if the data source is going to handle it. Plus there are alternatives. The custom datasource could use invokeOnce and a temporary cache to buffer all calls to retrieveRecord in order to build a single request out of multiple ids. This is the most feasible, because it's highly dependent on the datasource/backend configuration. The other alternative is that the backend should return the related records when the main record is requested in a single request (again, highly dependent on the datasource/backend configuration).
48
+
49
+ To do it within SproutCore itself would mean a big change along the lines of how every app works with the store and so it will certainly have to wait until a major version.
50
+
51
+ , function() {
45
52
  var store = SC.Store.create().from("MyDataSource");
46
53
  SC.RunLoop.begin();
47
54
  store.loadRecords(MyApp.Project, [
@@ -61,3 +68,4 @@ test("when retrieving records with toMany association, it should call retrieveRe
61
68
  // retrieveRecords should be called only once
62
69
  same(store.get('dataSource').get('retrieveRecordsArguments').length, 1);
63
70
  });
71
+ */
@@ -181,8 +181,7 @@ test("Use in Nested Store", function () {
181
181
  });
182
182
 
183
183
  test("Store#pushRetrieve for parent updates the child records", function () {
184
- var parent = store.materializeRecord(storeKeys[0]),
185
- nr = parent.get('contents').firstObject(),
184
+ var parent, nr,
186
185
  newDataHash = {
187
186
  type: 'Directory',
188
187
  name: 'Dir 1 Changed',
@@ -208,6 +207,11 @@ test("Store#pushRetrieve for parent updates the child records", function () {
208
207
  ]
209
208
  };
210
209
 
210
+ SC.run(function () {
211
+ parent = store.materializeRecord(storeKeys[0]);
212
+ nr = parent.get('contents').firstObject();
213
+ });
214
+
211
215
  ok(nr, "Got nested record");
212
216
  equals(nr.get('name'), 'Dir 2', "Dir id:2 has correct name");
213
217
 
@@ -20,7 +20,7 @@ SC.NeedsSplitParent = {
20
20
  * @property SC.SplitView
21
21
  */
22
22
  splitView: function () {
23
- var view = this;
23
+ var view = this.get('parentView');
24
24
  while (view && !view.isSplitView) view = view.get('parentView');
25
25
  return view;
26
26
  }.property('parentView').cacheable(),
@@ -460,8 +460,6 @@ SC.PickerPane = SC.PalettePane.extend(
460
460
  this.set('anchorCached', anchor);
461
461
  }
462
462
 
463
- if (anchor.x === 0 && anchor.y === 0) { return; }
464
-
465
463
  origin = SC.cloneRect(anchor);
466
464
 
467
465
  if (preferType) {