sproutit-sproutcore 1.0.20090721145281 → 1.0.20090721145282
Sign up to get free protection for your applications and to get access to all the features.
- data/Buildfile +4 -3
- data/VERSION.yml +2 -2
- data/buildtasks/entry.rake +3 -0
- data/buildtasks/manifest.rake +35 -9
- data/buildtasks/target.rake +25 -6
- data/frameworks/sproutcore/Buildfile +10 -0
- data/frameworks/sproutcore/frameworks/datastore/data_sources/data_source.js +41 -20
- data/frameworks/sproutcore/frameworks/datastore/data_sources/fixtures.js +14 -43
- data/frameworks/sproutcore/frameworks/datastore/models/record.js +11 -0
- data/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +6 -3
- data/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +5 -1
- data/frameworks/sproutcore/frameworks/datastore/system/query.js +10 -7
- data/frameworks/sproutcore/frameworks/datastore/system/record_array.js +19 -20
- data/frameworks/sproutcore/frameworks/datastore/system/store.js +126 -93
- data/frameworks/sproutcore/frameworks/datastore/tests/data_sources/fixtures.js +9 -3
- data/frameworks/sproutcore/frameworks/datastore/tests/models/many_attribute.js +6 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/core_methods.js +28 -3
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/destroy.js +13 -5
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/storeDidChangeProperties.js +46 -23
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record/writeAttribute.js +29 -5
- data/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +13 -4
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/chain.js +109 -0
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChanges.js +69 -15
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChangesFromNestedStore.js +20 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/dataHashDidChange.js +4 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/system/query/find_all.js +56 -6
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/commitRecord.js +9 -2
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/core_methods.js +45 -2
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/recordDidChange.js +0 -1
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/retrieveRecord.js +53 -6
- data/frameworks/sproutcore/frameworks/datastore/tests/system/store/writeDataHash.js +0 -5
- data/frameworks/sproutcore/frameworks/desktop/panes/menu.js +47 -27
- data/frameworks/sproutcore/frameworks/desktop/system/drag.js +5 -4
- data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/mouse.js +23 -12
- data/frameworks/sproutcore/frameworks/desktop/tests/views/list/render.js +92 -0
- data/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/methods.js +104 -53
- data/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/ui.js +2 -0
- data/frameworks/sproutcore/frameworks/desktop/views/button.js +4 -3
- data/frameworks/sproutcore/frameworks/desktop/views/collection.js +6 -2
- data/frameworks/sproutcore/frameworks/desktop/views/list.js +9 -0
- data/frameworks/sproutcore/frameworks/desktop/views/list_item.js +2 -2
- data/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +3 -3
- data/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +9 -1
- data/frameworks/sproutcore/frameworks/desktop/views/select_field.js +80 -102
- data/frameworks/sproutcore/frameworks/foundation/controllers/array.js +0 -1
- data/frameworks/sproutcore/frameworks/foundation/english.lproj/text_field.css +5 -1
- data/frameworks/sproutcore/frameworks/foundation/panes/pane.js +8 -1
- data/frameworks/sproutcore/frameworks/foundation/private/tree_item_observer.js +0 -1
- data/frameworks/sproutcore/frameworks/foundation/system/datetime.js +31 -3
- data/frameworks/sproutcore/frameworks/foundation/system/event.js +0 -4
- data/frameworks/sproutcore/frameworks/foundation/system/render_context.js +3 -7
- data/frameworks/sproutcore/frameworks/foundation/system/request.js +3 -4
- data/frameworks/sproutcore/frameworks/foundation/system/user_defaults.js +78 -17
- data/frameworks/sproutcore/frameworks/foundation/system/utils.js +9 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/single_case.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_selector.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/tests/system/datetime.js +5 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/system/request.js +2 -2
- data/frameworks/sproutcore/frameworks/foundation/tests/system/user_defaults.js +1 -4
- data/frameworks/sproutcore/frameworks/foundation/tests/validators/validator.js +20 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/views/image/ui.js +13 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +132 -0
- data/frameworks/sproutcore/frameworks/foundation/tests/views/view/isVisibleInWindow.js +6 -3
- data/frameworks/sproutcore/frameworks/foundation/validators/validator.js +8 -5
- data/frameworks/sproutcore/frameworks/foundation/views/image.js +18 -5
- data/frameworks/sproutcore/frameworks/foundation/views/text_field.js +292 -21
- data/frameworks/sproutcore/frameworks/foundation/views/view.js +13 -14
- data/frameworks/sproutcore/frameworks/mini/license.js +28 -0
- data/frameworks/sproutcore/frameworks/runtime/core.js +35 -0
- data/frameworks/sproutcore/frameworks/runtime/mixins/enumerable.js +1 -1
- data/frameworks/sproutcore/frameworks/runtime/system/sparse_array.js +79 -5
- data/frameworks/sproutcore/frameworks/runtime/tests/mixins/enumerable.js +6 -6
- data/frameworks/sproutcore/frameworks/runtime/tests/system/sparse_array.js +53 -0
- data/frameworks/sproutcore/frameworks/testing/system/plan.js +4 -0
- data/frameworks/sproutcore/frameworks/testing/system/runner.js +1 -1
- data/frameworks/sproutcore/themes/standard_theme/english.lproj/radio.css +4 -0
- data/gen/design/Buildfile +23 -0
- data/gen/design/README +1 -0
- data/gen/design/USAGE +10 -0
- data/gen/design/templates/english.lproj/@filename@.js +16 -0
- data/gen/page/Buildfile +36 -0
- data/gen/page/README +1 -0
- data/gen/page/USAGE +15 -0
- data/gen/page/templates/pages/@target_name@/Buildfile +16 -0
- data/gen/page/templates/pages/@target_name@/core.js +22 -0
- data/gen/page/templates/pages/@target_name@/english.lproj/body.css +1 -0
- data/gen/page/templates/pages/@target_name@/english.lproj/body.rhtml +7 -0
- data/gen/page/templates/pages/@target_name@/english.lproj/strings.js +15 -0
- data/gen/view/README +1 -1
- data/gen/view/USAGE +5 -5
- data/lib/sproutcore/builders/base.rb +13 -2
- data/lib/sproutcore/builders/html.rb +28 -1
- data/lib/sproutcore/builders/minify.rb +84 -18
- data/lib/sproutcore/builders/test.rb +2 -1
- data/lib/sproutcore/helpers/entry_sorter.rb +16 -1
- data/lib/sproutcore/helpers/static_helper.rb +32 -4
- data/lib/sproutcore/helpers/tag_helper.rb +65 -0
- data/lib/sproutcore/models/manifest.rb +40 -6
- data/lib/sproutcore/models/target.rb +12 -3
- data/lib/sproutcore/rack/builder.rb +56 -4
- data/lib/sproutcore/tools/manifest.rb +1 -0
- data/lib/sproutcore/tools/server.rb +1 -0
- data/lib/sproutcore/tools.rb +21 -1
- data/lib/sproutcore.rb +13 -0
- metadata +16 -1
@@ -0,0 +1,20 @@
|
|
1
|
+
// ========================================================================
|
2
|
+
// SC.Validator Tests
|
3
|
+
// ========================================================================
|
4
|
+
/*globals module test ok isObj equals expects */
|
5
|
+
|
6
|
+
module("SC.Validator");
|
7
|
+
|
8
|
+
test("Calling fieldValueForObject() should not fail if this.prototypefieldValueForObject() is null", function() {
|
9
|
+
var Klass = SC.Validator.extend({
|
10
|
+
fieldValueForObject: null
|
11
|
+
});
|
12
|
+
equals(null, Klass.fieldValueForObject({}, null, null), "should return null") ;
|
13
|
+
});
|
14
|
+
|
15
|
+
test("Calling objectForFieldValue() should not fail if this.prototype.objectForFieldValue() is null", function() {
|
16
|
+
var Klass = SC.Validator.extend({
|
17
|
+
objectForFieldValue: null
|
18
|
+
});
|
19
|
+
equals(null, Klass.objectForFieldValue({}, null, null), "should return null") ;
|
20
|
+
});
|
@@ -35,5 +35,18 @@ htmlbody('<style> .sc-static-layout { border: 1px red dotted; } </style>');
|
|
35
35
|
equals(pane.view('image_loaded').$().attr('src'), appleURL, "should be the same url");
|
36
36
|
});
|
37
37
|
|
38
|
+
test("Verify that the tooltip is correctly being set as both the title and attribute (disabling localization for this test)", function() {
|
39
|
+
var imageView = pane.view('image_loaded');
|
40
|
+
var testToolTip = 'This is a test tooltip';
|
41
|
+
|
42
|
+
SC.RunLoop.begin();
|
43
|
+
imageView.set('localization', NO);
|
44
|
+
imageView.set('toolTip', testToolTip);
|
45
|
+
SC.RunLoop.end();
|
46
|
+
|
47
|
+
ok((imageView.$().attr('title') === testToolTip), "The title attribute should be set to \"" + testToolTip + "\"");
|
48
|
+
ok((imageView.$().attr('alt') === testToolTip), "The alt attribute should be set to \"" + testToolTip + "\"");
|
49
|
+
});
|
50
|
+
|
38
51
|
})();
|
39
52
|
|
@@ -205,6 +205,138 @@ test("enabling disabled view", function() {
|
|
205
205
|
});
|
206
206
|
|
207
207
|
|
208
|
+
// ..........................................................
|
209
|
+
// TEST ACCESSORY VIEWS
|
210
|
+
//
|
211
|
+
|
212
|
+
test("Adding left accessory view", function() {
|
213
|
+
var view = pane.view('with value');
|
214
|
+
|
215
|
+
// test adding accessory view adds the view like it should
|
216
|
+
SC.RunLoop.begin();
|
217
|
+
var accessoryView = SC.View.create({
|
218
|
+
layout: { top:1, left:2, width:16, height:16 }
|
219
|
+
});
|
220
|
+
view.set('leftAccessoryView', accessoryView);
|
221
|
+
SC.RunLoop.end();
|
222
|
+
|
223
|
+
ok(view.get('leftAccessoryView') === accessoryView, 'left accessory view should be set to ' + accessoryView.toString());
|
224
|
+
ok(view.get('childViews').length === 1, 'there should only be one child view');
|
225
|
+
ok(view.get('childViews')[0] === accessoryView, 'first child view should be set to ' + accessoryView.toString());
|
226
|
+
|
227
|
+
|
228
|
+
// The hint and field elements should automatically have their padding-left
|
229
|
+
// set to the accessory view's offset + width
|
230
|
+
// (18 = 2 left offset + 16 width)
|
231
|
+
var hintElement = view.$('.sc-hint')[0];
|
232
|
+
var fieldElement = view.$field()[0];
|
233
|
+
ok(hintElement.style.paddingLeft === '18px', 'hint element should get 18px padding-left');
|
234
|
+
ok(fieldElement.style.paddingLeft === '18px', 'field element should get 18px padding-left');
|
235
|
+
|
236
|
+
// Test removing the accessory view.
|
237
|
+
SC.RunLoop.begin();
|
238
|
+
view.set('leftAccessoryView', null);
|
239
|
+
SC.RunLoop.end();
|
240
|
+
ok(view.get('childViews').length === 0, 'after removing the left accessory view there should be no child views left');
|
241
|
+
ok(!hintElement.style.paddingLeft, 'after removing the left accessory view the hint element should have no padding-left style');
|
242
|
+
ok(!fieldElement.style.paddingLeft, 'after removing the left accessory view the field element should have no padding-left style');
|
243
|
+
});
|
244
|
+
|
245
|
+
test("Adding right accessory view", function() {
|
246
|
+
var view = pane.view('with value');
|
247
|
+
|
248
|
+
// test adding accessory view adds the view like it should
|
249
|
+
SC.RunLoop.begin();
|
250
|
+
var accessoryView = SC.View.create({
|
251
|
+
layout: { top:1, right:3, width:17, height:16 }
|
252
|
+
});
|
253
|
+
view.set('rightAccessoryView', accessoryView);
|
254
|
+
SC.RunLoop.end();
|
255
|
+
|
256
|
+
ok(view.get('rightAccessoryView') === accessoryView, 'right accessory view should be set to ' + accessoryView.toString());
|
257
|
+
ok(view.get('childViews').length === 1, 'there should only be one child view');
|
258
|
+
ok(view.get('childViews')[0] === accessoryView, 'first child view should be set to ' + accessoryView.toString());
|
259
|
+
|
260
|
+
|
261
|
+
// The hint and field elements should automatically have their padding-right
|
262
|
+
// set to the accessory view's offset + width
|
263
|
+
// (20 = 3 right offset + 17 width)
|
264
|
+
var hintElement = view.$('.sc-hint')[0];
|
265
|
+
var fieldElement = view.$field()[0];
|
266
|
+
ok(hintElement.style.paddingRight === '20px', 'hint element should get 20px padding-right');
|
267
|
+
ok(fieldElement.style.paddingRight === '20px', 'field element should get 20px padding-right');
|
268
|
+
|
269
|
+
|
270
|
+
// If a right accessory view is set with only 'left' (and not 'right')
|
271
|
+
// defined in its layout, 'left' should be cleared out and 'right' should
|
272
|
+
// be set to 0.
|
273
|
+
SC.RunLoop.begin();
|
274
|
+
var accessoryView = SC.View.create({
|
275
|
+
layout: { top:1, left:2, width:16, height:16 }
|
276
|
+
});
|
277
|
+
view.set('rightAccessoryView', accessoryView);
|
278
|
+
SC.RunLoop.end();
|
279
|
+
|
280
|
+
ok(view.get('rightAccessoryView').get('layout').left === null, "right accessory view created with 'left' rather than 'right' in layout should have layout.left set to null");
|
281
|
+
ok(view.get('rightAccessoryView').get('layout').right === 0, "right accessory view created with 'left' rather than 'right' in layout should have layout.right set to 0");
|
282
|
+
|
283
|
+
|
284
|
+
// Test removing the accessory view.
|
285
|
+
SC.RunLoop.begin();
|
286
|
+
view.set('rightAccessoryView', null);
|
287
|
+
SC.RunLoop.end();
|
288
|
+
ok(view.get('childViews').length === 0, 'after removing the right accessory view there should be no child views left');
|
289
|
+
ok(!hintElement.style.paddingRight, 'after removing the right accessory view the hint element should have no padding-right style');
|
290
|
+
ok(!fieldElement.style.paddingRight, 'after removing the right accessory view the field element should have no padding-right style');
|
291
|
+
});
|
292
|
+
|
293
|
+
test("Adding both left and right accessory views", function() {
|
294
|
+
var view = pane.view('with value');
|
295
|
+
|
296
|
+
// test adding accessory view adds the view like it should
|
297
|
+
SC.RunLoop.begin();
|
298
|
+
var leftAccessoryView = SC.View.create({
|
299
|
+
layout: { top:1, left:2, width:16, height:16 }
|
300
|
+
});
|
301
|
+
view.set('leftAccessoryView', leftAccessoryView);
|
302
|
+
var rightAccessoryView = SC.View.create({
|
303
|
+
layout: { top:1, right:3, width:17, height:16 }
|
304
|
+
});
|
305
|
+
view.set('rightAccessoryView', rightAccessoryView);
|
306
|
+
SC.RunLoop.end();
|
307
|
+
|
308
|
+
ok(view.get('childViews').length === 2, 'we should have two child views since we added both a left and a right accessory view');
|
309
|
+
|
310
|
+
|
311
|
+
// The hint and field elements should automatically have their padding-left
|
312
|
+
// and padding-right values set to the accessory views' offset + width
|
313
|
+
// * left: 18 = 2 left offset + 16 width)
|
314
|
+
// * right: 20 = 3 left offset + 17 width)
|
315
|
+
var hintElement = view.$('.sc-hint')[0];
|
316
|
+
var fieldElement = view.$field()[0];
|
317
|
+
ok(hintElement.style.paddingLeft === '18px', 'hint element should get 18px padding-left');
|
318
|
+
ok(fieldElement.style.paddingLeft === '18px', 'field element should get 18px padding-left');
|
319
|
+
ok(hintElement.style.paddingRight === '20px', 'hint element should get 20px padding-right');
|
320
|
+
ok(fieldElement.style.paddingRight === '20px', 'field element should get 20px padding-right');
|
321
|
+
|
322
|
+
|
323
|
+
// Test removing the accessory views.
|
324
|
+
SC.RunLoop.begin();
|
325
|
+
view.set('rightAccessoryView', null);
|
326
|
+
SC.RunLoop.end();
|
327
|
+
ok(view.get('childViews').length === 1, 'after removing the right accessory view there should be one child view left (the left accessory view)');
|
328
|
+
ok(!hintElement.style.paddingRight, 'after removing the right accessory view the hint element should have no padding-right style');
|
329
|
+
ok(!fieldElement.style.paddingRight, 'after removing the right accessory view the field element should have no padding-right style');
|
330
|
+
SC.RunLoop.begin();
|
331
|
+
view.set('leftAccessoryView', null);
|
332
|
+
SC.RunLoop.end();
|
333
|
+
ok(view.get('childViews').length === 0, 'after removing both accessory views there should be no child views left');
|
334
|
+
ok(!hintElement.style.paddingLeft, 'after removing the left accessory view the hint element should have no padding-left style');
|
335
|
+
ok(!fieldElement.style.paddingLeft, 'after removing the left accessory view the field element should have no padding-left style');
|
336
|
+
});
|
337
|
+
|
338
|
+
|
339
|
+
|
208
340
|
// ..........................................................
|
209
341
|
// TEST EVENTS
|
210
342
|
//
|
@@ -63,19 +63,22 @@ test("removing a view from a visible pane should make it invisible again", funct
|
|
63
63
|
test("updateLayer should not be invoked even if layer becomes dirty until isVisibleInWindow changes, then it should invoke", function() {
|
64
64
|
|
65
65
|
var callCount = 0 ;
|
66
|
-
view.
|
66
|
+
view.createLayer = function() {
|
67
|
+
SC.View.prototype.createLayer.apply(this, arguments);
|
68
|
+
callCount++;
|
69
|
+
};
|
67
70
|
ok(!view.get('isVisibleInWindow'), 'precond - view should not be visible to start');
|
68
71
|
|
69
72
|
SC.RunLoop.begin();
|
70
73
|
view.displayDidChange();
|
71
74
|
SC.RunLoop.end();
|
72
|
-
equals(callCount, 0, '
|
75
|
+
equals(callCount, 0, 'createLayer should not run b/c its not visible');
|
73
76
|
|
74
77
|
SC.RunLoop.begin();
|
75
78
|
pane.appendChild(view); // make visible in window...
|
76
79
|
ok(view.get('isVisibleInWindow'), 'view should now be visible in window');
|
77
80
|
SC.RunLoop.end();
|
78
|
-
equals(callCount, 1, '
|
81
|
+
equals(callCount, 1, 'createLayer should exec now b/c isVisibleInWindow is YES');
|
79
82
|
});
|
80
83
|
|
81
84
|
test("layoutChildViewsIfNeeded should not be invoked even if layer needs layout until isVisibleInWindow changes, then it should invoke", function() {
|
@@ -296,16 +296,19 @@ SC.Validator.mixin(/** @scope SC.Validator */ {
|
|
296
296
|
method you define in your subclass.
|
297
297
|
*/
|
298
298
|
fieldValueForObject: function(object, form, field) {
|
299
|
-
|
299
|
+
if (this.prototype && this.prototype.fieldValueForObject)
|
300
|
+
return this.prototype.fieldValueForObject(object,form,field) ;
|
301
|
+
else return null ;
|
300
302
|
},
|
301
|
-
|
303
|
+
|
302
304
|
/**
|
303
305
|
Convenience class method to call the objectForFieldValue() instance
|
304
306
|
method you define in your subclass.
|
305
307
|
*/
|
306
308
|
objectForFieldValue: function(value, form, field) {
|
307
|
-
|
309
|
+
if (this.prototype && this.prototype.objectForFieldValue)
|
310
|
+
return this.prototype.objectForFieldValue(value,form,field) ;
|
311
|
+
else return null ;
|
308
312
|
}
|
309
313
|
|
310
|
-
})
|
311
|
-
|
314
|
+
});
|
@@ -89,17 +89,30 @@ SC.ImageView = SC.View.extend(SC.Control,
|
|
89
89
|
*/
|
90
90
|
canLoadInBackground: NO,
|
91
91
|
|
92
|
-
|
92
|
+
/**
|
93
|
+
If YES, any specified toolTip will be localized before display.
|
94
|
+
*/
|
95
|
+
localize: YES,
|
96
|
+
|
97
|
+
displayProperties: 'status toolTip'.w(),
|
93
98
|
|
94
99
|
render: function(context, firstTime) {
|
95
100
|
// the image source is the value if the status is LOADED or blank
|
96
|
-
var status = this.get('status'), value = this.get('value');
|
101
|
+
var status = this.get('status'), value = this.get('value') ;
|
97
102
|
|
98
103
|
if (status === SC.IMAGE_STATE_NONE && value) this._image_valueDidChange() ; // setup initial state
|
99
104
|
|
100
|
-
var src = (status === SC.IMAGE_STATE_LOADED) ? value : SC.BLANK_IMAGE_URL;
|
101
|
-
if (status === SC.IMAGE_STATE_SPRITE) context.addClass(value);
|
102
|
-
context.attr('src', src);
|
105
|
+
var src = (status === SC.IMAGE_STATE_LOADED) ? value : SC.BLANK_IMAGE_URL ;
|
106
|
+
if (status === SC.IMAGE_STATE_SPRITE) context.addClass(value) ;
|
107
|
+
context.attr('src', src) ;
|
108
|
+
|
109
|
+
// If there is a toolTip set, grab it and localize if necessary.
|
110
|
+
var toolTip = this.get('toolTip') ;
|
111
|
+
if (SC.typeOf(toolTip) === SC.T_STRING) {
|
112
|
+
if (this.get('localize')) toolTip = toolTip.loc() ;
|
113
|
+
context.attr('title', toolTip) ;
|
114
|
+
context.attr('alt', toolTip) ;
|
115
|
+
}
|
103
116
|
},
|
104
117
|
|
105
118
|
/** @private -
|
@@ -38,9 +38,68 @@ SC.TextFieldView = SC.FieldView.extend(SC.StaticLayout, SC.Editable,
|
|
38
38
|
hint: null,
|
39
39
|
|
40
40
|
/**
|
41
|
-
If YES then the text field is currently editing.
|
41
|
+
If YES then the text field is currently editing.
|
42
42
|
*/
|
43
43
|
isEditing: NO,
|
44
|
+
|
45
|
+
/**
|
46
|
+
An optional view instance, or view class reference, which will be visible
|
47
|
+
on the left side of the text field. Visually the accessory view will look
|
48
|
+
to be inside the field but the text editing will not overlap the accessory
|
49
|
+
view.
|
50
|
+
|
51
|
+
The view will be rooted to the top-left of the text field. You should use
|
52
|
+
a layout with 'left' and/or 'top' specified if you would like to adjust
|
53
|
+
the offset from the top-left.
|
54
|
+
|
55
|
+
One example use would be for a web site's icon, found to the left of the
|
56
|
+
URL field, in many popular web browsers.
|
57
|
+
|
58
|
+
Because the padding is set for you, it is recommended that you use the
|
59
|
+
'border-box' box-sizing CSS declaration for your hint and field elements
|
60
|
+
if you do not want them to resize as accessory views are added and
|
61
|
+
removed.
|
62
|
+
|
63
|
+
Note: If you set a left accessory view, the left padding of the text
|
64
|
+
field will automatically be set to the width of the accessory view,
|
65
|
+
overriding any CSS padding-left you may have defined. If you would like
|
66
|
+
to customize the amount of left padding used when the accessory view is
|
67
|
+
visible, make the accessory view wider, with empty space on the right.
|
68
|
+
*/
|
69
|
+
leftAccessoryView: null,
|
70
|
+
|
71
|
+
/**
|
72
|
+
An optional view instance, or view class reference, which will be visible
|
73
|
+
on the right side of the text field. Visually the accessory view will
|
74
|
+
look to be inside the field but the text editing will not overlap the
|
75
|
+
accessory view.
|
76
|
+
|
77
|
+
The view will be rooted to the top-right of the text field. You should
|
78
|
+
use a layout with 'right' and/or 'top' specified if you would like to
|
79
|
+
adjust the offset from the top-right. If 'left' is specified in the
|
80
|
+
layout it will be cleared.
|
81
|
+
|
82
|
+
One example use would be for a button to clear the contents of the text
|
83
|
+
field.
|
84
|
+
|
85
|
+
Note: If you set a right accessory view, the left padding of the text
|
86
|
+
field will automatically be set to the width of the accessory view,
|
87
|
+
overriding any CSS padding-right you may have defined. If you would like
|
88
|
+
to customize the amount of right padding used when the accessory view is
|
89
|
+
visible, make the accessory view wider, with empty space on the left.
|
90
|
+
|
91
|
+
Because the padding is set for you, it is recommended that you use the
|
92
|
+
'border-box' box-sizing CSS declaration for your hint and field elements
|
93
|
+
if you do not want them to resize as accessory views are added and
|
94
|
+
removed.
|
95
|
+
|
96
|
+
IMPORTANT NOTE: In Internet Explorer 7 and 8, fixed-width text views do
|
97
|
+
not respect the right padding property, so if you are using a right
|
98
|
+
accessory view in such a situation be aware that it may float over the
|
99
|
+
text if the text is long enough. You may wish to use a translucent
|
100
|
+
background to somewhat mitigate this issue.
|
101
|
+
*/
|
102
|
+
rightAccessoryView: null,
|
44
103
|
|
45
104
|
|
46
105
|
/** isEditable maps to isEnabled with a TextField. */
|
@@ -52,44 +111,256 @@ SC.TextFieldView = SC.FieldView.extend(SC.StaticLayout, SC.Editable,
|
|
52
111
|
// INTERNAL SUPPORT
|
53
112
|
//
|
54
113
|
|
55
|
-
displayProperties: 'hint fieldValue isEditing'.w(),
|
114
|
+
displayProperties: 'hint fieldValue isEditing leftAccessoryView rightAccessoryView'.w(),
|
115
|
+
|
116
|
+
|
117
|
+
createChildViews: function() {
|
118
|
+
this.accessoryViewObserver() ;
|
119
|
+
},
|
120
|
+
|
121
|
+
|
122
|
+
accessoryViewObserver: function() {
|
123
|
+
var viewProperties = ['leftAccessoryView', 'rightAccessoryView'] ;
|
124
|
+
var len = viewProperties.length ;
|
125
|
+
for (var i=0; i<len; i++) {
|
126
|
+
var viewProperty = viewProperties[i] ;
|
127
|
+
|
128
|
+
// Is there an accessory view specified?
|
129
|
+
var previousView = this['_'+viewProperty] ;
|
130
|
+
var accessoryView = this.get(viewProperty) ;
|
131
|
+
|
132
|
+
// If the view is the same, there's nothing to do. Otherwise, remove
|
133
|
+
// the old one (if any) and add the new one.
|
134
|
+
if (! (previousView
|
135
|
+
&& accessoryView
|
136
|
+
&& (previousView === accessoryView) ) ) {
|
137
|
+
|
138
|
+
// If there was a previous previous accessory view, remove it now.
|
139
|
+
if (previousView) {
|
140
|
+
// Remove the "sc-text-field-accessory-view" class name that we had
|
141
|
+
// added earlier.
|
142
|
+
var classNames = previousView.get('classNames') ;
|
143
|
+
classNames = classNames.without('sc-text-field-accessory-view') ;
|
144
|
+
previousView.set('classNames', classNames) ;
|
145
|
+
this.removeChild(previousView) ;
|
146
|
+
previousView = null ;
|
147
|
+
this['_'+viewProperty] = null ;
|
148
|
+
}
|
149
|
+
|
150
|
+
// If there's a new accessory view to add, do so now.
|
151
|
+
if (accessoryView) {
|
152
|
+
// If the user passed in a class rather than an instance, create an
|
153
|
+
// instance now.
|
154
|
+
if (accessoryView.isClass) {
|
155
|
+
accessoryView = accessoryView.create({
|
156
|
+
layoutView: this
|
157
|
+
}) ;
|
158
|
+
}
|
159
|
+
|
160
|
+
// Add in the "sc-text-field-accessory-view" class name so that the
|
161
|
+
// z-index gets set correctly.
|
162
|
+
var classNames = accessoryView.get('classNames') ;
|
163
|
+
var className = 'sc-text-field-accessory-view' ;
|
164
|
+
if (classNames.indexOf(className) < 0) {
|
165
|
+
classNames.push(className) ;
|
166
|
+
}
|
167
|
+
|
168
|
+
// Actually add the view to our hierarchy and cache a reference.
|
169
|
+
this.appendChild(accessoryView) ;
|
170
|
+
this['_'+viewProperty] = accessoryView ;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}.observes('leftAccessoryView', 'rightAccessoryView'),
|
175
|
+
|
176
|
+
|
177
|
+
layoutChildViewsIfNeeded: function(isVisible) {
|
178
|
+
// For the right accessory view, adjust the positioning such that the view
|
179
|
+
// is right-justified, unless 'right' is specified.
|
180
|
+
if (!isVisible) isVisible = this.get('isVisibleInWindow') ;
|
181
|
+
if (isVisible && this.get('childViewsNeedLayout')) {
|
182
|
+
var rightAccessoryView = this.get('rightAccessoryView') ;
|
183
|
+
if (rightAccessoryView && rightAccessoryView.get) {
|
184
|
+
var layout = rightAccessoryView.get('layout') ;
|
185
|
+
if (layout) {
|
186
|
+
// Clear out any 'left' value.
|
187
|
+
layout.left = null;
|
188
|
+
|
189
|
+
// Unless the user specified a 'right' value, specify a default to
|
190
|
+
// right-justify the view.
|
191
|
+
if (!layout.right) layout.right = 0 ;
|
192
|
+
|
193
|
+
rightAccessoryView.adjust({ layout: layout }) ;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
sc_super() ;
|
199
|
+
},
|
200
|
+
|
56
201
|
|
57
202
|
render: function(context, firstTime) {
|
203
|
+
sc_super() ;
|
58
204
|
|
59
205
|
var disabled = this.get('isEnabled') ? '' : 'disabled="disabled"';
|
60
206
|
var name = SC.guidFor(this);
|
61
|
-
var hint = this.get('hint');
|
62
207
|
var type = this.get('isPassword') ? 'password' : 'text';
|
63
208
|
|
64
209
|
// always have at least an empty string
|
65
210
|
var v = this.get('fieldValue');
|
66
|
-
if (SC.none(v)) v = '';
|
211
|
+
if (SC.none(v)) v = '';
|
67
212
|
|
68
213
|
// update layer classes always
|
69
|
-
context.setClass('not-empty', v.length>0);
|
70
|
-
|
214
|
+
context.setClass('not-empty', v.length > 0);
|
215
|
+
|
216
|
+
// If we have accessory views, we'll want to update the padding on the
|
217
|
+
// hint to compensate for the width of the accessory view. (It'd be nice
|
218
|
+
// if we could add in the original padding, too, but there's no efficient
|
219
|
+
// way to do that without first rendering the element somewhere on/off-
|
220
|
+
// screen, and we don't want to take the performance hit.)
|
221
|
+
var accessoryViewWidths = this._getAccessoryViewWidths();
|
222
|
+
var leftPadding = accessoryViewWidths['left'] ;
|
223
|
+
var rightPadding = accessoryViewWidths['right'] ;
|
224
|
+
if (leftPadding) leftPadding += 'px' ;
|
225
|
+
if (rightPadding) rightPadding += 'px' ;
|
226
|
+
|
227
|
+
|
228
|
+
this._renderHint(context, firstTime, leftPadding, rightPadding);
|
229
|
+
this._renderField(context, firstTime, v, leftPadding, rightPadding);
|
230
|
+
},
|
231
|
+
|
232
|
+
_renderHint: function(context, firstTime, leftPadding, rightPadding) {
|
233
|
+
// TODO: The cleanest thing might be to create a sub- rendering context
|
234
|
+
// here, but currently SC.RenderContext will render sibling
|
235
|
+
// contexts as parent/child.
|
236
|
+
var hint = this.get('hint') ;
|
237
|
+
|
71
238
|
if (firstTime) {
|
72
|
-
|
73
|
-
if(
|
74
|
-
|
75
|
-
|
76
|
-
|
239
|
+
var paddingStyle = '' ;
|
240
|
+
if (leftPadding || rightPadding) {
|
241
|
+
paddingStyle = 'style="' ;
|
242
|
+
if (leftPadding) paddingStyle += 'padding-left: ' + leftPadding + '; ' ;
|
243
|
+
if (rightPadding) paddingStyle += 'padding-right: ' + rightPadding + ';' ;
|
244
|
+
paddingStyle += '"' ;
|
77
245
|
}
|
78
|
-
|
79
|
-
|
80
|
-
|
246
|
+
context.push('<span class="sc-hint" %@>'.fmt(paddingStyle), hint, '</span>') ;
|
247
|
+
}
|
248
|
+
else {
|
249
|
+
// If this is not first time rendering, update the hint itself since we
|
250
|
+
// can't just blow away the text field like we might most other controls
|
251
|
+
var elements = this.$('.sc-hint') ;
|
81
252
|
if (hint !== this._textField_currentHint) {
|
82
253
|
this._textField_currentHint = hint ;
|
83
|
-
|
84
|
-
}
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
254
|
+
elements.text(hint) ;
|
255
|
+
}
|
256
|
+
|
257
|
+
// Adjust the padding to accommodate any accessory views.
|
258
|
+
var element = elements[0] ;
|
259
|
+
if (element) {
|
260
|
+
if (leftPadding) {
|
261
|
+
if (element.style.paddingLeft !== leftPadding) {
|
262
|
+
element.style.paddingLeft = leftPadding ;
|
263
|
+
}
|
264
|
+
}
|
265
|
+
else {
|
266
|
+
element.style.paddingLeft = null ;
|
267
|
+
}
|
268
|
+
|
269
|
+
if (rightPadding) {
|
270
|
+
if (element.style.paddingRight !== rightPadding) {
|
271
|
+
element.style.paddingRight = rightPadding ;
|
272
|
+
}
|
273
|
+
}
|
274
|
+
else {
|
275
|
+
element.style.paddingRight = null ;
|
276
|
+
}
|
89
277
|
}
|
90
|
-
|
91
278
|
}
|
92
279
|
},
|
280
|
+
|
281
|
+
_renderField: function(context, firstTime, value, leftPadding, rightPadding) {
|
282
|
+
// TODO: The cleanest thing might be to create a sub- rendering context
|
283
|
+
// here, but currently SC.RenderContext will render sibling
|
284
|
+
// contexts as parent/child.
|
285
|
+
if (firstTime) {
|
286
|
+
var disabled = this.get('isEnabled') ? '' : 'disabled="disabled"' ;
|
287
|
+
var name = SC.guidFor(this) ;
|
288
|
+
|
289
|
+
var paddingStyle = '' ;
|
290
|
+
if (leftPadding || rightPadding) {
|
291
|
+
paddingStyle = 'style="' ;
|
292
|
+
if (leftPadding) paddingStyle += 'padding-left: ' + leftPadding + '; ' ;
|
293
|
+
if (rightPadding) paddingStyle += 'padding-right: ' + rightPadding + ';' ;
|
294
|
+
paddingStyle += '"' ;
|
295
|
+
}
|
296
|
+
|
297
|
+
if (this.get('isTextArea')) {
|
298
|
+
context.push('<textarea name="%@" %@ value="%@" %@></textarea>'.fmt(name, disabled, value, paddingStyle)) ;
|
299
|
+
}
|
300
|
+
else {
|
301
|
+
var type = this.get('isPassword') ? 'password' : 'text' ;
|
302
|
+
context.push('<input type="%@" name="%@" %@ value="%@" %@></input>'.fmt(type, name, disabled, value, paddingStyle)) ;
|
303
|
+
}
|
304
|
+
}
|
305
|
+
else {
|
306
|
+
var element = this.$field()[0];
|
307
|
+
if (element) {
|
308
|
+
if (!this.get('isEnabled')) {
|
309
|
+
element.disabled = 'true' ;
|
310
|
+
}
|
311
|
+
else {
|
312
|
+
element.disabled = null ;
|
313
|
+
}
|
314
|
+
|
315
|
+
// Adjust the padding to accommodate any accessory views.
|
316
|
+
if (leftPadding) {
|
317
|
+
if (element.style.paddingLeft !== leftPadding) {
|
318
|
+
element.style.paddingLeft = leftPadding ;
|
319
|
+
}
|
320
|
+
}
|
321
|
+
else {
|
322
|
+
element.style.paddingLeft = null ;
|
323
|
+
}
|
324
|
+
|
325
|
+
if (rightPadding) {
|
326
|
+
if (element.style.paddingRight !== rightPadding) {
|
327
|
+
element.style.paddingRight = rightPadding ;
|
328
|
+
}
|
329
|
+
}
|
330
|
+
else {
|
331
|
+
element.style.paddingRight = null ;
|
332
|
+
}
|
333
|
+
}
|
334
|
+
}
|
335
|
+
},
|
336
|
+
|
337
|
+
_getAccessoryViewWidths: function() {
|
338
|
+
var widths = {};
|
339
|
+
var accessoryViewPositions = ['left', 'right'] ;
|
340
|
+
var numberOfAccessoryViewPositions = accessoryViewPositions.length ;
|
341
|
+
for (var i = 0; i < numberOfAccessoryViewPositions; i++) {
|
342
|
+
var position = accessoryViewPositions[i];
|
343
|
+
var accessoryView = this.get(position + 'AccessoryView');
|
344
|
+
if (accessoryView && accessoryView.get) {
|
345
|
+
var frame = accessoryView.get('frame');
|
346
|
+
if (frame) {
|
347
|
+
var width = frame.width;
|
348
|
+
if (width) {
|
349
|
+
// Also account for the accessory view's inset.
|
350
|
+
var layout = accessoryView.get('layout');
|
351
|
+
if (layout) {
|
352
|
+
var offset = layout[position];
|
353
|
+
width += offset;
|
354
|
+
}
|
355
|
+
widths[position] = width;
|
356
|
+
}
|
357
|
+
}
|
358
|
+
}
|
359
|
+
}
|
360
|
+
return widths;
|
361
|
+
},
|
362
|
+
|
363
|
+
|
93
364
|
|
94
365
|
// more efficient input
|
95
366
|
$field: function() {
|