sproutcore 0.9.4 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/History.txt +70 -2
  2. data/Manifest.txt +3 -15
  3. data/config/hoe.rb +1 -1
  4. data/frameworks/sproutcore/animation/animation.js +411 -0
  5. data/frameworks/sproutcore/controllers/array.js +68 -21
  6. data/frameworks/sproutcore/controllers/object.js +21 -2
  7. data/frameworks/sproutcore/drag/drag.js +13 -4
  8. data/frameworks/sproutcore/drag/drop_target.js +26 -19
  9. data/frameworks/sproutcore/english.lproj/core.css +4 -0
  10. data/frameworks/sproutcore/english.lproj/strings.js +5 -0
  11. data/frameworks/sproutcore/english.lproj/theme.css +5 -0
  12. data/frameworks/sproutcore/foundation/application.js +1 -2
  13. data/frameworks/sproutcore/foundation/set.js +31 -12
  14. data/frameworks/sproutcore/foundation/sorted_set.js +590 -0
  15. data/frameworks/sproutcore/foundation/string.js +43 -9
  16. data/frameworks/sproutcore/globals/window.js +34 -9
  17. data/frameworks/sproutcore/lib/button_views.rb +1 -0
  18. data/frameworks/sproutcore/lib/collection_view.rb +1 -0
  19. data/frameworks/sproutcore/lib/core_views.rb +3 -0
  20. data/frameworks/sproutcore/lib/index.rhtml +1 -1
  21. data/frameworks/sproutcore/mixins/collection_view_delegate.js +201 -0
  22. data/frameworks/sproutcore/mixins/observable.js +2 -7
  23. data/frameworks/sproutcore/models/record.js +1 -1
  24. data/frameworks/sproutcore/models/store.js +81 -28
  25. data/frameworks/sproutcore/tests/views/view/clippingFrame.rhtml +9 -6
  26. data/frameworks/sproutcore/views/collection/collection.js +649 -211
  27. data/frameworks/sproutcore/views/collection/grid.js +62 -26
  28. data/frameworks/sproutcore/views/collection/list.js +57 -21
  29. data/frameworks/sproutcore/views/collection/source_list.js +61 -13
  30. data/frameworks/sproutcore/views/image.js +7 -0
  31. data/frameworks/sproutcore/views/inline_text_field.js +4 -5
  32. data/frameworks/sproutcore/views/slider.js +2 -0
  33. data/frameworks/sproutcore/views/view.js +2 -2
  34. data/lib/sproutcore/build_tools/html_builder.rb +4 -6
  35. data/lib/sproutcore/build_tools/resource_builder.rb +32 -20
  36. data/lib/sproutcore/bundle.rb +130 -32
  37. data/lib/sproutcore/bundle_manifest.rb +24 -21
  38. data/lib/sproutcore/helpers/static_helper.rb +22 -9
  39. data/lib/sproutcore/merb/bundle_controller.rb +4 -3
  40. data/lib/sproutcore/version.rb +1 -1
  41. metadata +14 -17
  42. data/clients/view_builder/builders/builder.js +0 -339
  43. data/clients/view_builder/builders/button.js +0 -81
  44. data/clients/view_builder/controllers/document.js +0 -21
  45. data/clients/view_builder/core.js +0 -19
  46. data/clients/view_builder/english.lproj/body.css +0 -77
  47. data/clients/view_builder/english.lproj/body.rhtml +0 -39
  48. data/clients/view_builder/english.lproj/controls.css +0 -0
  49. data/clients/view_builder/english.lproj/strings.js +0 -14
  50. data/clients/view_builder/main.js +0 -38
  51. data/clients/view_builder/mixins/design_mode.js +0 -92
  52. data/clients/view_builder/tests/controllers/document.rhtml +0 -20
  53. data/clients/view_builder/tests/views/builder.rhtml +0 -20
  54. data/clients/view_builder/tests/views/palette.rhtml +0 -21
  55. data/clients/view_builder/views/builder.js +0 -26
  56. data/clients/view_builder/views/palette.js +0 -30
