sproutcore 1.4.2 → 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -0
- data/Rakefile +27 -436
- data/VERSION.yml +1 -1
- data/lib/frameworks/sproutcore/CHANGELOG +32 -2
- data/lib/frameworks/sproutcore/frameworks/animation/core.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +8 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record.js +42 -2
- data/lib/frameworks/sproutcore/frameworks/debug/invoke_once_last_debugging.js +7 -12
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/radio/ui.js +22 -1
- data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/desktop/views/list.js +6 -2
- data/lib/frameworks/sproutcore/frameworks/desktop/views/list_item.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/desktop/views/radio.js +38 -10
- data/lib/frameworks/sproutcore/frameworks/desktop/views/scroller.js +8 -8
- data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/desktop/views/select_field.js +12 -5
- data/lib/frameworks/sproutcore/frameworks/foundation/mixins/button.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/foundation/mixins/inline_text_field.js +14 -9
- data/lib/frameworks/sproutcore/frameworks/foundation/system/datetime.js +3 -1
- data/lib/frameworks/sproutcore/frameworks/foundation/system/root_responder.js +25 -17
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/inline_text_field/beginEditing.js +46 -36
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/validatable/ui.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/datetime.js +5 -0
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/validators/not_empty.js +56 -0
- data/lib/frameworks/sproutcore/frameworks/foundation/validators/date_time.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/foundation/validators/not_empty.js +7 -3
- data/lib/frameworks/sproutcore/frameworks/foundation/views/label.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/media_slider.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/video.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/private/observer_set.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +18 -23
- data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/observable.js +13 -0
- data/lib/sproutcore/builders/base.rb +4 -4
- data/lib/sproutcore/builders/javascript.rb +0 -7
- data/lib/sproutcore/builders/stylesheet.rb +0 -7
- data/lib/sproutcore/rack/proxy.rb +28 -13
- metadata +4 -4
- 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 =
|
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 =
|
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
|
146
|
-
|
147
|
-
|
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
|
-
|
190
|
-
|
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'),
|
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
|
210
|
+
classNames['sc-vertical'] = YES;
|
211
211
|
break;
|
212
212
|
case SC.LAYOUT_HORIZONTAL:
|
213
|
-
classNames
|
213
|
+
classNames['sc-horizontal'] = YES;
|
214
214
|
break;
|
215
215
|
}
|
216
216
|
|
217
217
|
// The appearance of the scroller changes if disabled
|
218
|
-
|
218
|
+
classNames['disabled'] = !this.get('isEnabled');
|
219
219
|
// Whether to hide the thumb and buttons
|
220
|
-
|
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.
|
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:
|
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
|
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.
|
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
|
-
|
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
|
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(
|
426
|
-
var next =
|
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(
|
436
|
-
var prev =
|
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
|
-
|
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
|
-
//
|
1813
|
-
//
|
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
|
data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/inline_text_field/beginEditing.js
CHANGED
@@ -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
|
-
|
68
|
-
|
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:
|
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:
|
5
|
+
// License: Licensed under MIT license (see license.js)
|
6
6
|
// ==========================================================================
|
7
7
|
|
8
8
|
require('validators/validator');
|