sproutcore 1.4.2 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/CHANGELOG +11 -0
  2. data/Rakefile +27 -436
  3. data/VERSION.yml +1 -1
  4. data/lib/frameworks/sproutcore/CHANGELOG +32 -2
  5. data/lib/frameworks/sproutcore/frameworks/animation/core.js +1 -1
  6. data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +8 -2
  7. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record.js +42 -2
  8. data/lib/frameworks/sproutcore/frameworks/debug/invoke_once_last_debugging.js +7 -12
  9. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/radio/ui.js +22 -1
  10. data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +1 -1
  11. data/lib/frameworks/sproutcore/frameworks/desktop/views/list.js +6 -2
  12. data/lib/frameworks/sproutcore/frameworks/desktop/views/list_item.js +1 -1
  13. data/lib/frameworks/sproutcore/frameworks/desktop/views/radio.js +38 -10
  14. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroller.js +8 -8
  15. data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +1 -1
  16. data/lib/frameworks/sproutcore/frameworks/desktop/views/select_field.js +12 -5
  17. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/button.js +3 -3
  18. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/inline_text_field.js +14 -9
  19. data/lib/frameworks/sproutcore/frameworks/foundation/system/datetime.js +3 -1
  20. data/lib/frameworks/sproutcore/frameworks/foundation/system/root_responder.js +25 -17
  21. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/inline_text_field/beginEditing.js +46 -36
  22. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/validatable/ui.js +1 -1
  23. data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/datetime.js +5 -0
  24. data/lib/frameworks/sproutcore/frameworks/foundation/tests/validators/not_empty.js +56 -0
  25. data/lib/frameworks/sproutcore/frameworks/foundation/validators/date_time.js +1 -1
  26. data/lib/frameworks/sproutcore/frameworks/foundation/validators/not_empty.js +7 -3
  27. data/lib/frameworks/sproutcore/frameworks/foundation/views/label.js +1 -1
  28. data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +1 -1
  29. data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +1 -1
  30. data/lib/frameworks/sproutcore/frameworks/media/views/media_slider.js +1 -1
  31. data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +1 -1
  32. data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +1 -1
  33. data/lib/frameworks/sproutcore/frameworks/media/views/video.js +1 -1
  34. data/lib/frameworks/sproutcore/frameworks/runtime/private/observer_set.js +1 -1
  35. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +18 -23
  36. data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/observable.js +13 -0
  37. data/lib/sproutcore/builders/base.rb +4 -4
  38. data/lib/sproutcore/builders/javascript.rb +0 -7
  39. data/lib/sproutcore/builders/stylesheet.rb +0 -7
  40. data/lib/sproutcore/rack/proxy.rb +28 -13
  41. metadata +4 -4
  42. data/DISTRIBUTION.yml +0 -20
@@ -10,7 +10,8 @@
10
10
  htmlbody('<style> .sc-static-layout { border: 1px red dotted; } </style>');
11
11
 
12
12
  var itemList = [{ title: "Red", value: "red", enabled: YES }, { title: "Green", value: "green" }, { title: "Blue", value: "blue" }],
13
- itemList2 = [{ title: "Cyan", value: "cyan", enabled: YES }, { title: "Magenta", value: "magenta" }, { title: "Yellow", value: "yellow" },{ title: "blacK", value: "black"}];
13
+ itemList2 = [{ title: "Cyan", value: "cyan", enabled: YES }, { title: "Magenta", value: "magenta" }, { title: "Yellow", value: "yellow" },{ title: "blacK", value: "black"}],
14
+ itemList3 = [{ title: "Red", value: "red", enabled: YES, width: 30 }, { title: "Green", value: "green", width: 50 }, { title: "Blue", value: "blue", width: 40 }];
14
15
 
15
16
  var pane = SC.ControlTestPane.design()
16
17
  .add("basic", SC.RadioView, {
@@ -56,6 +57,16 @@ var pane = SC.ControlTestPane.design()
56
57
  items: 'Yes No'.w(),
57
58
  // LAYOUT_VERTICAL is default
58
59
  layoutDirection: SC.LAYOUT_HORIZONTAL
60
+ })
61
+
62
+ .add("horizontal widths", SC.RadioView, {
63
+ value: "",
64
+ isEnabled: YES,
65
+ items: itemList3,
66
+ layoutDirection: SC.LAYOUT_HORIZONTAL,
67
+ itemTitleKey: 'title',
68
+ itemValueKey: 'value',
69
+ itemWidthKey: 'width'
59
70
  });
