bacuview 1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. data/AUTHORS +2 -0
  2. data/ChangeLog +454 -0
  3. data/README.bacuview +7 -0
  4. data/README.rails +153 -0
  5. data/Rakefile +94 -0
  6. data/app/controllers/application.rb +11 -0
  7. data/app/controllers/client_controller.rb +27 -0
  8. data/app/controllers/job_controller.rb +100 -0
  9. data/app/controllers/jobmedia_controller.rb +2 -0
  10. data/app/controllers/media_controller.rb +44 -0
  11. data/app/controllers/misc_controller.rb +14 -0
  12. data/app/controllers/pool_controller.rb +38 -0
  13. data/app/helpers/application_helper.rb +88 -0
  14. data/app/helpers/client_finder.rb +112 -0
  15. data/app/helpers/client_helper.rb +45 -0
  16. data/app/helpers/job_helper.rb +56 -0
  17. data/app/helpers/jobmedia_helper.rb +2 -0
  18. data/app/helpers/media_helper.rb +14 -0
  19. data/app/helpers/misc_helper.rb +15 -0
  20. data/app/helpers/pool_helper.rb +2 -0
  21. data/app/models/client.rb +14 -0
  22. data/app/models/job.rb +21 -0
  23. data/app/models/jobmedia.rb +9 -0
  24. data/app/models/media.rb +23 -0
  25. data/app/models/pool.rb +13 -0
  26. data/app/views/client/_spec.rhtml +1 -0
  27. data/app/views/client/check.rhtml +1 -0
  28. data/app/views/client/index.rhtml +27 -0
  29. data/app/views/client/show.rhtml +8 -0
  30. data/app/views/job/_spec.rhtml +7 -0
  31. data/app/views/job/index.rhtml +38 -0
  32. data/app/views/job/last.rhtml +52 -0
  33. data/app/views/job/show.rhtml +19 -0
  34. data/app/views/job/spec.rhtml +38 -0
  35. data/app/views/layouts/bacuview-layout.rhtml +50 -0
  36. data/app/views/media/_spec.rhtml +1 -0
  37. data/app/views/media/index.rhtml +30 -0
  38. data/app/views/media/show.rhtml +8 -0
  39. data/app/views/misc/about.rhtml +2 -0
  40. data/app/views/misc/help.rhtml +1 -0
  41. data/app/views/pool/_spec.rhtml +1 -0
  42. data/app/views/pool/index.rhtml +29 -0
  43. data/app/views/pool/show.rhtml +8 -0
  44. data/bin/bacuview +3 -0
  45. data/config/bacuview.yml.template +2 -0
  46. data/config/boot.rb +19 -0
  47. data/config/database.yml.template +13 -0
  48. data/config/environment.rb +87 -0
  49. data/config/environments/development.rb +19 -0
  50. data/config/environments/production.rb +19 -0
  51. data/config/environments/test.rb +19 -0
  52. data/config/routes.rb +61 -0
  53. data/log/development.log +0 -0
  54. data/log/production.log +0 -0
  55. data/log/test.log +0 -0
  56. data/public/404.html +8 -0
  57. data/public/500.html +8 -0
  58. data/public/bacu-bat.png +0 -0
  59. data/public/bacuweb.css +7 -0
  60. data/public/client-sophie-thumb.png +0 -0
  61. data/public/client-sophie.png +0 -0
  62. data/public/clients-thumb.png +0 -0
  63. data/public/clients.png +0 -0
  64. data/public/dispatch.cgi +10 -0
  65. data/public/dispatch.fcgi +24 -0
  66. data/public/dispatch.rb +10 -0
  67. data/public/favicon.ico +0 -0
  68. data/public/home.html +108 -0
  69. data/public/images/bacu-bat.png +0 -0
  70. data/public/images/busy.png +0 -0
  71. data/public/images/dunno.png +0 -0
  72. data/public/images/error.png +0 -0
  73. data/public/images/okay.png +0 -0
  74. data/public/images/pool_pie.png +0 -0
  75. data/public/install.html +156 -0
  76. data/public/javascripts/application.js +2 -0
  77. data/public/javascripts/client_check.js +24 -0
  78. data/public/javascripts/controls.js +815 -0
  79. data/public/javascripts/dragdrop.js +724 -0
  80. data/public/javascripts/effects.js +953 -0
  81. data/public/javascripts/prototype.js +1985 -0
  82. data/public/job-1449-thumb.png +0 -0
  83. data/public/job-1449.png +0 -0
  84. data/public/job-last-thumb.png +0 -0
  85. data/public/job-last.png +0 -0
  86. data/public/jobs-thumb.png +0 -0
  87. data/public/jobs.png +0 -0
  88. data/public/media-39-thumb.png +0 -0
  89. data/public/media-39.png +0 -0
  90. data/public/media-thumb.png +0 -0
  91. data/public/media.png +0 -0
  92. data/public/news.html +144 -0
  93. data/public/pool-lto3-thumb.png +0 -0
  94. data/public/pool-lto3.png +0 -0
  95. data/public/pools-thumb.png +0 -0
  96. data/public/pools.png +0 -0
  97. data/public/robots.txt +1 -0
  98. data/public/stylesheets/bacuview.css +37 -0
  99. data/script/about +3 -0
  100. data/script/breakpointer +3 -0
  101. data/script/console +3 -0
  102. data/script/destroy +3 -0
  103. data/script/generate +3 -0
  104. data/script/performance/benchmarker +3 -0
  105. data/script/performance/profiler +3 -0
  106. data/script/plugin +3 -0
  107. data/script/process/reaper +3 -0
  108. data/script/process/spawner +3 -0
  109. data/script/process/spinner +3 -0
  110. data/script/runner +3 -0
  111. data/script/server +3 -0
  112. metadata +168 -0
