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
@@ -1,10 +1,11 @@
1
1
  // ========================================================================
2
2
  // SproutCore
3
- // copyright 2006-2007 Sprout Systems, Inc.
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
4
  // ========================================================================
5
5
 
6
6
  require('controllers/controller') ;
7
- require('foundation/array') ;
7
+ require('mixins/array') ;
8
+ require('mixins/selection_support') ;
8
9
  require('foundation/binding') ;
9
10
 
10
11
  /** @class
@@ -18,7 +19,7 @@ until you call commitChanges().
18
19
  @extends SC.Array
19
20
 
20
21
  */
21
- SC.ArrayController = SC.Controller.extend(SC.Array,
22
+ SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
22
23
  /** @scope SC.ArrayController.prototype */
23
24
  {
24
25
  /**
@@ -26,7 +27,7 @@ SC.ArrayController = SC.Controller.extend(SC.Array,
26
27
  @property
27
28
  @type {SC.ArrayController}
28
29
  */
29
- arrangedObjects: function() { return this; }.property(),
30
+ arrangedObjects: function() { return this; }.property('content'),
30
31
 
31
32
  /**
32
33
  The array content that is being managed by the controller.
@@ -52,8 +53,12 @@ SC.ArrayController = SC.Controller.extend(SC.Array,
52
53
  @observes content
53
54
  */
54
55
  _contentObserver: function() {
56
+ this.beginPropertyChanges();
55
57
  this.contentCloneReset();
56
58
  this.arrayContentDidChange() ;
59
+ this.notifyPropertyChange('length') ;
60
+ this.updateSelectionAfterContentChange();
61
+ this.endPropertyChanges() ;
57
62
  }.observes('content'),
58
63
 
59
64
  /**
@@ -114,13 +119,14 @@ SC.ArrayController = SC.Controller.extend(SC.Array,
114
119
 
115
120
  // and record additions
116
121
  if (!this._changelog) this._changelog = [];
117
- this._changelog.push({ idx: idx, amt: amt, objects: objects });
122
+ this._changelog.push({ idx: idx, amt: amt, objects: sourceObjects });
118
123
 
119
124
  // then actually perform the edit on the contentClone
120
- contentClone.replace(idx, amt, objects);
125
+ contentClone.replace(idx, amt, sourceObjects);
121
126
 
122
127
  this.editorDidChange() ;
123
128
  this.arrayContentDidChange();
129
+ this.updateSelectionAfterContentChange();
124
130
 
125
131
  return this;
126
132
  },
@@ -132,7 +138,8 @@ SC.ArrayController = SC.Controller.extend(SC.Array,
132
138
  * return null.
133
139
  */
134
140
  objectAt: function(idx) {
135
- var obj = this._getSourceContent().objectAt(idx);
141
+ var obj = this._getSourceContent() ;
142
+ obj = (obj && obj.objectAt) ? obj.objectAt(idx) : null;
136
143
  return this._objectControllerFor(obj) ;
137
144
  },
138
145
  /**
@@ -141,7 +148,8 @@ SC.ArrayController = SC.Controller.extend(SC.Array,
141
148
  * @type {integer}
142
149
  */
143
150
  length: function( key, value ) {
144
- return this._getSourceContent().get('length');
151
+ var ret = this._getSourceContent() ;
152
+ return (ret && ret.get) ? (ret.get('length') || 0) : 0 ;
145
153
  }.property(),
146
154
 
147
155
  /**
@@ -237,7 +245,7 @@ SC.ArrayController = SC.Controller.extend(SC.Array,
237
245
  will be returned.
238
246
  */
239
247
  _sourceObjectFor: function(obj) {
240
- return (obj && obj.__isArrayController) ? obj.get('content') : obj ;
248
+ return (obj && obj.kindOf && obj.kindOf(SC.Controller)) ? obj.get('content') : obj ;
241
249
  }
242
250
 
243
251
  });
@@ -1,8 +1,9 @@
1
1
  // ========================================================================
2
2
  // SproutCore
3
- // copyright 2006-2007 Sprout Systems, Inc.
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
4
  // ========================================================================
5
5
 
6
+ require('mixins/selection_support') ;
6
7
  require('controllers/object') ;
7
8
 
