parlement 0.10 → 0.11

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 (109) hide show
  1. data/CHANGES +11 -0
  2. data/MEMORY +9 -1
  3. data/README +5 -4
  4. data/app/controllers/account_controller.rb +10 -13
  5. data/app/controllers/application.rb +4 -5
  6. data/app/controllers/elt_controller.rb +9 -7
  7. data/app/controllers/person_controller.rb +1 -3
  8. data/app/controllers/subscriber_controller.rb +10 -10
  9. data/app/helpers/elt_helper.rb +2 -0
  10. data/app/models/elt.rb +28 -19
  11. data/app/models/mail.rb +26 -14
  12. data/app/models/mail_notify.rb +5 -4
  13. data/app/models/person.rb +11 -2
  14. data/app/views/account/_login.rhtml +3 -3
  15. data/app/views/account/_show.rhtml +12 -14
  16. data/app/views/elt/_choice.rhtml +3 -3
  17. data/app/views/elt/_elt.rhtml +4 -4
  18. data/app/views/elt/_list.rhtml +2 -2
  19. data/app/views/elt/_listByDate.rhtml +1 -1
  20. data/app/views/elt/_listByVote.rhtml +1 -1
  21. data/app/views/elt/new.rhtml +3 -3
  22. data/app/views/elt/show.rhtml +2 -2
  23. data/app/views/layouts/top.rhtml +6 -0
  24. data/app/views/mail_notify/publish.text.html.rhtml +1 -1
  25. data/app/views/person/_listElts.rhtml +5 -3
  26. data/app/views/person/show.rhtml +1 -2
  27. data/config/boot.rb +5 -4
  28. data/config/environment.rb +6 -4
  29. data/config/routes.rb +3 -2
  30. data/db/development_structure.sql +15 -4
  31. data/db/migrate/006_last_activity.rb +10 -0
  32. data/db/schema.rb +67 -49
  33. data/public/dispatch.fcgi +1 -0
  34. data/public/javascripts/controls.js +41 -23
  35. data/public/javascripts/dragdrop.js +317 -99
  36. data/public/javascripts/effects.js +301 -166
  37. data/public/javascripts/prototype.js +932 -402
  38. data/public/stylesheets/default.css +3 -2
  39. data/test/unit/elt_test.rb +13 -0
  40. data/test/unit/mail_test.rb +3 -1
  41. data/vendor/plugins/engines/CHANGELOG +203 -99
  42. data/vendor/plugins/engines/MIT-LICENSE +1 -1
  43. data/vendor/plugins/engines/README +32 -384
  44. data/vendor/plugins/engines/Rakefile +14 -0
  45. data/vendor/plugins/engines/UPGRADING +93 -0
  46. data/vendor/plugins/engines/about.yml +7 -0
  47. data/vendor/plugins/engines/generators/plugin_migration/USAGE +45 -0
  48. data/vendor/plugins/engines/generators/plugin_migration/plugin_migration_generator.rb +79 -0
  49. data/vendor/plugins/engines/generators/plugin_migration/templates/plugin_migration.erb +13 -0
  50. data/vendor/plugins/engines/init.rb +34 -47
  51. data/vendor/plugins/engines/install.rb +32 -0
  52. data/vendor/plugins/engines/lib/engines/{ruby_extensions.rb → deprecated_config_support.rb} +135 -113
  53. data/vendor/plugins/engines/lib/engines/plugin.rb +214 -0
  54. data/vendor/plugins/engines/lib/engines/plugin_list.rb +31 -0
  55. data/vendor/plugins/engines/lib/engines/plugin_migrator.rb +60 -0
  56. data/vendor/plugins/engines/lib/engines/rails_extensions/active_record.rb +19 -0
  57. data/vendor/plugins/engines/lib/engines/rails_extensions/dependencies.rb +143 -0
  58. data/vendor/plugins/engines/lib/engines/rails_extensions/migrations.rb +155 -0
  59. data/vendor/plugins/engines/lib/engines/rails_extensions/public_asset_helpers.rb +116 -0
  60. data/vendor/plugins/engines/lib/engines/rails_extensions/rails.rb +20 -0
  61. data/vendor/plugins/engines/lib/engines/rails_extensions/rails_initializer.rb +86 -0
  62. data/vendor/plugins/engines/lib/engines/rails_extensions/routing.rb +77 -0
  63. data/vendor/plugins/engines/lib/engines/rails_extensions/templates.rb +140 -0
  64. data/vendor/plugins/engines/lib/engines/rails_extensions.rb +6 -0
  65. data/vendor/plugins/engines/lib/engines/testing.rb +88 -0
  66. data/vendor/plugins/engines/lib/engines.rb +281 -425
  67. data/vendor/plugins/engines/tasks/engines.rake +108 -137
  68. metadata +218 -250
  69. data/db/ROOT/perso.txt +0 -214
  70. data/public/images/indicator.gif +0 -0
  71. data/public/images/orange_by_darren_Hester_350o.jpg +0 -0
  72. data/public/images/smile.png +0 -0
  73. data/vendor/plugins/engines/generators/engine/USAGE +0 -26
  74. data/vendor/plugins/engines/generators/engine/engine_generator.rb +0 -199
  75. data/vendor/plugins/engines/generators/engine/templates/README +0 -85
  76. data/vendor/plugins/engines/generators/engine/templates/init_engine.erb +0 -15
  77. data/vendor/plugins/engines/generators/engine/templates/install.erb +0 -4
  78. data/vendor/plugins/engines/generators/engine/templates/lib/engine.erb +0 -6
  79. data/vendor/plugins/engines/generators/engine/templates/licenses/GPL +0 -18
  80. data/vendor/plugins/engines/generators/engine/templates/licenses/LGPL +0 -19
  81. data/vendor/plugins/engines/generators/engine/templates/licenses/MIT +0 -22
  82. data/vendor/plugins/engines/generators/engine/templates/licenses/None +0 -1
  83. data/vendor/plugins/engines/generators/engine/templates/public/javascripts/engine.js +0 -0
  84. data/vendor/plugins/engines/generators/engine/templates/public/stylesheets/engine.css +0 -0
  85. data/vendor/plugins/engines/generators/engine/templates/tasks/engine.rake +0 -0
  86. data/vendor/plugins/engines/generators/engine/templates/test/test_helper.erb +0 -17
  87. data/vendor/plugins/engines/lib/bundles/require_resource.rb +0 -124
  88. data/vendor/plugins/engines/lib/bundles.rb +0 -77
  89. data/vendor/plugins/engines/lib/engines/action_mailer_extensions.rb +0 -140
  90. data/vendor/plugins/engines/lib/engines/action_view_extensions.rb +0 -141
  91. data/vendor/plugins/engines/lib/engines/active_record_extensions.rb +0 -21
  92. data/vendor/plugins/engines/lib/engines/dependencies_extensions.rb +0 -129
  93. data/vendor/plugins/engines/lib/engines/migration_extensions.rb +0 -53
  94. data/vendor/plugins/engines/lib/engines/routing_extensions.rb +0 -28
  95. data/vendor/plugins/engines/lib/engines/testing_extensions.rb +0 -327
  96. data/vendor/plugins/engines/tasks/deprecated_engines.rake +0 -7
  97. data/vendor/plugins/engines/test/action_view_extensions_test.rb +0 -9
  98. data/vendor/plugins/engines/test/ruby_extensions_test.rb +0 -115
  99. data/vendor/plugins/guid/README.TXT +0 -29
  100. data/vendor/plugins/guid/init.rb +0 -30
  101. data/vendor/plugins/guid/lib/usesguid.rb +0 -37
  102. data/vendor/plugins/guid/lib/uuid22.rb +0 -43
  103. data/vendor/plugins/guid/lib/uuidtools.rb +0 -572
  104. data/vendor/plugins/responds_to_parent/MIT-LICENSE +0 -20
  105. data/vendor/plugins/responds_to_parent/README +0 -42
  106. data/vendor/plugins/responds_to_parent/Rakefile +0 -22
  107. data/vendor/plugins/responds_to_parent/init.rb +0 -1
  108. data/vendor/plugins/responds_to_parent/lib/responds_to_parent.rb +0 -46
  109. data/vendor/plugins/responds_to_parent/test/responds_to_parent_test.rb +0 -115
