rails 0.13.1 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rails might be problematic. Click here for more details.

Files changed (86) hide show
  1. data/CHANGELOG +105 -0
  2. data/Rakefile +39 -17
  3. data/bin/breakpointer +2 -2
  4. data/bin/console +2 -22
  5. data/bin/destroy +2 -6
  6. data/bin/generate +2 -6
  7. data/bin/performance/benchmarker +3 -0
  8. data/bin/performance/profiler +3 -0
  9. data/bin/process/reaper +3 -0
  10. data/bin/process/spawner +3 -0
  11. data/bin/process/spinner +3 -0
  12. data/bin/rails +4 -0
  13. data/bin/runner +2 -27
  14. data/bin/server +2 -48
  15. data/configs/apache.conf +8 -0
  16. data/configs/database.yml +8 -8
  17. data/environments/boot.rb +17 -0
  18. data/environments/development.rb +10 -7
  19. data/environments/environment.rb +37 -73
  20. data/environments/production.rb +15 -6
  21. data/environments/test.rb +12 -6
  22. data/fresh_rakefile +6 -198
  23. data/helpers/application.rb +2 -2
  24. data/helpers/application_helper.rb +1 -1
  25. data/helpers/test_helper.rb +6 -19
  26. data/html/javascripts/controls.js +427 -165
  27. data/html/javascripts/dragdrop.js +256 -277
  28. data/html/javascripts/effects.js +766 -277
  29. data/html/javascripts/prototype.js +903 -217
  30. data/html/javascripts/scriptaculous.js +47 -0
  31. data/html/javascripts/slider.js +258 -0
  32. data/html/robots.txt +1 -0
  33. data/lib/binding_of_caller.rb +3 -1
  34. data/lib/breakpoint.rb +5 -5
  35. data/lib/breakpoint_client.rb +1 -1
  36. data/lib/code_statistics.rb +7 -4
  37. data/lib/commands.rb +17 -0
  38. data/lib/commands/breakpointer.rb +1 -0
  39. data/lib/commands/console.rb +22 -0
  40. data/lib/commands/destroy.rb +6 -0
  41. data/lib/commands/generate.rb +6 -0
  42. data/{bin → lib/commands/ncgi}/listener +0 -0
  43. data/{bin → lib/commands/ncgi}/tracker +0 -0
  44. data/lib/commands/performance/benchmarker.rb +26 -0
  45. data/{bin/profiler → lib/commands/performance/profiler.rb} +3 -2
  46. data/lib/commands/process/reaper.rb +130 -0
  47. data/lib/commands/process/spawner.rb +52 -0
  48. data/lib/commands/process/spinner.rb +57 -0
  49. data/lib/commands/runner.rb +27 -0
  50. data/lib/commands/server.rb +59 -0
  51. data/{bin/update → lib/commands/update.rb} +1 -2
  52. data/lib/dispatcher.rb +20 -3
  53. data/lib/fcgi_handler.rb +59 -41
  54. data/lib/initializer.rb +479 -0
  55. data/lib/rails_generator/base.rb +2 -2
  56. data/lib/rails_generator/commands.rb +59 -7
  57. data/lib/rails_generator/generators/applications/app/app_generator.rb +26 -15
  58. data/lib/rails_generator/generators/components/controller/controller_generator.rb +3 -2
  59. data/lib/rails_generator/generators/components/controller/templates/view.rhtml +1 -1
  60. data/lib/rails_generator/generators/components/mailer/USAGE +2 -3
  61. data/lib/rails_generator/generators/components/mailer/templates/unit_test.rb +1 -1
  62. data/lib/rails_generator/generators/components/migration/migration_generator.rb +3 -1
  63. data/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +32 -31
  64. data/lib/rails_generator/generators/components/scaffold/templates/view_edit.rhtml +1 -1
  65. data/lib/rails_generator/generators/components/scaffold/templates/view_list.rhtml +1 -1
  66. data/lib/rails_generator/generators/components/scaffold/templates/view_new.rhtml +1 -1
  67. data/lib/rails_generator/lookup.rb +3 -3
  68. data/lib/rails_generator/options.rb +1 -0
  69. data/lib/rails_generator/scripts.rb +1 -1
  70. data/lib/rails_version.rb +9 -0
  71. data/lib/railties_path.rb +1 -0
  72. data/lib/rubyprof_ext.rb +1 -1
  73. data/lib/tasks/databases.rake +152 -0
  74. data/lib/tasks/documentation.rake +44 -0
  75. data/lib/tasks/framework.rake +33 -0
  76. data/lib/tasks/javascripts.rake +6 -0
  77. data/lib/tasks/misc.rake +15 -0
  78. data/lib/tasks/rails.rb +7 -0
  79. data/lib/tasks/statistics.rake +16 -0
  80. data/lib/tasks/testing.rake +37 -0
  81. data/lib/test_help.rb +13 -0
  82. data/lib/webrick_server.rb +31 -9
  83. metadata +121 -85
  84. data/bin/benchmarker +0 -19
  85. data/bin/breakpointer_for_gem +0 -4
  86. data/bin/console_sandbox +0 -0
