sproutcore 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. data/History.txt +233 -0
  2. data/Manifest.txt +67 -34
  3. data/bin/sc-build +12 -1
  4. data/bin/sc-gen +1 -1
  5. data/bin/sproutcore +14 -0
  6. data/clients/sc_docs/controllers/docs.js +38 -8
  7. data/clients/sc_docs/english.lproj/body.css +80 -127
  8. data/clients/sc_docs/english.lproj/body.rhtml +43 -23
  9. data/clients/sc_docs/english.lproj/no_docs.rhtml +2 -1
  10. data/clients/sc_docs/english.lproj/tabs.rhtml +16 -0
  11. data/clients/sc_docs/main.js +14 -9
  12. data/clients/sc_docs/models/doc.js +1 -1
  13. data/clients/sc_docs/tests/controllers/docs.rhtml +1 -2
  14. data/clients/sc_docs/tests/models/doc.rhtml +1 -2
  15. data/clients/sc_docs/tests/views/doc_frame.rhtml +1 -2
  16. data/clients/sc_docs/tests/views/doc_label_view.rhtml +1 -2
  17. data/clients/sc_docs/views/doc_frame.js +1 -1
  18. data/clients/sc_test_runner/controllers/runner.js +31 -8
  19. data/clients/sc_test_runner/english.lproj/body.css +62 -122
  20. data/clients/sc_test_runner/english.lproj/body.rhtml +62 -26
  21. data/clients/sc_test_runner/main.js +1 -6
  22. data/clients/sc_test_runner/models/test.js +14 -1
  23. data/clients/sc_test_runner/views/runner_frame.js +4 -2
  24. data/clients/view_builder/builders/builder.js +339 -0
  25. data/clients/view_builder/builders/button.js +81 -0
  26. data/clients/view_builder/controllers/document.js +21 -0
  27. data/clients/view_builder/core.js +19 -0
  28. data/clients/view_builder/english.lproj/body.css +77 -0
  29. data/clients/view_builder/english.lproj/body.rhtml +41 -0
  30. data/clients/{sc_docs → view_builder}/english.lproj/controls.css +0 -0
  31. data/clients/view_builder/english.lproj/strings.js +14 -0
  32. data/clients/view_builder/main.js +38 -0
  33. data/clients/view_builder/tests/controllers/document.rhtml +20 -0
  34. data/clients/view_builder/tests/views/builder.rhtml +20 -0
  35. data/clients/view_builder/views/builder.js +23 -0
  36. data/frameworks/prototype/prototype.js +1 -1
  37. data/frameworks/sproutcore/Core.js +32 -7
  38. data/frameworks/sproutcore/README +1 -1
  39. data/frameworks/sproutcore/animation/animation.js +411 -0
  40. data/frameworks/sproutcore/controllers/array.js +17 -9
  41. data/frameworks/sproutcore/controllers/collection.js +9 -110
  42. data/frameworks/sproutcore/controllers/controller.js +1 -1
  43. data/frameworks/sproutcore/controllers/object.js +2 -1
  44. data/frameworks/sproutcore/drag/drag.js +267 -56
  45. data/frameworks/sproutcore/drag/drag_data_source.js +24 -16
  46. data/frameworks/sproutcore/drag/drag_source.js +53 -42
  47. data/frameworks/sproutcore/drag/drop_target.js +2 -2
  48. data/frameworks/sproutcore/english.lproj/buttons.css +337 -236
  49. data/frameworks/sproutcore/english.lproj/core.css +115 -0
  50. data/frameworks/sproutcore/english.lproj/icons.css +227 -0
  51. data/{clients/sc_docs → frameworks/sproutcore}/english.lproj/images/indicator.gif +0 -0
  52. data/frameworks/sproutcore/english.lproj/images/sc-theme-sprite.png +0 -0
  53. data/frameworks/sproutcore/english.lproj/images/sc-theme-ysprite.png +0 -0
  54. data/frameworks/sproutcore/english.lproj/images/shared-icons.png +0 -0
  55. data/frameworks/sproutcore/english.lproj/menu.css +1 -1
  56. data/frameworks/sproutcore/english.lproj/strings.js +1 -1
  57. data/frameworks/sproutcore/english.lproj/theme.css +405 -31
  58. data/frameworks/sproutcore/foundation/application.js +15 -11
  59. data/frameworks/sproutcore/foundation/benchmark.js +1 -1
  60. data/frameworks/sproutcore/foundation/binding.js +2 -2
  61. data/frameworks/sproutcore/foundation/date.js +1 -1
  62. data/frameworks/sproutcore/foundation/error.js +1 -1
  63. data/frameworks/sproutcore/foundation/input_manager.js +32 -21
  64. data/frameworks/sproutcore/foundation/mock.js +1 -1
  65. data/frameworks/sproutcore/foundation/node_descriptor.js +9 -6
  66. data/frameworks/sproutcore/foundation/object.js +249 -177
  67. data/frameworks/sproutcore/foundation/page.js +5 -2
  68. data/frameworks/sproutcore/foundation/path_module.js +11 -10
  69. data/frameworks/sproutcore/foundation/responder.js +5 -2
  70. data/frameworks/sproutcore/foundation/routes.js +17 -13
  71. data/frameworks/sproutcore/foundation/run_loop.js +249 -11
  72. data/frameworks/sproutcore/foundation/server.js +1 -1
  73. data/frameworks/sproutcore/foundation/set.js +3 -3
  74. data/frameworks/sproutcore/foundation/string.js +5 -3
  75. data/frameworks/sproutcore/foundation/timer.js +371 -0
  76. data/frameworks/sproutcore/foundation/undo_manager.js +1 -1
  77. data/frameworks/sproutcore/foundation/unittest.js +3 -3
  78. data/frameworks/sproutcore/foundation/utils.js +161 -2
  79. data/frameworks/sproutcore/globals/panels.js +1 -1
  80. data/frameworks/sproutcore/globals/popups.js +4 -3
  81. data/frameworks/sproutcore/globals/window.js +44 -4
  82. data/frameworks/sproutcore/lib/button_views.rb +328 -0
  83. data/frameworks/sproutcore/lib/collection_view.rb +80 -0
  84. data/frameworks/sproutcore/lib/core_views.rb +281 -0
  85. data/frameworks/sproutcore/lib/form_views.rb +253 -0
  86. data/frameworks/sproutcore/lib/index.rhtml +2 -0
  87. data/frameworks/sproutcore/lib/menu_views.rb +88 -0
  88. data/frameworks/sproutcore/{foundation → mixins}/array.js +60 -29
  89. data/frameworks/sproutcore/mixins/control.js +265 -0
  90. data/frameworks/sproutcore/mixins/delegate_support.js +66 -0
  91. data/frameworks/sproutcore/{foundation → mixins}/observable.js +176 -6
  92. data/frameworks/sproutcore/mixins/scrollable.js +245 -0
  93. data/frameworks/sproutcore/mixins/selection_support.js +148 -0
  94. data/frameworks/sproutcore/mixins/validatable.js +152 -0
  95. data/frameworks/sproutcore/models/collection.js +5 -5
  96. data/frameworks/sproutcore/models/record.js +1 -1
  97. data/frameworks/sproutcore/models/store.js +1 -1
  98. data/frameworks/sproutcore/panes/dialog.js +1 -1
  99. data/frameworks/sproutcore/panes/manager.js +1 -1
  100. data/frameworks/sproutcore/panes/menu.js +1 -1
  101. data/frameworks/sproutcore/panes/overlay.js +2 -2
  102. data/frameworks/sproutcore/panes/panel.js +1 -1
  103. data/frameworks/sproutcore/panes/picker.js +1 -1
  104. data/frameworks/sproutcore/tests/controllers/array.rhtml +44 -4
  105. data/frameworks/sproutcore/tests/foundation/timer/invalidate.rhtml +33 -0
  106. data/frameworks/sproutcore/tests/foundation/timer/invokeLater.rhtml +145 -0
  107. data/frameworks/sproutcore/tests/foundation/timer/isPaused.rhtml +70 -0
  108. data/frameworks/sproutcore/tests/foundation/timer/schedule.rhtml +145 -0
  109. data/frameworks/sproutcore/tests/views/{scroll.rhtml → checkbox.rhtml} +3 -3
  110. data/frameworks/sproutcore/tests/views/{collection.rhtml → collection/base.rhtml} +33 -32
  111. data/frameworks/sproutcore/tests/views/collection/incremental_rendering.rhtml +260 -0
  112. data/frameworks/sproutcore/tests/views/image_cell.rhtml +19 -0
  113. data/frameworks/sproutcore/tests/views/label_item.rhtml +2 -4
  114. data/frameworks/sproutcore/tests/views/list.rhtml +2 -3
  115. data/frameworks/sproutcore/tests/views/list_item.rhtml +20 -0
  116. data/frameworks/sproutcore/tests/views/slider.rhtml +20 -0
  117. data/frameworks/sproutcore/tests/views/text_cell.rhtml +19 -0
  118. data/frameworks/sproutcore/tests/views/view/clippingFrame.rhtml +395 -0
  119. data/frameworks/sproutcore/tests/views/view/frame.rhtml +353 -0
  120. data/frameworks/sproutcore/tests/views/view/innerFrame.rhtml +347 -0
  121. data/frameworks/sproutcore/tests/views/view/isVisibleInWindow.rhtml +148 -0
  122. data/frameworks/sproutcore/tests/views/view/scrollFrame.rhtml +468 -0
  123. data/frameworks/sproutcore/validators/credit_card.js +33 -13
  124. data/frameworks/sproutcore/validators/date.js +26 -6
  125. data/frameworks/sproutcore/validators/email.js +21 -3
  126. data/frameworks/sproutcore/validators/not_empty.js +11 -1
  127. data/frameworks/sproutcore/validators/number.js +18 -4
  128. data/frameworks/sproutcore/validators/password.js +12 -1
  129. data/frameworks/sproutcore/validators/validator.js +204 -194
  130. data/frameworks/sproutcore/views/{button.js → button/button.js} +96 -94
  131. data/frameworks/sproutcore/views/button/checkbox.js +29 -0
  132. data/frameworks/sproutcore/views/button/disclosure.js +42 -0
  133. data/frameworks/sproutcore/views/button/radio.js +29 -0
  134. data/frameworks/sproutcore/views/{collection.js → collection/collection.js} +1373 -1024
  135. data/frameworks/sproutcore/views/collection/grid.js +124 -46
  136. data/frameworks/sproutcore/views/collection/image_cell.js +17 -46
  137. data/frameworks/sproutcore/views/collection/list.js +45 -35
  138. data/frameworks/sproutcore/views/collection/source_list.js +386 -0
  139. data/frameworks/sproutcore/views/collection/table.js +118 -0
  140. data/frameworks/sproutcore/views/container.js +7 -2
  141. data/frameworks/sproutcore/views/error_explanation.js +23 -10
  142. data/frameworks/sproutcore/views/{checkbox_field.js → field/checkbox_field.js} +16 -6
  143. data/frameworks/sproutcore/views/field/field.js +219 -0
  144. data/frameworks/sproutcore/views/{radio_field.js → field/radio_field.js} +27 -12
  145. data/frameworks/sproutcore/views/{select_field.js → field/select_field.js} +116 -90
  146. data/frameworks/sproutcore/views/{text_field.js → field/text_field.js} +57 -8
  147. data/frameworks/sproutcore/views/{textarea_field.js → field/textarea_field.js} +13 -3
  148. data/frameworks/sproutcore/views/filter_button.js +2 -2
  149. data/frameworks/sproutcore/views/form.js +3 -3
  150. data/frameworks/sproutcore/views/image.js +128 -21
  151. data/frameworks/sproutcore/views/inline_text_editor.js +1 -1
  152. data/frameworks/sproutcore/views/label.js +149 -92
  153. data/frameworks/sproutcore/views/list_item.js +225 -0
  154. data/frameworks/sproutcore/views/menu_item.js +10 -4
  155. data/frameworks/sproutcore/views/pagination.js +11 -4
  156. data/frameworks/sproutcore/views/popup_button.js +25 -21
  157. data/frameworks/sproutcore/views/popup_menu.js +10 -4
  158. data/frameworks/sproutcore/views/progress.js +29 -16
  159. data/frameworks/sproutcore/views/radio_group.js +1 -1
  160. data/frameworks/sproutcore/views/scroll.js +60 -20
  161. data/frameworks/sproutcore/views/segmented.js +1 -1
  162. data/frameworks/sproutcore/views/slider.js +132 -0
  163. data/frameworks/sproutcore/views/source_list_group.js +130 -0
  164. data/frameworks/sproutcore/views/spinner.js +1 -1
  165. data/frameworks/sproutcore/views/split.js +292 -0
  166. data/frameworks/sproutcore/views/split_divider.js +109 -0
  167. data/frameworks/sproutcore/views/tab.js +1 -1
  168. data/frameworks/sproutcore/views/toolbar.js +1 -1
  169. data/frameworks/sproutcore/views/view.js +1272 -591
  170. data/generators/client/templates/english.lproj/body.css +1 -1
  171. data/generators/controller/controller_generator.rb +1 -1
  172. data/generators/controller/templates/test.rhtml +2 -1
  173. data/generators/model/templates/test.rhtml +1 -1
  174. data/generators/test/templates/test.rhtml +1 -1
  175. data/generators/view/templates/test.rhtml +1 -1
  176. data/jsdoc/templates/sproutcore/class.tmpl +241 -338
  177. data/jsdoc/templates/sproutcore/default.css +105 -155
  178. data/jsdoc/templates/sproutcore/index.tmpl +43 -8
  179. data/jsdoc/templates/sproutcore/publish.js +9 -4
  180. data/lib/sproutcore/build_tools/html_builder.rb +29 -13
  181. data/lib/sproutcore/build_tools/resource_builder.rb +1 -1
  182. data/lib/sproutcore/bundle.rb +86 -25
  183. data/lib/sproutcore/jsdoc.rb +2 -0
  184. data/lib/sproutcore/version.rb +1 -1
  185. data/lib/sproutcore/view_helpers.rb +36 -3
  186. data/tasks/deployment.rake +1 -1
  187. metadata +69 -36
  188. data/clients/sc_docs/english.lproj/icons/small/next.png +0 -0
  189. data/clients/sc_docs/english.lproj/icons/small/reset.png +0 -0
  190. data/clients/sc_docs/english.lproj/images/gradients.png +0 -0
  191. data/clients/sc_docs/english.lproj/images/toolbar.png +0 -0
  192. data/clients/sc_docs/english.lproj/warning.rhtml +0 -6
  193. data/clients/sc_test_runner/english.lproj/warning.rhtml +0 -6
  194. data/frameworks/sproutcore/english.lproj/buttons.png +0 -0
  195. data/frameworks/sproutcore/english.lproj/collections.css +0 -82
  196. data/frameworks/sproutcore/english.lproj/images/buttons-sprite.png +0 -0
  197. data/frameworks/sproutcore/views/collection/collection_item.js +0 -36
  198. data/frameworks/sproutcore/views/collection/text_cell.js +0 -128
  199. data/frameworks/sproutcore/views/field.js +0 -214
  200. data/frameworks/sproutcore/views/workspace.js +0 -170
  201. data/generators/client/templates/english.lproj/controls.css +0 -0
  202. data/generators/framework/templates/english.lproj/body.css +0 -0
  203. data/generators/framework/templates/english.lproj/body.rhtml +0 -3
  204. data/generators/framework/templates/english.lproj/controls.css +0 -0
  205. data/lib/sproutcore/view_helpers/button_views.rb +0 -302
  206. data/lib/sproutcore/view_helpers/core_views.rb +0 -292
  207. data/lib/sproutcore/view_helpers/form_views.rb +0 -258
  208. data/lib/sproutcore/view_helpers/menu_views.rb +0 -94