@@ -44,7 +44,6 @@ SC.GridView = SC.CollectionView.extend(
44
44
  // computed function for keyboard handling.
45
45
  itemsPerRow: function() {
46
46
  var ret = this._computeItemsPerRow() ;
47
- console.log('ret = %@'.fmt(ret)) ;
48
47
  return ret ;
49
48
  }.property(),
50
49
 
@@ -167,49 +166,86 @@ SC.GridView = SC.CollectionView.extend(
167
166
  emptyElement: '<div class="grid-insertion-point"><span class="anchor"></span></div>'
168
167
  }),
169
168
 
170
- showInsertionPointBefore: function(itemView) {
171
-
169
+ showInsertionPoint: function(itemView, dropOperation) {
172
170
  if (!itemView) return ;
173
-
174
- if (!this._insertionPointView) {
175
- this._insertionPointView = this.insertionPointClass.create() ;
176
- } ;
171
+
172
+ // if drop on, then just add a class...
173
+ if (dropOperation === SC.DROP_ON) {
174
+ if (itemView !== this._dropOnInsertionPoint) {
175
+ this.hideInsertionPoint() ;
176
+ itemView.addClassName('drop-target') ;
177
+ this._dropOnInsertionPoint = itemView ;
178
+ }
179
+
180
+ } else {
181
+
182
+ if (this._dropOnInsertionPoint) {
183
+ this._dropOnInsertionPoint.removeClassName('drop-target') ;
184
+ this._dropOnInsertionPoint = null ;
185
+ }
177
186
 
178
- var insertionPoint = this._insertionPointView ;
179
- var itemViewFrame = itemView.get('frame') ;
180
- f = { height: itemViewFrame.height - 6,
181
- x: itemViewFrame.x,
182
- y: itemViewFrame.y + 6,
183
- width: 0
184
- };
185
-
186
- if (!SC.rectsEqual(insertionPoint.get('frame'), f)) {
187
- insertionPoint.set('frame', f) ;
188
- }
189
-
190
- if (insertionPoint.parentNode != itemView.parentNode) {
191
- itemView.parentNode.appendChild(insertionPoint) ;
187
+ if (!this._insertionPointView) {
188
+ this._insertionPointView = this.insertionPointClass.create() ;
189
+ } ;
190
+
191
+ var insertionPoint = this._insertionPointView ;
192
+ var itemViewFrame = itemView.get('frame') ;
193
+ f = { height: itemViewFrame.height - 6,
194
+ x: itemViewFrame.x,
195
+ y: itemViewFrame.y + 6,
196
+ width: 0
197
+ };
198
+
199
+ if (!SC.rectsEqual(insertionPoint.get('frame'), f)) {
200
+ insertionPoint.set('frame', f) ;
201
+ }
202
+
203
+ if (insertionPoint.parentNode != itemView.parentNode) {
204
+ itemView.parentNode.appendChild(insertionPoint) ;
205
+ }
192
206
  }
207
+
193
208
  },
194
-
209
+
195
210
  hideInsertionPoint: function() {
196
211
  var insertionPoint = this._insertionPointView ;
197
212
  if (insertionPoint) insertionPoint.removeFromParent() ;
213
+
214
+ if (this._dropOnInsertionPoint) {
215
+ this._dropOnInsertionPoint.removeClassName('drop-target') ;
216
+ this._dropOnInsertionPoint = null ;
217
+ }
198
218
  },
199
219
 
200
220
  // // We can do this much faster programatically using the rowHeight
201
- insertionIndexForLocation: function(loc) {
221
+ insertionIndexForLocation: function(loc, dropOperation) {
202
222
  var f = this.get('frame') ;
203
223
  var sf = this.get('scrollFrame') ;
204
224
 
205
225
  var itemsPerRow = this._computeItemsPerRow();
206
226
  var columnWidth = Math.floor(f.width / itemsPerRow) ;
207
227
  var row = Math.floor((loc.y - f.y - sf.y) / this.get('rowHeight')) ;
208
- var col = Math.floor(((loc.x - f.x - sf.x) / columnWidth) + 0.5) ;
228
+
229
+ var retOp = SC.DROP_BEFORE ;
209
230
 
231
+ var offset = (loc.x - f.x - sf.x) ;
232
+ var col = Math.floor(offset / columnWidth) ;
233
+ var percentage = (offset / columnWidth) - col ;
234
+
235
+ // if the dropOperation is SC.DROP_ON and we are in the center 60%
236
+ // then return the current item.
237
+ if (dropOperation === SC.DROP_ON) {
238
+ if (percentage > 0.80) col++ ;
239
+ if ((percentage >= 0.20) && (percentage <= 0.80)) {
240
+ retOp = SC.DROP_ON;
241
+ }
242
+ } else {
243
+ if (percentage > 0.45) col++ ;
244
+ }
245
+
246
+ // convert to index
210
247
  var ret= (row*itemsPerRow) + col ;
211
- //console.log("insertionIndexForLocation = %@".fmt(ret)) ;
212
- return ret ;
248
+ return [ret, retOp] ;
213
249
  }
214
250
 
215
251
  }) ;
@@ -49,16 +49,12 @@ SC.ListView = SC.CollectionView.extend(
49
49
  var min = Math.max(0,Math.floor(SC.minY(frame) / rowHeight)-1) ;
50
50
  var max = Math.ceil(SC.maxY(frame) / rowHeight) ;
51
51
  var ret = { start: min, length: max - min } ;
52
- // console.log('contentRangeInFrame(%@) = %@'.fmt($H(frame).inspect(), $H(ret).inspect()));
53
- //if (frame.height < 100) debugger ;
54
52
  return ret ;
55
53
  },
56
54
 
57
55
  /** @private */
58
56
  layoutItemView: function(itemView, contentIndex, firstLayout) {
59
- if (!itemView) debugger ;
60
- SC.Benchmark.start('SC.ListView.layoutItemViewsFor') ;
61
-
57
+
62
58
  var rowHeight = this.get('rowHeight') || 0 ;
63
59
  var parentView = itemView.get('parentView') ;
64
60
  var f = {
@@ -72,7 +68,6 @@ SC.ListView = SC.CollectionView.extend(
72
68
  itemView.set('frame', f) ;
73
69
  itemView.setStyle({ zIndex: contentIndex.toString() }) ;
74
70
  }
75
- SC.Benchmark.end('SC.ListView.layoutItemViewsFor') ;
76
71
  },
77
72
 
78
73
  computeFrame: function() {
@@ -85,41 +80,82 @@ SC.ListView = SC.CollectionView.extend(
85
80
 
86
81
  f.x = f.y = 0;
87
82
  f.height = Math.max(f.height, rows * rowHeight) ;
88
- // console.log('computeFrame(%@)'.fmt($H(f).inspect())) ;
89
83
  return f ;
90
84
  },
91
85
 
92
86
  insertionPointClass: SC.View.extend({
93
87
  emptyElement: '<div class="list-insertion-point"><span class="anchor"></span></div>'
94
88
  }),
95
-
96
- showInsertionPointBefore: function(itemView) {
89
+
90
+ showInsertionPoint: function(itemView, dropOperation) {
97
91
  if (!itemView) return ;
98
92
 
99
- if (!this._insertionPointView) {
100
- this._insertionPointView = this.insertionPointClass.create() ;
101
- } ;
93
+ // if drop on, then just add a class...
94
+ if (dropOperation === SC.DROP_ON) {
95
+ if (itemView !== this._dropOnInsertionPoint) {
96
+ this.hideInsertionPoint() ;
97
+ itemView.addClassName('drop-target') ;
98
+ this._dropOnInsertionPoint = itemView ;
99
+ }
100
+
101
+ } else {
102
+
103
+ if (this._dropOnInsertionPoint) {
104
+ this._dropOnInsertionPoint.removeClassName('drop-target') ;
105
+ this._dropOnInsertionPoint = null ;
106
+ }
102
107
 
103
- var insertionPoint = this._insertionPointView ;
104
- f = { height: 0, x: 8, y: itemView.get('frame').y, width: itemView.owner.get('frame').width };
105
- insertionPoint.set('frame', f) ;
108
+ if (!this._insertionPointView) {
109
+ this._insertionPointView = this.insertionPointClass.create() ;
110
+ } ;
111
+
112
+ var insertionPoint = this._insertionPointView ;
113
+ f = { height: 0, x: 8, y: itemView.get('frame').y, width: itemView.owner.get('frame').width };
114
+ insertionPoint.set('frame', f) ;
106
115
 
107
- if (insertionPoint.parentNode != itemView.parentNode) {
108
- itemView.parentNode.appendChild(insertionPoint) ;
116
+ if (insertionPoint.parentNode != itemView.parentNode) {
117
+ itemView.parentNode.appendChild(insertionPoint) ;
118
+ }
109
119
  }
120
+
110
121
  },
111
122
 
112
123
  hideInsertionPoint: function() {
113
124
  var insertionPoint = this._insertionPointView ;
114
125
  if (insertionPoint) insertionPoint.removeFromParent() ;
126
+
127
+ if (this._dropOnInsertionPoint) {
128
+ this._dropOnInsertionPoint.removeClassName('drop-target') ;
129
+ this._dropOnInsertionPoint = null ;
130
+ }
115
131
  },
116
-
132
+
117
133
  // We can do this much faster programatically using the rowHeight
118
- insertionIndexForLocation: function(loc) {
134
+ insertionIndexForLocation: function(loc, dropOperation) {
119
135
  var f = this.get('innerFrame') ;
120
136
  var sf = this.get('scrollFrame') ;
121
- var ret = Math.floor(((loc.y - f.y - sf.y) / this.get('rowHeight')) + 0.4) ;
122
- return ret ;
137
+ var rowHeight = this.get('rowHeight') || 0 ;
138
+
139
+ // find the row and offset to work with
140
+ var offset = loc.y - f.y - sf.y ;
141
+ var retOp = SC.DROP_BEFORE ;
142
+ var ret = Math.floor(offset / this.get('rowHeight')) ;
143
+
144
+ // find the percent through the row...
145
+ var percentage = (offset / rowHeight) - ret ;
146
+
147
+ // if the dropOperation is SC.DROP_ON and we are in the center 60%
148
+ // then return the current item.
149
+ if (dropOperation === SC.DROP_ON) {
150
+ if (percentage > 0.80) ret++ ;
151
+ if ((percentage >= 0.20) && (percentage <= 0.80)) {
152
+ retOp = SC.DROP_ON;
153
+ }
154
+ } else {
155
+ if (percentage > 0.45) ret++ ;
156
+ }
157
+
158
+ return [ret, retOp] ;
123
159
  }
124
160
 
125
161
  }) ;
@@ -31,6 +31,12 @@ SC.SourceListView = SC.CollectionView.extend(
31
31
  */
32
32
  contentValueIsEditable: NO,
33
33
 
34
+ /**
35
+ Allows reordering without modifiying the selection
36
+ */
37
+
38
+ selectOnMouseDown: false,
39
+
34
40
  /**
35
41
  Set to YES if you want source list items to display an icon.
36
42
 
@@ -338,29 +344,51 @@ SC.SourceListView = SC.CollectionView.extend(
338
344
  emptyElement: '<div class="list-insertion-point"><span class="anchor"></span></div>'
339
345
  }),
340
346
 
341
- showInsertionPointBefore: function(itemView) {
347
+ showInsertionPoint: function(itemView, dropOperation) {
342
348
  if (!itemView) return ;
343
349
 
344
- if (!this._insertionPointView) {
345
- this._insertionPointView = this.insertionPointClass.create() ;
346
- } ;
350
+ // if drop on, then just add a class...
351
+ if (dropOperation === SC.DROP_ON) {
352
+ if (itemView !== this._dropOnInsertionPoint) {
353
+ this.hideInsertionPoint() ;
354
+ itemView.addClassName('drop-target') ;
355
+ this._dropOnInsertionPoint = itemView ;
356
+ }
357
+
358
+ } else {
359
+
360
+ if (this._dropOnInsertionPoint) {
361
+ this._dropOnInsertionPoint.removeClassName('drop-target') ;
362
+ this._dropOnInsertionPoint = null ;
363
+ }
347
364
 
348
- var insertionPoint = this._insertionPointView ;
349
- f = { height: 0, x: 8, y: itemView.get('frame').y, width: itemView.owner.get('frame').width };
350
- insertionPoint.set('frame', f) ;
365
+ if (!this._insertionPointView) {
366
+ this._insertionPointView = this.insertionPointClass.create() ;
367
+ } ;
368
+
369
+ var insertionPoint = this._insertionPointView ;
370
+ f = { height: 0, x: 8, y: itemView.get('frame').y, width: itemView.owner.get('frame').width };
371
+ insertionPoint.set('frame', f) ;
351
372
 
352
- if (insertionPoint.parentNode != itemView.parentNode) {
353
- itemView.parentNode.appendChild(insertionPoint) ;
373
+ if (insertionPoint.parentNode != itemView.parentNode) {
374
+ itemView.parentNode.appendChild(insertionPoint) ;
375
+ }
354
376
  }
377
+
355
378
  },
356
379
 
357
380
  hideInsertionPoint: function() {
358
381
  var insertionPoint = this._insertionPointView ;
359
382
  if (insertionPoint) insertionPoint.removeFromParent() ;
383
+
384
+ if (this._dropOnInsertionPoint) {
385
+ this._dropOnInsertionPoint.removeClassName('drop-target') ;
386
+ this._dropOnInsertionPoint = null ;
387
+ }
360
388
  },
361
389
 
362
390
  // We can do this much faster programatically using the rowHeight
363
- insertionIndexForLocation: function(loc) {
391
+ insertionIndexForLocation: function(loc, dropOperation) {
364
392
  var f = this.get('innerFrame') ;
365
393
  var sf = this.get('scrollFrame') ;
366
394
  var rowHeight = this.get('rowHeight') || 0 ;
@@ -368,6 +396,7 @@ SC.SourceListView = SC.CollectionView.extend(
368
396
  // find the offset to work with.
369
397
  var offset = loc.y - f.y - sf.y ;
370
398
  var ret = -1; // the return value
399
+ var retOp = SC.DROP_BEFORE ;
371
400
 
372
401
  // search groups until we find one that matches
373
402
  var top = 0;
@@ -380,8 +409,26 @@ SC.SourceListView = SC.CollectionView.extend(
380
409
  // we hit there.
381
410
  if (max >= offset) {
382
411
  offset -= top ;
383
- ret = Math.floor((offset / rowHeight) + 0.4) ;
384
- if (ret < 1) return -1 ; // top row!
412
+ ret = Math.floor(offset / rowHeight) ;
413
+
414
+ // find the percent through the row...
415
+ var percentage = (offset / rowHeight) - ret ;
416
+
417
+ // if the dropOperation is SC.DROP_ON and we are in the center 60%
418
+ // then return the current item.
419
+ if (dropOperation === SC.DROP_ON) {
420
+ if (percentage > 0.80) ret++ ;
421
+ if ((percentage >= 0.20) && (percentage <= 0.80)) {
422
+ retOp = SC.DROP_ON;
423
+ }
424
+ } else {
425
+ if (percentage > 0.45) ret++ ;
426
+ }
427
+
428
+ // handle dropping on top row...
429
+ if (ret < 1) return [-1, SC.DROP_BEFORE] ; // top row!
430
+
431
+ // convert to index
385
432
  ret = (ret - 1) + idx ;
386
433
 
387
434
  // we are not yet within the group, go on to the next group.
@@ -390,7 +437,8 @@ SC.SourceListView = SC.CollectionView.extend(
390
437
  top = max ;
391
438
  }
392
439
  }
393
- return ret ;
440
+
441
+ return [ret, retOp] ;
394
442
  }
395
443
 
396
444
  }) ;
@@ -105,11 +105,13 @@ SC.ImageView = SC.View.extend(SC.Control,
105
105
  if (!value || value.length == 0) {
106
106
  this.rootElement.src = SC.BLANK_IMAGE_URL;
107
107
  this.set('status', SC.IMAGE_STATE_NONE) ;
108
+ this._imageUrl = null; //clear
108
109
 
109
110
  // if a new value was set that is a URL, load the image URL.
110
111
  } else if (SC.ImageView.valueIsUrl(value)) {
111
112
  this.beginPropertyChanges() ;
112
113
  this.set('status', SC.IMAGE_STATE_LOADING) ;
114
+ this._imageUrl = value ; // save to verify later.
113
115
  SC.imageCache.loadImage(value, this, this._onLoadComplete) ;
114
116
  this.endPropertyChanges() ;
115
117
 
@@ -130,6 +132,11 @@ SC.ImageView = SC.View.extend(SC.Control,
130
132
  this method will be invoked immediately.
131
133
  */
132
134
  _onLoadComplete: function(url, status, img) {
135
+
136
+ // sometimes this method gets called later after the url has already
137
+ // changed. If this is the case, bail...
138
+ if (url !== this._imageUrl) return ;
139
+
133
140
  this.beginPropertyChanges() ;
134
141
  this.set('imageWidth', parseInt(img.width,0)) ;
135
142
  this.set('imageHeight', parseInt(img.height,0)) ;
@@ -104,11 +104,11 @@ SC.InlineTextFieldView = SC.View.extend(SC.DelegateSupport, SC.InlineEditorDeleg
104
104
  this.endPropertyChanges(); return NO ;
105
105
  }
106
106
 
107
- this._frame = options.frame ;
107
+ this._optframe = options.frame ;
108
108
  this._exampleElement = options.exampleElement ;
109
109
  this._delegate = options.delegate ;
110
110
 
111
- if (!this._frame || !this._delegate) {
111
+ if (!this._optframe || !this._delegate) {
112
112
  throw "At least frame and delegate options are required for inline editor";
113
113
  }
114
114
 
@@ -188,11 +188,10 @@ SC.InlineTextFieldView = SC.View.extend(SC.DelegateSupport, SC.InlineEditorDeleg
188
188
 
189
189
  // OK, we are allowed to end editing. Notify delegate of final value
190
190
  // and clean up.
191
- console.log('applying finalValue: %@'.fmt(finalValue)) ;
192
191
  this.invokeDelegateMethod(del, 'inlineEditorDidEndEditing', this, finalValue) ;
193
192
 
194
193
  // cleanup cached values
195
- this._originalValue = this._delegate = this._exampleElement = this._frame = null ;
194
+ this._originalValue = this._delegate = this._exampleElement = this._optframe = null ;
196
195
  this.set('isEditing', NO) ;
197
196
 
198
197
  // resign first responder if not done already. This may call us in a
@@ -224,7 +223,7 @@ SC.InlineTextFieldView = SC.View.extend(SC.DelegateSupport, SC.InlineEditorDeleg
224
223
  */
225
224
  updateViewStyle: function() {
226
225
  // collect font and frame from target.
227
- var f= this._frame ;
226
+ var f= this._optframe ;
228
227
  var el = this._exampleElement ;
229
228
 
230
229
  var styles = {
@@ -95,6 +95,8 @@ SC.SliderView = SC.View.extend(SC.Control,
95
95
  }.observes('value', 'minimum', 'maximum'),
96
96
 
97
97
  mouseDown: function(evt) {
98
+
99
+ this.recacheFrames() ;
98
100
 
99
101
  if (!this.get('isEnabled')) return true ; // nothing to do
100
102
 
@@ -659,7 +659,7 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
659
659
  any of these properties to edit individual CSS style properties.
660
660
  */
661
661
  unknownProperty: function(key, value) {
662
- if (key.match(/^style/)) {
662
+ if (key && key.match && key.match(/^style/)) {
663
663
  key = key.slice(5,key.length).replace(/^./, function(x) {
664
664
  return x.toLowerCase();
665
665
  });
@@ -687,7 +687,6 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
687
687
  }
688
688
  ret = this.getStyle(key) ;
689
689
  }
690
- console.log('get Style: %@ -> %@'.fmt(key, ret)) ;
691
690
  return ret;
692
691
 
693
692
  } else return arguments.callee.base.call(this, key, value) ;
@@ -1763,6 +1762,7 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
1763
1762
  // if state changes, update and notify children.
1764
1763
  if (visible != this.get('isVisibleInWindow')) {
1765
1764
  this.set('isVisibleInWindow', visible) ;
1765
+ this.recacheFrames() ;
1766
1766
  var child = this.get('firstChild') ;
1767
1767
  while(child) {
1768
1768
  child._updateIsVisibleInWindow(visible) ;