@@ -0,0 +1,724 @@
1
+ // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
+ //
3
+ // See scriptaculous.js for full license.
4
+
5
+ /*--------------------------------------------------------------------------*/
6
+
7
+ var Droppables = {
8
+ drops: [],
9
+
10
+ remove: function(element) {
11
+ this.drops = this.drops.reject(function(d) { return d.element==$(element) });
12
+ },
13
+
14
+ add: function(element) {
15
+ element = $(element);
16
+ var options = Object.extend({
17
+ greedy: true,
18
+ hoverclass: null
19
+ }, arguments[1] || {});
20
+
21
+ // cache containers
22
+ if(options.containment) {
23
+ options._containers = [];
24
+ var containment = options.containment;
25
+ if((typeof containment == 'object') &&
26
+ (containment.constructor == Array)) {
27
+ containment.each( function(c) { options._containers.push($(c)) });
28
+ } else {
29
+ options._containers.push($(containment));
30
+ }
31
+ }
32
+
33
+ if(options.accept) options.accept = [options.accept].flatten();
34
+
35
+ Element.makePositioned(element); // fix IE
36
+ options.element = element;
37
+
38
+ this.drops.push(options);
39
+ },
40
+
41
+ isContained: function(element, drop) {
42
+ var parentNode = element.parentNode;
43
+ return drop._containers.detect(function(c) { return parentNode == c });
44
+ },
45
+
46
+ isAffected: function(point, element, drop) {
47
+ return (
48
+ (drop.element!=element) &&
49
+ ((!drop._containers) ||
50
+ this.isContained(element, drop)) &&
51
+ ((!drop.accept) ||
52
+ (Element.classNames(element).detect(
53
+ function(v) { return drop.accept.include(v) } ) )) &&
54
+ Position.within(drop.element, point[0], point[1]) );
55
+ },
56
+
57
+ deactivate: function(drop) {
58
+ if(drop.hoverclass)
59
+ Element.removeClassName(drop.element, drop.hoverclass);
60
+ this.last_active = null;
61
+ },
62
+
63
+ activate: function(drop) {
64
+ if(drop.hoverclass)
65
+ Element.addClassName(drop.element, drop.hoverclass);
66
+ this.last_active = drop;
67
+ },
68
+
69
+ show: function(point, element) {
70
+ if(!this.drops.length) return;
71
+
72
+ if(this.last_active) this.deactivate(this.last_active);
73
+ 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
+ }
82
+ });
83
+ },
84
+
85
+ fire: function(event, element) {
86
+ if(!this.last_active) return;
87
+ Position.prepare();
88
+
89
+ if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
90
+ if (this.last_active.onDrop)
91
+ this.last_active.onDrop(element, this.last_active.element, event);
92
+ },
93
+
94
+ reset: function() {
95
+ if(this.last_active)
96
+ this.deactivate(this.last_active);
97
+ }
98
+ }
99
+
100
+ var Draggables = {
101
+ drags: [],
102
+ observers: [],
103
+
104
+ register: function(draggable) {
105
+ if(this.drags.length == 0) {
106
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
107
+ this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
108
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
109
+
110
+ Event.observe(document, "mouseup", this.eventMouseUp);
111
+ Event.observe(document, "mousemove", this.eventMouseMove);
112
+ Event.observe(document, "keypress", this.eventKeypress);
113
+ }
114
+ this.drags.push(draggable);
115
+ },
116
+
117
+ unregister: function(draggable) {
118
+ this.drags = this.drags.reject(function(d) { return d==draggable });
119
+ if(this.drags.length == 0) {
120
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
121
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
122
+ Event.stopObserving(document, "keypress", this.eventKeypress);
123
+ }
124
+ },
125
+
126
+ activate: function(draggable) {
127
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
128
+ this.activeDraggable = draggable;
129
+ },
130
+
131
+ deactivate: function() {
132
+ this.activeDraggable = null;
133
+ },
134
+
135
+ updateDrag: function(event) {
136
+ if(!this.activeDraggable) return;
137
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
138
+ // Mozilla-based browsers fire successive mousemove events with
139
+ // the same coordinates, prevent needless redrawing (moz bug?)
140
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
141
+ this._lastPointer = pointer;
142
+ this.activeDraggable.updateDrag(event, pointer);
143
+ },
144
+
145
+ endDrag: function(event) {
146
+ if(!this.activeDraggable) return;
147
+ this._lastPointer = null;
148
+ this.activeDraggable.endDrag(event);
149
+ this.activeDraggable = null;
150
+ },
151
+
152
+ keyPress: function(event) {
153
+ if(this.activeDraggable)
154
+ this.activeDraggable.keyPress(event);
155
+ },
156
+
157
+ addObserver: function(observer) {
158
+ this.observers.push(observer);
159
+ this._cacheObserverCallbacks();
160
+ },
161
+
162
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
163
+ this.observers = this.observers.reject( function(o) { return o.element==element });
164
+ this._cacheObserverCallbacks();
165
+ },
166
+
167
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
168
+ if(this[eventName+'Count'] > 0)
169
+ this.observers.each( function(o) {
170
+ if(o[eventName]) o[eventName](eventName, draggable, event);
171
+ });
172
+ },
173
+
174
+ _cacheObserverCallbacks: function() {
175
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
176
+ Draggables[eventName+'Count'] = Draggables.observers.select(
177
+ function(o) { return o[eventName]; }
178
+ ).length;
179
+ });
180
+ }
181
+ }
182
+
183
+ /*--------------------------------------------------------------------------*/
184
+
185
+ var Draggable = Class.create();
186
+ Draggable.prototype = {
187
+ initialize: function(element) {
188
+ var options = Object.extend({
189
+ handle: false,
190
+ starteffect: function(element) {
191
+ new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
192
+ },
193
+ reverteffect: function(element, top_offset, left_offset) {
194
+ 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});
196
+ },
197
+ endeffect: function(element) {
198
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
199
+ },
200
+ zindex: 1000,
201
+ revert: false,
202
+ scroll: false,
203
+ scrollSensitivity: 20,
204
+ scrollSpeed: 15,
205
+ snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
206
+ }, arguments[1] || {});
207
+
208
+ this.element = $(element);
209
+
210
+ if(options.handle && (typeof options.handle == 'string'))
211
+ this.handle = Element.childrenWithClassName(this.element, options.handle, true)[0];
212
+ if(!this.handle) this.handle = $(options.handle);
213
+ if(!this.handle) this.handle = this.element;
214
+
215
+ if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
216
+ options.scroll = $(options.scroll);
217
+
218
+ Element.makePositioned(this.element); // fix IE
219
+
220
+ this.delta = this.currentDelta();
221
+ this.options = options;
222
+ this.dragging = false;
223
+
224
+ this.eventMouseDown = this.initDrag.bindAsEventListener(this);
225
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
226
+
227
+ Draggables.register(this);
228
+ },
229
+
230
+ destroy: function() {
231
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
232
+ Draggables.unregister(this);
233
+ },
234
+
235
+ currentDelta: function() {
236
+ return([
237
+ parseInt(Element.getStyle(this.element,'left') || '0'),
238
+ parseInt(Element.getStyle(this.element,'top') || '0')]);
239
+ },
240
+
241
+ initDrag: function(event) {
242
+ if(Event.isLeftClick(event)) {
243
+ // abort on form elements, fixes a Firefox issue
244
+ var src = Event.element(event);
245
+ if(src.tagName && (
246
+ src.tagName=='INPUT' ||
247
+ src.tagName=='SELECT' ||
248
+ src.tagName=='OPTION' ||
249
+ src.tagName=='BUTTON' ||
250
+ src.tagName=='TEXTAREA')) return;
251
+
252
+ if(this.element._revert) {
253
+ this.element._revert.cancel();
254
+ this.element._revert = null;
255
+ }
256
+
257
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
258
+ var pos = Position.cumulativeOffset(this.element);
259
+ this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
260
+
261
+ Draggables.activate(this);
262
+ Event.stop(event);
263
+ }
264
+ },
265
+
266
+ startDrag: function(event) {
267
+ this.dragging = true;
268
+
269
+ if(this.options.zindex) {
270
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
271
+ this.element.style.zIndex = this.options.zindex;
272
+ }
273
+
274
+ if(this.options.ghosting) {
275
+ this._clone = this.element.cloneNode(true);
276
+ Position.absolutize(this.element);
277
+ this.element.parentNode.insertBefore(this._clone, this.element);
278
+ }
279
+
280
+ if(this.options.scroll) {
281
+ if (this.options.scroll == window) {
282
+ var where = this._getWindowScroll(this.options.scroll);
283
+ this.originalScrollLeft = where.left;
284
+ this.originalScrollTop = where.top;
285
+ } else {
286
+ this.originalScrollLeft = this.options.scroll.scrollLeft;
287
+ this.originalScrollTop = this.options.scroll.scrollTop;
288
+ }
289
+ }
290
+
291
+ Draggables.notify('onStart', this, event);
292
+ if(this.options.starteffect) this.options.starteffect(this.element);
293
+ },
294
+
295
+ updateDrag: function(event, pointer) {
296
+ if(!this.dragging) this.startDrag(event);
297
+ Position.prepare();
298
+ Droppables.show(pointer, this.element);
299
+ Draggables.notify('onDrag', this, event);
300
+ this.draw(pointer);
301
+ if(this.options.change) this.options.change(this);
302
+
303
+ if(this.options.scroll) {
304
+ this.stopScrolling();
305
+
306
+ var p;
307
+ if (this.options.scroll == window) {
308
+ with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
309
+ } else {
310
+ p = Position.page(this.options.scroll);
311
+ p[0] += this.options.scroll.scrollLeft;
312
+ p[1] += this.options.scroll.scrollTop;
313
+ p.push(p[0]+this.options.scroll.offsetWidth);
314
+ p.push(p[1]+this.options.scroll.offsetHeight);
315
+ }
316
+ var speed = [0,0];
317
+ if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
318
+ if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
319
+ if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
320
+ if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
321
+ this.startScrolling(speed);
322
+ }
323
+
324
+ // fix AppleWebKit rendering
325
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
326
+
327
+ Event.stop(event);
328
+ },
329
+
330
+ finishDrag: function(event, success) {
331
+ this.dragging = false;
332
+
333
+ if(this.options.ghosting) {
334
+ Position.relativize(this.element);
335
+ Element.remove(this._clone);
336
+ this._clone = null;
337
+ }
338
+
339
+ if(success) Droppables.fire(event, this.element);
340
+ Draggables.notify('onEnd', this, event);
341
+
342
+ var revert = this.options.revert;
343
+ if(revert && typeof revert == 'function') revert = revert(this.element);
344
+
345
+ var d = this.currentDelta();
346
+ if(revert && this.options.reverteffect) {
347
+ this.options.reverteffect(this.element,
348
+ d[1]-this.delta[1], d[0]-this.delta[0]);
349
+ } else {
350
+ this.delta = d;
351
+ }
352
+
353
+ if(this.options.zindex)
354
+ this.element.style.zIndex = this.originalZ;
355
+
356
+ if(this.options.endeffect)
357
+ this.options.endeffect(this.element);
358
+
359
+ Draggables.deactivate(this);
360
+ Droppables.reset();
361
+ },
362
+
363
+ keyPress: function(event) {
364
+ if(event.keyCode!=Event.KEY_ESC) return;
365
+ this.finishDrag(event, false);
366
+ Event.stop(event);
367
+ },
368
+
369
+ endDrag: function(event) {
370
+ if(!this.dragging) return;
371
+ this.stopScrolling();
372
+ this.finishDrag(event, true);
373
+ Event.stop(event);
374
+ },
375
+
376
+ draw: function(point) {
377
+ var pos = Position.cumulativeOffset(this.element);
378
+ var d = this.currentDelta();
379
+ pos[0] -= d[0]; pos[1] -= d[1];
380
+
381
+ if(this.options.scroll && (this.options.scroll != window)) {
382
+ pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
383
+ pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
384
+ }
385
+
386
+ var p = [0,1].map(function(i){
387
+ return (point[i]-pos[i]-this.offset[i])
388
+ }.bind(this));
389
+
390
+ if(this.options.snap) {
391
+ if(typeof this.options.snap == 'function') {
392
+ p = this.options.snap(p[0],p[1]);
393
+ } else {
394
+ if(this.options.snap instanceof Array) {
395
+ p = p.map( function(v, i) {
396
+ return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
397
+ } else {
398
+ p = p.map( function(v) {
399
+ return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
400
+ }
401
+ }}
402
+
403
+ var style = this.element.style;
404
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
405
+ style.left = p[0] + "px";
406
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
407
+ style.top = p[1] + "px";
408
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
409
+ },
410
+
411
+ stopScrolling: function() {
412
+ if(this.scrollInterval) {
413
+ clearInterval(this.scrollInterval);
414
+ this.scrollInterval = null;
415
+ }
416
+ },
417
+
418
+ startScrolling: function(speed) {
419
+ this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
420
+ this.lastScrolled = new Date();
421
+ this.scrollInterval = setInterval(this.scroll.bind(this), 10);
422
+ },
423
+
424
+ scroll: function() {
425
+ var current = new Date();
426
+ var delta = current - this.lastScrolled;
427
+ this.lastScrolled = current;
428
+ if(this.options.scroll == window) {
429
+ with (this._getWindowScroll(this.options.scroll)) {
430
+ if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
431
+ var d = delta / 1000;
432
+ this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
433
+ }
434
+ }
435
+ } else {
436
+ this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
437
+ this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
438
+ }
439
+
440
+ Position.prepare();
441
+ Droppables.show(Draggables._lastPointer, this.element);
442
+ Draggables.notify('onDrag', this);
443
+ this.draw(Draggables._lastPointer);
444
+
445
+ if(this.options.change) this.options.change(this);
446
+ },
447
+
448
+ _getWindowScroll: function(w) {
449
+ var T, L, W, H;
450
+ with (w.document) {
451
+ if (w.document.documentElement && documentElement.scrollTop) {
452
+ T = documentElement.scrollTop;
453
+ L = documentElement.scrollLeft;
454
+ } else if (w.document.body) {
455
+ T = body.scrollTop;
456
+ L = body.scrollLeft;
457
+ }
458
+ if (w.innerWidth) {
459
+ W = w.innerWidth;
460
+ H = w.innerHeight;
461
+ } else if (w.document.documentElement && documentElement.clientWidth) {
462
+ W = documentElement.clientWidth;
463
+ H = documentElement.clientHeight;
464
+ } else {
465
+ W = body.offsetWidth;
466
+ H = body.offsetHeight
467
+ }
468
+ }
469
+ return { top: T, left: L, width: W, height: H };
470
+ }
471
+ }
472
+
473
+ /*--------------------------------------------------------------------------*/
474
+
475
+ var SortableObserver = Class.create();
476
+ SortableObserver.prototype = {
477
+ initialize: function(element, observer) {
478
+ this.element = $(element);
479
+ this.observer = observer;
480
+ this.lastValue = Sortable.serialize(this.element);
481
+ },
482
+
483
+ onStart: function() {
484
+ this.lastValue = Sortable.serialize(this.element);
485
+ },
486
+
487
+ onEnd: function() {
488
+ Sortable.unmark();
489
+ if(this.lastValue != Sortable.serialize(this.element))
490
+ this.observer(this.element)
491
+ }
492
+ }
493
+
494
+ var Sortable = {
495
+ sortables: new Array(),
496
+
497
+ options: function(element){
498
+ element = $(element);
499
+ return this.sortables.detect(function(s) { return s.element == element });
500
+ },
501
+
502
+ destroy: function(element){
503
+ element = $(element);
504
+ this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
505
+ Draggables.removeObserver(s.element);
506
+ s.droppables.each(function(d){ Droppables.remove(d) });
507
+ s.draggables.invoke('destroy');
508
+ });
509
+ this.sortables = this.sortables.reject(function(s) { return s.element == element });
510
+ },
511
+
512
+ create: function(element) {
513
+ element = $(element);
514
+ var options = Object.extend({
515
+ element: element,
516
+ tag: 'li', // assumes li children, override with tag: 'tagname'
517
+ dropOnEmpty: false,
518
+ tree: false, // fixme: unimplemented
519
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
520
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
521
+ containment: element, // also takes array of elements (or id's); or false
522
+ handle: false, // or a CSS class
523
+ only: false,
524
+ hoverclass: null,
525
+ ghosting: false,
526
+ scroll: false,
527
+ scrollSensitivity: 20,
528
+ scrollSpeed: 15,
529
+ format: /^[^_]*_(.*)$/,
530
+ onChange: Prototype.emptyFunction,
531
+ onUpdate: Prototype.emptyFunction
532
+ }, arguments[1] || {});
533
+
534
+ // clear any old sortable with same element
535
+ this.destroy(element);
536
+
537
+ // build options for the draggables
538
+ var options_for_draggable = {
539
+ revert: true,
540
+ scroll: options.scroll,
541
+ scrollSpeed: options.scrollSpeed,
542
+ scrollSensitivity: options.scrollSensitivity,
543
+ ghosting: options.ghosting,
544
+ constraint: options.constraint,
545
+ handle: options.handle };
546
+
547
+ if(options.starteffect)
548
+ options_for_draggable.starteffect = options.starteffect;
549
+
550
+ if(options.reverteffect)
551
+ options_for_draggable.reverteffect = options.reverteffect;
552
+ else
553
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
554
+ element.style.top = 0;
555
+ element.style.left = 0;
556
+ };
557
+
558
+ if(options.endeffect)
559
+ options_for_draggable.endeffect = options.endeffect;
560
+
561
+ if(options.zindex)
562
+ options_for_draggable.zindex = options.zindex;
563
+
564
+ // build options for the droppables
565
+ var options_for_droppable = {
566
+ overlap: options.overlap,
567
+ containment: options.containment,
568
+ hoverclass: options.hoverclass,
569
+ onHover: Sortable.onHover,
570
+ greedy: !options.dropOnEmpty
571
+ }
572
+
573
+ // fix for gecko engine
574
+ Element.cleanWhitespace(element);
575
+
576
+ options.draggables = [];
577
+ options.droppables = [];
578
+
579
+ // make it so
580
+
581
+ // drop on empty handling
582
+ if(options.dropOnEmpty) {
583
+ Droppables.add(element,
584
+ {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
585
+ options.droppables.push(element);
586
+ }
587
+
588
+ (this.findElements(element, options) || []).each( function(e) {
589
+ // handles are per-draggable
590
+ var handle = options.handle ?
591
+ Element.childrenWithClassName(e, options.handle)[0] : e;
592
+ options.draggables.push(
593
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
594
+ Droppables.add(e, options_for_droppable);
595
+ options.droppables.push(e);
596
+ });
597
+
598
+ // keep reference
599
+ this.sortables.push(options);
600
+
601
+ // for onupdate
602
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
603
+
604
+ },
605
+
606
+ // return all suitable-for-sortable elements in a guaranteed order
607
+ 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);
622
+ },
623
+
624
+ onHover: function(element, dropon, overlap) {
625
+ if(overlap>0.5) {
626
+ Sortable.mark(dropon, 'before');
627
+ if(dropon.previousSibling != element) {
628
+ var oldParentNode = element.parentNode;
629
+ element.style.visibility = "hidden"; // fix gecko rendering
630
+ dropon.parentNode.insertBefore(element, dropon);
631
+ if(dropon.parentNode!=oldParentNode)
632
+ Sortable.options(oldParentNode).onChange(element);
633
+ Sortable.options(dropon.parentNode).onChange(element);
634
+ }
635
+ } else {
636
+ Sortable.mark(dropon, 'after');
637
+ var nextElement = dropon.nextSibling || null;
638
+ if(nextElement != element) {
639
+ var oldParentNode = element.parentNode;
640
+ element.style.visibility = "hidden"; // fix gecko rendering
641
+ dropon.parentNode.insertBefore(element, nextElement);
642
+ if(dropon.parentNode!=oldParentNode)
643
+ Sortable.options(oldParentNode).onChange(element);
644
+ Sortable.options(dropon.parentNode).onChange(element);
645
+ }
646
+ }
647
+ },
648
+
649
+ onEmptyHover: function(element, dropon) {
650
+ if(element.parentNode!=dropon) {
651
+ var oldParentNode = element.parentNode;
652
+ dropon.appendChild(element);
653
+ Sortable.options(oldParentNode).onChange(element);
654
+ Sortable.options(dropon).onChange(element);
655
+ }
656
+ },
657
+
658
+ unmark: function() {
659
+ if(Sortable._marker) Element.hide(Sortable._marker);
660
+ },
661
+
662
+ mark: function(dropon, position) {
663
+ // mark on ghosting only
664
+ var sortable = Sortable.options(dropon.parentNode);
665
+ if(sortable && !sortable.ghosting) return;
666
+
667
+ 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';
672
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
673
+ }
674
+ var offsets = Position.cumulativeOffset(dropon);
675
+ Sortable._marker.style.left = offsets[0] + 'px';
676
+ Sortable._marker.style.top = offsets[1] + 'px';
677
+
678
+ if(position=='after')
679
+ if(sortable.overlap == 'horizontal')
680
+ Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
681
+ else
682
+ Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
683
+
684
+ Element.show(Sortable._marker);
685
+ },
686
+
687
+ sequence: function(element) {
688
+ element = $(element);
689
+ var options = Object.extend(this.options(element), arguments[1] || {});
690
+
691
+ return $(this.findElements(element, options) || []).map( function(item) {
692
+ return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
693
+ });
694
+ },
695
+
696
+ setSequence: function(element, new_sequence) {
697
+ element = $(element);
698
+ var options = Object.extend(this.options(element), arguments[2] || {});
699
+
700
+ var nodeMap = {};
701
+ this.findElements(element, options).each( function(n) {
702
+ if (n.id.match(options.format))
703
+ nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
704
+ n.parentNode.removeChild(n);
705
+ });
706
+
707
+ new_sequence.each(function(ident) {
708
+ var n = nodeMap[ident];
709
+ if (n) {
710
+ n[1].appendChild(n[0]);
711
+ delete nodeMap[ident];
712
+ }
713
+ });
714
+ },
715
+
716
+ serialize: function(element) {
717
+ element = $(element);
718
+ var name = encodeURIComponent(
719
+ (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('&');
723
+ }
724
+ }