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.
Files changed (105) hide show
  1. data/Buildfile +4 -3
  2. data/VERSION.yml +2 -2
  3. data/buildtasks/entry.rake +3 -0
  4. data/buildtasks/manifest.rake +35 -9
  5. data/buildtasks/target.rake +25 -6
  6. data/frameworks/sproutcore/Buildfile +10 -0
  7. data/frameworks/sproutcore/frameworks/datastore/data_sources/data_source.js +41 -20
  8. data/frameworks/sproutcore/frameworks/datastore/data_sources/fixtures.js +14 -43
  9. data/frameworks/sproutcore/frameworks/datastore/models/record.js +11 -0
  10. data/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +6 -3
  11. data/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +5 -1
  12. data/frameworks/sproutcore/frameworks/datastore/system/query.js +10 -7
  13. data/frameworks/sproutcore/frameworks/datastore/system/record_array.js +19 -20
  14. data/frameworks/sproutcore/frameworks/datastore/system/store.js +126 -93
  15. data/frameworks/sproutcore/frameworks/datastore/tests/data_sources/fixtures.js +9 -3
  16. data/frameworks/sproutcore/frameworks/datastore/tests/models/many_attribute.js +6 -1
  17. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/core_methods.js +28 -3
  18. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/destroy.js +13 -5
  19. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/storeDidChangeProperties.js +46 -23
  20. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/writeAttribute.js +29 -5
  21. data/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +13 -4
  22. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/chain.js +109 -0
  23. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChanges.js +69 -15
  24. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChangesFromNestedStore.js +20 -1
  25. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/dataHashDidChange.js +4 -1
  26. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/find_all.js +56 -6
  27. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/commitRecord.js +9 -2
  28. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/core_methods.js +45 -2
  29. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/recordDidChange.js +0 -1
  30. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/retrieveRecord.js +53 -6
  31. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/writeDataHash.js +0 -5
  32. data/frameworks/sproutcore/frameworks/desktop/panes/menu.js +47 -27
  33. data/frameworks/sproutcore/frameworks/desktop/system/drag.js +5 -4
  34. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/mouse.js +23 -12
  35. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/render.js +92 -0
  36. data/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/methods.js +104 -53
  37. data/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/ui.js +2 -0
  38. data/frameworks/sproutcore/frameworks/desktop/views/button.js +4 -3
  39. data/frameworks/sproutcore/frameworks/desktop/views/collection.js +6 -2
  40. data/frameworks/sproutcore/frameworks/desktop/views/list.js +9 -0
  41. data/frameworks/sproutcore/frameworks/desktop/views/list_item.js +2 -2
  42. data/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +3 -3
  43. data/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +9 -1
  44. data/frameworks/sproutcore/frameworks/desktop/views/select_field.js +80 -102
  45. data/frameworks/sproutcore/frameworks/foundation/controllers/array.js +0 -1
  46. data/frameworks/sproutcore/frameworks/foundation/english.lproj/text_field.css +5 -1
  47. data/frameworks/sproutcore/frameworks/foundation/panes/pane.js +8 -1
  48. data/frameworks/sproutcore/frameworks/foundation/private/tree_item_observer.js +0 -1
  49. data/frameworks/sproutcore/frameworks/foundation/system/datetime.js +31 -3
  50. data/frameworks/sproutcore/frameworks/foundation/system/event.js +0 -4
  51. data/frameworks/sproutcore/frameworks/foundation/system/render_context.js +3 -7
  52. data/frameworks/sproutcore/frameworks/foundation/system/request.js +3 -4
  53. data/frameworks/sproutcore/frameworks/foundation/system/user_defaults.js +78 -17
  54. data/frameworks/sproutcore/frameworks/foundation/system/utils.js +9 -0
  55. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/single_case.js +2 -2
  56. data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_selector.js +2 -2
  57. data/frameworks/sproutcore/frameworks/foundation/tests/system/datetime.js +5 -0
  58. data/frameworks/sproutcore/frameworks/foundation/tests/system/request.js +2 -2
  59. data/frameworks/sproutcore/frameworks/foundation/tests/system/user_defaults.js +1 -4
  60. data/frameworks/sproutcore/frameworks/foundation/tests/validators/validator.js +20 -0
  61. data/frameworks/sproutcore/frameworks/foundation/tests/views/image/ui.js +13 -0
  62. data/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +132 -0
  63. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/isVisibleInWindow.js +6 -3
  64. data/frameworks/sproutcore/frameworks/foundation/validators/validator.js +8 -5
  65. data/frameworks/sproutcore/frameworks/foundation/views/image.js +18 -5
  66. data/frameworks/sproutcore/frameworks/foundation/views/text_field.js +292 -21
  67. data/frameworks/sproutcore/frameworks/foundation/views/view.js +13 -14
  68. data/frameworks/sproutcore/frameworks/mini/license.js +28 -0
  69. data/frameworks/sproutcore/frameworks/runtime/core.js +35 -0
  70. data/frameworks/sproutcore/frameworks/runtime/mixins/enumerable.js +1 -1
  71. data/frameworks/sproutcore/frameworks/runtime/system/sparse_array.js +79 -5
  72. data/frameworks/sproutcore/frameworks/runtime/tests/mixins/enumerable.js +6 -6
  73. data/frameworks/sproutcore/frameworks/runtime/tests/system/sparse_array.js +53 -0
  74. data/frameworks/sproutcore/frameworks/testing/system/plan.js +4 -0
  75. data/frameworks/sproutcore/frameworks/testing/system/runner.js +1 -1
  76. data/frameworks/sproutcore/themes/standard_theme/english.lproj/radio.css +4 -0
  77. data/gen/design/Buildfile +23 -0
  78. data/gen/design/README +1 -0
  79. data/gen/design/USAGE +10 -0
  80. data/gen/design/templates/english.lproj/@filename@.js +16 -0
  81. data/gen/page/Buildfile +36 -0
  82. data/gen/page/README +1 -0
  83. data/gen/page/USAGE +15 -0
  84. data/gen/page/templates/pages/@target_name@/Buildfile +16 -0
  85. data/gen/page/templates/pages/@target_name@/core.js +22 -0
  86. data/gen/page/templates/pages/@target_name@/english.lproj/body.css +1 -0
  87. data/gen/page/templates/pages/@target_name@/english.lproj/body.rhtml +7 -0
  88. data/gen/page/templates/pages/@target_name@/english.lproj/strings.js +15 -0
  89. data/gen/view/README +1 -1
  90. data/gen/view/USAGE +5 -5
  91. data/lib/sproutcore/builders/base.rb +13 -2
  92. data/lib/sproutcore/builders/html.rb +28 -1
  93. data/lib/sproutcore/builders/minify.rb +84 -18
  94. data/lib/sproutcore/builders/test.rb +2 -1
  95. data/lib/sproutcore/helpers/entry_sorter.rb +16 -1
  96. data/lib/sproutcore/helpers/static_helper.rb +32 -4
  97. data/lib/sproutcore/helpers/tag_helper.rb +65 -0
  98. data/lib/sproutcore/models/manifest.rb +40 -6
  99. data/lib/sproutcore/models/target.rb +12 -3
  100. data/lib/sproutcore/rack/builder.rb +56 -4
  101. data/lib/sproutcore/tools/manifest.rb +1 -0
  102. data/lib/sproutcore/tools/server.rb +1 -0
  103. data/lib/sproutcore/tools.rb +21 -1
  104. data/lib/sproutcore.rb +13 -0
  105. 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.updateLayer = function() { callCount++; };
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, 'updateLayer should not run b/c its not visible');
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, 'updateLayer should exec now b/c isVisibleInWindow is YES');
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
- return this.prototype.fieldValueForObject(object,form,field) ;
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
- return this.prototype.objectForFieldValue(value,form,field) ;
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
- displayProperties: 'status'.w(),
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
- context.push('<span class="sc-hint">', hint, '</span>');
73
- if(this.get('isTextArea')){
74
- context.push('<textarea name="%@" %@ value="%@"></textarea>'.fmt(name, disabled, v));
75
- }else{
76
- context.push('<input type="%@" name="%@" %@ value="%@"></input>'.fmt(type, name, disabled, v));
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
- // if this is not first time rendering, update the hint itself since we
79
- // can't just blow away the text field like we might most other controls
80
- } else {
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
- this.$('.sc-hint').text(hint);
84
- }
85
- if(!this.get('isEnabled')){
86
- this.$field()[0].disabled='true';
87
- }else{
88
- this.$field()[0].disabled=null;
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() {