sproutcore 1.10.1 → 1.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG +13 -0
  3. data/VERSION.yml +1 -1
  4. data/lib/frameworks/sproutcore/CHANGELOG.md +69 -31
  5. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +14 -0
  6. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/object.js +14 -0
  7. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +7 -2
  8. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +13 -9
  9. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +57 -23
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/enabled_states_test.js +24 -6
  11. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +2 -2
  12. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/enabled.js +63 -13
  13. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +3 -3
  14. data/lib/frameworks/sproutcore/frameworks/datastore/models/single_attribute.js +7 -1
  15. data/lib/frameworks/sproutcore/frameworks/datastore/system/many_array.js +28 -5
  16. data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +15 -0
  17. data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +30 -3
  18. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +23 -1
  19. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/many_attribute.js +135 -89
  20. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/single_attribute.js +12 -0
  21. data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +18 -6
  22. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/picker/ui.js +58 -20
  23. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/date_field/methods.js +1 -1
  24. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/select/methods.js +15 -1
  25. data/lib/frameworks/sproutcore/frameworks/desktop/views/button.js +1 -1
  26. data/lib/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +10 -0
  27. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +1 -1
  28. data/lib/frameworks/sproutcore/frameworks/desktop/views/select.js +24 -23
  29. data/lib/frameworks/sproutcore/frameworks/desktop/views/split.js +4 -0
  30. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/views/popup_button.js +10 -0
  31. data/lib/frameworks/sproutcore/frameworks/foundation/delegates/inline_text_field.js +4 -4
  32. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/auto_mixin.js +33 -16
  33. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/content_value_support.js +14 -6
  34. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/control.js +23 -18
  35. data/lib/frameworks/sproutcore/frameworks/foundation/system/user_defaults.js +4 -4
  36. data/lib/frameworks/sproutcore/frameworks/foundation/tests/delegates/inline_text_field/inline_text_field.js +1 -0
  37. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/auto_mixin_tests.js +78 -0
  38. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/auto_resize_test.js +45 -1
  39. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/content_value_support/content.js +112 -58
  40. data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/image_queue.js +2 -2
  41. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/container/transition_test.js +141 -0
  42. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/methods.js +27 -2
  43. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +631 -593
  44. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/swap_fade_color_transition.js +5 -0
  45. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/swap_move_in_transition.js +5 -0
  46. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/swap_reveal_transition.js +68 -1
  47. data/lib/frameworks/sproutcore/frameworks/foundation/views/container.js +128 -49
  48. data/lib/frameworks/sproutcore/frameworks/foundation/views/field.js +33 -8
  49. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +209 -187
  50. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  51. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +7 -0
  52. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +34 -4
  53. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +0 -2
  54. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/binding.js +68 -9
  55. data/lib/frameworks/sproutcore/frameworks/testing/system/runner.js +2 -1
  56. data/lib/sproutcore/rack/builder.rb +45 -25
  57. data/sproutcore.gemspec +1 -0
  58. metadata +17 -2
@@ -29,12 +29,21 @@ SC.CoreView.mixin(
29
29
 
30
30
  /**
31
31
  The view is enabled itself, but is effectively disabled in the pane due to
32
- a disabled parent view.
32
+ a disabled ancestor.
33
33
 
34
34
  @static
35
35
  @constant
36
36
  */
37
- DISABLED_BY_PARENT: 0x12 // 18
37
+ DISABLED_BY_PARENT: 0x12, // 18
38
+
39
+ /**
40
+ The view is disabled itself and is also disabled in the pane due to
41
+ a disabled ancestor.
42
+
43
+ @static
44
+ @constant
45
+ */
46
+ DISABLED_AND_BY_PARENT: 0x13 // 19
38
47
 
39
48
  });
40
49
 