@@ -2,193 +2,79 @@
2
2
  //
3
3
  // Element.Class part Copyright (c) 2005 by Rick Olson
4
4
  //
5
- // Permission is hereby granted, free of charge, to any person obtaining
6
- // a copy of this software and associated documentation files (the
7
- // "Software"), to deal in the Software without restriction, including
8
- // without limitation the rights to use, copy, modify, merge, publish,
9
- // distribute, sublicense, and/or sell copies of the Software, and to
10
- // permit persons to whom the Software is furnished to do so, subject to
11
- // the following conditions:
12
- //
13
- // The above copyright notice and this permission notice shall be
14
- // included in all copies or substantial portions of the Software.
15
- //
16
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
-
24
- Element.Class = {
25
- // Element.toggleClass(element, className) toggles the class being on/off
26
- // Element.toggleClass(element, className1, className2) toggles between both classes,
27
- // defaulting to className1 if neither exist
28
- toggle: function(element, className) {
29
- if(Element.Class.has(element, className)) {
30
- Element.Class.remove(element, className);
31
- if(arguments.length == 3) Element.Class.add(element, arguments[2]);
32
- } else {
33
- Element.Class.add(element, className);
34
- if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
35
- }
36
- },
37
-
38
- // gets space-delimited classnames of an element as an array
39
- get: function(element) {
40
- element = $(element);
41
- return element.className.split(' ');
42
- },
43
-
44
- // functions adapted from original functions by Gavin Kistner
45
- remove: function(element) {
46
- element = $(element);
47
- var regEx;
48
- for(var i = 1; i < arguments.length; i++) {
49
- regEx = new RegExp("^" + arguments[i] + "\\b\\s*|\\s*\\b" + arguments[i] + "\\b", 'g');
50
- element.className = element.className.replace(regEx, '')
51
- }
52
- },
53
-
54
- add: function(element) {
55
- element = $(element);
56
- for(var i = 1; i < arguments.length; i++) {
57
- Element.Class.remove(element, arguments[i]);
58
- element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
59
- }
60
- },
61
-
62
- // returns true if all given classes exist in said element
63
- has: function(element) {
64
- element = $(element);
65
- if(!element || !element.className) return false;
66
- var regEx;
67
- for(var i = 1; i < arguments.length; i++) {
68
- regEx = new RegExp("\\b" + arguments[i] + "\\b");
69
- if(!regEx.test(element.className)) return false;
70
- }
71
- return true;
72
- },
73
-
74
- // expects arrays of strings and/or strings as optional paramters
75
- // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
76
- has_any: function(element) {
77
- element = $(element);
78
- if(!element || !element.className) return false;
79
- var regEx;
80
- for(var i = 1; i < arguments.length; i++) {
81
- if((typeof arguments[i] == 'object') &&
82
- (arguments[i].constructor == Array)) {
83
- for(var j = 0; j < arguments[i].length; j++) {
84
- regEx = new RegExp("\\b" + arguments[i][j] + "\\b");
85
- if(regEx.test(element.className)) return true;
86
- }
87
- } else {
88
- regEx = new RegExp("\\b" + arguments[i] + "\\b");
89
- if(regEx.test(element.className)) return true;
90
- }
91
- }
92
- return false;
93
- },
94
-
95
- childrenWith: function(element, className) {
96
- var children = $(element).getElementsByTagName('*');
97
- var elements = new Array();
98
-
99
- for (var i = 0; i < children.length; i++) {
100
- if (Element.Class.has(children[i], className)) {
101
- elements.push(children[i]);
102
- break;
103
- }
104
- }
105
-
106
- return elements;
107
- }
108
- }
5
+ // See scriptaculous.js for full license.
109
6
 
