sproutcore 1.10.2 → 1.10.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG +11 -0
  3. data/VERSION.yml +1 -1
  4. data/lib/frameworks/sproutcore/CHANGELOG.md +34 -0
  5. data/lib/frameworks/sproutcore/frameworks/core_foundation/child_view_layouts/horizontal_stack_layout.js +3 -1
  6. data/lib/frameworks/sproutcore/frameworks/core_foundation/child_view_layouts/vertical_stack_layout.js +3 -1
  7. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +79 -80
  8. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +115 -22
  9. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +54 -17
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/statechart.js +76 -34
  11. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/select/methods.js +18 -5
  12. data/lib/frameworks/sproutcore/frameworks/desktop/views/grid.js +14 -5
  13. data/lib/frameworks/sproutcore/frameworks/desktop/views/select.js +42 -18
  14. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/editable.js +41 -41
  15. data/lib/frameworks/sproutcore/frameworks/foundation/tests/transitions/view_transitions_test.js +235 -0
  16. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/bounce_transition.js +8 -4
  17. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/fade_transition.js +6 -2
  18. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/pop_transition.js +8 -4
  19. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/scale_transition.js +6 -2
  20. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/slide_transition.js +6 -2
  21. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/spring_transition.js +8 -4
  22. data/lib/frameworks/sproutcore/frameworks/foundation/views/inline_text_field.js +5 -4
  23. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +1 -1
  24. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +2 -2
  25. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +124 -80
  26. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/binding.js +134 -0
  27. data/lib/sproutcore.rb +1 -1
  28. data/lib/sproutcore/models/manifest.rb +12 -6
  29. data/lib/sproutcore/rack/builder.rb +20 -12
  30. data/lib/sproutcore/tools.rb +3 -3
  31. data/lib/sproutcore/tools/build.rb +22 -22
  32. data/sproutcore.gemspec +2 -5
  33. data/vendor/sproutcore/lib/yuicompressor-2.4.8.jar +0 -0
  34. metadata +10 -23
  35. data/vendor/sproutcore/lib/yuicompressor-2.4.6.jar +0 -0
@@ -105,8 +105,10 @@ SC.mixin(SC.View,
105
105
  { value: value, duration: duration, timing: 'ease-in-out' }
106
106
  ];
107
107
 
108
- var callback = function () {
109
- view.didTransitionIn();
108
+ var callback = function (data) {
109
+ if (!data.isCancelled) {
110
+ view.didTransitionIn();
111
+ }
110
112
  };
111
113
 
112
114
  // Animate through the frames.
@@ -198,8 +200,10 @@ SC.mixin(SC.View,
198
200
  { value: finalValue, duration: duration, timing: 'ease-in' }
199
201
  ];
200
202
 
201
- var callback = function () {
202
- view.didTransitionOut();
203
+ var callback = function (data) {
204
+ if (!data.isCancelled) {
205
+ view.didTransitionOut();
206
+ }
203
207
  };
204
208
 
205
209
  // Animate through the frames.
@@ -28,7 +28,9 @@ SC.mixin(SC.View,
28
28
  duration: options.duration || 0.4,
29
29
  timing: options.timing || 'ease'
30
30
  }, function (data) {
31
- this.didTransitionIn();
31
+ if (!data.isCancelled) {
32
+ this.didTransitionIn();
33
+ }
32
34
  });
33
35
  }
34
36
  },
@@ -48,7 +50,9 @@ SC.mixin(SC.View,
48
50
  duration: options.duration || 0.4,
49
51
  timing: options.timing || 'ease'
50
52
  }, function (data) {
51
- this.didTransitionOut();
53
+ if (!data.isCancelled) {
54
+ this.didTransitionOut();
55
+ }
52
56
  });
53
57
  }
54
58
 
@@ -37,8 +37,10 @@ SC.mixin(SC.View,
37
37
  { value: { scale: scale }, duration: duration * 0.4, timing: 'ease-in-out' }
38
38
  ];
