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