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