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