sproutcore 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
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) ;