switch to YUI compressor for making packed JS -- 49k -> 42k
[dygraphs.git] / mochikit_v14 / MochiKit / DragAndDrop.js
1 /***
2 MochiKit.DragAndDrop 1.4
3
4 See <http://mochikit.com/> for documentation, downloads, license, etc.
5
6 Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
7 Mochi-ized By Thomas Herve (_firstname_@nimail.org)
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.DragAndDrop');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Iter');
16 dojo.require('MochiKit.Visual');
17 dojo.require('MochiKit.Signal');
18 }
19
20 if (typeof(JSAN) != 'undefined') {
21 JSAN.use("MochiKit.Base", []);
22 JSAN.use("MochiKit.DOM", []);
23 JSAN.use("MochiKit.Visual", []);
24 JSAN.use("MochiKit.Iter", []);
25 JSAN.use("MochiKit.Signal", []);
26 }
27
28 try {
29 if (typeof(MochiKit.Base) == 'undefined' ||
30 typeof(MochiKit.DOM) == 'undefined' ||
31 typeof(MochiKit.Visual) == 'undefined' ||
32 typeof(MochiKit.Signal) == 'undefined' ||
33 typeof(MochiKit.Iter) == 'undefined') {
34 throw "";
35 }
36 } catch (e) {
37 throw "MochiKit.DragAndDrop depends on MochiKit.Base, MochiKit.DOM, MochiKit.Visual, MochiKit.Signal and MochiKit.Iter!";
38 }
39
40 if (typeof(MochiKit.DragAndDrop) == 'undefined') {
41 MochiKit.DragAndDrop = {};
42 }
43
44 MochiKit.DragAndDrop.NAME = 'MochiKit.DragAndDrop';
45 MochiKit.DragAndDrop.VERSION = '1.4';
46
47 MochiKit.DragAndDrop.__repr__ = function () {
48 return '[' + this.NAME + ' ' + this.VERSION + ']';
49 };
50
51 MochiKit.DragAndDrop.toString = function () {
52 return this.__repr__();
53 };
54
55 MochiKit.DragAndDrop.EXPORT = [
56 "Droppable",
57 "Draggable"
58 ];
59
60 MochiKit.DragAndDrop.EXPORT_OK = [
61 "Droppables",
62 "Draggables"
63 ];
64
65 MochiKit.DragAndDrop.Droppables = {
66 /***
67
68 Manage all droppables. Shouldn't be used, use the Droppable object instead.
69
70 ***/
71 drops: [],
72
73 remove: function (element) {
74 this.drops = MochiKit.Base.filter(function (d) {
75 return d.element != MochiKit.DOM.getElement(element);
76 }, this.drops);
77 },
78
79 register: function (drop) {
80 this.drops.push(drop);
81 },
82
83 unregister: function (drop) {
84 this.drops = MochiKit.Base.filter(function (d) {
85 return d != drop;
86 }, this.drops);
87 },
88
89 prepare: function (element) {
90 MochiKit.Base.map(function (drop) {
91 if (drop.isAccepted(element)) {
92 if (drop.options.activeclass) {
93 MochiKit.DOM.addElementClass(drop.element,
94 drop.options.activeclass);
95 }
96 drop.options.onactive(drop.element, element);
97 }
98 }, this.drops);
99 },
100
101 findDeepestChild: function (drops) {
102 deepest = drops[0];
103
104 for (i = 1; i < drops.length; ++i) {
105 if (MochiKit.DOM.isParent(drops[i].element, deepest.element)) {
106 deepest = drops[i];
107 }
108 }
109 return deepest;
110 },
111
112 show: function (point, element) {
113 if (!this.drops.length) {
114 return;
115 }
116 var affected = [];
117
118 if (this.last_active) {
119 this.last_active.deactivate();
120 }
121 MochiKit.Iter.forEach(this.drops, function (drop) {
122 if (drop.isAffected(point, element)) {
123 affected.push(drop);
124 }
125 });
126 if (affected.length > 0) {
127 drop = this.findDeepestChild(affected);
128 MochiKit.Position.within(drop.element, point.page.x, point.page.y);
129 drop.options.onhover(element, drop.element,
130 MochiKit.Position.overlap(drop.options.overlap, drop.element));
131 drop.activate();
132 }
133 },
134
135 fire: function (event, element) {
136 if (!this.last_active) {
137 return;
138 }
139 MochiKit.Position.prepare();
140
141 if (this.last_active.isAffected(event.mouse(), element)) {
142 this.last_active.options.ondrop(element,
143 this.last_active.element, event);
144 }
145 },
146
147 reset: function (element) {
148 MochiKit.Base.map(function (drop) {
149 if (drop.options.activeclass) {
150 MochiKit.DOM.removeElementClass(drop.element,
151 drop.options.activeclass);
152 }
153 drop.options.ondesactive(drop.element, element);
154 }, this.drops);
155 if (this.last_active) {
156 this.last_active.deactivate();
157 }
158 }
159 };
160
161 /** @id MochiKit.DragAndDrop.Droppable */
162 MochiKit.DragAndDrop.Droppable = function (element, options) {
163 var cls = arguments.callee;
164 if (!(this instanceof cls)) {
165 return new cls(element, options);
166 }
167 this.__init__(element, options);
168 };
169
170 MochiKit.DragAndDrop.Droppable.prototype = {
171 /***
172
173 A droppable object. Simple use is to create giving an element:
174
175 new MochiKit.DragAndDrop.Droppable('myelement');
176
177 Generally you'll want to define the 'ondrop' function and maybe the
178 'accept' option to filter draggables.
179
180 ***/
181 __class__: MochiKit.DragAndDrop.Droppable,
182
183 __init__: function (element, /* optional */options) {
184 var d = MochiKit.DOM;
185 var b = MochiKit.Base;
186 this.element = d.getElement(element);
187 this.options = b.update({
188
189 /** @id MochiKit.DragAndDrop.greedy */
190 greedy: true,
191
192 /** @id MochiKit.DragAndDrop.hoverclass */
193 hoverclass: null,
194
195 /** @id MochiKit.DragAndDrop.activeclass */
196 activeclass: null,
197
198 /** @id MochiKit.DragAndDrop.hoverfunc */
199 hoverfunc: b.noop,
200
201 /** @id MochiKit.DragAndDrop.accept */
202 accept: null,
203
204 /** @id MochiKit.DragAndDrop.onactive */
205 onactive: b.noop,
206
207 /** @id MochiKit.DragAndDrop.ondesactive */
208 ondesactive: b.noop,
209
210 /** @id MochiKit.DragAndDrop.onhover */
211 onhover: b.noop,
212
213 /** @id MochiKit.DragAndDrop.ondrop */
214 ondrop: b.noop,
215
216 /** @id MochiKit.DragAndDrop.containment */
217 containment: [],
218 tree: false
219 }, options || {});
220
221 // cache containers
222 this.options._containers = [];
223 b.map(MochiKit.Base.bind(function (c) {
224 this.options._containers.push(d.getElement(c));
225 }, this), this.options.containment);
226
227 d.makePositioned(this.element); // fix IE
228
229 MochiKit.DragAndDrop.Droppables.register(this);
230 },
231
232 /** @id MochiKit.DragAndDrop.isContained */
233 isContained: function (element) {
234 if (this.options._containers.length) {
235 var containmentNode;
236 if (this.options.tree) {
237 containmentNode = element.treeNode;
238 } else {
239 containmentNode = element.parentNode;
240 }
241 return MochiKit.Iter.some(this.options._containers, function (c) {
242 return containmentNode == c;
243 });
244 } else {
245 return true;
246 }
247 },
248
249 /** @id MochiKit.DragAndDrop.isAccepted */
250 isAccepted: function (element) {
251 return ((!this.options.accept) || MochiKit.Iter.some(
252 this.options.accept, function (c) {
253 return MochiKit.DOM.hasElementClass(element, c);
254 }));
255 },
256
257 /** @id MochiKit.DragAndDrop.isAffected */
258 isAffected: function (point, element) {
259 return ((this.element != element) &&
260 this.isContained(element) &&
261 this.isAccepted(element) &&
262 MochiKit.Position.within(this.element, point.page.x,
263 point.page.y));
264 },
265
266 /** @id MochiKit.DragAndDrop.deactivate */
267 deactivate: function () {
268 /***
269
270 A droppable is deactivate when a draggable has been over it and left.
271
272 ***/
273 if (this.options.hoverclass) {
274 MochiKit.DOM.removeElementClass(this.element,
275 this.options.hoverclass);
276 }
277 this.options.hoverfunc(this.element, false);
278 MochiKit.DragAndDrop.Droppables.last_active = null;
279 },
280
281 /** @id MochiKit.DragAndDrop.activate */
282 activate: function () {
283 /***
284
285 A droppable is active when a draggable is over it.
286
287 ***/
288 if (this.options.hoverclass) {
289 MochiKit.DOM.addElementClass(this.element, this.options.hoverclass);
290 }
291 this.options.hoverfunc(this.element, true);
292 MochiKit.DragAndDrop.Droppables.last_active = this;
293 },
294
295 /** @id MochiKit.DragAndDrop.destroy */
296 destroy: function () {
297 /***
298
299 Delete this droppable.
300
301 ***/
302 MochiKit.DragAndDrop.Droppables.unregister(this);
303 },
304
305 /** @id MochiKit.DragAndDrop.repr */
306 repr: function () {
307 return '[' + this.__class__.NAME + ", options:" + MochiKit.Base.repr(this.options) + "]";
308 }
309 };
310
311 MochiKit.DragAndDrop.Draggables = {
312 /***
313
314 Manage draggables elements. Not intended to direct use.
315
316 ***/
317 drags: [],
318
319 register: function (draggable) {
320 if (this.drags.length === 0) {
321 var conn = MochiKit.Signal.connect;
322 this.eventMouseUp = conn(document, 'onmouseup', this, this.endDrag);
323 this.eventMouseMove = conn(document, 'onmousemove', this,
324 this.updateDrag);
325 this.eventKeypress = conn(document, 'onkeypress', this,
326 this.keyPress);
327 }
328 this.drags.push(draggable);
329 },
330
331 unregister: function (draggable) {
332 this.drags = MochiKit.Base.filter(function (d) {
333 return d != draggable;
334 }, this.drags);
335 if (this.drags.length === 0) {
336 var disc = MochiKit.Signal.disconnect;
337 disc(this.eventMouseUp);
338 disc(this.eventMouseMove);
339 disc(this.eventKeypress);
340 }
341 },
342
343 activate: function (draggable) {
344 // allows keypress events if window is not currently focused
345 // fails for Safari
346 window.focus();
347 this.activeDraggable = draggable;
348 },
349
350 deactivate: function () {
351 this.activeDraggable = null;
352 },
353
354 updateDrag: function (event) {
355 if (!this.activeDraggable) {
356 return;
357 }
358 var pointer = event.mouse();
359 // Mozilla-based browsers fire successive mousemove events with
360 // the same coordinates, prevent needless redrawing (moz bug?)
361 if (this._lastPointer && (MochiKit.Base.repr(this._lastPointer.page) ==
362 MochiKit.Base.repr(pointer.page))) {
363 return;
364 }
365 this._lastPointer = pointer;
366 this.activeDraggable.updateDrag(event, pointer);
367 },
368
369 endDrag: function (event) {
370 if (!this.activeDraggable) {
371 return;
372 }
373 this._lastPointer = null;
374 this.activeDraggable.endDrag(event);
375 this.activeDraggable = null;
376 },
377
378 keyPress: function (event) {
379 if (this.activeDraggable) {
380 this.activeDraggable.keyPress(event);
381 }
382 },
383
384 notify: function (eventName, draggable, event) {
385 MochiKit.Signal.signal(this, eventName, draggable, event);
386 }
387 };
388
389 /** @id MochiKit.DragAndDrop.Draggable */
390 MochiKit.DragAndDrop.Draggable = function (element, options) {
391 var cls = arguments.callee;
392 if (!(this instanceof cls)) {
393 return new cls(element, options);
394 }
395 this.__init__(element, options);
396 };
397
398 MochiKit.DragAndDrop.Draggable.prototype = {
399 /***
400
401 A draggable object. Simple instantiate :
402
403 new MochiKit.DragAndDrop.Draggable('myelement');
404
405 ***/
406 __class__ : MochiKit.DragAndDrop.Draggable,
407
408 __init__: function (element, /* optional */options) {
409 var v = MochiKit.Visual;
410 var b = MochiKit.Base;
411 options = b.update({
412
413 /** @id MochiKit.DragAndDrop.handle */
414 handle: false,
415
416 /** @id MochiKit.DragAndDrop.starteffect */
417 starteffect: function (innerelement) {
418 this._savedOpacity = MochiKit.Style.getStyle(innerelement, 'opacity') || 1.0;
419 new v.Opacity(innerelement, {duration:0.2, from:this._savedOpacity, to:0.7});
420 },
421 /** @id MochiKit.DragAndDrop.reverteffect */
422 reverteffect: function (innerelement, top_offset, left_offset) {
423 var dur = Math.sqrt(Math.abs(top_offset^2) +
424 Math.abs(left_offset^2))*0.02;
425 return new v.Move(innerelement,
426 {x: -left_offset, y: -top_offset, duration: dur});
427 },
428
429 /** @id MochiKit.DragAndDrop.endeffect */
430 endeffect: function (innerelement) {
431 new v.Opacity(innerelement, {duration:0.2, from:0.7, to:this._savedOpacity});
432 },
433
434 /** @id MochiKit.DragAndDrop.onchange */
435 onchange: b.noop,
436
437 /** @id MochiKit.DragAndDrop.zindex */
438 zindex: 1000,
439
440 /** @id MochiKit.DragAndDrop.revert */
441 revert: false,
442
443 /** @id MochiKit.DragAndDrop.scroll */
444 scroll: false,
445
446 /** @id MochiKit.DragAndDrop.scrollSensitivity */
447 scrollSensitivity: 20,
448
449 /** @id MochiKit.DragAndDrop.scrollSpeed */
450 scrollSpeed: 15,
451 // false, or xy or [x, y] or function (x, y){return [x, y];}
452
453 /** @id MochiKit.DragAndDrop.snap */
454 snap: false
455 }, options || {});
456
457 var d = MochiKit.DOM;
458 this.element = d.getElement(element);
459
460 if (options.handle && (typeof(options.handle) == 'string')) {
461 this.handle = d.getFirstElementByTagAndClassName(null,
462 options.handle, this.element);
463 }
464 if (!this.handle) {
465 this.handle = d.getElement(options.handle);
466 }
467 if (!this.handle) {
468 this.handle = this.element;
469 }
470
471 if (options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
472 options.scroll = d.getElement(options.scroll);
473 this._isScrollChild = MochiKit.DOM.isChildNode(this.element, options.scroll);
474 }
475
476 d.makePositioned(this.element); // fix IE
477
478 this.delta = this.currentDelta();
479 this.options = options;
480 this.dragging = false;
481
482 this.eventMouseDown = MochiKit.Signal.connect(this.handle,
483 'onmousedown', this, this.initDrag);
484 MochiKit.DragAndDrop.Draggables.register(this);
485 },
486
487 /** @id MochiKit.DragAndDrop.destroy */
488 destroy: function () {
489 MochiKit.Signal.disconnect(this.eventMouseDown);
490 MochiKit.DragAndDrop.Draggables.unregister(this);
491 },
492
493 /** @id MochiKit.DragAndDrop.currentDelta */
494 currentDelta: function () {
495 var s = MochiKit.Style.getStyle;
496 return [
497 parseInt(s(this.element, 'left') || '0'),
498 parseInt(s(this.element, 'top') || '0')];
499 },
500
501 /** @id MochiKit.DragAndDrop.initDrag */
502 initDrag: function (event) {
503 if (!event.mouse().button.left) {
504 return;
505 }
506 // abort on form elements, fixes a Firefox issue
507 var src = event.target();
508 var tagName = (src.tagName || '').toUpperCase();
509 if (tagName === 'INPUT' || tagName === 'SELECT' ||
510 tagName === 'OPTION' || tagName === 'BUTTON' ||
511 tagName === 'TEXTAREA') {
512 return;
513 }
514
515 if (this._revert) {
516 this._revert.cancel();
517 this._revert = null;
518 }
519
520 var pointer = event.mouse();
521 var pos = MochiKit.Position.cumulativeOffset(this.element);
522 this.offset = [pointer.page.x - pos.x, pointer.page.y - pos.y];
523
524 MochiKit.DragAndDrop.Draggables.activate(this);
525 event.stop();
526 },
527
528 /** @id MochiKit.DragAndDrop.startDrag */
529 startDrag: function (event) {
530 this.dragging = true;
531 if (this.options.selectclass) {
532 MochiKit.DOM.addElementClass(this.element,
533 this.options.selectclass);
534 }
535 if (this.options.zindex) {
536 this.originalZ = parseInt(MochiKit.Style.getStyle(this.element,
537 'z-index') || '0');
538 this.element.style.zIndex = this.options.zindex;
539 }
540
541 if (this.options.ghosting) {
542 this._clone = this.element.cloneNode(true);
543 this.ghostPosition = MochiKit.Position.absolutize(this.element);
544 this.element.parentNode.insertBefore(this._clone, this.element);
545 }
546
547 if (this.options.scroll) {
548 if (this.options.scroll == window) {
549 var where = this._getWindowScroll(this.options.scroll);
550 this.originalScrollLeft = where.left;
551 this.originalScrollTop = where.top;
552 } else {
553 this.originalScrollLeft = this.options.scroll.scrollLeft;
554 this.originalScrollTop = this.options.scroll.scrollTop;
555 }
556 }
557
558 MochiKit.DragAndDrop.Droppables.prepare(this.element);
559 MochiKit.DragAndDrop.Draggables.notify('start', this, event);
560 if (this.options.starteffect) {
561 this.options.starteffect(this.element);
562 }
563 },
564
565 /** @id MochiKit.DragAndDrop.updateDrag */
566 updateDrag: function (event, pointer) {
567 if (!this.dragging) {
568 this.startDrag(event);
569 }
570 MochiKit.Position.prepare();
571 MochiKit.DragAndDrop.Droppables.show(pointer, this.element);
572 MochiKit.DragAndDrop.Draggables.notify('drag', this, event);
573 this.draw(pointer);
574 this.options.onchange(this);
575
576 if (this.options.scroll) {
577 this.stopScrolling();
578 var p, q;
579 if (this.options.scroll == window) {
580 var s = this._getWindowScroll(this.options.scroll);
581 p = new MochiKit.Style.Coordinates(s.left, s.top);
582 q = new MochiKit.Style.Coordinates(s.left + s.width,
583 s.top + s.height);
584 } else {
585 p = MochiKit.Position.page(this.options.scroll);
586 p.x += this.options.scroll.scrollLeft;
587 p.y += this.options.scroll.scrollTop;
588 p.x += (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0);
589 p.y += (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
590 q = new MochiKit.Style.Coordinates(p.x + this.options.scroll.offsetWidth,
591 p.y + this.options.scroll.offsetHeight);
592 }
593 var speed = [0, 0];
594 if (pointer.page.x > (q.x - this.options.scrollSensitivity)) {
595 speed[0] = pointer.page.x - (q.x - this.options.scrollSensitivity);
596 } else if (pointer.page.x < (p.x + this.options.scrollSensitivity)) {
597 speed[0] = pointer.page.x - (p.x + this.options.scrollSensitivity);
598 }
599 if (pointer.page.y > (q.y - this.options.scrollSensitivity)) {
600 speed[1] = pointer.page.y - (q.y - this.options.scrollSensitivity);
601 } else if (pointer.page.y < (p.y + this.options.scrollSensitivity)) {
602 speed[1] = pointer.page.y - (p.y + this.options.scrollSensitivity);
603 }
604 this.startScrolling(speed);
605 }
606
607 // fix AppleWebKit rendering
608 if (/AppleWebKit'/.test(navigator.appVersion)) {
609 window.scrollBy(0, 0);
610 }
611 event.stop();
612 },
613
614 /** @id MochiKit.DragAndDrop.finishDrag */
615 finishDrag: function (event, success) {
616 var dr = MochiKit.DragAndDrop;
617 this.dragging = false;
618 if (this.options.selectclass) {
619 MochiKit.DOM.removeElementClass(this.element,
620 this.options.selectclass);
621 }
622
623 if (this.options.ghosting) {
624 // XXX: from a user point of view, it would be better to remove
625 // the node only *after* the MochiKit.Visual.Move end when used
626 // with revert.
627 MochiKit.Position.relativize(this.element, this.ghostPosition);
628 MochiKit.DOM.removeElement(this._clone);
629 this._clone = null;
630 }
631
632 if (success) {
633 dr.Droppables.fire(event, this.element);
634 }
635 dr.Draggables.notify('end', this, event);
636
637 var revert = this.options.revert;
638 if (revert && typeof(revert) == 'function') {
639 revert = revert(this.element);
640 }
641
642 var d = this.currentDelta();
643 if (revert && this.options.reverteffect) {
644 this._revert = this.options.reverteffect(this.element,
645 d[1] - this.delta[1], d[0] - this.delta[0]);
646 } else {
647 this.delta = d;
648 }
649
650 if (this.options.zindex) {
651 this.element.style.zIndex = this.originalZ;
652 }
653
654 if (this.options.endeffect) {
655 this.options.endeffect(this.element);
656 }
657
658 dr.Draggables.deactivate();
659 dr.Droppables.reset(this.element);
660 },
661
662 /** @id MochiKit.DragAndDrop.keyPress */
663 keyPress: function (event) {
664 if (event.key().string != "KEY_ESCAPE") {
665 return;
666 }
667 this.finishDrag(event, false);
668 event.stop();
669 },
670
671 /** @id MochiKit.DragAndDrop.endDrag */
672 endDrag: function (event) {
673 if (!this.dragging) {
674 return;
675 }
676 this.stopScrolling();
677 this.finishDrag(event, true);
678 event.stop();
679 },
680
681 /** @id MochiKit.DragAndDrop.draw */
682 draw: function (point) {
683 var pos = MochiKit.Position.cumulativeOffset(this.element);
684 var d = this.currentDelta();
685 pos.x -= d[0];
686 pos.y -= d[1];
687
688 if (this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
689 pos.x -= this.options.scroll.scrollLeft - this.originalScrollLeft;
690 pos.y -= this.options.scroll.scrollTop - this.originalScrollTop;
691 }
692
693 var p = [point.page.x - pos.x - this.offset[0],
694 point.page.y - pos.y - this.offset[1]];
695
696 if (this.options.snap) {
697 if (typeof(this.options.snap) == 'function') {
698 p = this.options.snap(p[0], p[1]);
699 } else {
700 if (this.options.snap instanceof Array) {
701 var i = -1;
702 p = MochiKit.Base.map(MochiKit.Base.bind(function (v) {
703 i += 1;
704 return Math.round(v/this.options.snap[i]) *
705 this.options.snap[i];
706 }, this), p);
707 } else {
708 p = MochiKit.Base.map(MochiKit.Base.bind(function (v) {
709 return Math.round(v/this.options.snap) *
710 this.options.snap;
711 }, this), p);
712 }
713 }
714 }
715 var style = this.element.style;
716 if ((!this.options.constraint) ||
717 (this.options.constraint == 'horizontal')) {
718 style.left = p[0] + 'px';
719 }
720 if ((!this.options.constraint) ||
721 (this.options.constraint == 'vertical')) {
722 style.top = p[1] + 'px';
723 }
724 if (style.visibility == 'hidden') {
725 style.visibility = ''; // fix gecko rendering
726 }
727 },
728
729 /** @id MochiKit.DragAndDrop.stopScrolling */
730 stopScrolling: function () {
731 if (this.scrollInterval) {
732 clearInterval(this.scrollInterval);
733 this.scrollInterval = null;
734 MochiKit.DragAndDrop.Draggables._lastScrollPointer = null;
735 }
736 },
737
738 /** @id MochiKit.DragAndDrop.startScrolling */
739 startScrolling: function (speed) {
740 if (!speed[0] && !speed[1]) {
741 return;
742 }
743 this.scrollSpeed = [speed[0] * this.options.scrollSpeed,
744 speed[1] * this.options.scrollSpeed];
745 this.lastScrolled = new Date();
746 this.scrollInterval = setInterval(MochiKit.Base.bind(this.scroll, this), 10);
747 },
748
749 /** @id MochiKit.DragAndDrop.scroll */
750 scroll: function () {
751 var current = new Date();
752 var delta = current - this.lastScrolled;
753 this.lastScrolled = current;
754
755 if (this.options.scroll == window) {
756 var s = this._getWindowScroll(this.options.scroll);
757 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
758 var dm = delta / 1000;
759 this.options.scroll.scrollTo(s.left + dm * this.scrollSpeed[0],
760 s.top + dm * this.scrollSpeed[1]);
761 }
762 } else {
763 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
764 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
765 }
766
767 var d = MochiKit.DragAndDrop;
768
769 MochiKit.Position.prepare();
770 d.Droppables.show(d.Draggables._lastPointer, this.element);
771 d.Draggables.notify('drag', this);
772 if (this._isScrollChild) {
773 d.Draggables._lastScrollPointer = d.Draggables._lastScrollPointer || d.Draggables._lastPointer;
774 d.Draggables._lastScrollPointer.x += this.scrollSpeed[0] * delta / 1000;
775 d.Draggables._lastScrollPointer.y += this.scrollSpeed[1] * delta / 1000;
776 if (d.Draggables._lastScrollPointer.x < 0) {
777 d.Draggables._lastScrollPointer.x = 0;
778 }
779 if (d.Draggables._lastScrollPointer.y < 0) {
780 d.Draggables._lastScrollPointer.y = 0;
781 }
782 this.draw(d.Draggables._lastScrollPointer);
783 }
784
785 this.options.onchange(this);
786 },
787
788 _getWindowScroll: function (win) {
789 var vp, w, h;
790 MochiKit.DOM.withWindow(win, function () {
791 vp = MochiKit.Style.getViewportPosition(win.document);
792 });
793 if (win.innerWidth) {
794 w = win.innerWidth;
795 h = win.innerHeight;
796 } else if (win.document.documentElement && win.document.documentElement.clientWidth) {
797 w = win.document.documentElement.clientWidth;
798 h = win.document.documentElement.clientHeight;
799 } else {
800 w = win.document.body.offsetWidth;
801 h = win.document.body.offsetHeight;
802 }
803 return {top: vp.x, left: vp.y, width: w, height: h};
804 },
805
806 /** @id MochiKit.DragAndDrop.repr */
807 repr: function () {
808 return '[' + this.__class__.NAME + ", options:" + MochiKit.Base.repr(this.options) + "]";
809 }
810 };
811
812 MochiKit.DragAndDrop.__new__ = function () {
813 MochiKit.Base.nameFunctions(this);
814
815 this.EXPORT_TAGS = {
816 ":common": this.EXPORT,
817 ":all": MochiKit.Base.concat(this.EXPORT, this.EXPORT_OK)
818 };
819 };
820
821 MochiKit.DragAndDrop.__new__();
822
823 MochiKit.Base._exportSymbols(this, MochiKit.DragAndDrop);
824