60
71
 
61
72
  pane.show(); // add a test to show the test pane
@@ -230,3 +241,13 @@ test("enabled first", function() {
230
241
  });
231
242
  });
232
243
 
244
+ test("item widths", function() {
245
+ var radioButtons = pane.view('horizontal widths').$('.sc-radio-button'),
246
+ widths = [30, 50, 40],
247
+ idx = 0, radio;
248
+
249
+ radioButtons.forEach(function(elem) {
250
+ equals(SC.$(elem).width(), widths[idx], 'radio button %@ should width specified by itemWidthKey'.fmt(idx, widths[idx]));
251
+ idx++;
252
+ });
253
+ });
@@ -905,7 +905,7 @@ SC.CollectionView = SC.View.extend(
905
905
  // to reuse it.
906
906
  // (Charles Jolley personally guarantees this code)
907
907
  layer = existing.get('layer');
908
- layer.parentNode.removeChild(layer);
908
+ if (layer && layer.parentNode) layer.parentNode.removeChild(layer);
909
909
 
910
910
  containerView.removeChild(existing);
911
911
  }
@@ -217,7 +217,7 @@ SC.ListView = SC.CollectionView.extend(
217
217
  // prefill the cache with custom rows.
218
218
  cache = this._sclv_offsetCache;
219
219
  if (!cache) {
220
- cache = this._sclv_offsetCache = [];
220
+ cache = [];
221
221
  delta = max = 0 ;
222
222
  custom.forEach(function(idx) {
223
223
  delta += this.rowHeightForContentIndex(idx)-rowHeight;
@@ -225,6 +225,8 @@ SC.ListView = SC.CollectionView.extend(
225
225
  max = idx ;
226
226
  }, this);
227
227
  this._sclv_max = max+1;
228
+ // moved down so that the cache is not marked as initialized until it actually is
229
+ this._sclv_offsetCache = cache;
228
230
  }
229
231
 
230
232
  // now just get the delta for the last custom row before the current
@@ -259,11 +261,13 @@ SC.ListView = SC.CollectionView.extend(
259
261
  if (del.customRowHeightIndexes && (indexes=del.get('customRowHeightIndexes'))) {
260
262
  cache = this._sclv_heightCache ;
261
263
  if (!cache) {
262
- cache = this._sclv_heightCache = [];
264
+ cache = [];
263
265
  content = this.get('content');
264
266
  indexes.forEach(function(idx) {
265
267
  cache[idx] = del.contentIndexRowHeight(this, content, idx);
266
268
  }, this);
269
+ // moved down so that the cache is not marked as initialized until it actually is
270
+ this._sclv_heightCache = cache;
267
271
  }
268
272
 
269
273
  ret = cache[idx];
@@ -500,7 +500,7 @@ SC.ListItemView = SC.View.extend(
500
500
  */
501
501
  _isInsideRightIcon: function(evt) {
502
502
  var del = this.displayDelegate ;
503
- var rightIconKey = this.getDelegateProperty('hasContentRightIcon', del) ;
503
+ var rightIconKey = this.getDelegateProperty('hasContentRightIcon', del) || !SC.none(this.rightIcon);
504
504
  return rightIconKey && this._isInsideElementWithClassName('right-icon', evt);
505
505
  },
506
506
 
@@ -89,6 +89,17 @@ SC.RadioView = SC.View.extend(SC.Control,
89
89
  the title with this itemTitleKey property.
90
90
  */
91
91
  itemTitleKey: null,
92
+
93
+ /**
94
+ If items property is a hash, specify which property will function as
95
+ the item width with this itemWidthKey property. This is only used when
96
+ layoutDirection is set to SC.LAYOUT_HORIONZTAL and can be used to override
97
+ the default value provided by the framework or theme CSS.
98
+
99
+ @property {String}
100
+ @default null
101
+ */
102
+ itemWidthKey: null,
92
103
 
93
104
  /**
94
105
  If items property is a hash, specify which property will function as
@@ -142,9 +153,12 @@ SC.RadioView = SC.View.extend(SC.Control,
142
153
  displayProperties: ['value', '_displayItems'],
143
154
 
144
155
  render: function(context, firstTime) {
145
- var item, idx, icon, name, itemsLength, url, className, disabled, sel, labelText, selectionState, selectionStateClassNames, items = this.get('_displayItems'),
146
- value = this.get('value'),
147
- isArray = SC.isArray(value);
156
+ var items = this.get('_displayItems'),
157
+ value = this.get('value'),
158
+ isArray = SC.isArray(value),
159
+ item, idx, icon, name, width, itemsLength, url,
160
+ className, disabled, sel, labelText,
161
+ selectionState, selectionStateClassNames;
148
162
 
149
163
  context.addClass(this.get('layoutDirection'));
150
164
 
@@ -186,8 +200,11 @@ SC.RadioView = SC.View.extend(SC.Control,
186
200
 
187
201
  labelText = this.escapeHTML ? SC.RenderContext.escapeHTML(item[0]) : item[0];
188
202
 
189
- context.push('<div class="sc-radio-button ',
190
- selectionStateClassNames, '" ',
203
+ width = item[4];
204
+
205
+ context.push('<div class="sc-radio-button ',
206
+ selectionStateClassNames, '" ',
207
+ width ? 'style="width: ' + width + 'px;" ' : '',
191
208
  'aria-checked="', sel ? 'true':'false','" ',
192
209
  'role="radio"' , ' index="', idx,'">',
193
210
  '<span class="button"></span>',
@@ -209,6 +226,10 @@ SC.RadioView = SC.View.extend(SC.Control,
209
226
  } else {
210
227
  sel = NO;
211
228
  }
229
+
230
+ width = item[4];
231
+ if (width) button.width(width);
232
+
212
233
  selectionState = this._getSelectionStateClassNames(item, sel, value, isArray, true);
213
234
  button.attr('aria-checked', sel ? 'true': 'false');
214
235
  // set class of label
@@ -232,11 +253,14 @@ SC.RadioView = SC.View.extend(SC.Control,
232
253
  _displayItems: function() {
233
254
  var items = this.get('items'),
234
255
  loc = this.get('localize'),
235
- titleKey = this.get('itemTitleKey'), valueKey = this.get('itemValueKey'),
256
+ titleKey = this.get('itemTitleKey'),
257
+ valueKey = this.get('itemValueKey'),
258
+ widthKey = this.get('itemWidthKey'),
259
+ isHorizontal = this.get('layoutDirection') === SC.LAYOUT_HORIZONTAL,
236
260
  isEnabledKey = this.get('itemIsEnabledKey'),
237
261
  iconKey = this.get('itemIconKey'),
238
262
  ret = [], max = (items)? items.get('length') : 0,
239
- item, title, value, idx, isArray, isEnabled, icon;
263
+ item, title, width, value, idx, isArray, isEnabled, icon;
240
264
 
241
265
  for(idx=0;idx<max;idx++) {
242
266
  item = items.objectAt(idx);
@@ -253,7 +277,11 @@ SC.RadioView = SC.View.extend(SC.Control,
253
277
  if (titleKey) {
254
278
  title = item.get ? item.get(titleKey) : item[titleKey];
255
279
  } else title = (item.toString) ? item.toString() : null;
256
-
280
+
281
+ if (widthKey && isHorizontal) {
282
+ width = item.get ? item.get(widthKey) : item[widthKey];
283
+ }
284
+
257
285
  if (valueKey) {
258
286
  value = item.get ? item.get(valueKey) : item[valueKey];
259
287
  } else value = item;
@@ -274,11 +302,11 @@ SC.RadioView = SC.View.extend(SC.Control,
274
302
 
275
303
  // localize title if needed
276
304
  if (loc) title = title.loc();
277
- ret.push([title, value, isEnabled, icon]);
305
+ ret.push([title, value, isEnabled, icon, width]);
278
306
  }
279
307
 
280
308
  return ret; // done!
281
- }.property('items', 'itemTitleKey', 'itemValueKey', 'itemIsEnabledKey', 'localize', 'itemIconKey').cacheable(),
309
+ }.property('items', 'itemTitleKey', 'itemWidthKey', 'itemValueKey', 'itemIsEnabledKey', 'localize', 'itemIconKey').cacheable(),
282
310
 
283
311
  /** @private -
284
312
  Will figure out what class names to assign each radio button.
@@ -198,7 +198,7 @@ SC.ScrollerView = SC.View.extend(
198
198
  @private
199
199
  */
200
200
  render: function(context, firstTime) {
201
- var classNames = [],
201
+ var classNames = {},
202
202
  buttons = '',
203
203
  thumbPosition, thumbLength, thumbCenterLength, thumbElement,
204
204
  value, max, scrollerLength, length, pct;
@@ -207,21 +207,21 @@ SC.ScrollerView = SC.View.extend(
207
207
  // style them differently using CSS.
208
208
  switch (this.get('layoutDirection')) {
209
209
  case SC.LAYOUT_VERTICAL:
210
- classNames.push('sc-vertical');
210
+ classNames['sc-vertical'] = YES;
211
211
  break;
212
212
  case SC.LAYOUT_HORIZONTAL:
213
- classNames.push('sc-horizontal');
213
+ classNames['sc-horizontal'] = YES;
214
214
  break;
215
215
  }
216
216
 
217
217
  // The appearance of the scroller changes if disabled
218
- if (!this.get('isEnabled')) classNames.push('disabled');
218
+ classNames['disabled'] = !this.get('isEnabled');
219
219
  // Whether to hide the thumb and buttons
220
- if (this.get('controlsHidden')) classNames.push('controls-hidden');
220
+ classNames['controls-hidden'] = this.get('controlsHidden');
221
221
 
222
222
  // Change the class names of the DOM element all at once to improve
223
223
  // performance
224
- context.addClass(classNames);
224
+ context.setClass(classNames);
225
225
 
226
226
  // Calculate the position and size of the thumb
227
227
  thumbLength = this.get('thumbLength');
@@ -240,7 +240,7 @@ SC.ScrollerView = SC.View.extend(
240
240
  context.push('<div class="track"></div>',
241
241
  '<div class="cap"></div>',
242
242
  buttons,
243
- '<div class="thumb" style="height: '+thumbLength+'px;">',
243
+ '<div class="thumb" style="height: '+thumbLength+'px; top: ' + thumbPosition + 'px;">',
244
244
  '<div class="thumb-center"></div>',
245
245
  '<div class="thumb-top"></div>',
246
246
  '<div class="thumb-bottom"></div></div>');
@@ -249,7 +249,7 @@ SC.ScrollerView = SC.View.extend(
249
249
  context.push('<div class="track"></div>',
250
250
  '<div class="cap"></div>',
251
251
  buttons,
252
- '<div class="thumb" style="width: '+thumbLength+'px;">',
252
+ '<div class="thumb" style="width: '+thumbLength+'px; left: ' + thumbPosition + 'px;">',
253
253
  '<div class="thumb-center"></div>',
254
254
  '<div class="thumb-top"></div>',
255
255
  '<div class="thumb-bottom"></div></div>');
@@ -2,7 +2,7 @@
2
2
  // Project: SproutCore - JavaScript Application Framework
3
3
  // Copyright: ©2006-2010 Sprout Systems, Inc. and contributors.
4
4
  // Portions ©2008-2010 Apple Inc. All rights reserved.
5
- // License: Licened under MIT license (see license.js)
5
+ // License: Licensed under MIT license (see license.js)
6
6
  // ==========================================================================
7
7
 
8
8
  /**
@@ -122,7 +122,9 @@ SC.SelectFieldView = SC.FieldView.extend(
122
122
  var objects = this.get('objects') ;
123
123
  var fieldValue = this.get('value') ;
124
124
  var el, selectElement;
125
-
125
+
126
+ if ( !this.get('isEnabled') ) context.attr('disabled','disabled');
127
+
126
128
  // get the localization flag.
127
129
  var shouldLocalize = this.get('localize');
128
130
 
@@ -208,12 +210,17 @@ SC.SelectFieldView = SC.FieldView.extend(
208
210
  }
209
211
  },
210
212
 
211
- displayProperties: ['objects','nameKey','valueKey'],
213
+ displayProperties: ['objects','nameKey','valueKey','isEnabled'],
212
214
 
213
215
  _objectsObserver: function() {
214
216
  this.set('cpDidChange', YES);
215
217
  }.observes('objects'),
216
-
218
+
219
+ _objectArrayObserver: function() {
220
+ this.set('cpDidChange', YES);
221
+ this.propertyDidChange('objects');
222
+ }.observes('*objects.[]'),
223
+
217
224
  _nameKeyObserver: function() {
218
225
  this.set('cpDidChange', YES);
219
226
  }.observes('nameKey'),
@@ -247,7 +254,8 @@ SC.SelectFieldView = SC.FieldView.extend(
247
254
  var value = sc_super(); // get raw value...
248
255
  var valueKey = this.get('valueKey') ;
249
256
  var objects = this.get('objects') ;
250
- var found, object;
257
+ var found = null; // matching object goes here.
258
+ var object;
251
259
 
252
260
  // Handle empty selection.
253
261
  if (value == '***') {
@@ -260,7 +268,6 @@ SC.SelectFieldView = SC.FieldView.extend(
260
268
 
261
269
  var loc = (SC.typeOf(objects.length) === SC.T_FUNCTION) ? objects.length() : objects.length;
262
270
 
263
- found = null ; // matching object goes here.
264
271
  while(!found && (--loc >= 0)) {
265
272
  object = objects.objectAt? objects.objectAt(loc) : objects[loc] ;
266
273
  if (!object) continue; // null means placeholder; just skip
@@ -219,17 +219,17 @@ SC.Button = {
219
219
 
220
220
  var valueKey = this.getDelegateProperty('contentValueKey', del) ;
221
221
  if (valueKey && (key === valueKey || key === '*')) {
222
- this.set('value', content ? content.get(valueKey) : null) ;
222
+ this.set('value', content ? (content.get ? content.get(valueKey) : content[valueKey]) : null) ;
223
223
  }
224
224
 
225
225
  var titleKey = this.getDelegateProperty('contentTitleKey', del) ;
226
226
  if (titleKey && (key === titleKey || key === '*')) {
227
- this.set('title', content ? content.get(titleKey) : null) ;
227
+ this.set('title', content ? (content.get ? content.get(titleKey) : content[titleKey]) : null) ;
228
228
  }
229
229
 
230
230
  var iconKey = this.getDelegateProperty('contentIconKey', del);
231
231
  if (iconKey && (key === iconKey || key === '*')) {
232
- this.set('icon', content ? content.get(iconKey) : null) ;
232
+ this.set('icon', content ? (content.get ? content.get(iconKey) : content[iconKey]) : null) ;
233
233
  }
234
234
 
235
235
  return this ;
@@ -54,7 +54,7 @@ sc_require('views/text_field') ;
54
54
  editor expects your source view to implement the InlineTextFieldViewDelegate
55
55
  protocol.
56
56
 
57
- h2. Commiting or Discarding Changes
57
+ h2. Committing or Discarding Changes
58
58
 
59
59
  Normally the editor will automatically commit or discard its changes
60
60
  whenever the user exits the edit mode. If you need to force the editor to
@@ -66,13 +66,13 @@ sc_require('views/text_field') ;
66
66
  }}}
67
67
 
68
68
  Both methods will try to end the editing context and will call the
69
- relevent delegate methods on the delegate you passed to beginEditing().
69
+ relevant delegate methods on the delegate you passed to beginEditing().
70
70
 
71
71
  Note that it is possible an editor may not be able to commit editing
72
72
  changes because either the delegate disallowed it or because its validator
73
73
  failed. In this case commitEditing() will return NO. If you want to
74
74
  end editing anyway, you can discard the editing changes instead by calling
75
- discardEditing(). This method will generally suceed unless your delegate
75
+ discardEditing(). This method will generally succeed unless your delegate
76
76
  refuses it as well.
77
77
 
78
78
  @extends SC.TextFieldView
@@ -133,7 +133,9 @@ SC.InlineTextFieldView = SC.TextFieldView.extend(SC.DelegateSupport,
133
133
  throw "At least frame and delegate options are required for inline editor";
134
134
  }
135
135
 
136
- this._originalValue = options.value || '' ;
136
+ this._originalValue = options.value;
137
+ if (SC.none(this._originalValue))
138
+ this._originalValue = "";
137
139
  this._multiline = (options.multiline !== undefined) ? options.multiline : NO ;
138
140
  if (this._multiline) {
139
141
  this.set('isTextArea', YES);
@@ -420,10 +422,11 @@ SC.InlineTextFieldView = SC.TextFieldView.extend(SC.DelegateSupport,
420
422
  // editable, begins editing.
421
423
  /** @private */
422
424
  insertTab: function(evt) {
425
+ var delegate = this._delegate; // removed by commitEditing()
423
426
  this.resignFirstResponder();
424
427
  this.commitEditing() ;
425
- if(this._delegate){
426
- var next = this._delegate.nextValidKeyView();
428
+ if(delegate){
429
+ var next = delegate.get('nextValidKeyView');
427
430
  if(next && next.beginEditing) next.beginEditing();
428
431
  }
429
432
  return YES ;
@@ -431,10 +434,12 @@ SC.InlineTextFieldView = SC.TextFieldView.extend(SC.DelegateSupport,
431
434
 
432
435
  /** @private */
433
436
  insertBacktab: function(evt) {
437
+ var delegate = this._delegate; // removed by commitEditing()
438
+ this.resignFirstResponder();
434
439
  this.commitEditing() ;
435
- if(this._delegate){
436
- var prev = this._delegate.previousValidKeyView();
437
- if(prev) prev.beginEditing();
440
+ if(delegate){
441
+ var prev = delegate.get('previousValidKeyView');
442
+ if(prev && prev.beginEditing) prev.beginEditing();
438
443
  }
439
444
  return YES ;
440
445
  },
@@ -893,7 +893,9 @@ SC.DateTime.mixin(SC.Comparable,
893
893
  @returns {DateTime} the DateTime corresponding to the string parameter
894
894
  */
895
895
  parse: function(str, fmt) {
896
- var re = /(?:\%([aAbBcdHIjmMpSUWwxXyYZ\%])|(.))/g;
896
+ // Declared as an object not a literal since in some browsers the literal
897
+ // retains state across function calls
898
+ var re = new RegExp('(?:%([aAbBcdHIjmMpSUWwxXyYZ%])|(.))', "g");
897
899
  var d, parts, opts = {}, check = {}, scanner = SC.Scanner.create({string: str});
898
900
 
899
901
  if (SC.none(fmt)) fmt = SC.DATETIME_ISO8601;
@@ -1808,29 +1808,37 @@ SC.RootResponder = SC.Object.extend({
1808
1808
  } else {
1809
1809
  var lh = this._lastHovered || [] , nh = [] , exited, loc, len,
1810
1810
  view = this.targetViewForEvent(evt) ;
1811
-
1812
- // work up the view chain. Notify of mouse entered and
1813
- // mouseMoved if implemented.
1814
- while(view && (view !== this)) {
1811
+
1812
+ // first collect all the responding view starting with the
1813
+ // target view from the given mouse move event
1814
+ while (view && (view !== this)) {
1815
+ nh.push(view);
1816
+ view = view.get('nextResponder');
1817
+ }
1818
+
1819
+ // next exit views that are no longer part of the
1820
+ // responding chain
1821
+ for (loc=0, len=lh.length; loc < len; loc++) {
1822
+ view = lh[loc] ;
1823
+ exited = view.respondsTo('mouseExited');
1824
+ if (exited && nh.indexOf(view) === -1) {
1825
+ view.tryToPerform('mouseExited', evt);
1826
+ }
1827
+ }
1828
+
1829
+ // finally, either perform mouse moved or mouse entered depending on
1830
+ // whether a responding view was or was not part of the last
1831
+ // hovered views
1832
+ for (loc=0, len=nh.length; loc < len; loc++) {
1833
+ view = nh[loc];
1815
1834
  if (lh.indexOf(view) !== -1) {
1816
1835
  view.tryToPerform('mouseMoved', evt);
1817
- nh.push(view) ;
1818
1836
  } else {
1819
1837
  view.tryToPerform('mouseEntered', evt);
1820
- nh.push(view) ;
1821
- }
1822
-
1823
- view = view.get('nextResponder');
1824
- }
1825
- // now find those views last hovered over that were no longer found
1826
- // in this chain and notify of mouseExited.
1827
- for(loc=0, len=lh.length; loc < len; loc++) {
1828
- view = lh[loc] ;
1829
- exited = view.respondsTo('mouseExited') ;
1830
- if (exited && !(nh.indexOf(view) !== -1)) {
1831
- view.tryToPerform('mouseExited',evt);
1832
1838
  }
1833
1839
  }
1840
+
1841
+ // Keep track of the view that were last hovered
1834
1842
  this._lastHovered = nh;
1835
1843
 
1836
1844
  // also, if a mouseDownView exists, call the mouseDragged action, if
@@ -6,7 +6,7 @@
6
6
  // ==========================================================================
7
7
  /*global module test htmlbody ok equals same stop start Q$ */
8
8
 
9
- var pane, optionsForLabel1, optionsForLabel2, delegate;
9
+ var pane, optionsForLabel1, optionsForLabel2, delegate, optionsForLabelFromView;
10
10
 
11
11
  pane = SC.ControlTestPane.design().add("label1", SC.LabelView, {
12
12
  value: 'Some Text',
@@ -50,6 +50,30 @@ pane.resetView = function(view) {
50
50
  view.set('notifiedDidBegin', NO);
51
51
  };
52
52
 
53
+
54
+ optionsForLabelFromView = function(view) {
55
+ var el = view.$(),
56
+ f = SC.viewportOffset(el[0]),
57
+ frameTemp = view.convertFrameFromView(view.get('frame'), null);
58
+
59
+ f.width = frameTemp.width;
60
+ f.height = frameTemp.height;
61
+
62
+ var optionsForLabel = {
63
+ frame: f,
64
+ delegate: view,
65
+ exampleElement: view.$(),
66
+ value: view.get('value'),
67
+ multiline: view.get('isInlineEditorMultiline'),
68
+ isCollection: NO,
69
+ validator: view.get('validator'),
70
+ exampleInlineTextFieldView: view.get('exampleInlineTextFieldView')
71
+ };
72
+
73
+ return optionsForLabel;
74
+ };
75
+
76
+
53
77
  /**
54
78
 
55
79
  */
@@ -64,41 +88,8 @@ module("Test the beginEditing() function of SC.InlineTextFieldView", {
64
88
  // Reset view1 delegate functions
65
89
  pane.resetView(view1);
66
90
 
67
- var el = view1.$(),
68
- f = SC.viewportOffset(el[0]),
69
- frameTemp = view1.convertFrameFromView(view1.get('frame'), null);
70
-
71
- f.width = frameTemp.width;
72
- f.height = frameTemp.height;
73
-
74
- optionsForLabel1 = {
75
- frame: f,
76
- delegate: view1,
77
- exampleElement: view1.$(),
78
- value: view1.get('value'),
79
- multiline: view1.get('isInlineEditorMultiline'),
80
- isCollection: NO,
81
- validator: view1.get('validator'),
82
- exampleInlineTextFieldView: view1.get('exampleInlineTextFieldView')
83
- };
84
-
85
- el = view2.$();
86
- f = SC.viewportOffset(el[0]);
87
- frameTemp = view2.convertFrameFromView(view2.get('frame'), null);
88
-
89
- f.width = frameTemp.width;
90
- f.height = frameTemp.height;
91
-
92
- optionsForLabel2 = {
93
- frame: f,
94
- delegate: view2,
95
- exampleElement: view2.$(),
96
- value: view2.get('value'),
97
- multiline: view2.get('isInlineEditorMultiline'),
98
- isCollection: NO,
99
- validator: view2.get('validator'),
100
- exampleInlineTextFieldView: view2.get('exampleInlineTextFieldView')
101
- };
91
+ optionsForLabel1 = optionsForLabelFromView(view1);
92
+ optionsForLabel2 = optionsForLabelFromView(view2);
102
93
  },
103
94
 
104
95
  teardown: function() {
@@ -225,3 +216,22 @@ function() {
225
216
 
226
217
  ok(view1.get('notifiedDidBegin'), "the delegate should have been notified of begin editing at this point");
227
218
  });
219
+
220
+ test("inline editor does not display the defaultValue if the label's value is the number 0",
221
+ function() {
222
+ var view1 = pane.view('label1');;
223
+ view1.set('value', 0);
224
+ optionsForLabel1 = optionsForLabelFromView(view1);
225
+
226
+ SC.RunLoop.begin();
227
+ SC.InlineTextFieldView.beginEditing(optionsForLabel1);
228
+ SC.RunLoop.end();
229
+
230
+ // The inline editor is the last view appended to the pane
231
+ var length = pane._pane.childViews.length,
232
+ editor = pane._pane.childViews[length - 1];
233
+ same(editor.get('value'), 0, "editor should have number 0 as value");
234
+ editor.blurEditor();
235
+
236
+ same(view1.get('value'), 0, "view should still have number 0 as value");
237
+ });
@@ -2,7 +2,7 @@
2
2
  // Project: SproutCore - JavaScript Application Framework
3
3
  // Copyright: ©2006-2010 Sprout Systems, Inc. and contributors.
4
4
  // portions copyright @2009 Apple Inc.
5
- // License: Licened under MIT license (see license.js)
5
+ // License: Licensed under MIT license (see license.js)
6
6
  // ==========================================================================
7
7
 
8
8
  /*global module test htmlbody ok equals same stop start */
@@ -302,6 +302,11 @@ test('parse without a format uses default ISO8601', function() {
302
302
  equals(SC.DateTime.parse("2010-09-17T18:35:08Z").toISO8601(), "2010-09-17T18:35:08+00:00");
303
303
  });
304
304
 
305
+ test('bad parsing', function() {
306
+ equals(SC.DateTime.parse(SC.DateTime.parse("foo")), null);
307
+ equals(SC.DateTime.parse("2010-09-17T18:35:08Z", SC.DATETIME_ISO8601).toISO8601(), "2010-09-17T18:35:08+00:00");
308
+ });
309
+
305
310
  test('binding', function() {
306
311
  var fromObject = SC.Object.create({value: dt});
307
312
  var toObject = SC.Object.create({value: ''});
@@ -0,0 +1,56 @@
1
+ // ==========================================================================
2
+ // Project: SproutCore - JavaScript Application Framework
3
+ // Copyright: ©2006-2010 Apple Inc. and contributors.
4
+ // License: Licensed under MIT license (see license.js)
5
+ // ==========================================================================
6
+
7
+ /*global module test equals context ok same */
8
+ var notEmptyValidator, field;
9
+
10
+ module("SC.Validator.NotEmpty", {
11
+ setup: function () {
12
+ notEmptyValidator = SC.Validator.NotEmpty.create();
13
+ field = SC.Object.create();
14
+ },
15
+ teardown: function () {
16
+ notEmptyValidator.destroy();
17
+ notEmptyValidator = null;
18
+ }
19
+ });
20
+
21
+ test("Recognizes a non-empty string as valid",function(){
22
+ field.set('fieldValue', "fnord");
23
+ var isValid = notEmptyValidator.validate(undefined, field);
24
+ ok(isValid, "Not empty string is valid");
25
+ });
26
+
27
+ test("Recognizes empty string as invalid",function(){
28
+ field.set('fieldValue', "");
29
+ var isValid = notEmptyValidator.validate(undefined, field);
30
+ ok( ! isValid, "Empty string is not valid");
31
+ });
32
+
33
+ test("Recognizes null as empty",function(){
34
+ field.set('fieldValue', null);
35
+ var isValid = notEmptyValidator.validate(undefined, field);
36
+ ok( ! isValid, "null string is not valid");
37
+ });
38
+
39
+ test("Recognizes undefined as empty",function(){
40
+ field.set('fieldValue', undefined);
41
+ var isValid = notEmptyValidator.validate(undefined, field);
42
+ ok( ! isValid, "null string is not valid");
43
+ });
44
+
45
+ test("Recognizes some number as non-empty",function(){
46
+ field.set('fieldValue', 42);
47
+ var isValid = notEmptyValidator.validate(undefined, field);
48
+ ok(isValid, "42 string is not empty");
49
+ });
50
+
51
+ test("Recognizes zero as non-empty",function(){
52
+ field.set('fieldValue', 0);
53
+ var isValid = notEmptyValidator.validate(undefined, field);
54
+ ok(isValid, "0 string is not empty");
55
+ });
56
+
@@ -2,7 +2,7 @@
2
2
  // Project: SproutCore - JavaScript Application Framework
3
3
  // Copyright: ©2006-2010 Sprout Systems, Inc. and contributors.
4
4
  // Portions ©2008-2010 Apple Inc. All rights reserved.
5
- // License: Licened under MIT license (see license.js)
5
+ // License: Licensed under MIT license (see license.js)
6
6
  // ==========================================================================
7
7
 
8
8
  require('validators/validator');