sproutcore 1.11.0.rc1 → 1.11.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +8 -8
  2. data/VERSION.yml +1 -1
  3. data/lib/frameworks/sproutcore/CHANGELOG.md +93 -65
  4. data/lib/frameworks/sproutcore/apps/showcase/controllers/source_tree_controller.js +17 -7
  5. data/lib/frameworks/sproutcore/apps/showcase/resources/main_page.js +22 -2
  6. data/lib/frameworks/sproutcore/apps/showcase/resources/stylesheet.css +14 -0
  7. data/lib/frameworks/sproutcore/apps/showcase/resources/views_page.js +2 -2
  8. data/lib/frameworks/sproutcore/frameworks/ajax/system/request.js +20 -8
  9. data/lib/frameworks/sproutcore/frameworks/ajax/system/websocket.js +58 -43
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/action_support.js +192 -35
  11. data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/delegate_support.js +7 -3
  12. data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/responder_context.js +27 -7
  13. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/browser.js +20 -63
  14. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/color.js +16 -7
  15. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +279 -159
  16. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/render_context.js +1 -1
  17. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +21 -10
  18. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/action_support.js +32 -28
  19. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/root_responder/targetForAction.js +107 -90
  20. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/root_responder/touch.js +33 -25
  21. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/touch.js +23 -15
  22. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +1 -1
  23. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout.js +12 -6
  24. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +55 -33
  25. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +7 -1
  26. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/methods.js +228 -72
  27. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/touch.js +54 -100
  28. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/ui.js +15 -0
  29. data/lib/frameworks/sproutcore/frameworks/desktop/views/button.js +57 -45
  30. data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +9 -16
  31. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll_view.js +111 -44
  32. data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +2 -0
  33. data/lib/frameworks/sproutcore/frameworks/foundation/system/module.js +51 -5
  34. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  35. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +13 -10
  36. data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +2 -1
  37. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +28 -9
  38. data/lib/frameworks/sproutcore/frameworks/runtime/system/set.js +7 -5
  39. data/lib/frameworks/sproutcore/frameworks/statechart/ext/function.js +51 -46
  40. data/lib/frameworks/sproutcore/frameworks/statechart/system/state.js +8 -5
  41. data/lib/frameworks/sproutcore/frameworks/statechart/system/statechart.js +12 -3
  42. metadata +2 -2
@@ -40,35 +40,40 @@ module("SC.RootResponder", {
40
40
  evt2.changedTouches.push(evt);
41
41
  evt2.changedTouches.push(evt2);
42
42
  },
43
-
43
+
44
44
  teardown: function() {
45
45
  pane.destroy();
46
+ evt = evt2 = layer = view = pane = null;
46
47
  }
47
48
  });
48
49
 
49
50
  // With v1.11, SC.Touch now provides its own velocity along each axis.