@@ -1,8 +1,11 @@
1
- // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1
+ // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
+ // (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
2
3
  //
3
- // See scriptaculous.js for full license.
4
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
5
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
4
6
 
5
- /*--------------------------------------------------------------------------*/
7
+ if(typeof Effect == 'undefined')
8
+ throw("dragdrop.js requires including script.aculo.us' effects.js library");
6
9
 
7
10
  var Droppables = {
8
11
  drops: [],
@@ -15,7 +18,8 @@ var Droppables = {
15
18
  element = $(element);
16
19
  var options = Object.extend({
17
20
  greedy: true,
18
- hoverclass: null
21
+ hoverclass: null,
22
+ tree: false
19
23
  }, arguments[1] || {});
20
24
 
21
25
  // cache containers
@@ -37,12 +41,27 @@ var Droppables = {
37
41
 
38
42
  this.drops.push(options);
39
43
  },
44
+
45
+ findDeepestChild: function(drops) {
46
+ deepest = drops[0];
47
+
48
+ for (i = 1; i < drops.length; ++i)
49
+ if (Element.isParent(drops[i].element, deepest.element))
50
+ deepest = drops[i];
51
+
52
+ return deepest;
53
+ },
40
54
 
41
55
  isContained: function(element, drop) {
42
- var parentNode = element.parentNode;
43
- return drop._containers.detect(function(c) { return parentNode == c });
56
+ var containmentNode;
57
+ if(drop.tree) {
58
+ containmentNode = element.treeNode;
59
+ } else {
60
+ containmentNode = element.parentNode;
61
+ }
62
+ return drop._containers.detect(function(c) { return containmentNode == c });
44
63
  },
45
-
64
+
46
65
  isAffected: function(point, element, drop) {
47
66
  return (
48
67
  (drop.element!=element) &&
@@ -68,18 +87,22 @@ var Droppables = {
68
87
 
69
88
  show: function(point, element) {
70
89
  if(!this.drops.length) return;
90
+ var affected = [];
71
91
 
72
92
  if(this.last_active) this.deactivate(this.last_active);
73
93
  this.drops.each( function(drop) {
74
- if(Droppables.isAffected(point, element, drop)) {
75
- if(drop.onHover)
76
- drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
77
- if(drop.greedy) {
78
- Droppables.activate(drop);
79
- throw $break;
80
- }
81
- }
94
+ if(Droppables.isAffected(point, element, drop))
95
+ affected.push(drop);
82
96
  });
97
+
98
+ if(affected.length>0) {
99
+ drop = Droppables.findDeepestChild(affected);
100
+ Position.within(drop.element, point[0], point[1]);
101
+ if(drop.onHover)
102
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
103
+
104
+ Droppables.activate(drop);
105
+ }
83
106
  },
84
107
 
85
108
  fire: function(event, element) {
@@ -124,8 +147,16 @@ var Draggables = {
124
147
  },
125
148
 
126
149
  activate: function(draggable) {
127
- window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
128
- this.activeDraggable = draggable;
150
+ if(draggable.options.delay) {
151
+ this._timeout = setTimeout(function() {
152
+ Draggables._timeout = null;
153
+ window.focus();
154
+ Draggables.activeDraggable = draggable;
155
+ }.bind(this), draggable.options.delay);
156
+ } else {
157
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
158
+ this.activeDraggable = draggable;
159
+ }
129
160
  },
130
161
 
131
162
  deactivate: function() {
@@ -139,10 +170,15 @@ var Draggables = {
139
170
  // the same coordinates, prevent needless redrawing (moz bug?)
140
171
  if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
141
172
  this._lastPointer = pointer;
173
+
142
174
  this.activeDraggable.updateDrag(event, pointer);
143
175
  },
144
176
 
145
177
  endDrag: function(event) {
178
+ if(this._timeout) {
179
+ clearTimeout(this._timeout);
180
+ this._timeout = null;
181
+ }
146
182
  if(!this.activeDraggable) return;
147
183
  this._lastPointer = null;
148
184
  this.activeDraggable.endDrag(event);
@@ -169,6 +205,7 @@ var Draggables = {
169
205
  this.observers.each( function(o) {
170
206
  if(o[eventName]) o[eventName](eventName, draggable, event);
171
207
  });
208
+ if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
172
209
  },
173
210
 
174
211
  _cacheObserverCallbacks: function() {
@@ -183,37 +220,59 @@ var Draggables = {
183
220
  /*--------------------------------------------------------------------------*/
184
221
 
185
222
  var Draggable = Class.create();
223
+ Draggable._dragging = {};
224
+
186
225
  Draggable.prototype = {
187
226
  initialize: function(element) {
188
- var options = Object.extend({
227
+ var defaults = {
189
228
  handle: false,
190
- starteffect: function(element) {
191
- new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
192
- },
193
229
  reverteffect: function(element, top_offset, left_offset) {
194
230
  var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
195
- element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
231
+ new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
232
+ queue: {scope:'_draggable', position:'end'}
233
+ });
196
234
  },
197
- endeffect: function(element) {
198
- new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
235
+ endeffect: function(element) {
236
+ var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
237
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
238
+ queue: {scope:'_draggable', position:'end'},
239
+ afterFinish: function(){
240
+ Draggable._dragging[element] = false
241
+ }
242
+ });
199
243
  },
200
244
  zindex: 1000,
201
245
  revert: false,
202
246
  scroll: false,
203
247
  scrollSensitivity: 20,
204
248
  scrollSpeed: 15,
205
- snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
206
- }, arguments[1] || {});
249
+ snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
250
+ delay: 0
251
+ };
252
+
253
+ if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
254
+ Object.extend(defaults, {
255
+ starteffect: function(element) {
256
+ element._opacity = Element.getOpacity(element);
257
+ Draggable._dragging[element] = true;
258
+ new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
259
+ }
260
+ });
261
+
262
+ var options = Object.extend(defaults, arguments[1] || {});
207
263
 
208
264
  this.element = $(element);
209
265
 
210
266
  if(options.handle && (typeof options.handle == 'string'))
211
- this.handle = Element.childrenWithClassName(this.element, options.handle, true)[0];
267
+ this.handle = this.element.down('.'+options.handle, 0);
268
+
212
269
  if(!this.handle) this.handle = $(options.handle);
213
270
  if(!this.handle) this.handle = this.element;
214
271
 
215
- if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
272
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
216
273
  options.scroll = $(options.scroll);
274
+ this._isScrollChild = Element.childOf(this.element, options.scroll);
275
+ }
217
276
 
218
277
  Element.makePositioned(this.element); // fix IE
219
278
 
@@ -239,6 +298,8 @@ Draggable.prototype = {
239
298
  },
240
299
 
241
300
  initDrag: function(event) {
301
+ if(typeof Draggable._dragging[this.element] != 'undefined' &&
302
+ Draggable._dragging[this.element]) return;
242
303
  if(Event.isLeftClick(event)) {
243
304
  // abort on form elements, fixes a Firefox issue
244
305
  var src = Event.element(event);
@@ -249,11 +310,6 @@ Draggable.prototype = {
249
310
  src.tagName=='BUTTON' ||
250
311
  src.tagName=='TEXTAREA')) return;
251
312
 
252
- if(this.element._revert) {
253
- this.element._revert.cancel();
254
- this.element._revert = null;
255
- }
256
-
257
313
  var pointer = [Event.pointerX(event), Event.pointerY(event)];
258
314
  var pos = Position.cumulativeOffset(this.element);
259
315
  this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
@@ -289,6 +345,7 @@ Draggable.prototype = {
289
345
  }
290
346
 
291
347
  Draggables.notify('onStart', this, event);
348
+
292
349
  if(this.options.starteffect) this.options.starteffect(this.element);
293
350
  },
294
351
 
@@ -297,6 +354,7 @@ Draggable.prototype = {
297
354
  Position.prepare();
298
355
  Droppables.show(pointer, this.element);
299
356
  Draggables.notify('onDrag', this, event);
357
+
300
358
  this.draw(pointer);
301
359
  if(this.options.change) this.options.change(this);
302
360
 
@@ -308,8 +366,8 @@ Draggable.prototype = {
308
366
  with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
309
367
  } else {
310
368
  p = Position.page(this.options.scroll);
311
- p[0] += this.options.scroll.scrollLeft;
312
- p[1] += this.options.scroll.scrollTop;
369
+ p[0] += this.options.scroll.scrollLeft + Position.deltaX;
370
+ p[1] += this.options.scroll.scrollTop + Position.deltaY;
313
371
  p.push(p[0]+this.options.scroll.offsetWidth);
314
372
  p.push(p[1]+this.options.scroll.offsetHeight);
315
373
  }
@@ -355,7 +413,7 @@ Draggable.prototype = {
355
413
 
356
414
  if(this.options.endeffect)
357
415
  this.options.endeffect(this.element);
358
-
416
+
359
417
  Draggables.deactivate(this);
360
418
  Droppables.reset();
361
419
  },
@@ -375,10 +433,15 @@ Draggable.prototype = {
375
433
 
376
434
  draw: function(point) {
377
435
  var pos = Position.cumulativeOffset(this.element);
436
+ if(this.options.ghosting) {
437
+ var r = Position.realOffset(this.element);
438
+ pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
439
+ }
440
+
378
441
  var d = this.currentDelta();
379
442
  pos[0] -= d[0]; pos[1] -= d[1];
380
443
 
381
- if(this.options.scroll && (this.options.scroll != window)) {
444
+ if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
382
445
  pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
383
446
  pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
384
447
  }
@@ -389,7 +452,7 @@ Draggable.prototype = {
389
452
 
390
453
  if(this.options.snap) {
391
454
  if(typeof this.options.snap == 'function') {
392
- p = this.options.snap(p[0],p[1]);
455
+ p = this.options.snap(p[0],p[1],this);
393
456
  } else {
394
457
  if(this.options.snap instanceof Array) {
395
458
  p = p.map( function(v, i) {
@@ -405,6 +468,7 @@ Draggable.prototype = {
405
468
  style.left = p[0] + "px";
406
469
  if((!this.options.constraint) || (this.options.constraint=='vertical'))
407
470
  style.top = p[1] + "px";
471
+
408
472
  if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
409
473
  },
410
474
 
@@ -412,10 +476,12 @@ Draggable.prototype = {
412
476
  if(this.scrollInterval) {
413
477
  clearInterval(this.scrollInterval);
414
478
  this.scrollInterval = null;
479
+ Draggables._lastScrollPointer = null;
415
480
  }
416
481
  },
417
482
 
418
483
  startScrolling: function(speed) {
484
+ if(!(speed[0] || speed[1])) return;
419
485
  this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
420
486
  this.lastScrolled = new Date();
421
487
  this.scrollInterval = setInterval(this.scroll.bind(this), 10);
@@ -440,7 +506,16 @@ Draggable.prototype = {
440
506
  Position.prepare();
441
507
  Droppables.show(Draggables._lastPointer, this.element);
442
508
  Draggables.notify('onDrag', this);
443
- this.draw(Draggables._lastPointer);
509
+ if (this._isScrollChild) {
510
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
511
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
512
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
513
+ if (Draggables._lastScrollPointer[0] < 0)
514
+ Draggables._lastScrollPointer[0] = 0;
515
+ if (Draggables._lastScrollPointer[1] < 0)
516
+ Draggables._lastScrollPointer[1] = 0;
517
+ this.draw(Draggables._lastScrollPointer);
518
+ }
444
519
 
445
520
  if(this.options.change) this.options.change(this);
446
521
  },
@@ -492,41 +567,55 @@ SortableObserver.prototype = {
492
567
  }
493
568
 
494
569
  var Sortable = {
495
- sortables: new Array(),
570
+ SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
496
571
 
497
- options: function(element){
498
- element = $(element);
499
- return this.sortables.detect(function(s) { return s.element == element });
572
+ sortables: {},
573
+
574
+ _findRootElement: function(element) {
575
+ while (element.tagName != "BODY") {
576
+ if(element.id && Sortable.sortables[element.id]) return element;
577
+ element = element.parentNode;
578
+ }
579
+ },
580
+
581
+ options: function(element) {
582
+ element = Sortable._findRootElement($(element));
583
+ if(!element) return;
584
+ return Sortable.sortables[element.id];
500
585
  },
501
586
 
502
587
  destroy: function(element){
503
- element = $(element);
504
- this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
588
+ var s = Sortable.options(element);
589
+
590
+ if(s) {
505
591
  Draggables.removeObserver(s.element);
506
592
  s.droppables.each(function(d){ Droppables.remove(d) });
507
593
  s.draggables.invoke('destroy');
508
- });
509
- this.sortables = this.sortables.reject(function(s) { return s.element == element });
594
+
595
+ delete Sortable.sortables[s.element.id];
596
+ }
510
597
  },
511
-
598
+
512
599
  create: function(element) {
513
600
  element = $(element);
514
601
  var options = Object.extend({
515
602
  element: element,
516
603
  tag: 'li', // assumes li children, override with tag: 'tagname'
517
604
  dropOnEmpty: false,
518
- tree: false, // fixme: unimplemented
605
+ tree: false,
606
+ treeTag: 'ul',
519
607
  overlap: 'vertical', // one of 'vertical', 'horizontal'
520
608
  constraint: 'vertical', // one of 'vertical', 'horizontal', false
521
609
  containment: element, // also takes array of elements (or id's); or false
522
610
  handle: false, // or a CSS class
523
611
  only: false,
612
+ delay: 0,
524
613
  hoverclass: null,
525
614
  ghosting: false,
526
615
  scroll: false,
527
616
  scrollSensitivity: 20,
528
617
  scrollSpeed: 15,
529
- format: /^[^_]*_(.*)$/,
618
+ format: this.SERIALIZE_RULE,
530
619
  onChange: Prototype.emptyFunction,
531
620
  onUpdate: Prototype.emptyFunction
532
621
  }, arguments[1] || {});
@@ -540,6 +629,7 @@ var Sortable = {
540
629
  scroll: options.scroll,
541
630
  scrollSpeed: options.scrollSpeed,
542
631
  scrollSensitivity: options.scrollSensitivity,
632
+ delay: options.delay,
543
633
  ghosting: options.ghosting,
544
634
  constraint: options.constraint,
545
635
  handle: options.handle };
@@ -565,9 +655,16 @@ var Sortable = {
565
655
  var options_for_droppable = {
566
656
  overlap: options.overlap,
567
657
  containment: options.containment,
658
+ tree: options.tree,
568
659
  hoverclass: options.hoverclass,
569
- onHover: Sortable.onHover,
570
- greedy: !options.dropOnEmpty
660
+ onHover: Sortable.onHover
661
+ }
662
+
663
+ var options_for_tree = {
664
+ onHover: Sortable.onEmptyHover,
665
+ overlap: options.overlap,
666
+ containment: options.containment,
667
+ hoverclass: options.hoverclass
571
668
  }
572
669
 
573
670
  // fix for gecko engine
@@ -576,27 +673,33 @@ var Sortable = {
576
673
  options.draggables = [];
577
674
  options.droppables = [];
578
675
 
579
- // make it so
580
-
581
676
  // drop on empty handling
582
- if(options.dropOnEmpty) {
583
- Droppables.add(element,
584
- {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
677
+ if(options.dropOnEmpty || options.tree) {
678
+ Droppables.add(element, options_for_tree);
585
679
  options.droppables.push(element);
586
680
  }
587
681
 
588
682
  (this.findElements(element, options) || []).each( function(e) {
589
683
  // handles are per-draggable
590
684
  var handle = options.handle ?
591
- Element.childrenWithClassName(e, options.handle)[0] : e;
685
+ $(e).down('.'+options.handle,0) : e;
592
686
  options.draggables.push(
593
687
  new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
594
688
  Droppables.add(e, options_for_droppable);
689
+ if(options.tree) e.treeNode = element;
595
690
  options.droppables.push(e);
596
691
  });
692
+
693
+ if(options.tree) {
694
+ (Sortable.findTreeElements(element, options) || []).each( function(e) {
695
+ Droppables.add(e, options_for_tree);
696
+ e.treeNode = element;
697
+ options.droppables.push(e);
698
+ });
699
+ }
597
700
 
598
701
  // keep reference
599
- this.sortables.push(options);
702
+ this.sortables[element.id] = options;
600
703
 
601
704
  // for onupdate
602
705
  Draggables.addObserver(new SortableObserver(element, options.onUpdate));
@@ -605,24 +708,21 @@ var Sortable = {
605
708
 
606
709
  // return all suitable-for-sortable elements in a guaranteed order
607
710
  findElements: function(element, options) {
608
- if(!element.hasChildNodes()) return null;
609
- var elements = [];
610
- var only = options.only ? [options.only].flatten() : null;
611
- $A(element.childNodes).each( function(e) {
612
- if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
613
- (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
614
- elements.push(e);
615
- if(options.tree) {
616
- var grandchildren = this.findElements(e, options);
617
- if(grandchildren) elements.push(grandchildren);
618
- }
619
- });
620
-
621
- return (elements.length>0 ? elements.flatten() : null);
711
+ return Element.findChildren(
712
+ element, options.only, options.tree ? true : false, options.tag);
713
+ },
714
+
715
+ findTreeElements: function(element, options) {
716
+ return Element.findChildren(
717
+ element, options.only, options.tree ? true : false, options.treeTag);
622
718
  },
623
719
 
624
720
  onHover: function(element, dropon, overlap) {
625
- if(overlap>0.5) {
721
+ if(Element.isParent(dropon, element)) return;
722
+
723
+ if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
724
+ return;
725
+ } else if(overlap>0.5) {
626
726
  Sortable.mark(dropon, 'before');
627
727
  if(dropon.previousSibling != element) {
628
728
  var oldParentNode = element.parentNode;
@@ -645,18 +745,42 @@ var Sortable = {
645
745
  }
646
746
  }
647
747
  },
648
-
649
- onEmptyHover: function(element, dropon) {
650
- if(element.parentNode!=dropon) {
651
- var oldParentNode = element.parentNode;
652
- dropon.appendChild(element);
748
+
749
+ onEmptyHover: function(element, dropon, overlap) {
750
+ var oldParentNode = element.parentNode;
751
+ var droponOptions = Sortable.options(dropon);
752
+
753
+ if(!Element.isParent(dropon, element)) {
754
+ var index;
755
+
756
+ var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
757
+ var child = null;
758
+
759
+ if(children) {
760
+ var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
761
+
762
+ for (index = 0; index < children.length; index += 1) {
763
+ if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
764
+ offset -= Element.offsetSize (children[index], droponOptions.overlap);
765
+ } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
766
+ child = index + 1 < children.length ? children[index + 1] : null;
767
+ break;
768
+ } else {
769
+ child = children[index];
770
+ break;
771
+ }
772
+ }
773
+ }
774
+
775
+ dropon.insertBefore(element, child);
776
+
653
777
  Sortable.options(oldParentNode).onChange(element);
654
- Sortable.options(dropon).onChange(element);
778
+ droponOptions.onChange(element);
655
779
  }
656
780
  },
657
781
 
658
782
  unmark: function() {
659
- if(Sortable._marker) Element.hide(Sortable._marker);
783
+ if(Sortable._marker) Sortable._marker.hide();
660
784
  },
661
785
 
662
786
  mark: function(dropon, position) {
@@ -665,23 +789,79 @@ var Sortable = {
665
789
  if(sortable && !sortable.ghosting) return;
666
790
 
667
791
  if(!Sortable._marker) {
668
- Sortable._marker = $('dropmarker') || document.createElement('DIV');
669
- Element.hide(Sortable._marker);
670
- Element.addClassName(Sortable._marker, 'dropmarker');
671
- Sortable._marker.style.position = 'absolute';
792
+ Sortable._marker =
793
+ ($('dropmarker') || Element.extend(document.createElement('DIV'))).
794
+ hide().addClassName('dropmarker').setStyle({position:'absolute'});
672
795
  document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
673
796
  }
674
797
  var offsets = Position.cumulativeOffset(dropon);
675
- Sortable._marker.style.left = offsets[0] + 'px';
676
- Sortable._marker.style.top = offsets[1] + 'px';
798
+ Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
677
799
 
678
800
  if(position=='after')
679
801
  if(sortable.overlap == 'horizontal')
680
- Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
802
+ Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
681
803
  else
682
- Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
804
+ Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
683
805
 
684
- Element.show(Sortable._marker);
806
+ Sortable._marker.show();
807
+ },
808
+
809
+ _tree: function(element, options, parent) {
810
+ var children = Sortable.findElements(element, options) || [];
811
+
812
+ for (var i = 0; i < children.length; ++i) {
813
+ var match = children[i].id.match(options.format);
814
+
815
+ if (!match) continue;
816
+
817
+ var child = {
818
+ id: encodeURIComponent(match ? match[1] : null),
819
+ element: element,
820
+ parent: parent,
821
+ children: [],
822
+ position: parent.children.length,
823
+ container: $(children[i]).down(options.treeTag)
824
+ }
825
+
826
+ /* Get the element containing the children and recurse over it */
827
+ if (child.container)
828
+ this._tree(child.container, options, child)
829
+
830
+ parent.children.push (child);
831
+ }
832
+
833
+ return parent;
834
+ },
835
+
836
+ tree: function(element) {
837
+ element = $(element);
838
+ var sortableOptions = this.options(element);
839
+ var options = Object.extend({
840
+ tag: sortableOptions.tag,
841
+ treeTag: sortableOptions.treeTag,
842
+ only: sortableOptions.only,
843
+ name: element.id,
844
+ format: sortableOptions.format
845
+ }, arguments[1] || {});
846
+
847
+ var root = {
848
+ id: null,
849
+ parent: null,
850
+ children: [],
851
+ container: element,
852
+ position: 0
853
+ }
854
+
855
+ return Sortable._tree(element, options, root);
856
+ },
857
+
858
+ /* Construct a [i] index for a particular node */
859
+ _constructIndex: function(node) {
860
+ var index = '';
861
+ do {
862
+ if (node.id) index = '[' + node.position + ']' + index;
863
+ } while ((node = node.parent) != null);
864
+ return index;
685
865
  },
686
866
 
687
867
  sequence: function(element) {
@@ -705,20 +885,58 @@ var Sortable = {
705
885
  });
706
886
 
707
887
  new_sequence.each(function(ident) {
708
- var n = nodeMap[ident];
709
- if (n) {
710
- n[1].appendChild(n[0]);
711
- delete nodeMap[ident];
712
- }
888
+ var n = nodeMap[ident];
889
+ if (n) {
890
+ n[1].appendChild(n[0]);
891
+ delete nodeMap[ident];
892
+ }
713
893
  });
714
894
  },
715
-
895
+
716
896
  serialize: function(element) {
717
897
  element = $(element);
898
+ var options = Object.extend(Sortable.options(element), arguments[1] || {});
718
899
  var name = encodeURIComponent(
719
900
  (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
720
- return Sortable.sequence(element, arguments[1]).map( function(item) {
721
- return name + "[]=" + encodeURIComponent(item);
722
- }).join('&');
901
+
902
+ if (options.tree) {
903
+ return Sortable.tree(element, arguments[1]).children.map( function (item) {
904
+ return [name + Sortable._constructIndex(item) + "[id]=" +
905
+ encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
906
+ }).flatten().join('&');
907
+ } else {
908
+ return Sortable.sequence(element, arguments[1]).map( function(item) {
909
+ return name + "[]=" + encodeURIComponent(item);
910
+ }).join('&');
911
+ }
723
912
  }
724
913
  }
914
+
915
+ // Returns true if child is contained within element
916
+ Element.isParent = function(child, element) {
917
+ if (!child.parentNode || child == element) return false;
918
+ if (child.parentNode == element) return true;
919
+ return Element.isParent(child.parentNode, element);
920
+ }
921
+
922
+ Element.findChildren = function(element, only, recursive, tagName) {
923
+ if(!element.hasChildNodes()) return null;
924
+ tagName = tagName.toUpperCase();
925
+ if(only) only = [only].flatten();
926
+ var elements = [];
927
+ $A(element.childNodes).each( function(e) {
928
+ if(e.tagName && e.tagName.toUpperCase()==tagName &&
929
+ (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
930
+ elements.push(e);
931
+ if(recursive) {
932
+ var grandchildren = Element.findChildren(e, only, recursive, tagName);
933
+ if(grandchildren) elements.push(grandchildren);
934
+ }
935
+ });
936
+
937
+ return (elements.length>0 ? elements.flatten() : []);
938
+ }
939
+
940
+ Element.offsetSize = function (element, type) {
941
+ return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
942
+ }