39
39
 
40
- var callback = function () {
41
- view.didTransitionIn();
40
+ var callback = function (data) {
41
+ if (!data.isCancelled) {
42
+ view.didTransitionIn();
43
+ }
42
44
  };
43
45
 
44
46
  // Animate through the frames.
@@ -71,8 +73,10 @@ SC.mixin(SC.View,
71
73
  { value: { scale: 0 }, duration: duration * 0.6, timing: 'ease-in-out' }
72
74
  ];
73
75
 
74
- var callback = function () {
75
- view.didTransitionOut();
76
+ var callback = function (data) {
77
+ if (!data.isCancelled) {
78
+ view.didTransitionOut();
79
+ }
76
80
  };
77
81
 
78
82
  // Animate through the frames.
@@ -27,7 +27,9 @@ SC.mixin(SC.View,
27
27
  duration: options.duration || 0.4,
28
28
  timing: options.timing || 'ease'
29
29
  }, function (data) {
30
- this.didTransitionIn();
30
+ if (!data.isCancelled) {
31
+ this.didTransitionIn();
32
+ }
31
33
  });
32
34
  }
33
35
  },
@@ -46,7 +48,9 @@ SC.mixin(SC.View,
46
48
  duration: options.duration || 0.4,
47
49
  timing: options.timing || 'ease'
48
50
  }, function (data) {
49
- this.didTransitionOut();
51
+ if (!data.isCancelled) {
52
+ this.didTransitionOut();
53
+ }
50
54
  });
51
55
  }
52
56
 
@@ -71,7 +71,9 @@ SC.mixin(SC.View,
71
71
  duration: options.duration || 0.4,
72
72
  timing: options.timing || 'ease'
73
73
  }, function (data) {
74
- this.didTransitionIn();
74
+ if (!data.isCancelled) {
75
+ this.didTransitionIn();
76
+ }
75
77
  });
76
78
  }
77
79
  },
@@ -132,7 +134,9 @@ SC.mixin(SC.View,
132
134
  duration: options.duration || 0.4,
133
135
  timing: options.timing || 'ease'
134
136
  }, function (data) {
135
- this.didTransitionOut();
137
+ if (!data.isCancelled) {
138
+ this.didTransitionOut();
139
+ }
136
140
  });
137
141
  }
138
142
  }
@@ -108,8 +108,10 @@ SC.mixin(SC.View,
108
108
  { value: value, duration: duration, timing: 'ease-in-out' } // Hit target.
109
109
  ];
110
110
 