50
51
  test("SC.Touch velocity", function() {
51
52
  // Get a layer
52
- var touch;
53
+ var touch,
54
+ lastTimestamp;
55
+
56
+ lastTimestamp = evt.timeStamp;
53
57
 
54
58
  // Trigger touchstart
55
- SC.run(function() {
56
- SC.Event.trigger(layer, 'touchstart', [evt]);
57
- });
59
+ SC.Event.trigger(layer, 'touchstart', [evt]);
58
60
 
59
61
  touch = SC.RootResponder.responder._touches[evt.identifier];
60
62
 
61
63
  equals(touch.velocityX, 0, "Horizontal velocity begin at zero");
62
64
  equals(touch.velocityY, 0, "Vertical velocity begin at zero");
63
65
 
64
- evt.type = 'touchmove';
65
- evt.timeStamp += 100;
66
- evt.pageX += 100;
67
- evt.pageY += 100;
68
-
69
- SC.run(function() {
70
- SC.Event.trigger(layer, 'touchmove', [evt]);
71
- });
66
+ // Copy the last DOM touch as the basis of an updated DOM touch.
67
+ touch = SC.copy(touch);
68
+ touch.pageX += 100;
69
+ touch.pageY += 100;
70
+
71
+ evt = SC.Event.simulateEvent(layer, 'touchmove', { touches: [touch], identifier: 4, changedTouches: [touch] });
72
+ evt.timeStamp = lastTimestamp + 100;
73
+
74
+ SC.Event.trigger(layer, 'touchmove', [evt]);
75
+
76
+ touch = SC.RootResponder.responder._touches[evt.identifier];
72
77
 
73
78
  equals(touch.velocityX, 1, 'VelocityX for 100 pixels in 100 ms is 1.');
74
79
  equals(touch.velocityY, 1, 'VelocityY for 100 pixels in 100 ms is 1.');
@@ -77,13 +82,15 @@ test("SC.Touch velocity", function() {
77
82
 
78
83
  test("averagedTouchesForView", function() {
79
84
  // Start touch.
80
- SC.run(function() {
81
- SC.Event.trigger(layer, 'touchstart', [evt, evt2]);
82
- });
85
+ SC.Event.trigger(layer, 'touchstart', evt);
86
+
87
+ // Copy the last DOM touch as the basis of an updated DOM touch.
88
+ var touch1 = SC.RootResponder.responder._touches[evt.identifier];
89
+ var touch2 = SC.RootResponder.responder._touches[evt2.identifier];
83
90
 
84
91
  // Get our starting average.
85
- var expectedAverageX = (evt.pageX + evt2.pageX) / 2,
86
- expectedAverageY = (evt.pageY + evt2.pageY) / 2,
92
+ var expectedAverageX = (touch1.pageX + touch2.pageX) / 2,
93
+ expectedAverageY = (touch1.pageY + touch2.pageY) / 2,
87
94
  startAverage = SC.clone(SC.RootResponder.responder.averagedTouchesForView(view));
88
95
 
89
96
  equals(startAverage.x, expectedAverageX, "averagedTouchesForView correctly returns touch location averages (x)");
@@ -91,13 +98,14 @@ test("averagedTouchesForView", function() {
91
98
  ok(startAverage.d, "averagedTouchesForView's distance measurement should ... be a nonzero number. (Pythagoras doesn't play nice with integers.)");
92
99
 
93
100
  // Pinch out by 50 pixels in every direction.
94
- evt.pageX = 50;
95
- evt.pageY = 50;
96
- evt2.pageX = 250;
97
- evt2.pageY = 250;
98
- SC.run(function() {
99
- SC.Event.trigger(layer, 'touchmove', [evt, evt2]);
100
- });
101
+ touch1.pageX = 50;
102
+ touch1.pageY = 50;
103
+ touch2.pageX = 250;
104
+ touch2.pageY = 250;
105
+
106
+ var moveEvt = SC.Event.simulateEvent(layer, 'touchmove', { touches: [touch1, touch2], identifier: 6, changedTouches: [touch1, touch2] });
107
+ SC.Event.trigger(layer, 'touchmove', moveEvt);
108
+
101
109
  // Get our post-pinch average.
102
110
  var endAverage = SC.RootResponder.responder.averagedTouchesForView(view);
103
111
 
@@ -65,16 +65,15 @@ test("Touch event handling and juggling.", function() {
65
65
  var outer = pane.outerView,
66
66
  inner = outer.innerView,
67
67
  layer = inner.get('layer'),
68
- event = SC.Event.simulateEvent(layer, 'touchstart');
68
+ event = SC.Event.simulateEvent(layer, 'touchstart'),
69
+ touch;
69
70
 
70
71
  event.touches = [];
71
72
  event.identifier = 4;
72
73
  event.changedTouches = [event];
73
74
 
74
75
  // Trigger touchstart: outerView.captureTouch > outerView.touchStart
75
- SC.run(function() {
76
- SC.Event.trigger(layer, 'touchstart', [event]);
77
- });
76
+ SC.Event.trigger(layer, 'touchstart', [event]);
78
77
 
79
78
  equals(outerCapture, 1, "To capture the initial touch, outerView.captureTouch should have run:");
80
79
  equals(outerStart, 1, "Having captured the initial touch, outerView.touchStart should have run:");
@@ -87,10 +86,13 @@ test("Touch event handling and juggling.", function() {
87
86
  if (outerEnd) ok(false, "outerView.touchEnd should not have been called yet!");
88
87
 
89
88
  // Trigger touchmoved: outerView.touchesDragged > [passes touch to innerView] > innerView.touchStart
90
- event.type = 'touchmove';
91
- SC.run(function() {
92
- SC.Event.trigger(layer, 'touchmove', [event]);
93
- });
89
+
90
+ // Copy the last DOM touch as the basis of an updated DOM touch.
91
+ touch = SC.RootResponder.responder._touches[event.identifier];
92
+ touch = SC.copy(touch);
93
+
94
+ event = SC.Event.simulateEvent(layer, 'touchmove', { touches: [touch], identifier: 4, changedTouches: [touch] });
95
+ SC.Event.trigger(layer, 'touchmove', [event]);
94
96
 
95
97
  if (outerCapture !== 1) ok(false, "outerView.captureTouch should only have been called once!");
96
98
  if (outerStart !== 1) ok(false, "outerView.touchStart should only have been called once!");
@@ -103,9 +105,12 @@ test("Touch event handling and juggling.", function() {
103
105
  if (outerEnd) ok(false, "outerView.touchEnd should not have been called yet!");
104
106
 
105
107
  // Trigger touchmoved x2: innerView.touchesDragged > [passes touch back to outerView] > outerView.touchStart > innerView.touchCancelled
106
- SC.run(function() {
107
- SC.Event.trigger(layer, 'touchmove', [event]);
108
- });
108
+ // Copy the last DOM touch as the basis of an updated DOM touch.
109
+ touch = SC.RootResponder.responder._touches[event.identifier];
110
+ touch = SC.copy(touch);
111
+
112
+ event = SC.Event.simulateEvent(layer, 'touchmove', { touches: [touch], identifier: 4, changedTouches: [touch] });
113
+ SC.Event.trigger(layer, 'touchmove', [event]);
109
114
 
110
115
  if (outerCapture !== 1) ok(false, "outerView.captureTouch should only have been called once!");
111
116
  equals(outerDragged, 1, "Having passed ownership to innerView, outerView.touchesDragged should not have been called again:");
@@ -118,10 +123,13 @@ test("Touch event handling and juggling.", function() {
118
123
  if (outerEnd) ok(false, "outerView.touchEnd should not have been called yet!");
119
124
 
120
125
  // Trigger touchend: outerView.touchEnd
121
- event.type = 'touchend';
122
- SC.run(function() {
123
- SC.Event.trigger(layer, 'touchend', [event]);
124
- });
126
+
127
+ // Copy the last DOM touch as the basis of an updated DOM touch.
128
+ touch = SC.RootResponder.responder._touches[event.identifier];
129
+ touch = SC.copy(touch);
130
+
131
+ event = SC.Event.simulateEvent(layer, 'touchend', { touches: [touch], identifier: 4, changedTouches: [touch] });
132
+ SC.Event.trigger(layer, 'touchend', [event]);
125
133
 
126
134
  if (outerCapture !== 1) ok(false, "outerView.captureTouch should only have been called once!");
127
135
  if (outerStart !== 1) ok(false, "outerView.touchStart should only have been called once!");
@@ -189,7 +189,7 @@ SC.View.reopen(
189
189
  );
190
190
 
191
191
  The animate functions are intelligent in how they apply animations and
192
- calling animate in a manner that would effect an ongoing animation (i.e.
192
+ calling animate in a manner that would affect an ongoing animation (i.e.
193
193
  animating left again while it is still in transition) will result in
194
194
  the ongoing animation callback firing immediately with isCancelled set to
195
195
  true and adjusting the transition to accomodate the new settings.
@@ -145,6 +145,7 @@ SC.View.reopen(
145
145
  }
146
146
 
147
147
  // Ignore undefined values or values equal to the current value.
148
+ /*jshint eqeqeq:false*/
148
149
  if (newValue !== undefined && layout[key] != newValue) { // coerced so '100' == 100
149
150
  // Only clone the layout if it is not given.
150
151
  if (!newLayout) newLayout = SC.clone(this.get('layout'));
@@ -543,13 +544,16 @@ SC.View.reopen(
543
544
  // Add the original, unscaled height and width.
544
545
  frame.originalWidth = originalWidth;
545
546
  frame.originalHeight = originalHeight;
547
+
546
548
  return frame;
547
549
  }
550
+
548
551
  // Get the scale and transform origins, if not provided. (Note inlining of SC.none for performance)
549
552
  /*jshint eqnull:true*/
550
553
  scale = scale == null ? layout.scale : scale;
551
554
  oX = oX == null ? layout.transformOriginX : oX;
552
555
  oY = oY == null ? layout.transformOriginY : oY;
556
+
553
557
  // Get defaults.
554
558
  if (scale == null) scale = 1;
555
559
  if (oX == null) oX = 0.5;
@@ -559,6 +563,7 @@ SC.View.reopen(
559
563
  if (scale !== 1) {
560
564
  frame = SC.scaleRect(frame, scale, oX, oY);
561
565
  }
566
+
562
567
  // Regardless, attach the scale numbers for reference.
563
568
  frame.scale = scale;
564
569
  frame.transformOriginX = oX;
@@ -797,8 +802,8 @@ SC.View.reopen(
797
802
  if (!SC.none(currentLayout.rotate)) {
798
803
  if (SC.none(currentLayout.rotateZ) && SC.platform.get('supportsCSS3DTransforms')) {
799
804
  currentLayout.rotateZ = currentLayout.rotate;
800
- delete currentLayout.rotate;
801
- }
805
+ delete currentLayout.rotate;
806
+ }
802
807
  }
803
808
 
804
809
  // Optimize notifications depending on if we resized or just moved.
@@ -1139,11 +1144,12 @@ SC.View.reopen(
1139
1144
 
1140
1145
  /** @private */
1141
1146
  _doUpdateLayoutStyle: function () {
1142
- var context;
1147
+ var layer = this.get('layer'),
1148
+ layoutStyle = this.get('layoutStyle');
1143
1149
 
1144
- context = this.renderContext(this.get('layer'));
1145
- context.setStyle(this.get('layoutStyle'));
1146
- context.update();
1150
+ for (var styleName in layoutStyle) {
1151
+ layer.style[styleName] = layoutStyle[styleName];
1152
+ }
1147
1153
 
1148
1154
  // Reset that an update is required.
1149
1155
  this._layoutStyleNeedsUpdate = false;
@@ -11,6 +11,7 @@ sc_require('views/view/animation');
11
11
  SC.CSS_TRANSFORM_NAMES = ['rotateX', 'rotateY', 'rotateZ', 'scale'];
12
12
 
13
13
  SC.CSS_TRANSFORM_MAP = {
14
+
14
15
  rotate: function (val) {
15
16
  if (SC.typeOf(val) === SC.T_NUMBER) { val += 'deg'; }
16
17
  return 'rotate(' + val + ')';
@@ -41,21 +42,30 @@ SC.CSS_TRANSFORM_MAP = {
41
42
  /** @private */
42
43
  SC.View.LayoutStyleCalculator = {
43
44
 
45
+ /** @private Shared object used to avoid continually initializing/destroying objects. */
46
+ _SC_STATE_MAP: null,
47
+
48
+ /** @private Shared object used to avoid continually initializing/destroying objects. */
49
+ _SC_TRANSFORMS_ARRAY: null,
50
+
51
+ /** @private Shared object used to avoid continually initializing/destroying objects. */
52
+ _SC_TRANSITIONS_ARRAY: null,
53
+
44
54
  /** @private If the value is undefined, make it null. */
45
55
  _valueOrNull: function (value) {
46
56
  return value === undefined ? null : value;
47
57
  },
48
58
 
49
59
  /** @private */
50
- _prepareStyle: function (layout) {
60
+ _prepareStyle: function (style, layout) {
51
61
  /*jshint eqnull:true */
52
62
  // It's important to provide null defaults to reset any previous style when
53
63
  // this is applied.
54
- var commonBorder = this._valueOrNull(layout.border),
55
- style = {
56
- marginLeft: null,
57
- marginTop: null
58
- };
64
+ var commonBorder = this._valueOrNull(layout.border);
65
+
66
+ // Reset properties that might not be set from style to style.
67
+ style.marginLeft = null;
68
+ style.marginTop = null;
59
69
 
60
70
  // Position and size.
61
71
  style.bottom = layout.bottom;
@@ -88,9 +98,12 @@ SC.View.LayoutStyleCalculator = {
88
98
  // Handle transforms (including reset).
89
99
  if (SC.platform.supportsCSSTransforms) {
90
100
  var transformAttribute = SC.browser.experimentalStyleNameFor('transform'),
91
- transforms = [],
101
+ transforms = this._SC_TRANSFORMS_ARRAY, // Shared object used to avoid continually initializing/destroying objects.
92
102
  transformMap = SC.CSS_TRANSFORM_MAP;
93
103
 
104
+ // Create the array once. Note: This is a shared array; it must be set to 0 length each time.
105
+ if (!transforms) { transforms = this._SC_TRANSFORMS_ARRAY = []; }
106
+
94
107
  // The order of the transforms is important so that we can decompose them
95
108
  // from the transformation matrix later if necessary.
96
109
  for (var i = 0, len = SC.CSS_TRANSFORM_NAMES.length; i < len; i++) {
@@ -103,6 +116,7 @@ SC.View.LayoutStyleCalculator = {
103
116
  }
104
117
  }
105
118
 
119
+ // Set or reset the transform style.
106
120
  style[transformAttribute] = transforms.length > 0 ? transforms.join(' ') : null;
107
121
 
108
122
  // Transform origin.
@@ -123,24 +137,13 @@ SC.View.LayoutStyleCalculator = {
123
137
  style[SC.browser.experimentalStyleNameFor('transition')] = null;
124
138
  }
125
139
 
126
- // for ie, we will NOT use alpha. It is just a source of pain.
127
- // a) it will not affect absolutely positioned child elements, and is therefore
128
- // useless for most SC purposes.
129
- //
130
- // b) It completely breaks semitransparent background images (PNGs with opacity)
131
- //
132
- // If users want to use alpha, they should do it on their own.
133
-
134
- // if(!SC.none(layout.opacity)) style.filter = "alpha(opacity=%@)".fmt(layout.opacity * 100);
135
-
136
- return style;
140
+ // Reset shared object!
141
+ this._SC_TRANSFORMS_ARRAY.length = 0;
137
142
  },
138
143
 
139
144
  /** @private */
140
- _prepareState: function (style) {
145
+ _prepareState: function (state, style) {
141
146
  /*jshint eqnull:true */
142
- var state = {};
143
-
144
147
  state.hasBottom = (style.bottom != null);
145
148
  state.hasRight = (style.right != null);
146
149
  state.hasLeft = (style.left != null);
@@ -151,8 +154,6 @@ SC.View.LayoutStyleCalculator = {
151
154
  state.hasWidth = (style.width != null);
152
155
  state.hasMaxWidth = (style.maxWidth != null);
153
156
  state.hasMaxHeight = (style.maxHeight != null);
154
-
155
- return state;
156
157
  },
157
158
 
158
159
 
@@ -277,7 +278,7 @@ SC.View.LayoutStyleCalculator = {
277
278
  },
278
279
 
279
280
  /** @private */
280
- // return "auto" for "auto", null for null, converts 0.XY into "XY%".
281
+ // return "auto" for "auto", null for null, converts 0.X into "X%".
281
282
  // otherwise returns the original number, rounded down
282
283
  _cssNumber: function (val) {
283
284
  /*jshint eqnull:true*/
@@ -293,12 +294,11 @@ SC.View.LayoutStyleCalculator = {
293
294
 
294
295
  @return {Object} Layout style hash.
295
296
  */
296
- calculate: function (view) {
297
+ calculate: function (view, style) {
297
298
  var layout = view.get('layout'),
298
299
  animations = view._activeAnimations,
299
- state,
300
- useStaticLayout = view.get('useStaticLayout'),
301
- style;
300
+ state = this._SC_STATE_MAP, // Shared object used to avoid continually initializing/destroying objects.
301
+ useStaticLayout = view.get('useStaticLayout');
302
302
 
303
303
  // Fast path!
304
304
  // If the developer sets useStaticLayout and doesn't provide a unique `layout` property, we
@@ -307,8 +307,14 @@ SC.View.LayoutStyleCalculator = {
307
307
  // use it.
308
308
  if (useStaticLayout && layout === SC.View.prototype.layout) { return {}; }
309
309
 
310
- style = this._prepareStyle(layout);
311
- state = this._prepareState(style);
310
+ // Reset and prep the style object.
311
+ this._prepareStyle(style, layout);
312
+
313
+ // Create the object once. Note: This is a shared object; all properties must be overwritten each time.
314
+ if (!state) { state = this._SC_STATE_MAP = {}; }
315
+
316
+ // Reset and prep the state object.
317
+ this._prepareState(state, style);
312
318
 
313
319
  // handle invalid use of auto in absolute layouts
314
320
  if (!useStaticLayout) {
@@ -389,7 +395,10 @@ SC.View.LayoutStyleCalculator = {
389
395
  // Handle animations
390
396
  if (animations) {
391
397
  if (SC.platform.supportsCSSTransitions) {
392
- var transitions = [];
398
+ var transitions = this._SC_TRANSITIONS_ARRAY; // Shared object used to avoid continually initializing/destroying objects.
399
+
400
+ // Create the array once. Note: This is a shared array; it must be set to 0 length each time.
401
+ if (!transitions) { transitions = this._SC_TRANSITIONS_ARRAY = []; }
393
402
 
394
403
  for (key in animations) {
395
404
  var animation = animations[key],
@@ -436,6 +445,9 @@ SC.View.LayoutStyleCalculator = {
436
445
  }
437
446
 
438
447
  style[SC.browser.experimentalStyleNameFor('transition')] = transitions.join(", ");
448
+
449
+ // Reset shared object!
450
+ this._SC_TRANSITIONS_ARRAY.length = 0;
439
451
  } else {
440
452
  // TODO: Do it the JS way
441
453
  }
@@ -449,6 +461,9 @@ SC.View.LayoutStyleCalculator = {
449
461
  SC.View.reopen(
450
462
  /** @scope SC.View.prototype */ {
451
463
 
464
+ /** @private Shared object used to avoid continually initializing/destroying objects. */
465
+ _SC_STYLE_MAP: null,
466
+
452
467
  /**
453
468
  layoutStyle describes the current styles to be written to your element
454
469
  based on the layout you defined. Both layoutStyle and frame reset when
@@ -460,7 +475,14 @@ SC.View.reopen(
460
475
  @readOnly
461
476
  */
462
477
  layoutStyle: function () {
463
- return SC.View.LayoutStyleCalculator.calculate(this);
464
- // 'hasAcceleratedLayer' is dependent on 'layout' so we don't need 'layout' to be a dependency here
478
+ var style = this._SC_STYLE_MAP; // Shared object used to avoid continually initializing/destroying objects.
479
+
480
+ // Create the object once. Note: This is a shared object; all properties must be overwritten each time.
481
+ if (!style) { style = this._SC_STYLE_MAP = {}; }
482
+
483
+ return SC.View.LayoutStyleCalculator.calculate(this, style);
484
+
485
+ // 'hasAcceleratedLayer' is dependent on 'layout' so we don't need 'layout' to be a dependency here
465
486
  }.property('hasAcceleratedLayer', 'useStaticLayout').cacheable()
487
+
466
488
  });
@@ -109,7 +109,13 @@ SC.Store = SC.Object.extend( /** @scope SC.Store.prototype */ {
109
109
  @returns {SC.Store} receiver
110
110
  */
111
111
  cascade: function(dataSource) {
112
- var dataSources = SC.A(arguments) ;
112
+ var dataSources;
113
+
114
+ // Fast arguments access.
115
+ // Accessing `arguments.length` is just a Number and doesn't materialize the `arguments` object, which is costly.
116
+ dataSources = new Array(arguments.length); // SC.A(arguments)
117
+ for (var i = 0, len = dataSources.length; i < len; i++) { dataSources[i] = arguments[i]; }
118
+
113
119
  dataSource = SC.CascadeDataSource.create({
114
120
  dataSources: dataSources
115
121
  });
@@ -91,7 +91,7 @@ module("SC.ScrollView", {
91
91
  SC.run(function () {
92
92
  pane.destroy();
93
93
  });
94
- pane = view = null;
94
+ pane = view = view2 = view3 = view4 = null;
95
95
  }
96
96
  });
97
97
 
@@ -117,6 +117,194 @@ test("Scrolling to a certain co-ordinate of the container view", function () {
117
117
  });
118
118
  });
119
119
 
120
+
121
+ test("Scroll offsets are correct when scroll view's frame changes", function () {
122
+
123
+ // Scenario: left & top aligned, at maximums, scroll view shrinks: don't move content
124
+ SC.run(function () {
125
+ view.scrollBy(3900, 3900);
126
+ pane.adjust({ width: 64, height: 64 });
127
+ equals(view.get('horizontalScrollOffset'), 3900, "After resizing the pane with adjust height of 64, horizontal offset must not change");
128
+ equals(view.get('verticalScrollOffset'), 3900, "After resizing the pane with adjust width of 64, vertical offset must not change");
129
+ });
130
+
131
+ // Reset the parent view.
132
+ SC.run(function () {
133
+ view.scrollTo(0, 0);
134
+ pane.adjust({ width: 114, height: 114 });
135
+ });
136
+
137
+ // Scenario: left & top aligned, at maximums, scroll view grows: hug the right & bottom sides.
138
+ SC.run(function () {
139
+ view.scrollBy(3900, 3900);
140
+ pane.adjust({ width: 1014, height: 1014 });
141
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset'), "After resizing the pane with adjust height of 1014, horizontal offset must be maximum");
142
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset'), "After resizing the pane with adjust width of 1014, vertical offset must be maximum");
143
+ });
144
+
145
+ // Reset the parent view.
146
+ SC.run(function () {
147
+ view.scrollTo(0, 0);
148
+ pane.adjust({ width: 114, height: 114 });
149
+ });
150
+
151
+ // Set alignments to hug right and bottom edges.
152
+ SC.run(function() {
153
+ view.set('horizontalAlign', SC.ALIGN_RIGHT);
154
+ view.set('verticalAlign', SC.ALIGN_BOTTOM);
155
+ });
156
+
157
+ // Scenario: right & bottom aligned, at maximums, scroll view shrinks: hug the right & bottom sides.
158
+ SC.run(function () {
159
+ view.scrollBy(3900, 3900);
160
+ pane.adjust({ width: 64, height: 64 });
161
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset'), "After resizing the pane with adjust height of 64, horizontal offset must be maximum");
162
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset'), "After resizing the pane with adjust width of 64, vertical offset must be maximum");
163
+ });
164
+
165
+ // Reset the parent view.
166
+ SC.run(function () {
167
+ view.scrollTo(0, 0);
168
+ pane.adjust({ width: 114, height: 114 });
169
+ });
170
+
171
+ // Scenario: right & bottom aligned, at maximums, scroll view grows: hug the right & bottom sides.
172
+ SC.run(function () {
173
+ view.scrollBy(3900, 3900);
174
+ pane.adjust({ width: 1014, height: 1014 });
175
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset'), "After resizing the pane with adjust height of 1014, horizontal offset must be maximum");
176
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset'), "After resizing the pane with adjust width of 1014, vertical offset must be maximum");
177
+ });
178
+
179
+ // Reset the parent view.
180
+ SC.run(function () {
181
+ view.scrollTo(0, 0);
182
+ pane.adjust({ width: 114, height: 114 });
183
+ });
184
+
185
+ // Set alignments to be center and middle.
186
+ SC.run(function() {
187
+ view.set('horizontalAlign', SC.ALIGN_CENTER);
188
+ view.set('verticalAlign', SC.ALIGN_MIDDLE);
189
+ });
190
+
191
+ // Scenario: center & middle aligned, at center & middle, scroll view shrinks: stick to center & middle
192
+ SC.run(function () {
193
+ view.scrollBy(1950, 1950);
194
+ pane.adjust({ width: 64, height: 64 });
195
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset') / 2, "After resizing the pane with adjust height of 64, horizontal offset should stay centered");
196
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset') / 2, "After resizing the pane with adjust width of 64, vertical offset should stay centered");
197
+ });
198
+
199
+ // Reset the parent view.
200
+ SC.run(function () {
201
+ view.scrollTo(0, 0);
202
+ pane.adjust({ width: 114, height: 114 });
203
+ });
204
+
205
+ // Scenario: center & middle aligned, at center & middle, scroll view grows: stick to center & middle
206
+ SC.run(function () {
207
+ view.scrollBy(1950, 1950);
208
+ pane.adjust({ width: 1014, height: 1014 });
209
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset') / 2, "After resizing the pane with adjust height of 1014, horizontal offset should stay centered");
210
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset') / 2, "After resizing the pane with adjust width of 1014, vertical offset should stay centered");
211
+ });
212
+ });
213
+
214
+ test("Scroll offsets are correct when scroll view's content frame changes", function () {
215
+ var contentView = view.get('contentView');
216
+
217
+ // Scenario: left & top aligned, at maximums, content view shrinks: hug the right & bottom sides
218
+ SC.run(function () {
219
+ view.scrollBy(3900, 3900);
220
+ contentView.adjust({ width: 3900, height: 3900 });
221
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset'), "After resizing the content with adjust width of 3900, horizontal offset must be at maximum");
222
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset'), "After resizing the content with adjust height of 3900 vertical offset must be at maximum");
223
+ });
224
+
225
+ // Reset the view.
226
+ SC.run(function () {
227
+ view.scrollTo(0, 0);
228
+ contentView.adjust({ width: 4000, height: 4000 });
229
+ });
230
+
231
+ // Scenario: left & top aligned, at maximums, content view grows: don't move
232
+ SC.run(function () {
233
+ view.scrollBy(3900, 3900);
234
+ contentView.adjust({ width: 4100, height: 4100 });
235
+ equals(view.get('horizontalScrollOffset'), 3900, "After resizing the content with adjust width of 4100, horizontal offset must not change");
236
+ equals(view.get('verticalScrollOffset'), 3900, "After resizing the content with adjust height of 4100, vertical offset must not change");
237
+ });
238
+
239
+ // Reset the view.
240
+ SC.run(function () {
241
+ view.scrollTo(0, 0);
242
+ contentView.adjust({ width: 4000, height: 4000 });
243
+ });
244
+
245
+ // Set alignments to hug right and bottom edges.
246
+ SC.run(function() {
247
+ view.set('horizontalAlign', SC.ALIGN_RIGHT);
248
+ view.set('verticalAlign', SC.ALIGN_BOTTOM);
249
+ });
250
+
251
+ // Scenario: right & bottom aligned, at maximums, content view shrinks: hug the right & bottom sides.
252
+ SC.run(function () {
253
+ view.scrollBy(3900, 3900);
254
+ contentView.adjust({ width: 3900, height: 3900 });
255
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset'), "After resizing the content with adjust width of 3900, horizontal offset must be at maximum");
256
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset'), "After resizing the content with adjust height of 3900 vertical offset must be at maximum");
257
+ });
258
+
259
+ // Reset the view.
260
+ SC.run(function () {
261
+ view.scrollTo(0, 0);
262
+ contentView.adjust({ width: 4000, height: 4000 });
263
+ });
264
+
265
+ // Scenario: right & bottom aligned, at maximums, content view grows: hug the right & bottom sides.
266
+ SC.run(function () {
267
+ view.scrollBy(3900, 3900);
268
+ contentView.adjust({ width: 4100, height: 4100 });
269
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset'), "After resizing the content with adjust width of 4100, horizontal offset must be at maximum");
270
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset'), "After resizing the content with adjust height of 4100 vertical offset must be at maximum");
271
+ });
272
+
273
+ // Reset the view.
274
+ SC.run(function () {
275
+ view.scrollTo(0, 0);
276
+ contentView.adjust({ width: 4000, height: 4000 });
277
+ });
278
+
279
+ // Set alignments to be center and middle.
280
+ SC.run(function() {
281
+ view.set('horizontalAlign', SC.ALIGN_CENTER);
282
+ view.set('verticalAlign', SC.ALIGN_MIDDLE);
283
+ });
284
+
285
+ // Scenario: center & middle aligned, at center & middle, content view shrinks: stick to center & middle
286
+ SC.run(function () {
287
+ view.scrollBy(1950, 1950);
288
+ contentView.adjust({ width: 3900, height: 3900 });
289
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset') / 2, "After resizing the content with adjust width of 3900, horizontal offset should stay centered");
290
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset') / 2, "After resizing the content with adjust height of 3900 vertical offset should stay centered");
291
+ });
292
+
293
+ // Reset the view.
294
+ SC.run(function () {
295
+ view.scrollTo(0, 0);
296
+ contentView.adjust({ width: 4000, height: 4000 });
297
+ });
298
+
299
+ // Scenario: center & middle aligned, at center & middle, scroll view grows: stick to center & middle
300
+ SC.run(function () {
301
+ view.scrollBy(1950, 1950);
302
+ contentView.adjust({ width: 4100, height: 4100 });
303
+ equals(view.get('horizontalScrollOffset'), view.get('maximumHorizontalScrollOffset') / 2, "After resizing the content with adjust width of 4100, horizontal offset should stay centered");
304
+ equals(view.get('verticalScrollOffset'), view.get('maximumVerticalScrollOffset') / 2, "After resizing the content with adjust height of 4100 vertical offset should stay centered");
305
+ });
306
+ });
307
+
120
308
  test("Scrolling relative to the current possition of the container view", function () {
121
309
  equals(view.get('horizontalScrollOffset'), 0, "Initial horizontal offset must be zero");
122
310
  equals(view.get('verticalScrollOffset'), 0, "Initial vertical offset must be zero");
@@ -156,22 +344,26 @@ test("Scrolling to a rectangle", function () {
156
344
  });
157
345
 
158
346
  test("Scrolling to a view", function() {
159
- var pane = SC.MainPane.create({
160
- childViews: ['scrollView'],
161
- scrollView: SC.ScrollView.create({
162
- layout: { height: 100, width: 100 },
163
- canScale: YES,
164
- contentView: SC.View.create({
165
- layout: { height: 500, width: 500 },
166
- childViews: ['innerView1', 'innerView2'],
167
- innerView1: SC.View.create({
168
- layout: { height: 100, width: 100, top: 150, left: 150 }
169
- }),
170
- innerView2: SC.View.create({
171
- layout: { height: 100, width: 100, top: 200, left: 200 }
347
+ var pane;
348
+
349
+ SC.run(function () {
350
+ pane = SC.MainPane.create({
351
+ childViews: ['scrollView'],
352
+ scrollView: SC.ScrollView.create({
353
+ layout: { height: 100, width: 100 },
354
+ canScale: YES,
355
+ contentView: SC.View.create({
356
+ layout: { height: 500, width: 500 },
357
+ childViews: ['innerView1', 'innerView2'],
358
+ innerView1: SC.View.create({
359
+ layout: { height: 100, width: 100, top: 150, left: 150 }
360
+ }),
361
+ innerView2: SC.View.create({
362
+ layout: { height: 100, width: 100, top: 200, left: 200 }
363
+ })
172
364
  })
173
365
  })
174
- })
366
+ });
175
367
  });
176
368
 
177
369
  var scrollView = pane.scrollView,
@@ -286,7 +478,7 @@ test("maximumVerticalScrollOffset() returns the maximum vertical scroll dimensio
286
478
 
287
479
  // ------------------------------------
288
480
  // mouseWheel events
289
- //
481
+
290
482
 
291
483
  test("Mouse wheel events should only be captured if the scroll can scroll in the direction (both TOP-LEFT).", function () {
292
484
  // FIRST GROUP: everything scrolled all the way to the top left
@@ -344,18 +536,13 @@ test("Mouse wheel events not capturable by the inner scroll should bubble to the
344
536
  view4.scrollTo(114, 114);
345
537
  });
346
538
 
347
- window.stop();
348
-
349
539
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: 10, wheelDeltaY: 0 });
350
540
  SC.Event.trigger(elem, 'mousewheel', event);
351
541
 
352
- SC.RunLoop.begin();
353
- SC.Timer.schedule({ target: this, action: function () {
542
+ SC.run(function () {
354
543
  equals(view4.get('horizontalScrollOffset'), 114, 'The inner scroll view should still have horizontalScrollOffset');
355
544
  equals(view3.get('horizontalScrollOffset'), 10, 'The outer scroll view should now have horizontalScrollOffset');
356
- window.start();
357
- }, interval: 200});
358
- SC.RunLoop.end();
545
+ });
359
546
  });
360
547
 
361
548
  test("Mouse wheel events not capturable by the inner scroll should bubble to the outer scroll (scroll down).", function () {
@@ -367,18 +554,13 @@ test("Mouse wheel events not capturable by the inner scroll should bubble to the
367
554
  view4.scrollTo(114, 114);
368
555
  });
369
556
 
370
- window.stop();
371
-
372
557
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: 0, wheelDeltaY: 10 });
373
558
  SC.Event.trigger(elem, 'mousewheel', event);
374
559
 
375
- SC.RunLoop.begin();
376
- SC.Timer.schedule({ target: this, action: function () {
560
+ SC.run(function () {
377
561
  equals(view4.get('verticalScrollOffset'), 114, 'The inner scroll view should still have verticalScrollOffset');
378
562
  equals(view3.get('verticalScrollOffset'), 10, 'The outer scroll view should now have verticalScrollOffset');
379
- window.start();
380
- }, interval: 200});
381
- SC.RunLoop.end();
563
+ });
382
564
  });
383
565
 
384
566
  test("Mouse wheel events not capturable by the inner scroll should bubble to the outer scroll (scroll left).", function () {
@@ -388,19 +570,16 @@ test("Mouse wheel events not capturable by the inner scroll should bubble to the
388
570
  SC.run(function () {
389
571
  view3.scrollTo(114, 114);
390
572
  view4.scrollTo(0, 0);
391
-
392
- SC.Timer.schedule({ target: this, action: function () {
393
- equals(view4.get('horizontalScrollOffset'), 0, 'The inner scroll view should still have horizontalScrollOffset');
394
- equals(view3.get('horizontalScrollOffset'), 104, 'The outer scroll view should now have horizontalScrollOffset');
395
- window.start();
396
- }, interval: 200});
397
573
  });
398
574
 
399
- window.stop();
400
-
401
575
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: -10, wheelDeltaY: 0 });
402
576
  SC.Event.trigger(elem, 'mousewheel', event);
403
577
 
578
+ SC.run(function () {
579
+ equals(view4.get('horizontalScrollOffset'), 0, 'The inner scroll view should still have horizontalScrollOffset');
580
+ equals(view3.get('horizontalScrollOffset'), 104, 'The outer scroll view should now have horizontalScrollOffset');
581
+
582
+ });
404
583
  });
405
584
 
406
585
  test("Mouse wheel events not capturable by the inner scroll should bubble to the outer scroll (scroll up).", function () {
@@ -412,18 +591,13 @@ test("Mouse wheel events not capturable by the inner scroll should bubble to the
412
591
  view4.scrollTo(0, 0);
413
592
  });
414
593
 
415
- window.stop();
416
-
417
594
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: 0, wheelDeltaY: -10 });
418
595
  SC.Event.trigger(elem, 'mousewheel', event);
419
596
 
420
- SC.RunLoop.begin();
421
- SC.Timer.schedule({ target: this, action: function () {
597
+ SC.run(function () {
422
598
  equals(view4.get('verticalScrollOffset'), 0, 'The inner scroll view should still have verticalScrollOffset');
423
599
  equals(view3.get('verticalScrollOffset'), 104, 'The outer scroll view should now have verticalScrollOffset');
424
- window.start();
425
- }, interval: 200 });
426
- SC.RunLoop.end();
600
+ });
427
601
  });
428
602
 
429
603
  test("Mouse wheel events capturable by the inner scroll should not bubble to the outer scroll (scroll right).", function () {
@@ -435,18 +609,13 @@ test("Mouse wheel events capturable by the inner scroll should not bubble to the
435
609
  view4.scrollTo(0, 0);
436
610
  });
437
611
 
438
- window.stop();
439
-
440
612
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: 10, wheelDeltaY: 0 });
441
613
  SC.Event.trigger(elem, 'mousewheel', event);
442
614
 
443
- SC.RunLoop.begin();
444
- SC.Timer.schedule({ target: this, action: function () {
615
+ SC.run(function () {
445
616
  equals(view4.get('horizontalScrollOffset'), 10, 'The inner scroll view should now have horizontalScrollOffset');
446
617
  equals(view3.get('horizontalScrollOffset'), 0, 'The outer scroll view should still have horizontalScrollOffset');
447
- window.start();
448
- }, interval: 200 });
449
- SC.RunLoop.end();
618
+ });
450
619
  });
451
620
 
452
621
  test("Mouse wheel events capturable by the inner scroll should not bubble to the outer scroll (scroll up).", function () {
@@ -458,18 +627,13 @@ test("Mouse wheel events capturable by the inner scroll should not bubble to the
458
627
  view4.scrollTo(114, 114);
459
628
  });
460
629
 
461
- window.stop();
462
-
463
630
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: 0, wheelDeltaY: -10 });
464
631
  SC.Event.trigger(elem, 'mousewheel', event);
465
632
 
466
- SC.RunLoop.begin();
467
- SC.Timer.schedule({ target: this, action: function () {
633
+ SC.run(function () {
468
634
  equals(view4.get('verticalScrollOffset'), 104, 'The inner scroll view should now have verticalScrollOffset');
469
635
  equals(view3.get('verticalScrollOffset'), 114, 'The outer scroll view should still have verticalScrollOffset');
470
- window.start();
471
- }, interval: 200 });
472
- SC.RunLoop.end();
636
+ });
473
637
  });
474
638
 
475
639
  test("Mouse wheel events capturable by the inner scroll should not bubble to the outer scroll (scroll left).", function () {
@@ -481,18 +645,13 @@ test("Mouse wheel events capturable by the inner scroll should not bubble to the
481
645
  view4.scrollTo(114, 114);
482
646
  });
483
647
 
484
- window.stop();
485
-
486
648
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: -10, wheelDeltaY: 0 });
487
649
  SC.Event.trigger(elem, 'mousewheel', event);
488
650
 
489
- SC.RunLoop.begin();
490
- SC.Timer.schedule({ target: this, action: function () {
651
+ SC.run(function () {
491
652
  equals(view4.get('horizontalScrollOffset'), 104, 'The inner scroll view should now have horizontalScrollOffset');
492
653
  equals(view3.get('horizontalScrollOffset'), 114, 'The outer scroll view should still have horizontalScrollOffset');
493
- window.start();
494
- }, interval: 200 });
495
- SC.RunLoop.end();
654
+ });
496
655
  });
497
656
 
498
657
  test("Mouse wheel events capturable by the inner scroll should not bubble to the outer scroll (scroll down).", function () {
@@ -504,18 +663,15 @@ test("Mouse wheel events capturable by the inner scroll should not bubble to the
504
663
  view4.scrollTo(0, 0);
505
664
  });
506
665
 
507
- window.stop();
666
+ // window.stop();
508
667
 
509
668
  event = SC.Event.simulateEvent(elem, 'mousewheel', { wheelDeltaX: 0, wheelDeltaY: 10 });
510
669
  SC.Event.trigger(elem, 'mousewheel', event);
511
670
 
512
- SC.RunLoop.begin();
513
- SC.Timer.schedule({ target: this, action: function () {
671
+ SC.run(function () {
514
672
  equals(view4.get('verticalScrollOffset'), 10, 'The inner scroll view should now have verticalScrollOffset');
515
673
  equals(view3.get('verticalScrollOffset'), 0, 'The outer scroll view should still have verticalScrollOffset');
516
- window.start();
517
- }, interval: 200 });
518
- SC.RunLoop.end();
674
+ });
519
675
  });
520
676
 
521
677
  // ------------------------------------