| 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 | |