@@ -112,11 +121,16 @@ SC.View.reopen(
112
121
 
113
122
  /** @private */
114
123
  _doEnable: function () {
115
- var handled = true;
124
+ var handled = true,
125
+ enabledState = this.get('enabledState');
116
126
 
117
- if (this.get('enabledState') & SC.CoreView.IS_DISABLED) {
127
+ if (enabledState === SC.CoreView.DISABLED) {
128
+ // If the view itself is disabled, then we can enable it.
118
129
  this._callOnChildViews('_parentDidEnableInPane');
119
130
  this._gotoEnabledState();
131
+ } else if (enabledState === SC.CoreView.DISABLED_AND_BY_PARENT) {
132
+ // The view is no longer disabled itself, but still disabled by an ancestor.
133
+ this._gotoDisabledByParentState();
120
134
  } else {
121
135
  handled = false;
122
136
  }
@@ -126,15 +140,20 @@ SC.View.reopen(
126
140
 
127
141
  /** @private */
128
142
  _doDisable: function () {
129
- var handled = true;
143
+ var handled = true,
144
+ enabledState = this.get('enabledState');
130
145
 
131
- if (this.get('enabledState') !== SC.CoreView.DISABLED) {
146
+ // If the view is not itself disabled, then we can disable it.
147
+ if (enabledState === SC.CoreView.ENABLED) {
132
148
  if (this.get('isFirstResponder')) {
133
149
  this.resignFirstResponder();
134
150
  }
135
151
 
136
152
  this._callOnChildViews('_parentDidDisableInPane');
137
153
  this._gotoDisabledState();
154
+ } else if (enabledState === SC.CoreView.DISABLED_BY_PARENT) {
155
+ // The view is now disabled itself and disabled by an ancestor.
156
+ this._gotoDisabledAndByParentState();
138
157
  } else {
139
158
  handled = false;
140
159
  }
@@ -146,6 +165,14 @@ SC.View.reopen(
146
165
  // Methods
147
166
  //
148
167
 
168
+ /** @private */
169
+ init: function (original) {
170
+ original();
171
+
172
+ // If the view is pre-configured as disabled, then go to the proper initial state.
173
+ if (!this.get('isEnabled')) { this._doDisable(); }
174
+ }.enhance(),
175
+
149
176
  /** @private
150
177
  Observes the isEnabled property and resigns first responder if set to NO.
151
178
  This will avoid cases where, for example, a disabled text field retains
@@ -206,10 +233,18 @@ SC.View.reopen(
206
233
 
207
234
  /** @private */
208
235
  _parentDidEnableInPane: function () {
209
- var isEnabled = this.get('isEnabled');
236
+ var enabledState = this.get('enabledState');
210
237
 
211
- if (isEnabled) {
212
- this._gotoEnabledState();
238
+ if (this.get('shouldInheritEnabled')) {
239
+
240
+ if (enabledState === SC.CoreView.DISABLED_BY_PARENT) { // Was enabled before.
241
+ this._gotoEnabledState();
242
+ } else if (enabledState === SC.CoreView.DISABLED_AND_BY_PARENT) { // Was disabled before.
243
+ this._gotoDisabledState();
244
+
245
+ // There's no need to continue to further child views.
246
+ return false;
247
+ }
213
248
  } else {
214
249
  // There's no need to continue to further child views.
215
250
  return false;
@@ -218,10 +253,19 @@ SC.View.reopen(
218
253
 
219
254
  /** @private */
220
255
  _parentDidDisableInPane: function () {
221
- var isEnabled = this.get('isEnabled');
256
+ var enabledState = this.get('enabledState');
257
+
258
+ if (this.get('shouldInheritEnabled')) {
222
259
 
223
- if (isEnabled && this.get('shouldInheritEnabled')) {
224
- this._gotoDisableByParentState();
260
+ if (enabledState === SC.CoreView.ENABLED) { // Was enabled.
261
+ this._gotoDisabledByParentState();
262
+ } else if (enabledState === SC.CoreView.DISABLED) { // Was disabled.
263
+ this._gotoDisabledAndByParentState();
264
+ } else { // Was already disabled by ancestor.
265
+
266
+ // There's no need to continue to further child views.
267
+ return false;
268
+ }
225
269
  } else {
226
270
  // There's no need to continue to further child views.
227
271
  return false;
@@ -249,7 +293,13 @@ SC.View.reopen(
249
293
  },
250
294
 
251
295
  /** @private */
252
- _gotoDisableByParentState: function () {
296
+ _gotoDisabledAndByParentState: function () {
297
+ // Update the state.
298
+ this.set('enabledState', SC.CoreView.DISABLED_AND_BY_PARENT);
299
+ },
300
+
301
+ /** @private */
302
+ _gotoDisabledByParentState: function () {
253
303
  // Update the state.
254
304
  this.set('enabledState', SC.CoreView.DISABLED_BY_PARENT);
255
305
  }
@@ -42,13 +42,13 @@ SC.Record = SC.Object.extend(
42
42
 
43
43
  //@if(debug)
44
44
  /* BEGIN DEBUG ONLY PROPERTIES AND METHODS */
45
+
45
46
  /** @private
46
47
  Creates string representation of record, with status.
47
48
 
48
49
  @returns {String}
49
50
  */
50
-
51
- toString: function() {
51
+ toString: function () {
52
52
  // We won't use 'readOnlyAttributes' here because accessing them directly
53
53
  // avoids a SC.clone() -- we'll be careful not to edit anything.
54
54
  var attrs = this.get('store').readDataHash(this.get('storeKey'));
@@ -61,7 +61,7 @@ SC.Record = SC.Object.extend(
61
61
  @returns {String}
62
62
  */
63
63
 
64
- statusString: function() {
64
+ statusString: function () {
65
65
  var ret = [], status = this.get('status');
66
66
 
67
67
  for(var prop in SC.Record) {
@@ -59,10 +59,16 @@ SC.SingleAttribute = SC.RecordAttribute.extend(
59
59
  if (newRec !== undefined && this.get('isEditable')) {
60
60
 
61
61
  // can only take other records or null
62
+ //@if(debug)
62
63
  if (newRec && !SC.kindOf(newRec, SC.Record)) {
63
- throw new Error("%@ is not an instance of SC.Record".fmt(newRec));
64
+ throw new Error("Developer Error: %@ is not an instance of SC.Record.".fmt(newRec));
64
65
  }
65
66
 
67
+ if (newRec && SC.none(newRec.get('id'))) {
68
+ throw new Error("Developer Error: Attempted to add a record without a primary key to a to-one relationship. Relationships require that the id always be specified. The record, \"%@\", must be assigned an id (i.e. be saved) before it can be used in the '%@' relationship.".fmt(newRec, attrKey));
69
+ }
70
+ //@endif
71
+
66
72
  inverseKey = this.get('inverse');
67
73
  if (inverseKey) oldRec = this._scsa_call(record, key);
68
74
 
@@ -23,6 +23,20 @@
23
23
  SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
24
24
  /** @scope SC.ManyArray.prototype */ {
25
25
 
26
+ //@if(debug)
27
+ /* BEGIN DEBUG ONLY PROPERTIES AND METHODS */
28
+
29
+ /* @private */
30
+ toString: function () {
31
+ var readOnlyStoreIds = this.get('readOnlyStoreIds'),
32
+ length = this.get('length');
33
+
34
+ return "%@({\n ids: [%@],\n length: %@,\n … })".fmt(this.constructor.toString(), readOnlyStoreIds, length);
35
+ },
36
+
37
+ /* END DEBUG ONLY PROPERTIES AND METHODS */
38
+ //@endif
39
+
26
40
  /**
27
41
  `recordType` will tell what type to transform the record to when
28
42
  materializing the record.
@@ -222,10 +236,11 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
222
236
  records, which can be converted to `storeId`s.
223
237
  */
224
238
  replace: function(idx, amt, recs) {
225
-
239
+ //@if(debug)
226
240
  if (!this.get('isEditable')) {
227
- throw new Error("%@.%@[] is not editable".fmt(this.get('record'), this.get('propertyName')));
241
+ throw new Error("Developer Error: %@.%@[] is not editable.".fmt(this.get('record'), this.get('propertyName')));
228
242
  }
243
+ //@endif
229
244
 
230
245
  var storeIds = this.get('editableStoreIds'),
231
246
  len = recs ? (recs.get ? recs.get('length') : recs.length) : 0,
@@ -235,12 +250,20 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
235
250
 
236
251
  // map to store keys
237
252
  ids = [] ;
238
- for(i=0;i<len;i++) ids[i] = recs.objectAt(i).get('id');
253
+ for(i=0;i<len;i++) {
254
+ //@if(debug)
255
+ if (SC.none(recs.objectAt(i).get('id'))) {
256
+ throw new Error("Developer Error: Attempted to add a record without a primary key to a to-many relationship. Relationships require that the id always be specified. The record, \"%@\", must be assigned an id (i.e. be saved) before it can be used in the '%@' relationship.".fmt(recs.objectAt(i), pname));
257
+ }
258
+ //@endif
259
+
260
+ ids[i] = recs.objectAt(i).get('id');
261
+ }
239
262
 
240
263
  // if we have an inverse - collect the list of records we are about to
241
264
  // remove
242
265
  inverse = this.get('inverse');
243
- if (inverse && amt>0) {
266
+ if (inverse && amt > 0) {
244
267
  toRemove = SC.ManyArray._toRemove;
245
268
  if (toRemove) SC.ManyArray._toRemove = null; // reuse if possible
246
269
  else toRemove = [];
@@ -456,4 +479,4 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
456
479
  this.recordPropertyDidChange();
457
480
  }
458
481
 
459
- }) ;
482
+ });
@@ -139,6 +139,21 @@ sc_require('models/record');
139
139
  SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
140
140
  /** @scope SC.Query.prototype */ {
141
141
 
142
+ //@if(debug)
143
+ /* BEGIN DEBUG ONLY PROPERTIES AND METHODS */
144
+
145
+ /* @private */
146
+ toString: function () {
147
+ var conditions = this.get('conditions'),
148
+ location = this.get('location'),
149
+ parameters = this.get('parameters');
150
+
151
+ return "%@.%@({ conditions: '%@', parameters: %@, … })".fmt(this.constructor.toString(), location, conditions, SC.inspect(parameters));
152
+ },
153
+
154
+ /* END DEBUG ONLY PROPERTIES AND METHODS */
155
+ //@endif
156
+
142
157
  // ..........................................................
143
158
  // PROPERTIES
144
159
  //
@@ -56,6 +56,35 @@ sc_require('models/record');
56
56
  SC.RecordArray = SC.Object.extend(SC.Enumerable, SC.Array,
57
57
  /** @scope SC.RecordArray.prototype */ {
58
58
 
59
+ //@if(debug)
60
+ /* BEGIN DEBUG ONLY PROPERTIES AND METHODS */
61
+
62
+ /* @private */
63
+ toString: function () {
64
+ var statusString = this.statusString(),
65
+ storeKeys = this.get('storeKeys'),
66
+ query = this.get('query'),
67
+ length = this.get('length');
68
+
69
+ return "%@({\n query: %@,\n storeKeys: [%@],\n length: %@,\n … }) %@".fmt(this.constructor.toString(), query, storeKeys, length, statusString);
70
+ },
71
+
72
+ /** @private */
73
+ statusString: function() {
74
+ var ret = [], status = this.get('status');
75
+
76
+ for (var prop in SC.Record) {
77
+ if (prop.match(/[A-Z_]$/) && SC.Record[prop] === status) {
78
+ ret.push(prop);
79
+ }
80
+ }
81
+
82
+ return ret.join(" ");
83
+ },
84
+
85
+ /* END DEBUG ONLY PROPERTIES AND METHODS */
86
+ //@endif
87
+
59
88
  /**
60
89
  The store that owns this record array. All record arrays must have a
61
90
  store to function properly.
@@ -676,9 +705,7 @@ SC.RecordArray = SC.Object.extend(SC.Enumerable, SC.Array,
676
705
  _storeKeysDidChange: function() {
677
706
  var storeKeys = this.get('storeKeys');
678
707
 
679
- var prev = this._prevStoreKeys, oldLen, newLen,
680
- f = this._storeKeysContentDidChange,
681
- fs = this._storeKeysStateDidChange;
708
+ var prev = this._prevStoreKeys, oldLen, newLen;
682
709
 
683
710
  if (storeKeys === prev) { return; } // nothing to do
684
711
 
@@ -1057,7 +1057,11 @@ SC.Store = SC.Object.extend( /** @scope SC.Store.prototype */ {
1057
1057
  @returns {SC.Record} Returns a record instance.
1058
1058
  */
1059
1059
  materializeRecord: function (storeKey) {
1060
- var records = this.records, ret, recordType, attrs;
1060
+ var records = this.records,
1061
+ //@if(debug)
1062
+ updatingRecords = this.updatingRecords,
1063
+ //@endif
1064
+ ret, recordType, attrs;
1061
1065
 
1062
1066
  // look up in cached records
1063
1067
  if (!records) records = this.records = {}; // load cached records
@@ -1071,8 +1075,26 @@ SC.Store = SC.Object.extend( /** @scope SC.Store.prototype */ {
1071
1075
  attrs = this._TMP_REC_ATTRS ;
1072
1076
  attrs.storeKey = storeKey ;
1073
1077
  attrs.store = this ;
1078
+
1079
+ //@if(debug)
1080
+ // Add some developer support to prevent a tough to diagnose bug that if
1081
+ // materializeRecord is called during the creation of a record, the store
1082
+ // will inadvertently create a duplicate record instance not because the
1083
+ // actual instance won't have been cached to this.records yet.
1084
+ if (!this.updatingRecords) { updatingRecords = this.updatingRecords = {}; }
1085
+ if (updatingRecords[storeKey]) {
1086
+ throw new Error("Developer Error: The record of type, %@, with storeKey, %@, was materialized a second time before the first call had finished. This will result in two separate instances of the same object being created and should be fixed. A likely cause is using `.observes` code in the record class, which can cause the record to be retrieved somehow while it is still being created.".fmt(recordType, storeKey));
1087
+ }
1088
+
1089
+ updatingRecords[storeKey] = storeKey;
1090
+ //@endif
1091
+
1074
1092
  ret = records[storeKey] = recordType.create(attrs);
1075
1093
 
1094
+ //@if(debug)
1095
+ updatingRecords[storeKey] = null;
1096
+ //@endif
1097
+
1076
1098
  return ret ;
1077
1099
  },
1078
1100