sproutit-sproutcore 1.0.20090721145281 → 1.0.20090721145282
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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() {
|