@@ -22,6 +22,7 @@
22
22
  # :requires option in routes.rb.
23
23
  -%>
24
24
  <%= stylesheets_for_client %>
25
+ <%= @content_for_page_styles %>
25
26
  </head>
26
27
  <body class="<%= @theme || 'sc-theme' %>">
27
28
  <% #
@@ -50,6 +51,7 @@
50
51
  -%>
51
52
  <!-- Include Site Javascript -->
52
53
  <%= javascripts_for_client %>
54
+ <%= @content_for_page_javascript %>
53
55
 
54
56
  <% #
55
57
  # The following lines to the closing body tag must be included at the
@@ -0,0 +1,88 @@
1
+ ############################################################
2
+ # MENU VIEW HELPERS
3
+ #
4
+ # The view helpers defined in this file help you create popup menus. You can
5
+ # define a menu in your RHTML helper to be used somewhere else with code like
6
+ # this:
7
+ #
8
+
9
+ # This is the quick way to define a menu item. Use this approach if you just
10
+ # want to create a menu with item names and perform an action:
11
+ #
12
+ # <% menu_view :action_menu, :validate=>'My.controller.validate' do |m| %>
13
+ # <%= m.item :item_1, 'Item 1', :action => 'doSomething' %>
14
+ # <%= m.separator_item %>
15
+ # <%= m.item :item_2, 'Item 2', :action => 'doAnother Thing' %>
16
+ # <% end %>
17
+ #
18
+ require_helpers 'core_views'
19
+ require_helpers 'button_views'
20
+
21
+ # This will create a popup menu. You should define internal outlets
22
+ # for the menu items. More options to follow.
23
+ view_helper :popup_menu_view do
24
+ var :tag, 'ul'
25
+ view 'SC.PopupMenuView'
26
+ end
27
+
28
+ # Creates a menu item view. Normally you don't want to create these
29
+ # directly. Instead use the menu_view helpers.
30
+ #
31
+ # OPTIONS:
32
+ # :action =>
33
+ # The action to invoke when the menu item is selected.
34
+ #
35
+ # :label =>
36
+ # The label for the menu item.
37
+ #
38
+ # :icon =>
39
+ # The icon for the menu item. No icon will show if this is not set.
40
+ #
41
+ # :shortcut =>
42
+ # The shortcut key for this menu item. Shortcuts are only active when
43
+ # the anchorview the popup menu is attached to is part of the in-focus
44
+ # pane. Shortcuts should be named in the standard input manager
45
+ # syntax like this: alt_ctrl_shift_k (for Alt-Ctrl-Shift-K)
46
+ #
47
+ # Note that on the web, Cmd (on the Mac) and Ctrl are equivalent.
48
+ # Always use ctrl when defining shortcuts.
49
+ #
50
+ # :enabled (bindable) =>
51
+ # Determines if the menu item will be enabled or not. This is generally
52
+ # handled by your validate method or through bindings.
53
+ #
54
+ # :selected (bindable) =>
55
+ # Determines if the menu item is selected or not. May also be a mixed
56
+ # state. This is generally handled by your validate method or through
57
+ # bindings.
58
+ #
59
+ # :alt =>
60
+ # name another item in this menu that is the alternate form of the
61
+ # receiver. If the alt item is enabled, this one will be hidden and
62
+ # visa versa.
63
+ #
64
+ view_helper :menu_item_view, :extends => :button_view do
65
+
66
+ # JavaScript
67
+ view 'SC.MenuItemView'
68
+
69
+ # HTML
70
+ var :tag, 'li'
71
+ var(:shortcut) { |sc| sc.split('_').map { |x| x.capitalize } * '-' }
72
+ css_class_names << 'menu-item'
73
+
74
+ @my_href = @href || 'javascript:;'
75
+ @href = nil
76
+ @inner_html = [
77
+ %(<a href="#{@my_href}">),
78
+ '<span class="sel">&#x2713;</span>',
79
+ '<span class="mixed">-</span>',
80
+ '<span class="inner">',
81
+ @image,
82
+ %(<span class="label">#{@label}</span>),
83
+ '</span>',
84
+ %(<span class="shortcut">#{@shortcut}</span>),
85
+ '</a>'
86
+ ] * ''
87
+
88
+ end
@@ -1,9 +1,9 @@
1
1
  // ==========================================================================
2
2
  // SproutCore -- JavaScript Application Framework
3
- // copyright 2006-2007, Sprout Systems, Inc. and contributors.
3
+ // copyright 2006-2008, Sprout Systems, Inc. and contributors.
4
4
  // ==========================================================================
5
5
 
6
- require('foundation/observable');
6
+ require('mixins/observable');
7
7
 
8
8
  // Make Arrays observable
9
9
  Object.extend(Array.prototype, SC.Observable) ;
@@ -11,7 +11,7 @@ Object.extend(Array.prototype, SC.Observable) ;
11
11
  SC.OUT_OF_RANGE_EXCEPTION = "Index out of range" ;
12
12
 
13
13
  /**
14
- @namespace SC.Array
14
+ @namespace
15
15
 
16
16
  This module implements Observer-friendly Array-like behavior. This mixin is
17
17
  picked up by the Array class as well as other controllers, etc. that want to
@@ -125,8 +125,9 @@ SC.Array = {
125
125
  */
126
126
  removeAt: function(idx) {
127
127
  if ((idx < 0) || (idx >= this.get('length'))) throw SC.OUT_OF_RANGE_EXCEPTION;
128
+ var ret = this.objectAt(idx) ;
128
129
  this.replace(idx,1,[]);
129
- return this ;
130
+ return ret ;
130
131
  },
131
132
 
132
133
  /**
@@ -198,8 +199,35 @@ SC.Array = {
198
199
  if (ary.objectAt(loc) != this.objectAt(loc)) return false ;
199
200
  }
200
201
  return true ;
202
+ },
203
+
204
+ /**
205
+ Invoke the passed method and arguments on the member elements as long as
206
+ the value returned is the first argument.
207
+
208
+ @param {Object} retValue the expected return value
209
+ @param {String} methodName the method to call
210
+ @returns {Object} the return value of the last time the method was
211
+ invoked.
212
+ */
213
+ invokeWhile: function(retValue, methodName) {
214
+ var ret ;
215
+ var args = $A(arguments) ;
216
+ retValue = args.shift() ;
217
+ methodName = args.shift() ;
218
+
219
+ try {
220
+ this._each(function(item) {
221
+ var func = (item) ? item[methodName] : null ;
222
+ ret = func.apply(item, args) ;
223
+ if (ret != retValue) throw $break ;
224
+ });
225
+ } catch (e) {
226
+ if (e != $break) throw e ;
227
+ }
228
+ return ret ;
201
229
  }
202
-
230
+
203
231
  } ;
204
232
 
205
233
  // All arrays have the SC.Array mixin. Do this before we add the
@@ -279,21 +307,15 @@ Object.extend(Array.prototype, {
279
307
  return ret ;
280
308
  },
281
309
 
282
- map: function(iterator) {
283
- var ret = [] ;
284
- try {
285
- for(var index=0;index<this.length;index++) {
286
- var item = this[index] ;
287
- ret.push((iterator || Prototype.K).call(item,item,index)) ;
288
- } ;
289
- } catch (e) {
290
- if (e != $break) throw e ;
291
- }
292
- return ret ;
293
- },
294
-
295
- // This will invoke the passed method and arguments on the member elements
296
- // as long as the value returned is the first argument.
310
+ /*
311
+ Invoke the passed method and arguments on the member elements as long as
312
+ the value returned is the first argument.
313
+
314
+ @param {Object} retValue the expected return value
315
+ @param {String} methodName the method to call
316
+ @returns {Object} the return value of the last time the method was
317
+ invoked.
318
+ */
297
319
  invokeWhile: function(retValue, methodName) {
298
320
  var ret ;
299
321
  var args = $A(arguments) ;
@@ -313,6 +335,20 @@ Object.extend(Array.prototype, {
313
335
  return ret ;
314
336
  },
315
337
 
338
+ map: function(iterator) {
339
+ var ret = [] ;
340
+ try {
341
+ for(var index=0;index<this.length;index++) {
342
+ var item = this[index] ;
343
+ ret.push((iterator || Prototype.K).call(item,item,index)) ;
344
+ } ;
345
+ } catch (e) {
346
+ if (e != $break) throw e ;
347
+ }
348
+ return ret ;
349
+ },
350
+
351
+
316
352
  // If you ask for an unknown property, then try to collect the value
317
353
  // from member items.
318
354
  unknownProperty: function(key, value) {
@@ -328,17 +364,12 @@ Array.prototype.collect = Array.prototype.map ;
328
364
  // Returns the passed item as an array. If the item is already an array,
329
365
  // it is returned as is. If it is not an array, it is placed into one. If
330
366
  // it is null, an empty array is returned.
331
- Array.asArray = function (array)
332
- {
333
- if(array && !(array instanceof Array))
334
- {
367
+ Array.asArray = function (array) {
368
+ if(array &&
369
+ ((array.length === undefined) || ($type(array) == T_FUNCTION))) {
335
370
  return [array];
336
371
  }
337
- if(array)
338
- {
339
- return array;
340
- }
341
- return [];
372
+ return (array) ? array : [] ;
342
373
  };
343
374
 
344
375
  // Alias for asArray
@@ -0,0 +1,265 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ /**
7
+ Indicates a value has a mixed state of both on and off.
8
+ */
9
+ SC.MIXED_STATE = '__MIXED__' ;
10
+
11
+ /**
12
+ @namespace
13
+
14
+ A Control is a view that also implements some basic state functionality.
15
+ Apply this mixin to any view that you want to have standard control
16
+ functionality including showing a selected state, enabled state, focus
17
+ state, etc.
18
+
19
+ h2. About Values and Content
20
+
21
+ Controls typically are used to represent a single value, such as a number,
22
+ boolean or string. The value a control is managing is typically stored in
23
+ a "value" property. You will typically use the value property when working
24
+ with controls such as buttons and text fields in a form.
25
+
26
+ An alternative way of working with a control is to use it to manage some
27
+ specific aspect of a content object. For example, you might use a label
28
+ view control to display the "name" property of a Contact record. This
29
+ approach is often necessary when using the control as part of a collection
30
+ view.
31
+
32
+ You can use the content-approach to work with a control by setting the
33
+ "content" and "contentValueKey" properties of the control. The
34
+ "content" property is the content object you want to manage, while the
35
+ "contentValueKey" is the name of the property on the content object
36
+ you want the control to display.
37
+
38
+ The default implementation of the Control mixin will essentially map the
39
+ contentValueKey of a content object to the value property of the
40
+ control. Thus if you are writing a custom control yourself, you can simply
41
+ work with the value property and the content object support will come for
42
+ free. Just write an observer for the value property and update your
43
+ view accordingly.
44
+
45
+ If you are working with a control that needs to display multiple aspects
46
+ of a single content object (for example showing an icon and label), then
47
+ you can override the contentValueDidChange() method instead of observing
48
+ the value property. This method will be called anytime _any_ property
49
+ on the content object changes. You should use this method to check the
50
+ properties you care about on the content object and update your view if
51
+ anything you care about has changed.
52
+
53
+ h2. Delegate Support
54
+
55
+ Controls can optionally get the contentDisplayProperty from a
56
+ displayDelegate, if it is set. The displayDelegate is often used to
57
+ delegate common display-related configurations such as which content value
58
+ to show. Anytime your control is shown as part of a collection view, the
59
+ collection view will be automatically set as its displayDelegate.
60
+
61
+ */
62
+ SC.Control = {
63
+
64
+ initMixin: function() {
65
+ this._contentObserver(); // setup content observing if needed.
66
+ this.isSelectedObserver() ;
67
+ this.isEnabledObserver() ;
68
+ this.isFocusedObserver();
69
+ },
70
+
71
+ /**
72
+ Set to true when the item is selected.
73
+
74
+ This property is observable and bindable.
75
+ */
76
+ isSelected: false,
77
+ isSelectedBindingDefault: SC.Binding.OneWayBool,
78
+
79
+ /**
80
+ Set to true when the item is enabled.
81
+
82
+ This property is observable and bindable.
83
+ */
84
+ isEnabled: true,
85
+ isEnabledBindingDefault: SC.Binding.OneWayBool,
86
+
87
+ /**
88
+ The value represented by this control.
89
+
90
+ Most controls represent a value of some type, such as a number, string
91
+ or image URL. This property should hold that value. It is bindable
92
+ and observable. Changing this value will immediately change the
93
+ appearance of the control. Likewise, editing the control
94
+ will immediately change this value.
95
+
96
+ If instead of setting a single value on a control, you would like to
97
+ set a content object and have the control display a single property
98
+ of that control, then you should use the content property instead.
99
+ */
100
+ value: null,
101
+
102
+ /**
103
+ The content object represented by this control.
104
+
105
+ Often you need to use a control to display some single aspect of an
106
+ object, especially if you are using the control as an item view in a
107
+ collection view.
108
+
109
+ In those cases, you can set the content and contentValueKey for the
110
+ control. This will cause the control to observe the content object for
111
+ changes to the value property and then set the value of that property
112
+ on the "value" property of this object.
113
+
114
+ Note that unless you are using this control as part of a form or
115
+ collection view, then it would be better to instead bind the value of
116
+ the control directly to a controller property.
117
+ */
118
+ content: null,
119
+
120
+ /**
121
+ The property on the content object that would want to represent the
122
+ value of this control. This property should only be set before the
123
+ content object is first set. If you have a displayDelegate, then
124
+ you can also use the contentValueKey of the displayDelegate.
125
+ */
126
+ contentValueKey: null,
127
+
128
+ /**
129
+ Invoked whenever any property on the content object changes.
130
+
131
+ The default implementation will update the value property of the view
132
+ if the contentValueKey property has changed. You can override this
133
+ method to implement whatever additional changes you would like.
134
+
135
+ The key will typically contain the name of the property that changed or
136
+ '*' if the content object itself has changed. You should generally do
137
+ a total reset of '*' is changed.
138
+
139
+ @param {Object} target the content object
140
+ @param {String} key the property that changes
141
+ */
142
+ contentPropertyDidChange: function(target, key) {
143
+ if (!!this._contentValueKey && ((key == this._contentValueKey) || (key == '*'))) {
144
+ var content = this.get('content') ;
145
+ var value = (content) ? content.get(this._contentValueKey) : null;
146
+ if (value != this._contentValue) {
147
+ this._contentValue = value ;
148
+ this.set('value', value) ;
149
+ }
150
+ }
151
+ },
152
+
153
+ /**
154
+ Relays changes to the value back to the content object if you are using
155
+ a content object.
156
+
157
+ This observer is triggered whenever the value changes. It will only do
158
+ something if it finds you are using the content property and
159
+ contentValueKey and the new value does not match the old value of the
160
+ content object.
161
+
162
+ If you are using contentValueKey in some other way than typically
163
+ implemented by this mixin, then you may want to override this method as
164
+ well.
165
+ */
166
+ updateContentWithValueObserver: function() {
167
+ if (!this._contentValueKey) return; // do nothing if disabled
168
+
169
+ // get value. return if value matches current content value.
170
+ // this avoids infinite loops where setting the value from the content
171
+ // in turns sets the content and so on.
172
+ var value = this.get('value') ;
173
+ if (value == this._contentValue) return ;
174
+
175
+ var content = this.get('content') ;
176
+ if (!content) return; // do nothing if no content.
177
+
178
+ // passed all of our checks, update the content (and the _contentValue
179
+ // to avoid infinite loops)
180
+ this._contentValue = value ;
181
+ content.set(this._contentValueKey, value) ;
182
+
183
+ }.observes('value'),
184
+
185
+ /**
186
+ Default observer for selected state changes
187
+
188
+ The default will simply add either a "mixed" or "sel" class name to the
189
+ root element of your view based on the state. You can override this with
190
+ your own behavior if you prefer.
191
+ */
192
+ isSelectedObserver: function() {
193
+ var sel = this.get('isSelected') ;
194
+ this.setClassName('mixed', sel == SC.MIXED_STATE) ;
195
+ this.setClassName('sel', sel && (sel != SC.MIXED_STATE)) ;
196
+ }.observes('isSelected'),
197
+
198
+ /**
199
+ Default observer for the isEnabled state.
200
+
201
+ The default will simply add or remove a "disabled" class name to the root
202
+ element of your view based on the state. You can override this with your
203
+ own behavior if you prefer.
204
+ */
205
+ isEnabledObserver: function() {
206
+ var disabled = !this.get('isEnabled') ;
207
+ this.setClassName('disabled', disabled);
208
+
209
+ // set disabled attr as well if relevant
210
+ if (this.rootElement && (this.rootElement.disabled !== undefined) && (this.rootElement.disabled != disabled)) {
211
+ this.rootElement.disabled = disabled ;
212
+ }
213
+ }.observes('isEnabled'),
214
+
215
+ /**
216
+ Default observer for the isFirstResponder state.
217
+
218
+ The default will add or remove a "focus" class name ot the root element
219
+ of your view based on the state. You can override this with your own
220
+ behavior if you prefer.
221
+ */
222
+ isFocusedObserver: function() {
223
+ this.setClassName('focus', !!this.get('isFirstResponder')) ;
224
+ }.observes('isFirstResponder'),
225
+
226
+ // This should be null so that if content is also null, the
227
+ // _contentObserver won't do anything on init.
228
+ _content: null,
229
+
230
+ /** @private
231
+ Observes when a content object has changed and handles notifying
232
+ changes to the value of the content object.
233
+ */
234
+ _contentObserver: function() {
235
+ var content = this.get('content') ;
236
+ if (this._content == content) return; // nothing changed
237
+
238
+ // create bound observer function
239
+ if (!this._boundContentPropertyDidChangeObserver) {
240
+ this._boundContentPropertyDidChangeObserver = this.contentPropertyDidChange.bind(this) ;
241
+ }
242
+ var f = this._boundContentPropertyDidChangeObserver ;
243
+
244
+ // remove an observer from the old content if necessary
245
+ if (this._content && this._content.removeObserver) {
246
+ this._content.removeObserver('*', f) ;
247
+ }
248
+
249
+ // cache for future use
250
+ var del = this.displayDelegate ;
251
+ this._contentValueKey = this.getDelegateProperty(del, 'contentValueKey');
252
+
253
+
254
+ // add observer to new content if necessary.
255
+ this._content = content ;
256
+ if (this._content && this._content.addObserver) {
257
+ this._content.addObserver('*', f) ;
258
+ }
259
+
260
+ // notify that value did change.
261
+ this.contentPropertyDidChange(this._content, '*') ;
262
+
263
+ }.observes('content')
264
+
265
+ };