8
9
  /** @class
@@ -15,7 +16,7 @@ require('controllers/object') ;
15
16
 
16
17
  @extends SC.ObjectController
17
18
  */
18
- SC.CollectionController = SC.ObjectController.extend(
19
+ SC.CollectionController = SC.ObjectController.extend(SC.SelectionSupport,
19
20
  /** @scope SC.CollectionController.prototype */
20
21
  {
21
22
 
@@ -30,85 +31,7 @@ SC.CollectionController = SC.ObjectController.extend(
30
31
  */
31
32
  arrangedObjects: [],
32
33
 
33
- /**
34
- @property {Array} selection
35
- This is the current selection. You can make this selection and another
36
- controller's selection work in concert by binding them together. You
37
- generally have a master selection that relays changes TO all the others.
38
- */
39
- selection: function(key,value)
40
- {
41
- if (value !== undefined)
42
- {
43
- // selection must be an array... force if needed....
44
- value = (value) ? ((value instanceof Array) ? value : [value]) : [];
45
-
46
- var allowsSelection = this.get('allowsSelection');
47
- var allowsEmptySelection = this.get('allowsEmptySelection');
48
- var allowsMultipleSelection = this.get('allowsMultipleSelection');
49
-
50
- // are we even allowing selection at all?
51
- // if not, then bail out.
52
- if ( !allowsSelection ) return this._selection;
53
-
54
- // ok, new decide if the *type* of seleciton is allowed...
55
- switch ( value.length )
56
- {
57
- case 0:
58
- // check to see if we're attemting to set an empty array
59
- // if that's not allowed, set to the first available item in arrangedObjects
60
- this._selection = allowsEmptySelection ? value : (this.get('arrangedObjects') || []).first();
61
- break;
62
- case 1:
63
- // check to see id we've just taken focus away from a new record
64
- // if so, queue up the callback to be triggered...
65
- if ( this._editingNewRecord && (this._editingNewRecord != value[0]) )
66
- {
67
- setTimeout(this._newRecordDidLoseFocus.bind(this,this._editingNewRecord),1);
68
- this._editingNewRecord = null;
69
- }
70
- this._selection = value;
71
- break;
72
- default:
73
- // fall through for >= 2 items...
74
- // only allow if configured for multi-select
75
- this._selection = allowsMultipleSelection ? value : this._selection;
76
- break;
77
- }
78
-
79
- if ( this._selection && (this._selection.length > 1) )
80
- {
81
- var collection = this.get('content');
82
- var order = collection ? collection.get('orderBy') : null;
83
- if ( !order ) order = ['guid'];
84
- this._selection = this._selection.sort(function(a,b) { return a.compareTo(b,order); });
85
- }
86
- }
87
-
88
- return this._selection;
89
- }.property(),
90
-
91
- /**
92
- If true, selection is allowed.
93
34
 
94
- @type bool
95
- */
96
- allowsSelection: true,
97
-
98
- /**
99
- If true, multiple selection is allowed.
100
-
101
- @type bool
102
- */
103
- allowsMultipleSelection: true,
104
-
105
- /**
106
- If true, allow empty selection
107
-
108
- @type bool
109
- */
110
- allowsEmptySelection: true,
111
-
112
35
  /**
113
36
  If true, new, add, remove will work.
114
37
 
@@ -167,11 +90,10 @@ SC.CollectionController = SC.ObjectController.extend(
167
90
  try {
168
91
  if (content.newRecord) {
169
92
  var rec = content.newRecord(settings) ;
170
- var controller = this ;
171
- setTimeout(function() {
172
- controller.set('selection',(rec) ? [rec] : []) ;
173
- controller._editingNewRecord = rec ;
174
- },1) ;
93
+ var t = function() {
94
+ this.set('selection',(rec) ? [rec] : []) ;
95
+ this._editingNewRecord = rec ;
96
+ }.invokeLater(this, 1) ;
175
97
  return rec;
176
98
  }
177
99
  }
@@ -273,33 +195,10 @@ SC.CollectionController = SC.ObjectController.extend(
273
195
  value = Array.asArray(target.get(key)) ;
274
196
 
275
197
  this.set('arrangedObjects',value.slice()) ;
276
- // update selection.
277
- var objects = this.get('arrangedObjects') || [] ;
278
- if (!(objects instanceof Array)) objects = [objects] ;
279
198
 
280
- var currentSelection = this.get('selection') || [] ;
281
- var sel = [] ;
199
+ // update selection.
200
+ this.updateSelectionAfterContentChange() ;
282
201
 
283
- // the new selection is the current selection that exists in
284
- // arrangedObjects.
285
- currentSelection.each(function(obj) {
286
- if (objects.include(obj)) sel.push(obj) ;
287
- }) ;
288
-
289
- // make new selection match current selection settings.
290
- if (!this.allowsSelection) {
291
- sel = [] ;
292
- }
293
-
294
- if ((sel.length>1) && !this.allowsMultipleSelection) {
295
- sel = [sel[0]] ;
296
- }
297
-
298
- if ((sel.length == 0) && !this.allowsEmptySelection) {
299
- if (objects.length > 0) sel = [objects[0]] ;
300
- }
301
-
302
- this.set('selection',sel) ;
303
202
  }.observes('records')
304
203
 
305
204
  }) ;
@@ -1,6 +1,6 @@
1
1
  // ========================================================================
2
2
  // SproutCore
3
- // copyright 2006-2007 Sprout Systems, Inc.
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
4
  // ========================================================================
5
5
 
6
6
  require('foundation/object') ;
@@ -1,6 +1,6 @@
1
1
  // ========================================================================
2
2
  // SproutCore
3
- // copyright 2006-2007 Sprout Systems, Inc.
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
4
  // ========================================================================
5
5
 
6
6
  require('controllers/controller') ;
@@ -134,6 +134,7 @@ SC.ObjectController = SC.Controller.extend(
134
134
  var oldValue = content.get ? content.get(key) : content[key];
135
135
  var newValue = this._changes[key];
136
136
 
137
+ if (oldValue == null && newValue == '') newValue = null;
137
138
  if (newValue != oldValue)
138
139
  {
139
140
  (content.set) ? content.set('isDirty', true) : (content['isDirty'] = true);
@@ -1,6 +1,6 @@
1
1
  // ========================================================================
2
2
  // SproutCore
3
- // copyright 2006-2007 Sprout Systems, Inc.
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
4
  // ========================================================================
5
5
 
6
6
  require('Core') ;
@@ -8,6 +8,7 @@ require('views/view') ;
8
8
 
9
9
  SC.DRAG_LINK = 0x0004; SC.DRAG_COPY = 0x0001; SC.DRAG_MOVE = 0x0002;
10
10
  SC.DRAG_NONE = 0x0000; SC.DRAG_ANY = 0x0007 ;
11
+ SC.DRAG_AUTOSCROLL_ZONE_THICKNESS = 20 ;
11
12
 
12
13
  /**
13
14
 
@@ -20,57 +21,49 @@ SC.DRAG_NONE = 0x0000; SC.DRAG_ANY = 0x0007 ;
20
21
  To initiate a drag, you should call SC.Drag.start() with the options below
21
22
  specified in a hash. Pass the ones you need to get the drag you want:
22
23
 
23
- {{{
24
- event: (req)
25
- The mouse event that triggered the drag. This will be used to
26
- position the element.
27
-
28
- source: (req)
29
- The drag source object that should be consulted during the drag operations.
30
- This is usually the container view that initiated the drag.
31
-
32
- dragView: (req)
33
- This should point to a view that will be used as the source image for
34
- the drag. The drag operation will clone the DOM elements for this
35
- view and add the class name 'drag-image' to the outermost element.
36
-
37
- ghost: YES | NO
38
- if YES or not passed, then drag view image will show, but the source
39
- dragView will not be hidden. set to NO to make it appear that the
40
- dragView itself is being dragged around.
41
-
42
- slideBack: YES | NO
43
- If YES or not specified, then if the drag operation is cancelled, the
44
- dragView will slide back to its source origin.
45
-
46
- origin:
47
- If passed, this will be used as the origin point for the ghostView
48
- when it slides back. You normally do not need to pass this unless
49
- the ghost view does not appear in the main UI.
50
-
51
- data:
52
- Optional hash of data types and values. You can use this to pass a
53
- static set of data instead of providing a dataSource. If you provide
24
+ - *event: (req)* The mouse event that triggered the drag. This will be used
25
+ to position the element.
26
+
27
+ - *source: (req)* The drag source object that should be consulted during
28
+ the drag operations. This is usually the container view that initiated
29
+ the drag.
30
+
31
+ - *dragView: (req)* This should point to a view that will be used as the
32
+ source image for the drag. The drag operation will clone the DOM elements
33
+ for this view and add the class name 'drag-image' to the outermost
34
+ element.
35
+
36
+ - *ghost: YES | NO* If YES or not passed, then drag view image will show,
37
+ but the source dragView will not be hidden. set to NO to make it appear
38
+ that the dragView itself is being dragged around.
39
+
40
+ - *slideBack: YES | NO* If YES or not specified, then if the drag
41
+ operation is cancelled, the dragView will slide back to its source
42
+ origin.
43
+
44
+ - *origin:* If passed, this will be used as the origin point for the
45
+ ghostView when it slides back. You normally do not need to pass this
46
+ unless the ghost view does not appear in the main UI.
47
+
48
+ - *data:* Optional hash of data types and values. You can use this to pass
49
+ a static set of data instead of providing a dataSource. If you provide
54
50
  a dataSource, it will override this.
55
51
 
56
- dataSource:
57
- Optional object that will provide the data for the drag to be consumed
58
- by the drop target. If you do not pass this parameter or the data
59
- hash, then the source object will be used if it implements the
52
+ - *dataSource:* Optional object that will provide the data for the drag to
53
+ be consumed by the drop target. If you do not pass this parameter or the
54
+ data hash, then the source object will be used if it implements the
60
55
  SC.DragDataSource protocol.
61
56
 
62
- anchorView:
63
- if you pass this optional view, then the drag will only be allowed to
64
- happen within this view. The ghostView will actually be added as a
65
- child of this view during the drag. Normally the anchorView is the
57
+ - *anchorView:* if you pass this optional view, then the drag will only be
58
+ allowed to happen within this view. The ghostView will actually be added
59
+ as a child of this view during the drag. Normally the anchorView is the
66
60
  window.
67
- }}}
68
61
 
69
62
  @extends SC.Object
70
63
  */
71
64
  SC.Drag = SC.Object.extend(
72
- /** @scope SC.Drag.prototype */
73
- {
65
+ /** @scope SC.Drag.prototype */ {
66
+
74
67
  /** [RO] The source object used to coordinate this drag. */
75
68
  source: null,
76
69
 
@@ -120,7 +113,7 @@ SC.Drag = SC.Object.extend(
120
113
  //
121
114
 
122
115
  /**
123
- (Property) Data types supported by this drag operation.
116
+ Data types supported by this drag operation.
124
117
 
125
118
  Returns an array of data types supported by the drag source. This may
126
119
  be generated dynamically depending on the data source.
@@ -132,7 +125,7 @@ SC.Drag = SC.Object.extend(
132
125
  on your dragEntered() and prepareForDragOperation() methods to determine
133
126
  if you can handle any of the data types offered up by the drag source.
134
127
 
135
- @returns {Array} available data types
128
+ @field {Array} available data types
136
129
  */
137
130
  dataTypes: function() {
138
131
 
@@ -220,6 +213,9 @@ SC.Drag = SC.Object.extend(
220
213
  */
221
214
  data: null,
222
215
 
216
+ // required by autoscroll
217
+ _dragInProgress: YES,
218
+
223
219
  // this will actually start the drag process.
224
220
  startDrag: function() {
225
221
 
@@ -227,8 +223,11 @@ SC.Drag = SC.Object.extend(
227
223
  this._createGhostView() ;
228
224
 
229
225
  // compute the offset from the original mouse location.
230
- var origin = this.dragView.convertFrameToView(this.dragView.get('origin'), null);
226
+ var origin = this.dragView.convertFrameToView(this.dragView.get('frame'), null);
231
227
  var pointer = Event.pointerLocation(this.event) ;
228
+
229
+ window.dragEvent = this.event ;
230
+
232
231
  this.ghostOffset = { x: (pointer.x-origin.x), y: (pointer.y-origin.y) };
233
232
 
234
233
  // position the ghost view.
@@ -254,10 +253,11 @@ SC.Drag = SC.Object.extend(
254
253
  */
255
254
  mouseDragged: function(evt) {
256
255
 
257
- // ignore duplicate calls.
258
256
  var loc = Event.pointerLocation(evt) ;
257
+ var scrolled = this._autoscroll(evt) ;
259
258
 
260
- if ((loc.x == this._lastLoc.x) && (loc.y == this._lastLoc.y)) return ;
259
+ // ignore duplicate calls.
260
+ if (!scrolled && (loc.x == this._lastLoc.x) && (loc.y == this._lastLoc.y)) return ;
261
261
  this._lastLoc = loc ;
262
262
  this.set('location', loc) ;
263
263
 
@@ -372,18 +372,25 @@ SC.Drag = SC.Object.extend(
372
372
  this.source.dragDidEnd(this, loc, op) ;
373
373
  }
374
374
 
375
+ this._dragInProgress = NO ; // required by autoscroll.
376
+
375
377
  },
376
378
 
377
379
  // ..........................................
378
380
  // PRIVATE PROPERTIES AND METHODS
379
381
  //
380
382
 
383
+ _ghostViewClass: SC.View.extend({
384
+ emptyElement: '<div class="sc-ghost-view"></div>'
385
+ }),
386
+
381
387
  // positions the ghost view underneath the mouse with the initial offset
382
388
  // recorded by when the drag started.
383
389
  _positionGhostView: function(evt) {
384
- var loc = { x: Event.pointerX(evt), y: Event.pointerY(evt) } ;
390
+ var loc = Event.pointerLocation(evt) ;
385
391
  loc.x -= this.ghostOffset.x ;
386
392
  loc.y -= this.ghostOffset.y ;
393
+ loc = this._ghostView.convertFrameFromView(loc, null) ;
387
394
  this._ghostView.set('origin', loc) ;
388
395
  },
389
396
 
@@ -397,8 +404,7 @@ SC.Drag = SC.Object.extend(
397
404
  // create the ghost view instance add ghost class name.
398
405
  this._ghostView = this._ghostViewClass.viewFor(el) ;
399
406
  this._ghostView.owner = this ;
400
- this._ghostView.addClassName('ghost') ;
401
- this._ghostView.set('isPositioned', true) ; // make absolute position.
407
+ this._ghostView.addClassName('sc-ghost-view') ;
402
408
 
403
409
  // add to bottom of main document body and to window.
404
410
  SC.window.appendChild(this._ghostView) ;
@@ -451,7 +457,7 @@ SC.Drag = SC.Object.extend(
451
457
  // target area.
452
458
  _findDropTarget: function(evt) {
453
459
  var dt = this._dropTargets() ;
454
- var loc = { x: Event.pointerX(evt), y: Event.pointerY(evt) } ;
460
+ var loc = Event.pointerLocation(evt) ;
455
461
 
456
462
  var ret = null ;
457
463
  for(var idx=0;idx<dt.length;idx++) {
@@ -460,7 +466,7 @@ SC.Drag = SC.Object.extend(
460
466
  if(!t.get('isVisibleInWindow')) continue ;
461
467
 
462
468
  // get frame, converted to view.
463
- var f = t.convertFrameToView(t.get('frame'), null) ;
469
+ var f = t.convertFrameToView(t.get('clippingFrame'), null) ;
464
470
 
465
471
  // check to see if loc is inside. If so, then make this the drop
466
472
  // target unless there is a drop target and the current one
@@ -479,13 +485,201 @@ SC.Drag = SC.Object.extend(
479
485
  return null ;
480
486
  },
481
487
 
482
- _ghostViewClass: SC.View.extend({})
488
+ // ............................................
489
+ // AUTOSCROLLING
490
+ //
491
+
492
+ // Performs auto-scrolling for the drag. This will only do anything if
493
+ // the user keeps the mouse within a few pixels of one location for a little
494
+ // while.
495
+ //
496
+ // Returns true if a scroll was performed
497
+ _autoscroll: function(evt) {
498
+
499
+ // If drag has ended, exit
500
+ if (!this._dragInProgress) return ;
501
+
502
+ // STEP 1: Find the first view that we can actually scroll. This view
503
+ // must be:
504
+ // - scrollable
505
+ // - the mouse pointer must be within a scrolling hot zone
506
+ // - there must be room left to scroll in that direction.
507
+
508
+ // NOTE: an event is passed only when called from mouseDragged
509
+ var loc = (evt) ? Event.pointerLocation(evt) : this._lastMouseLocation ;
510
+ if (!loc) return false ;
511
+ this._lastMouseLocation = loc ;
512
+
513
+ var view = this._findScrollableView(loc) ;
514
+
515
+ // these will become either 1 or -1 to indicate scroll direction or 0 for no scroll.
516
+ var verticalScroll, horizontalScroll ;
517
+ var min, max, edge ;
518
+ var scrollableView = null;
519
+
520
+ while(view && !scrollableView) {
521
+
522
+ // quick check...can we scroll this view right now?
523
+ verticalScroll = view.get('hasVerticalScroller') ? 1 : 0;
524
+ horizontalScroll = view.get('hasHorizontalScroller') ? 1 : 0;
525
+
526
+ // at least one direction might be scrollable. Collect some extra
527
+ // info to investigate further.
528
+ if ((verticalScroll != 0) || (horizontalScroll != 0)) {
529
+ var f = view.convertFrameToView(view.get('frame'), null) ;
530
+ var innerSize = view.get('innerFrame') ;
531
+ var scrollFrame = view.get('scrollFrame') ;
532
+ }
533
+
534
+
535
+ if (verticalScroll != 0) {
536
+
537
+ // bottom hotzone?
538
+ max = SC.maxY(f); min = max - SC.DRAG_AUTOSCROLL_ZONE_THICKNESS ;
539
+ edge = SC.maxY(scrollFrame) ;
540
+
541
+ if ((edge >= innerSize.height) && (loc.y >= min) && (loc.y <= max)) {
542
+ verticalScroll = 1 ;
543
+
544
+ // no...how about top hotzone?
545
+ } else {
546
+ min = SC.minY(f); max = min + SC.DRAG_AUTOSCROLL_ZONE_THICKNESS ;
547
+ edge = SC.minY(scrollFrame) ;
548
+ if ((edge <= innerSize.height) && (loc.y >= min) && (loc.y <= max)) {
549
+ verticalScroll = -1 ;
550
+
551
+ // no, ok don't scroll vertical
552
+ } else verticalScroll = 0 ;
553
+ }
554
+ }
555
+
556
+ if (horizontalScroll != 0) {
557
+ // right hotzone?
558
+ max = SC.maxX(f); min = max - SC.DRAG_AUTOSCROLL_ZONE_THICKNESS ;
559
+ edge = SC.maxX(scrollFrame) ;
560
+ if ((edge >= innerSize.width) && (loc.x >= min) && (loc.x <= max)) {
561
+ horizontalScroll = 1 ;
562
+
563
+ // no...how about left hotzone?
564
+ } else {
565
+ min = SC.minY(f); max = min + SC.DRAG_AUTOSCROLL_ZONE_THICKNESS ;
566
+ edge = SC.minY(scrollFrame) ;
567
+ if ((edge <= innerSize.width) && (loc.x >= min) && (loc.x <= max)) {
568
+ horizontalScroll = -1 ;
569
+
570
+ // no, ok don't scroll vertical
571
+ } else horizontalScroll = 0 ;
572
+ }
573
+ }
574
+
575
+ // if we can scroll, then set this.
576
+ if ((verticalScroll != 0) || (horizontalScroll != 0)) {
577
+ scrollableView = view ;
578
+ } else view = this._findNextScrollableView(view) ;
579
+ }
580
+
581
+
582
+ // STEP 2: Only scroll if the user remains within the hot-zone for a period of
583
+ // time
584
+ if (scrollableView && (this._lastScrollableView == scrollableView)) {
585
+ if ((Date.now() - this._hotzoneStartTime) > 100) {
586
+ this._horizontalScrollAmount *= 1.05 ;
587
+ this._verticalScrollAmount *= 1.05 ;
588
+ }
589
+
590
+ // otherwise, reset everything and disallow scroll
591
+ } else {
592
+ this._lastScrollableView = scrollableView ;
593
+ this._horizontalScrollAmount = 15 ;
594
+ this._verticalScrollAmount = 15 ;
595
+ this._hotzoneStartTime = (scrollableView) ? Date.now() : null ;
596
+
597
+ horizontalScroll = verticalScroll = 0 ;
598
+ }
599
+
600
+ // STEP 3: Scroll!
601
+ if (scrollableView && ((horizontalScroll != 0) || (verticalScroll != 0))) {
602
+ var scroll = {
603
+ x: horizontalScroll * this._horizontalScrollAmount,
604
+ y: verticalScroll * this._verticalScrollAmount
605
+ } ;
606
+
607
+ scrollableView.scrollBy(scroll) ;
608
+
609
+ }
610
+
611
+ // If a scrollable view was found, then reschedule
612
+ if (scrollableView) {
613
+ this.invokeLater('_autoscroll', 100, null);
614
+ return true ;
615
+ } else return false ;
616
+ },
617
+
618
+ // Returns an array of scrollable views, sorted with nested scrollable
619
+ // views at the top of the array. The first time this method is called
620
+ // during a drag, it will reconstrut this array using the current ste of
621
+ // scrollable views. Afterwards it uses the cached set until the drop
622
+ // completes.
623
+ _scrollableViews: function() {
624
+ if (this._cachedScrollableView) return this._cachedScrollableView ;
625
+ var ret = [];
626
+
627
+ // build array of drop targets
628
+ var dt = SC.Drag._scrollableViews ;
629
+ for(var key in dt) {
630
+ if (!dt.hasOwnProperty(key)) continue ;
631
+ ret.push(dt[key]) ;
632
+ }
633
+
634
+ // now resort. This custom function will sort nested drop targets
635
+ // at the start of the list.
636
+ ret = ret.sort(function(a,b) {
637
+ var view = a;
638
+ while((view = view.parentNode) && (view != SC.window)) {
639
+ if (b == view) return -1 ;
640
+ }
641
+ return 1;
642
+ }) ;
643
+
644
+ this._cachedScrollableView = ret ;
645
+
646
+ return ret ;
647
+ },
648
+
649
+ // This will search through the scrollable views, looking for one in the
650
+ // target area.
651
+ _findScrollableView: function(loc) {
652
+ var dt = this._scrollableViews() ;
653
+
654
+ var ret = null ;
655
+ for(var idx=0;idx<dt.length;idx++) {
656
+ var t = dt[idx] ;
657
+
658
+ if(!t.get('isVisibleInWindow')) continue ;
659
+
660
+ // get frame, converted to view.
661
+ var f = t.convertFrameToView(t.get('frame'), null) ;
662
+
663
+ // check to see if loc is inside.
664
+ if (SC.pointInRect(loc, f)) return t;
665
+ }
666
+ return null ;
667
+ },
668
+
669
+ // Search the parent nodes of the target to find another scrollable view.
670
+ // return null if none is found.
671
+ _findNextScrollableView: function(view) {
672
+ while ((view = view.parentNode) && (view != SC.window)) {
673
+ if (SC.Drag._scrollableViews[view._guid]) return view ;
674
+ }
675
+ return null ;
676
+ }
677
+
483
678
 
484
679
  }) ;
485
680
 
486
681
  SC.Drag.mixin(
487
- /** @scope SC.Drag */
488
- {
682
+ /** @scope SC.Drag */ {
489
683
 
490
684
 
491
685
  /**
@@ -502,6 +696,23 @@ SC.Drag.mixin(
502
696
  },
503
697
 
504
698
  _dropTargets: {},
699
+ _scrollableViews: {},
700
+
701
+ /**
702
+ Register the view object as a scrollable view. These views will auto-scroll
703
+ during a drag.
704
+ */
705
+ addScrollableView: function(target) {
706
+ this._scrollableViews[target._guid] = target ;
707
+ },
708
+
709
+ /**
710
+ Remove the view object as a scrollable view. These views will auto-scroll
711
+ during a drag.
712
+ */
713
+ removeScrollableView: function(target) {
714
+ delete this._scrollableViews[target._guid] ;
715
+ },
505
716
 
506
717
  /**
507
718
  Register the view object as a drop target.