DragAndDrop.js (27242B)
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);