110
7
  /*--------------------------------------------------------------------------*/
111
8
 
112
9
  var Droppables = {
113
- drops: false,
114
-
10
+ drops: [],
11
+
115
12
  remove: function(element) {
116
- for(var i = 0; i < this.drops.length; i++)
117
- if(this.drops[i].element == element)
118
- this.drops.splice(i,1);
13
+ this.drops = this.drops.reject(function(d) { return d.element==element });
119
14
  },
120
-
15
+
121
16
  add: function(element) {
122
- var element = $(element);
17
+ element = $(element);
123
18
  var options = Object.extend({
124
19
  greedy: true,
125
20
  hoverclass: null
126
21
  }, arguments[1] || {});
127
-
22
+
128
23
  // cache containers
129
24
  if(options.containment) {
130
- options._containers = new Array();
25
+ options._containers = [];
131
26
  var containment = options.containment;
132
27
  if((typeof containment == 'object') &&
133
28
  (containment.constructor == Array)) {
134
- for(var i=0; i<containment.length; i++)
135
- options._containers.push($(containment[i]));
29
+ containment.each( function(c) { options._containers.push($(c)) });
136
30
  } else {
137
31
  options._containers.push($(containment));
138
32
  }
139
- options._containers_length =
140
- options._containers.length-1;
141
33
  }
142
-
34
+
143
35
  Element.makePositioned(element); // fix IE
144
-
145
36
  options.element = element;
146
-
147
- // activate the droppable
148
- if(!this.drops) this.drops = [];
37
+
149
38
  this.drops.push(options);
150
39
  },
151
-
152
- is_contained: function(element, drop) {
153
- var containers = drop._containers;
40
+
41
+ isContained: function(element, drop) {
154
42
  var parentNode = element.parentNode;
155
- var i = drop._containers_length;
156
- do { if(parentNode==containers[i]) return true; } while (i--);
157
- return false;
43
+ return drop._containers.detect(function(c) { return parentNode == c });
158
44
  },
159
-
160
- is_affected: function(pX, pY, element, drop) {
45
+
46
+ isAffected: function(pX, pY, element, drop) {
161
47
  return (
162
48
  (drop.element!=element) &&
163
49
  ((!drop._containers) ||
164
- this.is_contained(element, drop)) &&
50
+ this.isContained(element, drop)) &&
165
51
  ((!drop.accept) ||
166
52
  (Element.Class.has_any(element, drop.accept))) &&
167
53
  Position.within(drop.element, pX, pY) );
168
54
  },
169
-
55
+
170
56
  deactivate: function(drop) {
171
- Element.Class.remove(drop.element, drop.hoverclass);
57
+ if(drop.hoverclass)
58
+ Element.Class.remove(drop.element, drop.hoverclass);
172
59
  this.last_active = null;
173
60
  },
174
-
61
+
175
62
  activate: function(drop) {
176
63
  if(this.last_active) this.deactivate(this.last_active);
177
- if(drop.hoverclass) {
64
+ if(drop.hoverclass)
178
65
  Element.Class.add(drop.element, drop.hoverclass);
179
- this.last_active = drop;
180
- }
66
+ this.last_active = drop;
181
67
  },
182
-
68
+
183
69
  show: function(event, element) {
184
- if(!this.drops) return;
70
+ if(!this.drops.length) return;
185
71
  var pX = Event.pointerX(event);
186
72
  var pY = Event.pointerY(event);
187
73
  Position.prepare();
188
-
74
+
189
75
  var i = this.drops.length-1; do {
190
76
  var drop = this.drops[i];
191
- if(this.is_affected(pX, pY, element, drop)) {
77
+ if(this.isAffected(pX, pY, element, drop)) {
192
78
  if(drop.onHover)
193
79
  drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
194
80
  if(drop.greedy) {
@@ -197,43 +83,41 @@ var Droppables = {
197
83
  }
198
84
  }
199
85
  } while (i--);
86
+
87
+ if(this.last_active) this.deactivate(this.last_active);
200
88
  },
201
-
89
+
202
90
  fire: function(event, element) {
203
91
  if(!this.last_active) return;
204
92
  Position.prepare();
205
-
206
- if (this.is_affected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
93
+
94
+ if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
207
95
  if (this.last_active.onDrop)
208
- this.last_active.onDrop(element, this.last_active);
209
-
96
+ this.last_active.onDrop(element, this.last_active.element, event);
210
97
  },
211
-
98
+
212
99
  reset: function() {
213
100
  if(this.last_active)
214
101
  this.deactivate(this.last_active);
215
102
  }
216
103
  }
217
104
 
218
- Draggables = {
219
- observers: new Array(),
105
+ var Draggables = {
106
+ observers: [],
220
107
  addObserver: function(observer) {
221
108
  this.observers.push(observer);
222
109
  },
223
110
  removeObserver: function(element) { // element instead of obsever fixes mem leaks
224
- for(var i = 0; i < this.observers.length; i++)
225
- if(this.observers[i].element && (this.observers[i].element == element))
226
- this.observers.splice(i,1);
111
+ this.observers = this.observers.reject( function(o) { return o.element==element });
227
112
  },
228
113
  notify: function(eventName, draggable) { // 'onStart', 'onEnd'
229
- for(var i = 0; i < this.observers.length; i++)
230
- this.observers[i][eventName](draggable);
114
+ this.observers.invoke(eventName, draggable);
231
115
  }
232
116
  }
233
117
 
234
118
  /*--------------------------------------------------------------------------*/
235
119
 
236
- Draggable = Class.create();
120
+ var Draggable = Class.create();
237
121
  Draggable.prototype = {
238
122
  initialize: function(element) {
239
123
  var options = Object.extend({
@@ -242,7 +126,8 @@ Draggable.prototype = {
242
126
  new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
243
127
  },
244
128
  reverteffect: function(element, top_offset, left_offset) {
245
- new Effect.MoveBy(element, -top_offset, -left_offset, {duration:0.4});
129
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
130
+ new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
246
131
  },
247
132
  endeffect: function(element) {
248
133
  new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
@@ -250,40 +135,50 @@ Draggable.prototype = {
250
135
  zindex: 1000,
251
136
  revert: false
252
137
  }, arguments[1] || {});
253
-
138
+
254
139
  this.element = $(element);
255
- this.handle = options.handle ? $(options.handle) : this.element;
256
-
257
- Element.makePositioned(this.element); // fix IE
258
-
140
+ if(options.handle && (typeof options.handle == 'string'))
141
+ this.handle = Element.Class.childrenWith(this.element, options.handle)[0];
142
+
143
+ if(!this.handle) this.handle = $(options.handle);
144
+ if(!this.handle) this.handle = this.element;
145
+
146
+ Element.makePositioned(this.element); // fix IE
147
+
259
148
  this.offsetX = 0;
260
149
  this.offsetY = 0;
261
150
  this.originalLeft = this.currentLeft();
262
151
  this.originalTop = this.currentTop();
263
152
  this.originalX = this.element.offsetLeft;
264
153
  this.originalY = this.element.offsetTop;
265
- this.originalZ = parseInt(this.element.style.zIndex || "0");
266
-
154
+
267
155
  this.options = options;
268
-
156
+
269
157
  this.active = false;
270
158
  this.dragging = false;
271
-
159
+
272
160
  this.eventMouseDown = this.startDrag.bindAsEventListener(this);
273
161
  this.eventMouseUp = this.endDrag.bindAsEventListener(this);
274
162
  this.eventMouseMove = this.update.bindAsEventListener(this);
275
163
  this.eventKeypress = this.keyPress.bindAsEventListener(this);
276
164
 
277
- Event.observe(this.handle, "mousedown", this.eventMouseDown);
165
+ this.registerEvents();
166
+ },
167
+ destroy: function() {
168
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
169
+ this.unregisterEvents();
170
+ },
171
+ registerEvents: function() {
278
172
  Event.observe(document, "mouseup", this.eventMouseUp);
279
173
  Event.observe(document, "mousemove", this.eventMouseMove);
280
174
  Event.observe(document, "keypress", this.eventKeypress);
175
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
281
176
  },
282
- destroy: function() {
283
- Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
284
- Event.stopObserving(document, "mouseup", this.eventMouseUp);
285
- Event.stopObserving(document, "mousemove", this.eventMouseMove);
286
- Event.stopObserving(document, "keypress", this.eventKeypress);
177
+ unregisterEvents: function() {
178
+ //if(!this.active) return;
179
+ //Event.stopObserving(document, "mouseup", this.eventMouseUp);
180
+ //Event.stopObserving(document, "mousemove", this.eventMouseMove);
181
+ //Event.stopObserving(document, "keypress", this.eventKeypress);
287
182
  },
288
183
  currentLeft: function() {
289
184
  return parseInt(this.element.style.left || '0');
@@ -293,27 +188,42 @@ Draggable.prototype = {
293
188
  },
294
189
  startDrag: function(event) {
295
190
  if(Event.isLeftClick(event)) {
296
- this.active = true;
297
191
 
298
- var style = this.element.style;
299
- this.originalY = this.element.offsetTop - this.currentTop() - this.originalTop;
300
- this.originalX = this.element.offsetLeft - this.currentLeft() - this.originalLeft;
301
- this.offsetY = event.clientY - this.originalY - this.originalTop;
302
- this.offsetX = event.clientX - this.originalX - this.originalLeft;
192
+ // abort on form elements, fixes a Firefox issue
193
+ var src = Event.element(event);
194
+ if(src.tagName && (
195
+ src.tagName=='INPUT' ||
196
+ src.tagName=='SELECT' ||
197
+ src.tagName=='BUTTON' ||
198
+ src.tagName=='TEXTAREA')) return;
303
199
 
200
+ // this.registerEvents();
201
+ this.active = true;
202
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
203
+ var offsets = Position.cumulativeOffset(this.element);
204
+ this.offsetX = (pointer[0] - offsets[0]);
205
+ this.offsetY = (pointer[1] - offsets[1]);
304
206
  Event.stop(event);
305
207
  }
306
208
  },
307
209
  finishDrag: function(event, success) {
210
+ // this.unregisterEvents();
211
+
308
212
  this.active = false;
309
213
  this.dragging = false;
310
-
214
+
215
+ if(this.options.ghosting) {
216
+ Position.relativize(this.element);
217
+ Element.remove(this._clone);
218
+ this._clone = null;
219
+ }
220
+
311
221
  if(success) Droppables.fire(event, this.element);
312
222
  Draggables.notify('onEnd', this);
313
-
223
+
314
224
  var revert = this.options.revert;
315
225
  if(revert && typeof revert == 'function') revert = revert(this.element);
316
-
226
+
317
227
  if(revert && this.options.reverteffect) {
318
228
  this.options.reverteffect(this.element,
319
229
  this.currentTop()-this.originalTop,
@@ -322,12 +232,14 @@ Draggable.prototype = {
322
232
  this.originalLeft = this.currentLeft();
323
233
  this.originalTop = this.currentTop();
324
234
  }
325
-
326
- this.element.style.zIndex = this.originalZ;
327
-
235
+
236
+ if(this.options.zindex)
237
+ this.element.style.zIndex = this.originalZ;
238
+
328
239
  if(this.options.endeffect)
329
240
  this.options.endeffect(this.element);
330
-
241
+
242
+
331
243
  Droppables.reset();
332
244
  },
333
245
  keyPress: function(event) {
@@ -347,13 +259,15 @@ Draggable.prototype = {
347
259
  this.dragging = false;
348
260
  },
349
261
  draw: function(event) {
262
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
263
+ var offsets = Position.cumulativeOffset(this.element);
264
+ offsets[0] -= this.currentLeft();
265
+ offsets[1] -= this.currentTop();
350
266
  var style = this.element.style;
351
- this.originalX = this.element.offsetLeft - this.currentLeft() - this.originalLeft;
352
- this.originalY = this.element.offsetTop - this.currentTop() - this.originalTop;
353
267
  if((!this.options.constraint) || (this.options.constraint=='horizontal'))
354
- style.left = ((event.clientX - this.originalX) - this.offsetX) + "px";
268
+ style.left = (pointer[0] - offsets[0] - this.offsetX) + "px";
355
269
  if((!this.options.constraint) || (this.options.constraint=='vertical'))
356
- style.top = ((event.clientY - this.originalY) - this.offsetY) + "px";
270
+ style.top = (pointer[1] - offsets[1] - this.offsetY) + "px";
357
271
  if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
358
272
  },
359
273
  update: function(event) {
@@ -361,19 +275,32 @@ Draggable.prototype = {
361
275
  if(!this.dragging) {
362
276
  var style = this.element.style;
363
277
  this.dragging = true;
364
- if(style.position=="") style.position = "relative";
365
- style.zIndex = this.options.zindex;
278
+
279
+ if(Element.getStyle(this.element,'position')=='')
280
+ style.position = "relative";
281
+
282
+ if(this.options.zindex) {
283
+ this.options.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
284
+ style.zIndex = this.options.zindex;
285
+ }
286
+
287
+ if(this.options.ghosting) {
288
+ this._clone = this.element.cloneNode(true);
289
+ Position.absolutize(this.element);
290
+ this.element.parentNode.insertBefore(this._clone, this.element);
291
+ }
292
+
366
293
  Draggables.notify('onStart', this);
367
294
  if(this.options.starteffect) this.options.starteffect(this.element);
368
295
  }
369
-
296
+
370
297
  Droppables.show(event, this.element);
371
298
  this.draw(event);
372
299
  if(this.options.change) this.options.change(this);
373
-
300
+
374
301
  // fix AppleWebKit rendering
375
302
  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
376
-
303
+
377
304
  Event.stop(event);
378
305
  }
379
306
  }
@@ -381,7 +308,7 @@ Draggable.prototype = {
381
308
 
382
309
  /*--------------------------------------------------------------------------*/
383
310
 
384
- SortableObserver = Class.create();
311
+ var SortableObserver = Class.create();
385
312
  SortableObserver.prototype = {
386
313
  initialize: function(element, observer) {
387
314
  this.element = $(element);
@@ -391,147 +318,199 @@ SortableObserver.prototype = {
391
318
  onStart: function() {
392
319
  this.lastValue = Sortable.serialize(this.element);
393
320
  },
394
- onEnd: function() {
321
+ onEnd: function() {
322
+ Sortable.unmark();
395
323
  if(this.lastValue != Sortable.serialize(this.element))
396
324
  this.observer(this.element)
397
325
  }
398
326
  }
399
327
 
400
- Sortable = {
328
+ var Sortable = {
401
329
  sortables: new Array(),
402
330
  options: function(element){
403
- var element = $(element);
404
- for(var i=0;i<this.sortables.length;i++)
405
- if(this.sortables[i].element == element)
406
- return this.sortables[i];
407
- return null;
331
+ element = $(element);
332
+ return this.sortables.detect(function(s) { return s.element == element });
408
333
  },
409
334
  destroy: function(element){
410
- var element = $(element);
411
- for(var i=0;i<this.sortables.length;i++) {
412
- if(this.sortables[i].element == element) {
413
- var s = this.sortables[i];
414
- Draggables.removeObserver(s.element);
415
- for(var j=0;j<s.droppables.length;j++)
416
- Droppables.remove(s.droppables[j]);
417
- for(var j=0;j<s.draggables.length;j++)
418
- s.draggables[j].destroy();
419
- this.sortables.splice(i,1);
420
- }
421
- }
335
+ element = $(element);
336
+ this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
337
+ Draggables.removeObserver(s.element);
338
+ s.droppables.each(function(d){ Droppables.remove(d) });
339
+ s.draggables.invoke('destroy');
340
+ });
341
+ this.sortables = this.sortables.reject(function(s) { return s.element == element });
422
342
  },
423
343
  create: function(element) {
424
- var element = $(element);
344
+ element = $(element);
425
345
  var options = Object.extend({
426
346
  element: element,
427
347
  tag: 'li', // assumes li children, override with tag: 'tagname'
348
+ dropOnEmpty: false,
349
+ tree: false, // fixme: unimplemented
428
350
  overlap: 'vertical', // one of 'vertical', 'horizontal'
429
351
  constraint: 'vertical', // one of 'vertical', 'horizontal', false
430
352
  containment: element, // also takes array of elements (or id's); or false
431
353
  handle: false, // or a CSS class
432
354
  only: false,
433
355
  hoverclass: null,
356
+ ghosting: false,
357
+ format: null,
434
358
  onChange: function() {},
435
359
  onUpdate: function() {}
436
360
  }, arguments[1] || {});
437
-
361
+
438
362
  // clear any old sortable with same element
439
363
  this.destroy(element);
440
-
364
+
441
365
  // build options for the draggables
442
366
  var options_for_draggable = {
443
367
  revert: true,
368
+ ghosting: options.ghosting,
444
369
  constraint: options.constraint,
445
- handle: handle };
370
+ handle: options.handle };
371
+
446
372
  if(options.starteffect)
447
373
  options_for_draggable.starteffect = options.starteffect;
374
+
448
375
  if(options.reverteffect)
449
376
  options_for_draggable.reverteffect = options.reverteffect;
377
+ else
378
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
379
+ element.style.top = 0;
380
+ element.style.left = 0;
381
+ };
382
+
450
383
  if(options.endeffect)
451
384
  options_for_draggable.endeffect = options.endeffect;
385
+
452
386
  if(options.zindex)
453
387
  options_for_draggable.zindex = options.zindex;
454
-
388
+
455
389
  // build options for the droppables
456
390
  var options_for_droppable = {
457
391
  overlap: options.overlap,
458
392
  containment: options.containment,
459
393
  hoverclass: options.hoverclass,
460
- onHover: function(element, dropon, overlap) {
461
- if(overlap>0.5) {
462
- if(dropon.previousSibling != element) {
463
- var oldParentNode = element.parentNode;
464
- element.style.visibility = "hidden"; // fix gecko rendering
465
- dropon.parentNode.insertBefore(element, dropon);
466
- if(dropon.parentNode!=oldParentNode && oldParentNode.sortable)
467
- oldParentNode.sortable.onChange(element);
468
- if(dropon.parentNode.sortable)
469
- dropon.parentNode.sortable.onChange(element);
470
- }
471
- } else {
472
- var nextElement = dropon.nextSibling || null;
473
- if(nextElement != element) {
474
- var oldParentNode = element.parentNode;
475
- element.style.visibility = "hidden"; // fix gecko rendering
476
- dropon.parentNode.insertBefore(element, nextElement);
477
- if(dropon.parentNode!=oldParentNode && oldParentNode.sortable)
478
- oldParentNode.sortable.onChange(element);
479
- if(dropon.parentNode.sortable)
480
- dropon.parentNode.sortable.onChange(element);
481
- }
482
- }
483
- }
394
+ onHover: Sortable.onHover,
395
+ greedy: !options.dropOnEmpty
484
396
  }
485
397
 
486
398
  // fix for gecko engine
487
399
  Element.cleanWhitespace(element);
488
-
400
+
489
401
  options.draggables = [];
490
402
  options.droppables = [];
491
-
492
- // make it so
493
- var elements = element.childNodes;
494
- for (var i = 0; i < elements.length; i++)
495
- if(elements[i].tagName && elements[i].tagName==options.tag.toUpperCase() &&
496
- (!options.only || (Element.Class.has(elements[i], options.only)))) {
497
-
498
- // handles are per-draggable
499
- var handle = options.handle ?
500
- Element.Class.childrenWith(elements[i], options.handle)[0] : elements[i];
501
-
502
- options.draggables.push(new Draggable(elements[i], Object.extend(options_for_draggable, { handle: handle })));
503
-
504
- Droppables.add(elements[i], options_for_droppable);
505
- options.droppables.push(elements[i]);
506
-
507
- }
508
-
403
+
404
+ // make it so
405
+
406
+ // drop on empty handling
407
+ if(options.dropOnEmpty) {
408
+ Droppables.add(element,
409
+ {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
410
+ options.droppables.push(element);
411
+ }
412
+
413
+ (this.findElements(element, options) || []).each( function(e) {
414
+ // handles are per-draggable
415
+ var handle = options.handle ?
416
+ Element.Class.childrenWith(e, options.handle)[0] : e;
417
+ options.draggables.push(
418
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
419
+ Droppables.add(e, options_for_droppable);
420
+ options.droppables.push(e);
421
+ });
422
+
509
423
  // keep reference
510
424
  this.sortables.push(options);
511
-
425
+
512
426
  // for onupdate
513
427
  Draggables.addObserver(new SortableObserver(element, options.onUpdate));
514
428
 
515
429
  },
430
+
431
+ // return all suitable-for-sortable elements in a guaranteed order
432
+ findElements: function(element, options) {
433
+ if(!element.hasChildNodes()) return null;
434
+ var elements = [];
435
+ $A(element.childNodes).each( function(e) {
436
+ if(e.tagName && e.tagName==options.tag.toUpperCase() &&
437
+ (!options.only || (Element.Class.has(e, options.only))))
438
+ elements.push(e);
439
+ if(options.tree) {
440
+ var grandchildren = this.findElements(e, options);
441
+ if(grandchildren) elements.push(grandchildren);
442
+ }
443
+ });
444
+
445
+ return (elements.length>0 ? elements.flatten() : null);
446
+ },
447
+
448
+ onHover: function(element, dropon, overlap) {
449
+ if(overlap>0.5) {
450
+ Sortable.mark(dropon, 'before');
451
+ if(dropon.previousSibling != element) {
452
+ var oldParentNode = element.parentNode;
453
+ element.style.visibility = "hidden"; // fix gecko rendering
454
+ dropon.parentNode.insertBefore(element, dropon);
455
+ if(dropon.parentNode!=oldParentNode)
456
+ Sortable.options(oldParentNode).onChange(element);
457
+ Sortable.options(dropon.parentNode).onChange(element);
458
+ }
459
+ } else {
460
+ Sortable.mark(dropon, 'after');
461
+ var nextElement = dropon.nextSibling || null;
462
+ if(nextElement != element) {
463
+ var oldParentNode = element.parentNode;
464
+ element.style.visibility = "hidden"; // fix gecko rendering
465
+ dropon.parentNode.insertBefore(element, nextElement);
466
+ if(dropon.parentNode!=oldParentNode)
467
+ Sortable.options(oldParentNode).onChange(element);
468
+ Sortable.options(dropon.parentNode).onChange(element);
469
+ }
470
+ }
471
+ },
472
+
473
+ onEmptyHover: function(element, dropon) {
474
+ if(element.parentNode!=dropon) {
475
+ dropon.appendChild(element);
476
+ }
477
+ },
478
+
479
+ unmark: function() {
480
+ if(Sortable._marker) Element.hide(Sortable._marker);
481
+ },
482
+
483
+ mark: function(dropon, position) {
484
+ // mark on ghosting only
485
+ var sortable = Sortable.options(dropon.parentNode);
486
+ if(sortable && !sortable.ghosting) return;
487
+
488
+ if(!Sortable._marker) {
489
+ Sortable._marker = $('dropmarker') || document.createElement('DIV');
490
+ Element.hide(Sortable._marker);
491
+ Element.Class.add(Sortable._marker, 'dropmarker');
492
+ Sortable._marker.style.position = 'absolute';
493
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
494
+ }
495
+ var offsets = Position.cumulativeOffset(dropon);
496
+ Sortable._marker.style.top = offsets[1] + 'px';
497
+ if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
498
+ Sortable._marker.style.left = offsets[0] + 'px';
499
+ Element.show(Sortable._marker);
500
+ },
501
+
516
502
  serialize: function(element) {
517
- var element = $(element);
503
+ element = $(element);
518
504
  var sortableOptions = this.options(element);
519
505
  var options = Object.extend({
520
506
  tag: sortableOptions.tag,
521
507
  only: sortableOptions.only,
522
- name: element.id
508
+ name: element.id,
509
+ format: sortableOptions.format || /^[^_]*_(.*)$/
523
510
  }, arguments[1] || {});
524
-
525
- var items = $(element).childNodes;
526
- var queryComponents = new Array();
527
-
528
- for(var i=0; i<items.length; i++)
529
- if(items[i].tagName && items[i].tagName==options.tag.toUpperCase() &&
530
- (!options.only || (Element.Class.has(items[i], options.only))))
531
- queryComponents.push(
532
- encodeURIComponent(options.name) + "[]=" +
533
- encodeURIComponent(items[i].id.split("_")[1]));
534
-
535
- return queryComponents.join("&");
511
+ return $(this.findElements(element, options) || []).collect( function(item) {
512
+ return (encodeURIComponent(options.name) + "[]=" +
513
+ encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
514
+ }).join("&");
536
515
  }
537
- }
516
+ }