111
- var callback = function () {
112
- view.didTransitionIn();
111
+ var callback = function (data) {
112
+ if (!data.isCancelled) {
113
+ view.didTransitionIn();
114
+ }
113
115
  };
114
116
 
115
117
  // Animate through the frames.
@@ -192,8 +194,10 @@ SC.mixin(SC.View,
192
194
  { value: finalValue, duration: duration, timing: 'ease-in' }
193
195
  ];
194
196
 
195
- var callback = function () {
196
- view.didTransitionOut();
197
+ var callback = function (data) {
198
+ if (!data.isCancelled) {
199
+ view.didTransitionOut();
200
+ }
197
201
  };
198
202
 
199
203
  // Animate through the frames.
@@ -335,8 +335,11 @@ SC.InlineTextFieldView = SC.TextFieldView.extend(SC.InlineEditor,
335
335
  */
336
336
  // TODO: this seems to do almost the same thing as fieldDidBlur
337
337
  blurEditor: function(evt) {
338
- if (!this.get('isEditing')) return YES ;
339
- return this.commitOnBlur ? this.commitEditing() : this.discardEditing();
338
+ if (this.get('isEditing')) {
339
+ return this.commitOnBlur ? this.commitEditing() : this.discardEditing();
340
+ } else {
341
+ return true;
342
+ }
340
343
  },
341
344
 
342
345
  /**
@@ -436,7 +439,6 @@ SC.InlineTextFieldView = SC.TextFieldView.extend(SC.InlineEditor,
436
439
  insertTab: function(evt) {
437
440
  var target = this.target; // removed by commitEditing()
438
441
  this.resignFirstResponder();
439
- this.commitEditing() ;
440
442
  if(target){
441
443
  var next = target.get('nextValidKeyView');
442
444
  if(next && next.beginEditing) next.beginEditing();
@@ -448,7 +450,6 @@ SC.InlineTextFieldView = SC.TextFieldView.extend(SC.InlineEditor,
448
450
  insertBacktab: function(evt) {
449
451
  var target = this.target; // removed by commitEditing()
450
452
  this.resignFirstResponder();
451
- this.commitEditing() ;
452
453
  if(target){
453
454
  var prev = target.get('previousValidKeyView');
454
455
  if(prev && prev.beginEditing) prev.beginEditing();
@@ -59,7 +59,7 @@ window.SproutCore = window.SproutCore || SC;
59
59
  */
60
60
  SC = window.SC; // This is dumb but necessary for jsdoc to get it right
61
61
 
62
- SC.VERSION = '1.10.2';
62
+ SC.VERSION = '1.10.3';
63
63
 
64
64
  /**
65
65
  @private
@@ -1179,7 +1179,7 @@ SC.Observable = /** @scope SC.Observable.prototype */{
1179
1179
  // observer during that round of notification. Similarly, if you're
1180
1180
  // added as an observer during the notification round by another
1181
1181
  // observer, you will not be notified until the next time.)
1182
- members = SC.clone(observers.getMembers());
1182
+ members = observers.getMembers();
1183
1183
  membersLength = members.length;
1184
1184
  for (memberLoc = 0; memberLoc < membersLength; memberLoc++) {
1185
1185
  member = members[memberLoc];
@@ -1224,7 +1224,7 @@ SC.Observable = /** @scope SC.Observable.prototype */{
1224
1224
  if (starObservers && key !== '*') {
1225
1225
  // We clone the structure per the justification, above, for regular
1226
1226
  // observers.
1227
- members = SC.clone(starObservers.getMembers());
1227
+ members = starObservers.getMembers();
1228
1228
  membersLength = members.length;
1229
1229
  for (memberLoc = 0; memberLoc < membersLength; memberLoc++) {
1230
1230
  member = members[memberLoc];
@@ -267,6 +267,12 @@ SC.Binding = /** @scope SC.Binding.prototype */{
267
267
  beget: function (fromPath) {
268
268
  var ret = SC.beget(this);
269
269
  ret.parentBinding = this;
270
+ // Logic gates must be recreated on beget.
271
+ if (ret._LogicGate) {
272
+ ret._logicGate = ret._LogicGate.create(ret._logicGateHash);
273
+ ret = ret.from('logicProperty', ret._logicGate).oneWay();
274
+ }
275
+ // Enables duplicate API calls for SC.Binding.beget and SC.Binding.from
270
276
  if (fromPath !== undefined) ret = ret.from(fromPath);
271
277
  return ret;
272
278
  },
@@ -471,8 +477,13 @@ SC.Binding = /** @scope SC.Binding.prototype */{
471
477
  // Mark it destroyed.
472
478
  this.isDestroyed = YES;
473
479
 
474
- // Destroy the logic gate, if any. (See and & or methods.)
475
- if (this._logicGate) this._logicGate.destroy();
480
+ // Clean up the logic gate, if any. (See logic gate methods.)
481
+ if (this._logicGate) {
482
+ this._logicGate.destroy();
483
+ this._logicGate = null;
484
+ this._LogicGate = null;
485
+ this._logicGateHash = null;
486
+ }
476
487
 
477
488
  // Disconnect the binding.
478
489
  this.disconnect();
@@ -494,6 +505,20 @@ SC.Binding = /** @scope SC.Binding.prototype */{
494
505
  fromPropertyDidChange: function (target, key) {
495
506
  var v = target ? target.get(key) : null;
496
507
 
508
+ // In rare circumstances, getting a property can result in observers firing,
509
+ // which may in turn run code that disconnects the binding. The cause of
510
+ // this pattern has been difficult to determine and so until a concrete test
511
+ // scenario and a lower level fix can be found, show a warning and ignore
512
+ // the update.
513
+ if (!this.isConnected) {
514
+ //@if(debug)
515
+ SC.Logger.warn("Developer Warning: A binding attempted to update after it was disconnected. The update will be ignored for binding: %@".fmt(this._fromPropertyPath, this._fromTarget, this));
516
+ //@endif
517
+
518
+ // Break early.
519
+ return;
520
+ }
521
+
497
522
  // if the new value is different from the current binding value, then
498
523
  // schedule to register an update.
499
524
  if (v !== this._bindingValue || key === '[]') {
@@ -523,6 +548,20 @@ SC.Binding = /** @scope SC.Binding.prototype */{
523
548
 
524
549
  var v = target.get(key);
525
550
 
551
+ // In rare circumstances, getting a property can result in observers firing,
552
+ // which may in turn run code that disconnects the binding. The cause of
553
+ // this pattern has been difficult to determine and so until a concrete test
554
+ // scenario and a lower level fix can be found, show a warning and ignore
555
+ // the update.
556
+ if (!this.isConnected) {
557
+ //@if(debug)
558
+ SC.Logger.warn("Developer Warning: A binding attempted to update after it was disconnected. The update will be ignored for binding: %@".fmt(this));
559
+ //@endif
560
+
561
+ // Break early.
562
+ return;
563
+ }
564
+
526
565
  // if the new value is different from the current binding value, then
527
566
  // schedule to register an update.
528
567
  if (v !== this._transformedBindingValue) {
@@ -773,7 +812,7 @@ SC.Binding = /** @scope SC.Binding.prototype */{
773
812
  if (tuple) {
774
813
  this._toTarget = tuple[0];
775
814
  this._toPropertyKey = tuple[1];
776
- // Hook up _logicGate if needed (see and & or methods).
815
+ // Hook up _logicGate if needed (see logic gate methods).
777
816
  if (this._logicGate) {
778
817
  this._logicGate.set('localObject', this._toTarget);
779
818
  }
@@ -978,112 +1017,117 @@ SC.Binding = /** @scope SC.Binding.prototype */{
978
1017
  },
979
1018
 
980
1019
  /**
981
- Adds a transform that forwards the logical 'AND' of values at 'pathA' and
982
- 'pathB' whenever either source changes. Note that the transform acts strictly
983
- as a one-way binding, working only in the direction
984
-
985
- 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' && 'pathB'))
1020
+ Adds a transform to convert the value to the inverse of a bool value. This
1021
+ uses the same transform as bool() but inverts it.
986
1022
 
987
- Usage example where a delete button's 'isEnabled' value is determined by whether
988
- something is selected in a list and whether the current user is allowed to delete:
1023
+ @param {String} [fromPath]
1024
+ @returns {SC.Binding} this
1025
+ */
1026
+ not: function (fromPath) {
1027
+ return this.from(fromPath).transform(function (v) {
1028
+ var t = SC.typeOf(v);
1029
+ if (t === SC.T_ERROR) return v;
1030
+ return !((t == SC.T_ARRAY) ? (v.length > 0) : (v === '') ? NO : !!v);
1031
+ });
1032
+ },
989
1033
 
990
- deleteButton: SC.ButtonView.design({
991
- isEnabledBinding: SC.Binding.and('MyApp.itemsController.hasSelection', 'MyApp.userController.canDelete')
992
- })
1034
+ /**
1035
+ Adds a transform that will return YES if the value is null or undefined, NO otherwise.
993
1036
 
994
- @param {String} pathA The first part of the conditional
995
- @param {String} pathB The second part of the conditional
1037
+ @param {String} [fromPath]
1038
+ @returns {SC.Binding} this
996
1039
  */
997
- and: function (pathA, pathB) {
1040
+ isNull: function (fromPath) {
1041
+ return this.from(fromPath).transform(function (v) {
1042
+ var t = SC.typeOf(v);
1043
+ return (t === SC.T_ERROR) ? v : SC.none(v);
1044
+ });
1045
+ },
998
1046
 
999
- // If either path is local, append the localObject path to it.
1047
+ /* @private Used with the logic gate bindings. */
1048
+ _LogicGateAnd: SC.Object.extend({
1049
+ logicProperty: function() {
1050
+ return (this.get('valueA') && this.get('valueB'));
1051
+ }.property('valueA', 'valueB').cacheable()
1052
+ }),
1053
+ /* @private Used with the logic gate bindings. */
1054
+ _LogicGateOr: SC.Object.extend({
1055
+ logicProperty: function() {
1056
+ return (this.get('valueA') || this.get('valueB'));
1057
+ }.property('valueA', 'valueB').cacheable()
1058
+ }),
1059
+ /* @private Used by logic gate bindings. */
1060
+ _logicGateBinding: function (gateClass, pathA, pathB) {
1061
+ // If either path is local, remove any * chains and append the localObject path to it.
1000
1062
  if (pathA.indexOf('*') === 0 || pathA.indexOf('.') === 0) {
1001
- pathA = '*localObject.' + pathA.slice(1);
1063
+ pathA = pathA.slice(1).replace(/\*/g, '.');
1064
+ pathA = '*localObject.' + pathA;
1002
1065
  }
1003
1066
  if (pathB.indexOf('*') === 0 || pathB.indexOf('.') === 0) {
1004
- pathB = '*localObject.' + pathB.slice(1);
1067
+ pathB = pathB.slice(1).replace(/\*/g, '.');
1068
+ pathB = '*localObject.' + pathB;
1005
1069
  }
1006
1070
 
1007
- // create an object to do the logical computation
1008
- var gate = SC.Object.create({
1009
- localObject: null,
1010
-
1011
- valueABinding: SC.Binding.oneWay(pathA),
1012
- valueBBinding: SC.Binding.oneWay(pathB),
1013
-
1014
- and: function () {
1015
- return (this.get('valueA') && this.get('valueB'));
1016
- }.property('valueA', 'valueB').cacheable()
1017
- });
1018
-
1019
- // add a transform that depends on the result of that computation.
1020
- var ret = this.from('and', gate).oneWay();
1071
+ // Gets the gate class and instantiates a nice copy.
1072
+ var gateHash = {
1073
+ localObject: null,
1074
+ valueABinding: SC.Binding.oneWay(pathA),
1075
+ valueBBinding: SC.Binding.oneWay(pathB)
1076
+ },
1077
+ gate = gateClass.create(gateHash);
1078
+
1079
+ // Creates and populates the return binding.
1080
+ var ret = this.from('logicProperty', gate).oneWay();
1081
+ // This is all needed later on by beget, which must create a new logic gate instance
1082
+ // or risk bad behavior.
1083
+ ret._LogicGate = gateClass;
1084
+ ret._logicGateHash = gateHash;
1021
1085
  ret._logicGate = gate;
1086
+
1087
+ // On our way.
1022
1088
  return ret;
1023
1089
  },
1024
1090
 
1025
1091
  /**
1026
- Adds a transform that forwards the 'OR' of values at 'pathA' and
1092
+ Adds a transform that forwards the logical 'AND' of values at 'pathA' and
1027
1093
  'pathB' whenever either source changes. Note that the transform acts strictly
1028
1094
  as a one-way binding, working only in the direction
1029
1095
 
1030
- 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' || 'pathB'))
1096
+ 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' && 'pathB'))
1097
+
1098
+ Usage example where a delete button's 'isEnabled' value is determined by whether
1099
+ something is selected in a list and whether the current user is allowed to delete:
1100
+
1101
+ deleteButton: SC.ButtonView.design({
1102
+ isEnabledBinding: SC.Binding.and('MyApp.itemsController.hasSelection', 'MyApp.userController.canDelete')
1103
+ })
1031
1104
 
1032
1105
  @param {String} pathA The first part of the conditional
1033
1106
  @param {String} pathB The second part of the conditional
1034
1107
  */
1035
- or: function (pathA, pathB) {
1036
- // If either path is local, append the localObject path to it.
1037
- if (pathA.indexOf('*') === 0 || pathA.indexOf('.') === 0) {
1038
- pathA = '*localObject.' + pathA.slice(1);
1039
- }
1040
- if (pathB.indexOf('*') === 0 || pathB.indexOf('.') === 0) {
1041
- pathB = '*localObject.' + pathB.slice(1);
1042
- }
1043
-
1044
- // create an object to the logical computation
1045
- var gate = SC.Object.create({
1046
- localObject: null,
1047
-
1048
- valueABinding: pathA,
1049
- valueBBinding: pathB,
1050
-
1051
- or: function () {
1052
- return (this.get('valueA') || this.get('valueB'));
1053
- }.property('valueA', 'valueB').cacheable()
1054
- });
1055
-
1056
- var ret = this.from('or', gate).oneWay();
1057
- ret._logicGate = gate;
1058
- return ret;
1108
+ and: function (pathA, pathB) {
1109
+ return this._logicGateBinding(this._LogicGateAnd, pathA, pathB);
1059
1110
  },
1060
1111
 
1061
1112
  /**
1062
- Adds a transform to convert the value to the inverse of a bool value. This
1063
- uses the same transform as bool() but inverts it.
1113
+ Adds a transform that forwards the 'OR' of values at 'pathA' and
1114
+ 'pathB' whenever either source changes. Note that the transform acts strictly
1115
+ as a one-way binding, working only in the direction
1064
1116
 
1065
- @param {String} [fromPath]
1066
- @returns {SC.Binding} this
1067
- */
1068
- not: function (fromPath) {
1069
- return this.from(fromPath).transform(function (v) {
1070
- var t = SC.typeOf(v);
1071
- if (t === SC.T_ERROR) return v;
1072
- return !((t == SC.T_ARRAY) ? (v.length > 0) : (v === '') ? NO : !!v);
1073
- });
1074
- },
1117
+ 'pathA' OR 'pathB' --> value (value returned is the result of ('pathA' || 'pathB'))
1075
1118
 
1076
- /**
1077
- Adds a transform that will return YES if the value is null or undefined, NO otherwise.
1119
+ Usage example where a delete button's 'isEnabled' value is determined by if the
1120
+ content is editable, or if the user has admin rights:
1078
1121
 
1079
- @param {String} [fromPath]
1080
- @returns {SC.Binding} this
1122
+ deleteButton: SC.ButtonView.design({
1123
+ isEnabledBinding: SC.Binding.or('*content.isEditable', 'MyApp.userController.isAdmin')
1124
+ })
1125
+
1126
+ @param {String} pathA The first part of the conditional
1127
+ @param {String} pathB The second part of the conditional
1081
1128
  */
1082
- isNull: function (fromPath) {
1083
- return this.from(fromPath).transform(function (v) {
1084
- var t = SC.typeOf(v);
1085
- return (t === SC.T_ERROR) ? v : SC.none(v);
1086
- });
1129
+ or: function (pathA, pathB) {
1130
+ return this._logicGateBinding(this._LogicGateOr, pathA, pathB);
1087
1131
  },
1088
1132
 
1089
1133
  toString: function () {