source: main/branches/64_bit_Greenstone/greenstone3/web/interfaces/default-client-xslt/js/dragdrop.js@ 24007

Last change on this file since 24007 was 24007, checked in by sjm84, 13 years ago

Updating this branch to match the latest Greenstone3 changes

File size: 71.8 KB
Line 
1/*
2Copyright (c) 2006 Yahoo! Inc. All rights reserved.
3version 0.9.0
4*/
5
6/**
7 * @class Defines the interface and base operation of items that that can be
8 * dragged or can be drop targets. It was designed to be extended, overriding
9 * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
10 * Up to three html elements can be associated with a DragDrop instance:
11 * <ul>
12 * <li>linked element: the element that is passed into the constructor.
13 * This is the element which defines the boundaries for interaction with
14 * other DragDrop objects.</li>
15 * <li>handle element(s): The drag operation only occurs if the element that
16 * was clicked matches a handle element. By default this is the linked
17 * element, but there are times that you will want only a portion of the
18 * linked element to initiate the drag operation, and the setHandleElId()
19 * method provides a way to define this.</li>
20 * <li>drag element: this represents an the element that would be moved along
21 * with the cursor during a drag operation. By default, this is the linked
22 * element itself as in {@link YAHOO.util.DD}. setDragElId() lets you define
23 * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
24 * </li>
25 * </ul>
26 * This class should not be instantiated until the onload event to ensure that
27 * the associated elements are available.
28 * The following would define a DragDrop obj that would interact with any
29 * other * DragDrop obj in the "group1" group:
30 * <pre>
31 * dd = new YAHOO.util.DragDrop("div1", "group1");
32 * </pre>
33 * Since none of the event handlers have been implemented, nothing would
34 * actually happen if you were to run the code above. Normally you would
35 * override this class or one of the default implementations, but you can
36 * also override the methods you want on an instance of the class...
37 * <pre>
38 * dd.onDragDrop = function(e, id) {
39 * alert("dd was dropped on " + id);
40 * }
41 * </pre>
42 * @constructor
43 * @param {String} id of the element that is linked to this instance
44 * @param {String} sGroup the group of related DragDrop objects
45 */
46YAHOO.util.DragDrop = function(id, sGroup) {
47 if (id) {
48 this.init(id, sGroup);
49 }
50};
51
52YAHOO.util.DragDrop.prototype = {
53
54 /**
55 * The id of the element associated with this object. This is what we
56 * refer to as the "linked element" because the size and position of
57 * this element is used to determine when the drag and drop objects have
58 * interacted.
59 *
60 * @type String
61 */
62 id: null,
63
64 /**
65 * The id of the element that will be dragged. By default this is same
66 * as the linked element , but could be changed to another element. Ex:
67 * YAHOO.util.DDProxy
68 *
69 * @type String
70 * @private
71 */
72 dragElId: null,
73
74 /**
75 * the id of the element that initiates the drag operation. By default
76 * this is the linked element, but could be changed to be a child of this
77 * element. This lets us do things like only starting the drag when the
78 * header element within the linked html element is clicked.
79 *
80 * @type String
81 * @private
82 */
83 handleElId: null,
84
85 /**
86 * An array of HTML tags that will be ignored if clicked.
87 */
88 invalidHandleTypes: null,
89
90 /**
91 * The linked element's absolute X position at the time the drag was
92 * started
93 *
94 * @type int
95 * @private
96 */
97 startPageX: 0,
98
99 /**
100 * The linked element's absolute X position at the time the drag was
101 * started
102 *
103 * @type int
104 * @private
105 */
106 startPageY: 0,
107
108 /**
109 * The group defines a logical collection of DragDrop objects that are
110 * related. Instances only get events when interacting with other
111 * DragDrop object in the same group. This lets us define multiple
112 * groups using a single DragDrop subclass if we want.
113 *
114 */
115 groups: null,
116
117 /**
118 * Individual drag/drop instances can be locked. This will prevent
119 * onmousedown start drag.
120 *
121 * @type boolean
122 * @private
123 */
124 locked: false,
125
126 /**
127 * Lock this instance
128 */
129 lock: function() { this.locked = true; },
130
131 /**
132 * Unlock this instace
133 */
134 unlock: function() { this.locked = false; },
135
136 /**
137 * By default, all insances can be a drop target. This can be disabled by
138 * setting isTarget to false.
139 *
140 * @type boolean
141 */
142 isTarget: true,
143
144 /**
145 * The padding configured for this drag and drop object for calculating
146 * the drop zone intersection with this object.
147 */
148 padding: null,
149
150 /**
151 * @private
152 */
153 _domRef: null,
154
155 /**
156 * Internal typeof flag
157 * @private
158 */
159 __ygDragDrop: true,
160
161 /**
162 * Set to true when horizontal contraints are applied
163 *
164 * @type boolean
165 * @private
166 */
167 constrainX: false,
168
169 /**
170 * Set to true when vertical contraints are applied
171 *
172 * @type boolean
173 * @private
174 */
175 constrainY: false,
176
177 /**
178 * The left constraint
179 *
180 * @type int
181 * @private
182 */
183 minX: 0,
184
185 /**
186 * The right constraint
187 *
188 * @type int
189 * @private
190 */
191 maxX: 0,
192
193 /**
194 * The up constraint
195 *
196 * @type int
197 * @private
198 */
199 minY: 0,
200
201 /**
202 * The down constraint
203 *
204 * @type int
205 * @private
206 */
207 maxY: 0,
208
209 /**
210 * Maintain offsets when we resetconstraints. Used to maintain the
211 * slider thumb value, and this needs to be fixed.
212 * @type boolean
213 */
214 maintainOffset: false,
215
216 /**
217 * Array of pixel locations the element will snap to if we specified a
218 * horizontal graduation/interval. This array is generated automatically
219 * when you define a tick interval.
220 * @type int[]
221 */
222 xTicks: null,
223
224 /**
225 * Array of pixel locations the element will snap to if we specified a
226 * vertical graduation/interval. This array is generated automatically
227 * when you define a tick interval.
228 * @type int[]
229 */
230 yTicks: null,
231
232 /**
233 * By default the drag and drop instance will only respond to the primary
234 * button click (left button for a right-handed mouse). Set to true to
235 * allow drag and drop to start with any mouse click that is propogated
236 * by the browser
237 * @type boolean
238 */
239 primaryButtonOnly: true,
240
241 /**
242 * Code that executes immediately before the startDrag event
243 * @private
244 */
245 b4StartDrag: function(x, y) { },
246
247 /**
248 * Abstract method called after a drag/drop object is clicked
249 * and the drag or mousedown time thresholds have beeen met.
250 *
251 * @param {int} X click location
252 * @param {int} Y click location
253 */
254 startDrag: function(x, y) { /* override this */ },
255
256 /**
257 * Code that executes immediately before the onDrag event
258 * @private
259 */
260 b4Drag: function(e) { },
261
262 /**
263 * Abstract method called during the onMouseMove event while dragging an
264 * object.
265 *
266 * @param {Event} e
267 */
268 onDrag: function(e) { /* override this */ },
269
270 /**
271 * Code that executes immediately before the onDragEnter event
272 * @private
273 */
274 // b4DragEnter: function(e) { },
275
276 /**
277 * Abstract method called when this element fist begins hovering over
278 * another DragDrop obj
279 *
280 * @param {Event} e
281 * @param {String || YAHOO.util.DragDrop[]} id In POINT mode, the element
282 * id this is hovering over. In INTERSECT mode, an array of one or more
283 * dragdrop items being hovered over.
284 */
285 onDragEnter: function(e, id) { /* override this */ },
286
287 /**
288 * Code that executes immediately before the onDragOver event
289 * @private
290 */
291 b4DragOver: function(e) { },
292
293 /**
294 * Abstract method called when this element is hovering over another
295 * DragDrop obj
296 *
297 * @param {Event} e
298 * @param {String || YAHOO.util.DragDrop[]} id In POINT mode, the element
299 * id this is hovering over. In INTERSECT mode, an array of dd items
300 * being hovered over.
301 */
302 onDragOver: function(e, id) { /* override this */ },
303
304 /**
305 * Code that executes immediately before the onDragOut event
306 * @private
307 */
308 b4DragOut: function(e) { },
309
310 /**
311 * Abstract method called when we are no longer hovering over an element
312 *
313 * @param {Event} e
314 * @param {String || YAHOO.util.DragDrop[]} id In POINT mode, the element
315 * id this was hovering over. In INTERSECT mode, an array of dd items
316 * that the mouse is no longer over.
317 */
318 onDragOut: function(e, id) { /* override this */ },
319
320 /**
321 * Code that executes immediately before the onDragDrop event
322 * @private
323 */
324 b4DragDrop: function(e) { },
325
326 /**
327 * Abstract method called when this item is dropped on another DragDrop
328 * obj
329 *
330 * @param {Event} e
331 * @param {String || YAHOO.util.DragDrop[]} id In POINT mode, the element
332 * id this was dropped on. In INTERSECT mode, an array of dd items this
333 * was dropped on.
334 */
335 onDragDrop: function(e, id) { /* override this */ },
336
337 /**
338 * Code that executes immediately before the endDrag event
339 * @private
340 */
341 b4EndDrag: function(e) { },
342
343 /**
344 * Fired when we are done dragging the object
345 *
346 * @param {Event} e
347 */
348 endDrag: function(e) { /* override this */ },
349
350 /**
351 * Code executed immediately before the onMouseDown event
352
353 * @param {Event} e
354 * @private
355 */
356 b4MouseDown: function(e) { },
357
358 /**
359 * Event handler that fires when a drag/drop obj gets a mousedown
360 * @param {Event} e
361 */
362 onMouseDown: function(e) { /* override this */ },
363
364 /**
365 * Event handler that fires when a drag/drop obj gets a mouseup
366 * @param {Event} e
367 */
368 onMouseUp: function(e) { /* override this */ },
369
370 /**
371 * Returns a reference to the linked element
372 *
373 * @return {Object} the html element
374 */
375 getEl: function() {
376 if (!this._domRef) {
377 this._domRef = this.DDM.getElement(this.id);
378 }
379
380 return this._domRef;
381 },
382
383 /**
384 * Returns a reference to the actual element to drag. By default this is
385 * the same as the html element, but it can be assigned to another
386 * element. An example of this can be found in YAHOO.util.DDProxy
387 *
388 * @return {Object} the html element
389 */
390 getDragEl: function() {
391 return this.DDM.getElement(this.dragElId);
392 },
393
394 /**
395 * Sets up the DragDrop object. Must be called in the constructor of any
396 * YAHOO.util.DragDrop subclass
397 *
398 * @param id the id of the linked element
399 * @param {String} sGroup the group of related items
400 * element is supposed to be a target only, set to false.
401 */
402 init: function(id, sGroup) {
403 this.initTarget(id, sGroup);
404 YAHOO.util.Event.addListener(id, "mousedown",
405 this.handleMouseDown, this, true);
406 },
407
408 /**
409 * Initializes Targeting functionality only... the object does not
410 * get a mousedown handler.
411 *
412 * @param id the id of the linked element
413 * @param {String} sGroup the group of related items
414 * element is supposed to be a target only, set to false.
415 */
416 initTarget: function(id, sGroup) {
417
418 // create a local reference to the drag and drop manager
419 this.DDM = YAHOO.util.DDM;
420
421
422 // set the default padding
423 this.padding = [0, 0, 0, 0];
424
425 // initialize the groups array
426 this.groups = {};
427
428 // set the id
429 this.id = id;
430
431 // the element is a drag handle by default
432 this.setDragElId(id);
433
434 // by default, clicked anchors will not start drag operations
435 this.invalidHandleTypes = {a : "a"};
436
437 // We don't want to register this as the handle with the manager
438 // so we just set the id rather than calling the setter
439 this.handleElId = id;
440
441 // cache the position of the element if we can
442 if (document && document.body) {
443 this.setInitPosition();
444 }
445
446 // add to an interaction group
447 this.addToGroup((sGroup) ? sGroup : "default");
448
449 },
450
451 /**
452 * Configures the padding for the target zone in px. Effectively expands
453 * (or reduces) the virtual object size for targeting calculations.
454 * Supports css-style shorthand; if only one parameter is passed, all sides
455 * will have that padding, and if only two are passed, the top and bottom
456 * will have the first param, the left and right the second.
457 * @param {int} iTop Top pad
458 * @param {int} iRight Right pad
459 * @param {int} iBot Bot pad
460 * @param {int} iLeft Left pad
461 */
462 setPadding: function(iTop, iRight, iBot, iLeft) {
463 // this.padding = [iLeft, iRight, iTop, iBot];
464 if (!iRight && 0 !== iRight) {
465 this.padding = [iTop, iTop, iTop, iTop];
466 } else if (!iBot && 0 !== iBot) {
467 this.padding = [iTop, iRight, iTop, iRight];
468 } else {
469 this.padding = [iTop, iRight, iBot, iLeft];
470 }
471 },
472
473 /**
474 * Stores the initial placement of the dd element
475 */
476 setInitPosition: function(diffX, diffY) {
477 var el = this.getEl();
478
479 if (!this.DDM.verifyEl(el)) {
480 return;
481 }
482
483 var dx = diffX || 0;
484 var dy = diffY || 0;
485
486 var p = YAHOO.util.Dom.getXY( el );
487
488 this.initPageX = p[0] - dx;
489 this.initPageY = p[1] - dy;
490
491 this.lastPageX = p[0];
492 this.lastPageY = p[1];
493
494 this.setStartPosition(p);
495 },
496
497 /**
498 * Sets the start position of the element. This is set when the obj
499 * is initialized, the reset when a drag is started.
500 * @param pos current position (from previous lookup)
501 * @private
502 */
503 setStartPosition: function(pos) {
504
505 var p = pos || YAHOO.util.Dom.getXY( this.getEl() );
506
507 this.startPageX = p[0];
508 this.startPageY = p[1];
509 },
510
511 /**
512 * Add this instance to a group of related drag/drop objects. All
513 * instances belong to at least one group, and can belong to as many
514 * groups as needed.
515 *
516 * @param sGroup {string} the name of the group
517 */
518 addToGroup: function(sGroup) {
519 this.groups[sGroup] = true;
520 this.DDM.regDragDrop(this, sGroup);
521 },
522
523 /**
524 * Allows you to specify that an element other than the linked element
525 * will be moved with the cursor during a drag
526 *
527 * @param id the id of the element that will be used to initiate the drag
528 */
529 setDragElId: function(id) {
530 this.dragElId = id;
531 },
532
533 /**
534 * Allows you to specify a child of the linked element that should be
535 * used to initiate the drag operation. An example of this would be if
536 * you have a content div with text and links. Clicking anywhere in the
537 * content area would normally start the drag operation. Use this method
538 * to specify that an element inside of the content div is the element
539 * that starts the drag operation.
540 *
541 * @param id the id of the element that will be used to initiate the drag
542 */
543 setHandleElId: function(id) {
544 this.handleElId = id;
545 this.DDM.regHandle(this.id, id);
546 },
547
548 /**
549 * Allows you to set an element outside of the linked element as a drag
550 * handle
551 */
552 setOuterHandleElId: function(id) {
553 YAHOO.util.Event.addListener(id, "mousedown",
554 this.handleMouseDown, this, true);
555 this.setHandleElId(id);
556 },
557
558 /**
559 * Remove all drag and drop hooks for this element
560 */
561 unreg: function() {
562 YAHOO.util.Event.removeListener(this.id, "mousedown",
563 this.handleMouseDown);
564 this._domRef = null;
565 this.DDM._remove(this);
566 },
567
568 /**
569 * Returns true if this instance is locked, or the drag drop mgr is locked
570 * (meaning that all drag/drop is disabled on the page.)
571 *
572 * @return {boolean} true if this obj or all drag/drop is locked, else
573 * false
574 */
575 isLocked: function() {
576 return (this.DDM.isLocked() || this.locked);
577 },
578
579 /**
580 * Fired when this object is clicked
581 *
582 * @param {Event} e
583 * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
584 * @private
585 */
586 handleMouseDown: function(e, oDD) {
587
588
589 var EU = YAHOO.util.Event;
590
591
592 var button = e.which || e.button;
593
594 if (this.primaryButtonOnly && button > 1) {
595 return;
596 }
597
598 if (this.isLocked()) {
599 return;
600 }
601
602
603 this.DDM.refreshCache(this.groups);
604
605 // Only process the event if we really clicked within the linked
606 // element. The reason we make this check is that in the case that
607 // another element was moved between the clicked element and the
608 // cursor in the time between the mousedown and mouseup events. When
609 // this happens, the element gets the next mousedown event
610 // regardless of where on the screen it happened.
611 var pt = new YAHOO.util.Point(EU.getPageX(e), EU.getPageY(e));
612 if ( this.DDM.isOverTarget(pt, this) ) {
613
614
615 // check to see if the handle was clicked
616 var srcEl = EU.getTarget(e);
617
618 if (this.isValidHandleChild(srcEl) &&
619 (this.id == this.handleElId ||
620 this.DDM.handleWasClicked(srcEl, this.id)) ) {
621
622 // set the initial element position
623 this.setStartPosition();
624
625
626 this.b4MouseDown(e);
627 this.onMouseDown(e);
628 this.DDM.handleMouseDown(e, this);
629
630 this.DDM.stopEvent(e);
631 }
632 }
633 },
634
635 /**
636 * Allows you to specify a tag name that should not start a drag operation
637 * when clicked. This is designed to facilitate embedding links within a
638 * drag handle that do something other than start the drag.
639 *
640 * @param {string} tagName the type of element to exclude
641 */
642 addInvalidHandleType: function(tagName) {
643 var type = tagName.toUpperCase();
644 this.invalidHandleTypes[type] = type;
645 },
646
647 /**
648 * Unsets an excluded tag name set by addInvalidHandleType
649 *
650 * @param {string} tagName the type of element to unexclude
651 */
652 removeInvalidHandleType: function(tagName) {
653 var type = tagName.toUpperCase();
654 this.invalidHandleTypes[type] = null;
655 },
656
657 /**
658 * Checks the tag exclusion list to see if this click should be ignored
659 *
660 * @param {ygNode} node
661 * @return {boolean} true if this is a valid tag type, false if not
662 */
663 isValidHandleChild: function(node) {
664 var type = node.nodeName;
665
666 if (type == "#text") {
667 type = node.parentNode.nodeName;
668 }
669
670 return (!this.invalidHandleTypes[type]);
671 },
672
673 /**
674 * Create the array of horizontal tick marks if an interval was specified
675 * in setXConstraint().
676 *
677 * @private
678 */
679 setXTicks: function(iStartX, iTickSize) {
680 this.xTicks = [];
681 this.xTickSize = iTickSize;
682
683 var tickMap = {};
684
685 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
686 if (!tickMap[i]) {
687 this.xTicks[this.xTicks.length] = i;
688 tickMap[i] = true;
689 }
690 }
691
692 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
693 if (!tickMap[i]) {
694 this.xTicks[this.xTicks.length] = i;
695 tickMap[i] = true;
696 }
697 }
698
699 this.xTicks.sort(this.DDM.numericSort) ;
700 },
701
702 /**
703 * Create the array of vertical tick marks if an interval was specified in
704 * setYConstraint().
705 *
706 * @private
707 */
708 setYTicks: function(iStartY, iTickSize) {
709 this.yTicks = [];
710 this.yTickSize = iTickSize;
711
712 var tickMap = {};
713
714 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
715 if (!tickMap[i]) {
716 this.yTicks[this.yTicks.length] = i;
717 tickMap[i] = true;
718 }
719 }
720
721 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
722 if (!tickMap[i]) {
723 this.yTicks[this.yTicks.length] = i;
724 tickMap[i] = true;
725 }
726 }
727
728 this.yTicks.sort(this.DDM.numericSort) ;
729 },
730
731 /**
732 * By default, the element can be dragged any place on the screen. Use
733 * this method to limit the horizontal travel of the element. Pass in
734 * 0,0 for the parameters if you want to lock the drag to the y axis.
735 *
736 * @param {int} iLeft the number of pixels the element can move to the left
737 * @param {int} iRight the number of pixels the element can move to the
738 * right
739 * @param {int} iTickSize optional parameter for specifying that the
740 * element
741 * should move iTickSize pixels at a time.
742 */
743 setXConstraint: function(iLeft, iRight, iTickSize) {
744 this.leftConstraint = iLeft;
745 this.rightConstraint = iRight;
746
747 this.minX = this.initPageX - iLeft;
748 this.maxX = this.initPageX + iRight;
749 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
750
751 this.constrainX = true;
752 },
753
754 /**
755 * By default, the element can be dragged any place on the screen. Set
756 * this to limit the vertical travel of the element. Pass in 0,0 for the
757 * parameters if you want to lock the drag to the x axis.
758 *
759 * @param {int} iUp the number of pixels the element can move up
760 * @param {int} iDown the number of pixels the element can move down
761 * @param {int} iTickSize optional parameter for specifying that the
762 * element should move iTickSize pixels at a time.
763 */
764 setYConstraint: function(iUp, iDown, iTickSize) {
765 this.topConstraint = iUp;
766 this.bottomConstraint = iDown;
767
768 this.minY = this.initPageY - iUp;
769 this.maxY = this.initPageY + iDown;
770 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
771
772 this.constrainY = true;
773
774 },
775
776 /**
777 * resetConstraints must be called if you manually reposition a dd element.
778 * @param {boolean} maintainOffset
779 */
780 resetConstraints: function() {
781
782
783 // figure out how much this thing has moved
784 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
785 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
786
787
788 // reset the initial location
789 this.setInitPosition(dx, dy);
790
791 if (this.constrainX) {
792 this.setXConstraint( this.leftConstraint,
793 this.rightConstraint,
794 this.xTickSize );
795 }
796
797 if (this.constrainY) {
798 this.setYConstraint( this.topConstraint,
799 this.bottomConstraint,
800 this.yTickSize );
801 }
802 },
803
804 /**
805 * Normally the drag element is moved pixel by pixel, but we can specify
806 * that it move a number of pixels at a time. This method resolves the
807 * location when we have it set up like this.
808 *
809 * @param {int} val where we want to place the object
810 * @param {int[]} tickArray sorted array of valid points
811 * @return {int} the closest tick
812 * @private
813 */
814 getTick: function(val, tickArray) {
815
816 if (!tickArray) {
817 // If tick interval is not defined, it is effectively 1 pixel,
818 // so we return the value passed to us.
819 return val;
820 } else if (tickArray[0] >= val) {
821 // The value is lower than the first tick, so we return the first
822 // tick.
823 return tickArray[0];
824 } else {
825 for (var i = 0; i < tickArray.length; ++i) {
826 var next = i + 1;
827 if (tickArray[next] && tickArray[next] >= val) {
828 var diff1 = val - tickArray[i];
829 var diff2 = tickArray[next] - val;
830 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
831 }
832 }
833
834 // The value is larger than the last tick, so we return the last
835 // tick.
836 return tickArray[tickArray.length - 1];
837 }
838 },
839
840 /**
841 * toString method
842 * @return {string} string representation of the dd obj
843 */
844 toString: function(val, tickArray) {
845 return ("YAHOO.util.DragDrop {" + this.id + "}");
846 }
847
848};
849
850/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
851
852// Only load the library once. Rewriting the manager class would orphan
853// existing drag and drop instances.
854if (!YAHOO.util.DragDropMgr) {
855
856 /**
857 * @class Handles the element interaction for all DragDrop items in the
858 * window. Generally, you will not call this class directly, but it does
859 * have helper methods that could be useful in your DragDrop
860 * implementations. This class should not be instantiated; all methods
861 * are are static.
862 *
863 * @constructor
864 */
865 YAHOO.util.DragDropMgr = new function() {
866
867 /**
868 * utility package shorthand
869 * @private
870 */
871 var UTIL = YAHOO.util;
872
873 /**
874 * Two dimensional Array of registered DragDrop objects. The first
875 * dimension is the DragDrop item group, the second the DragDrop
876 * object.
877 *
878 * @private
879 */
880 this.ids = {};
881
882 /**
883 * Array of element ids defined as drag handles. Used to determine
884 * if the element that generated the mousedown event is actually the
885 * handle and not the html element itself.
886 *
887 * @private
888 */
889 this.handleIds = {};
890
891 /**
892 * the DragDrop object that is currently being dragged
893 *
894 * @type DragDrop
895 * @private
896 **/
897 this.dragCurrent = null;
898
899 /**
900 * the DragDrop object(s) that are being hovered over
901 *
902 * @type Array
903 * @private
904 */
905 this.dragOvers = {};
906
907 /**
908 * @private
909 */
910
911 /**
912 * the X distance between the cursor and the object being dragged
913 *
914 * @type int
915 * @private
916 */
917 this.deltaX = 0;
918
919 /**
920 * the Y distance between the cursor and the object being dragged
921 *
922 * @type int
923 * @private
924 */
925 this.deltaY = 0;
926
927 /**
928 * Flag to determine if we should prevent the default behavior of the
929 * events we define. By default this is true, but this can be set to
930 * false if you need the default behavior (not recommended)
931 *
932 * @type boolean
933 */
934 this.preventDefault = true;
935
936 /**
937 * Flag to determine if we should stop the propagation of the events
938 * we generate. This is true by default but you may want to set it to
939 * false if the html element contains other features that require the
940 * mouse click.
941 *
942 * @type boolean
943 */
944 this.stopPropagation = true;
945
946 /**
947 * @private
948 */
949 this.initalized = false;
950
951 /**
952 * All drag and drop can be disabled.
953 *
954 * @private
955 */
956 this.locked = false;
957
958 /**
959 * Called the first time an element is registered.
960 *
961 * @private
962 */
963 this.init = function() {
964 };
965
966 /**
967 * In point mode, drag and drop interaction is defined by the
968 * location of the cursor during the drag/drop
969 * @type int
970 */
971 this.POINT = 0;
972
973 /**
974 * In intersect mode, drag and drop interactio nis defined by the
975 * overlap of two or more drag and drop objects.
976 * @type int
977 */
978 this.INTERSECT = 1;
979
980 /**
981 * The current drag and drop mode. Default it point mode
982 * @type int
983 */
984 this.mode = this.POINT;
985
986 /**
987 * Runs method on all drag and drop objects
988 * @private
989 */
990 this._execOnAll = function(sMethod, args) {
991 for (var i in this.ids) {
992 for (var j in this.ids[i]) {
993 var oDD = this.ids[i][j];
994 if (! this.isTypeOfDD(oDD)) {
995 continue;
996 }
997 oDD[sMethod].apply(oDD, args);
998 }
999 }
1000 };
1001
1002 /**
1003 * Drag and drop initialization. Sets up the global event handlers
1004 * @private
1005 */
1006 this._onLoad = function() {
1007
1008 this._execOnAll("setInitPosition", []);
1009
1010
1011 var EU = UTIL.Event;
1012
1013 EU.addListener(document, "mouseup", this.handleMouseUp, this, true);
1014 EU.addListener(document, "mousemove", this.handleMouseMove, this, true);
1015 EU.addListener(window, "unload", this._onUnload, this, true);
1016 EU.addListener(window, "resize", this._onResize, this, true);
1017 // EU.addListener(window, "mouseout", this._test);
1018
1019 this.initalized = true;
1020
1021 };
1022
1023 /**
1024 * Reset constraints on all drag and drop objs
1025 * @private
1026 */
1027 this._onResize = function(e) {
1028 this._execOnAll("resetConstraints", []);
1029 };
1030
1031 /**
1032 * Lock all drag and drop functionality
1033 */
1034 this.lock = function() { this.locked = true; };
1035
1036 /**
1037 * Unlock all drag and drop functionality
1038 */
1039 this.unlock = function() { this.locked = false; };
1040
1041 /**
1042 * Is drag and drop locked?
1043 *
1044 * @return {boolean} True if drag and drop is locked, false otherwise.
1045 */
1046 this.isLocked = function() { return this.locked; };
1047
1048 /**
1049 * Location cache that is set for all drag drop objects when a drag is
1050 * initiated, cleared when the drag is finished.
1051 *
1052 * @private
1053 */
1054 this.locationCache = {};
1055
1056 /**
1057 * Set useCache to false if you want to force object the lookup of each
1058 * drag and drop linked element constantly during a drag.
1059 * @type boolean
1060 */
1061 this.useCache = true;
1062
1063 /**
1064 * The number of pixels that the mouse needs to move after the
1065 * mousedown before the drag is initiated. Default=3;
1066 * @type int
1067 */
1068 this.clickPixelThresh = 3;
1069
1070 /**
1071 * The number of milliseconds after the mousedown event to initiate the
1072 * drag if we don't get a mouseup event. Default=1000
1073 * @type int
1074 */
1075 this.clickTimeThresh = 1000;
1076
1077 /**
1078 * Flag that indicates that either the drag pixel threshold or the
1079 * mousdown time threshold has been met
1080 * @type boolean
1081 * @private
1082 */
1083 this.dragThreshMet = false;
1084
1085 /**
1086 * Timeout used for the click time threshold
1087 * @type Object
1088 * @private
1089 */
1090 this.clickTimeout = null;
1091
1092 /**
1093 * The X position of the mousedown event stored for later use when a
1094 * drag threshold is met.
1095 * @type int
1096 * @private
1097 */
1098 this.startX = 0;
1099
1100 /**
1101 * The Y position of the mousedown event stored for later use when a
1102 * drag threshold is met.
1103 * @type int
1104 * @private
1105 */
1106 this.startY = 0;
1107
1108 /**
1109 * Each DragDrop instance must be registered with the DragDropMgr.
1110 * This is executed in ygDragDrop.init()
1111 *
1112 * @param {DragDrop} oDD the DragDrop object to register
1113 * @param {String} sGroup the name of the group this element belongs to
1114 */
1115 this.regDragDrop = function(oDD, sGroup) {
1116 if (!this.initialized) { this.init(); }
1117
1118 if (!this.ids[sGroup]) {
1119 this.ids[sGroup] = {};
1120 }
1121 this.ids[sGroup][oDD.id] = oDD;
1122 };
1123
1124 /**
1125 * Unregisters a drag and drop item. This is executed in
1126 * ygDragDrop.unreg, use that method instead of calling this directly.
1127 * @private
1128 */
1129 this._remove = function(oDD) {
1130 for (var g in oDD.groups) {
1131 if (g && this.ids[g][oDD.id]) {
1132 delete this.ids[g][oDD.id];
1133 }
1134 }
1135 delete this.handleIds[oDD.id];
1136 };
1137
1138 /**
1139 * Each DragDrop handle element must be registered. This is done
1140 * automatically when executing ygDragDrop.setHandleElId()
1141 *
1142 * @param {String} sDDId the DragDrop id this element is a handle for
1143 * @param {String} sHandleId the id of the element that is the drag
1144 * handle
1145 */
1146 this.regHandle = function(sDDId, sHandleId) {
1147 if (!this.handleIds[sDDId]) {
1148 this.handleIds[sDDId] = {};
1149 }
1150 this.handleIds[sDDId][sHandleId] = sHandleId;
1151 };
1152
1153 /**
1154 * Utility function to determine if a given element has been
1155 * registered as a drag drop item.
1156 *
1157 * @param {String} id the element id to check
1158 * @return {boolean} true if this element is a DragDrop item,
1159 * false otherwise
1160 */
1161 this.isDragDrop = function(id) {
1162 return ( this.getDDById(id) ) ? true : false;
1163 };
1164
1165 /**
1166 * Returns the drag and drop instances that are in all groups the
1167 * passed in instance belongs to.
1168 *
1169 * @param {ygDragDrop} p_oDD the obj to get related data for
1170 * @param {boolean} bTargetsOnly if true, only return targetable objs
1171 * @return {ygDragDrop[]} the related instances
1172 */
1173 this.getRelated = function(p_oDD, bTargetsOnly) {
1174 var oDDs = [];
1175 for (var i in p_oDD.groups) {
1176 for (j in this.ids[i]) {
1177 var dd = this.ids[i][j];
1178 if (! this.isTypeOfDD(dd)) {
1179 continue;
1180 }
1181 if (!bTargetsOnly || dd.isTarget) {
1182 oDDs[oDDs.length] = dd;
1183 }
1184 }
1185 }
1186
1187 return oDDs;
1188 };
1189
1190 /**
1191 * Returns true if the specified dd target is a legal target for
1192 * the specifice drag obj
1193 *
1194 * @param {ygDragDrop} the drag obj
1195 * @param {ygDragDrop) the target
1196 * @return {boolean} true if the target is a legal target for the
1197 * dd obj
1198 */
1199 this.isLegalTarget = function (oDD, oTargetDD) {
1200 var targets = this.getRelated(oDD);
1201 for (var i =0;i<targets.length;++i) {
1202 if (targets[i].id == oTargetDD.id) {
1203 return true;
1204 }
1205 }
1206
1207 return false;
1208 };
1209
1210 /**
1211 * My goal is to be able to transparently determine if an object is
1212 * typeof ygDragDrop, and the exact subclass of ygDragDrop. typeof
1213 * returns "object", oDD.constructor.toString() always returns
1214 * "ygDragDrop" and not the name of the subclass. So for now it just
1215 * evaluates a well-known variable in ygDragDrop.
1216 *
1217 * @param {Object} the object to evaluate
1218 * @return {boolean} true if typeof oDD = ygDragDrop
1219 */
1220 this.isTypeOfDD = function (oDD) {
1221 return (oDD && oDD.__ygDragDrop);
1222 };
1223
1224 /**
1225 * Utility function to determine if a given element has been
1226 * registered as a drag drop handle for the given Drag Drop object.
1227 *
1228 * @param {String} id the element id to check
1229 * @return {boolean} true if this element is a DragDrop handle, false
1230 * otherwise
1231 */
1232 this.isHandle = function(sDDId, sHandleId) {
1233 return ( this.handleIds[sDDId] &&
1234 this.handleIds[sDDId][sHandleId] );
1235 };
1236
1237 /**
1238 * Returns the DragDrop instance for a given id
1239 *
1240 * @param {String} id the id of the DragDrop object
1241 * @return {DragDrop} the drag drop object, null if it is not found
1242 */
1243 this.getDDById = function(id) {
1244 for (var i in this.ids) {
1245 if (this.ids[i][id]) {
1246 return this.ids[i][id];
1247 }
1248 }
1249 return null;
1250 };
1251
1252 /**
1253 * Fired after a registered DragDrop object gets the mousedown event.
1254 * Sets up the events required to track the object being dragged
1255 *
1256 * @param {Event} e the event
1257 * @param oDD the DragDrop object being dragged
1258 * @private
1259 */
1260 this.handleMouseDown = function(e, oDD) {
1261 this.dragCurrent = oDD;
1262
1263 var el = oDD.getEl();
1264
1265 // track start position
1266 this.startX = UTIL.Event.getPageX(e);
1267 this.startY = UTIL.Event.getPageY(e);
1268
1269 this.deltaX = this.startX - el.offsetLeft;
1270 this.deltaY = this.startY - el.offsetTop;
1271
1272 this.dragThreshMet = false;
1273
1274 this.clickTimeout = setTimeout(
1275 "var DDM=YAHOO.util.DDM;DDM.startDrag(DDM.startX, DDM.startY)",
1276 this.clickTimeThresh );
1277 };
1278
1279 /**
1280 * Fired when either the drag pixel threshol or the mousedown hold
1281 * time threshold has been met.
1282 *
1283 * @param x {int} the X position of the original mousedown
1284 * @param y {int} the Y position of the original mousedown
1285 */
1286 this.startDrag = function(x, y) {
1287 clearTimeout(this.clickTimeout);
1288 if (this.dragCurrent) {
1289 this.dragCurrent.b4StartDrag(x, y);
1290 this.dragCurrent.startDrag(x, y);
1291 }
1292 this.dragThreshMet = true;
1293 };
1294
1295 /**
1296 * Internal function to handle the mouseup event. Will be invoked
1297 * from the context of the document.
1298 *
1299 * @param {Event} e the event
1300 * @private
1301 */
1302 this.handleMouseUp = function(e) {
1303
1304 if (! this.dragCurrent) {
1305 return;
1306 }
1307
1308 clearTimeout(this.clickTimeout);
1309
1310 if (this.dragThreshMet) {
1311 this.fireEvents(e, true);
1312 } else {
1313 }
1314
1315 this.stopDrag(e);
1316
1317 this.stopEvent(e);
1318 };
1319
1320 /**
1321 * Utility to stop event propagation and event default, if these
1322 * features are turned on.
1323 *
1324 * @param {Event} e the event as returned by this.getEvent()
1325 */
1326 this.stopEvent = function(e) {
1327 if (this.stopPropagation) {
1328 UTIL.Event.stopPropagation(e);
1329 }
1330
1331 if (this.preventDefault) {
1332 UTIL.Event.preventDefault(e);
1333 }
1334 };
1335
1336 /**
1337 * Internal function to clean up event handlers after the drag
1338 * operation is complete
1339 *
1340 * @param {Event} e the event
1341 * @private
1342 */
1343 this.stopDrag = function(e) {
1344
1345 // Fire the drag end event for the item that was dragged
1346 if (this.dragCurrent) {
1347 if (this.dragThreshMet) {
1348 this.dragCurrent.b4EndDrag(e);
1349 this.dragCurrent.endDrag(e);
1350 }
1351
1352 this.dragCurrent.onMouseUp(e);
1353 }
1354
1355 this.dragCurrent = null;
1356 this.dragOvers = {};
1357 };
1358
1359 /**
1360 * Internal function to handle the mousemove event. Will be invoked
1361 * from the context of the html element.
1362 *
1363 * @TODO figure out what we can do about mouse events lost when the
1364 * user drags objects beyond the window boundary. Currently we can
1365 * detect this in internet explorer by verifying that the mouse is
1366 * down during the mousemove event. Firefox doesn't give us the
1367 * button state on the mousemove event.
1368 *
1369 * @param {Event} e the event
1370 * @private
1371 */
1372 this.handleMouseMove = function(e) {
1373 if (! this.dragCurrent) {
1374 return;
1375 }
1376
1377 // var button = e.which || e.button;
1378
1379
1380 // check for IE mouseup outside of page boundary
1381 if (UTIL.Event.isIE && !e.button) {
1382 this.stopEvent(e);
1383 return this.handleMouseUp(e);
1384 }
1385
1386 if (!this.dragThreshMet) {
1387 var diffX = Math.abs(this.startX - UTIL.Event.getPageX(e));
1388 var diffY = Math.abs(this.startY - UTIL.Event.getPageY(e));
1389 if (diffX > this.clickPixelThresh ||
1390 diffY > this.clickPixelThresh) {
1391 this.startDrag(this.startX, this.startY);
1392 }
1393 }
1394
1395 if (this.dragThreshMet) {
1396 this.dragCurrent.b4Drag(e);
1397 this.dragCurrent.onDrag(e);
1398 this.fireEvents(e, false);
1399 }
1400
1401 this.stopEvent(e);
1402 };
1403
1404 /**
1405 * Iterates over all of the DragDrop elements to find ones we are
1406 * hovering over or dropping on
1407 *
1408 * @param {Event} e the event
1409 * @param {boolean} isDrop is this a drop op or a mouseover op?
1410 * @private
1411 */
1412 this.fireEvents = function(e, isDrop) {
1413 var dc = this.dragCurrent;
1414
1415 // If the user did the mouse up outside of the window, we could
1416 // get here even though we have ended the drag.
1417 if (!dc || dc.isLocked()) {
1418 return;
1419 }
1420
1421 var x = UTIL.Event.getPageX(e);
1422 var y = UTIL.Event.getPageY(e);
1423 var pt = new YAHOO.util.Point(x,y);
1424
1425 // cache the previous dragOver array
1426 var oldOvers = [];
1427
1428 var outEvts = [];
1429 var overEvts = [];
1430 var dropEvts = [];
1431 var enterEvts = [];
1432
1433 // Check to see if the object we were hovering over is no longer
1434 // being hovered over so we can fire the onDragOut event
1435 for (var i in this.dragOvers) {
1436
1437 var ddo = this.dragOvers[i];
1438
1439 if (! this.isTypeOfDD(ddo)) {
1440 continue;
1441 }
1442
1443 if (! this.isOverTarget(pt, ddo, this.mode)) {
1444 outEvts.push( ddo );
1445 }
1446
1447 oldOvers[i] = true;
1448 delete this.dragOvers[i];
1449 }
1450
1451 for (var sGroup in dc.groups) {
1452
1453 if ("string" != typeof sGroup) {
1454 continue;
1455 }
1456
1457 for (i in this.ids[sGroup]) {
1458 var oDD = this.ids[sGroup][i];
1459 if (! this.isTypeOfDD(oDD)) {
1460 continue;
1461 }
1462
1463 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1464 if (this.isOverTarget(pt, oDD, this.mode)) {
1465 // look for drop interactions
1466 if (isDrop) {
1467 dropEvts.push( oDD );
1468 // look for drag enter and drag over interactions
1469 } else {
1470
1471 // initial drag over: dragEnter fires
1472 if (!oldOvers[oDD.id]) {
1473 enterEvts.push( oDD );
1474 // subsequent drag overs: dragOver fires
1475 } else {
1476 overEvts.push( oDD );
1477 }
1478
1479 this.dragOvers[oDD.id] = oDD;
1480 }
1481 }
1482 }
1483 }
1484 }
1485
1486 if (this.mode) {
1487 if (outEvts.length > 0) {
1488 dc.b4DragOut(e, outEvts);
1489 dc.onDragOut(e, outEvts);
1490 }
1491
1492 if (enterEvts.length > 0) {
1493 dc.onDragEnter(e, enterEvts);
1494 }
1495
1496 if (overEvts.length > 0) {
1497 dc.b4DragOver(e, overEvts);
1498 dc.onDragOver(e, overEvts);
1499 }
1500
1501 if (dropEvts.length > 0) {
1502 dc.b4DragDrop(e, dropEvts);
1503 dc.onDragDrop(e, dropEvts);
1504 }
1505
1506 } else {
1507 // fire dragout events
1508 for (i=0; i < outEvts.length; ++i) {
1509 dc.b4DragOut(e, outEvts[i].id);
1510 dc.onDragOut(e, outEvts[i].id);
1511 }
1512
1513 // fire enter events
1514 for (i=0; i < enterEvts.length; ++i) {
1515 // dc.b4DragEnter(e, oDD.id);
1516 dc.onDragEnter(e, enterEvts[i].id);
1517 }
1518
1519 // fire over events
1520 for (i=0; i < overEvts.length; ++i) {
1521 dc.b4DragOver(e, overEvts[i].id);
1522 dc.onDragOver(e, overEvts[i].id);
1523 }
1524
1525 // fire drop events
1526 for (i=0; i < dropEvts.length; ++i) {
1527 dc.b4DragDrop(e, dropEvts[i].id);
1528 dc.onDragDrop(e, dropEvts[i].id);
1529 }
1530
1531 }
1532
1533 };
1534
1535 /**
1536 * Helper function for getting the best match from the list of drag
1537 * and drop objects returned by the drag and drop events when we are
1538 * in INTERSECT mode. It returns either the first object that the
1539 * cursor is over, or the object that has the greatest overlap with
1540 * the dragged element.
1541 *
1542 * @param {ygDragDrop[]} dds The array of drag and drop objects
1543 * targeted
1544 * @return {ygDragDrop} The best single match
1545 */
1546 this.getBestMatch = function(dds) {
1547 var winner = null;
1548 // Return null if the input is not what we expect
1549 //if (!dds || !dds.length || dds.length == 0) {
1550 // winner = null;
1551 // If there is only one item, it wins
1552 //} else if (dds.length == 1) {
1553
1554 if (dds.length == 1) {
1555 winner = dds[0];
1556 } else {
1557 // Loop through the targeted items
1558 for (var i=0; i<dds.length; ++i) {
1559 var dd = dds[i];
1560 // If the cursor is over the object, it wins. If the
1561 // cursor is over multiple matches, the first one we come
1562 // to wins.
1563 if (dd.cursorIsOver) {
1564 winner = dd;
1565 break;
1566 // Otherwise the object with the most overlap wins
1567 } else {
1568 if (!winner ||
1569 winner.overlap.getArea() < dd.overlap.getArea()) {
1570 winner = dd;
1571 }
1572 }
1573 }
1574 }
1575
1576 return winner;
1577 };
1578
1579 /**
1580 * Refreshes the cache of the top-left and bottom-right points of the
1581 * drag and drop objects in the specified groups
1582 *
1583 * @param {Array} aGroups an associative array of groups to refresh
1584 */
1585 this.refreshCache = function(aGroups) {
1586 for (sGroup in aGroups) {
1587 if ("string" != typeof sGroup) {
1588 continue;
1589 }
1590 for (i in this.ids[sGroup]) {
1591 var oDD = this.ids[sGroup][i];
1592
1593 if (this.isTypeOfDD(oDD)) {
1594 var loc = this.getLocation(oDD);
1595 if (loc) {
1596 this.locationCache[oDD.id] = loc;
1597 } else {
1598 delete this.locationCache[oDD.id];
1599 // this will unregister the drag and drop object if
1600 // the element is not in a usable state
1601 oDD.unreg();
1602 }
1603 }
1604 }
1605 }
1606 };
1607
1608 /**
1609 * This checks to make sure an element exists and is in the DOM. The
1610 * main purpose is to handle cases where innerHTML is used to remove
1611 * drag and drop objects from the DOM. IE provides an 'unspecified
1612 * error' when trying to access the offsetParent of such an element
1613 * @param {HTMLElement} el the element to check
1614 * @return {boolean} true if the element looks usable
1615 */
1616 this.verifyEl = function(el) {
1617 try {
1618 if (el) {
1619 var parent = el.offsetParent;
1620 if (parent) {
1621 return true;
1622 }
1623 }
1624 } catch(e) {
1625 }
1626
1627 return false;
1628 };
1629
1630 /**
1631 * Returns the an array containing the drag and drop element's position
1632 * and size, including the ygDragDrop.padding configured for it
1633 *
1634 * @param {ygDragDrop} oDD the drag and drop object to get the
1635 * location for
1636 * @return array containing the top left and bottom right points of the
1637 * element
1638 */
1639 this.getLocation = function(oDD) {
1640 if (! this.isTypeOfDD(oDD)) {
1641 return null;
1642 }
1643
1644 var el = oDD.getEl();
1645
1646 if (!this.verifyEl(el)) {
1647 return null;
1648 }
1649
1650
1651 // var aPos = ygPos.getPos(el);
1652 var aPos = YAHOO.util.Dom.getXY(el);
1653
1654 x1 = aPos[0];
1655 x2 = x1 + el.offsetWidth;
1656
1657 y1 = aPos[1];
1658 y2 = y1 + el.offsetHeight;
1659
1660 var t = y1 - oDD.padding[0];
1661 var r = x2 + oDD.padding[1];
1662 var b = y2 + oDD.padding[2];
1663 var l = x1 - oDD.padding[3];
1664
1665 return new YAHOO.util.Region( t, r, b, l );
1666
1667 };
1668
1669 /**
1670 * Checks the cursor location to see if it over the target
1671 *
1672 * @param {YAHOO.util.Point} pt The point to evaluate
1673 * @param {ygDragDrop} oDDTarget the DragDrop object we are inspecting
1674 * @return {boolean} true if the mouse is over the target
1675 * @private
1676 */
1677 this.isOverTarget = function(pt, oDDTarget, intersect) {
1678 // use cache if available
1679 var loc = this.locationCache[oDDTarget.id];
1680 if (!loc || !this.useCache) {
1681 loc = this.getLocation(oDDTarget);
1682 this.locationCache[oDDTarget.id] = loc;
1683
1684 }
1685
1686
1687 // var cursorIsOver = (x >= loc[3] && x <= loc[1] && y >= loc[0] && y <= loc[2]);
1688 //oDDTarget.cursorIsOver = loc.contains( new YAHOO.util.Point(x, y) );
1689 oDDTarget.cursorIsOver = loc.contains( pt );
1690 oDDTarget.overlap = null;
1691
1692 // if (this.INTERSECT == this.mode) {
1693 if (intersect) {
1694
1695 var curRegion =
1696 YAHOO.util.Region.getRegion(this.dragCurrent.getDragEl());
1697 var overlap = curRegion.intersect(loc);
1698
1699 if (overlap) {
1700 oDDTarget.overlap = overlap;
1701 return true;
1702 } else {
1703 return false;
1704 }
1705
1706 } else {
1707 return oDDTarget.cursorIsOver;
1708 }
1709 };
1710
1711 /**
1712 * @private
1713 */
1714 this._onUnload = function(e, me) {
1715 this.unregAll();
1716 };
1717
1718 /**
1719 * Cleans up the drag and drop events and objects.
1720 *
1721 * @private
1722 */
1723 this.unregAll = function() {
1724
1725 if (this.dragCurrent) {
1726 this.stopDrag();
1727 this.dragCurrent = null;
1728 }
1729
1730 this._execOnAll("unreg", []);
1731
1732 for (i in this.elementCache) {
1733 delete this.elementCache[i];
1734 }
1735
1736 this.elementCache = {};
1737 this.ids = {};
1738 };
1739
1740 /**
1741 * A cache of DOM elements
1742 *
1743 * @private
1744 */
1745 this.elementCache = {};
1746
1747 /**
1748 * Get the wrapper for the DOM element specified
1749 *
1750 * @param {String} id the id of the elment to get
1751 * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
1752 * @private
1753 */
1754 this.getElWrapper = function(id) {
1755 var oWrapper = this.elementCache[id];
1756 if (!oWrapper || !oWrapper.el) {
1757 oWrapper = this.elementCache[id] =
1758 new this.ElementWrapper(document.getElementById(id));
1759 }
1760 return oWrapper;
1761 };
1762
1763 /**
1764 * Returns the actual DOM element
1765 *
1766 * @param {String} id the id of the elment to get
1767 * @return {Object} The element
1768 */
1769 this.getElement = function(id) {
1770 // return this.getElWrapper(id).el;
1771 return document.getElementById(id);
1772 };
1773
1774 /**
1775 * Returns the style property for the DOM element (i.e.,
1776 * document.getElById(id).style)
1777 *
1778 * @param {String} id the id of the elment to get
1779 * @return {Object} The style property of the element
1780 */
1781 this.getCss = function(id) {
1782 // return this.getElWrapper(id).css;
1783 var css = null;
1784 var el = document.getElementById(id);
1785 if (el) {
1786 css = el.style;
1787 }
1788
1789 return css;
1790 };
1791
1792 /**
1793 * Inner class for cached elements
1794 */
1795 this.ElementWrapper = function(el) {
1796 /**
1797 * @private
1798 */
1799 this.el = el || null;
1800 /**
1801 * @private
1802 */
1803 this.id = this.el && el.id;
1804 /**
1805 * @private
1806 */
1807 this.css = this.el && el.style;
1808 };
1809
1810 /**
1811 * Returns the X position of an html element
1812 * @param el the element for which to get the position
1813 * @return {int} the X coordinate
1814 */
1815 this.getPosX = function(el) {
1816 return YAHOO.util.Dom.getX(el);
1817 };
1818
1819 /**
1820 * Returns the Y position of an html element
1821 * @param el the element for which to get the position
1822 * @return {int} the Y coordinate
1823 */
1824 this.getPosY = function(el) {
1825 return YAHOO.util.Dom.getY(el);
1826 };
1827
1828 /**
1829 * Swap two nodes. In IE, we use the native method, for others we
1830 * emulate the IE behavior
1831 *
1832 * @param n1 the first node to swap
1833 * @param n2 the other node to swap
1834 */
1835 this.swapNode = function(n1, n2) {
1836 if (n1.swapNode) {
1837 n1.swapNode(n2);
1838 } else {
1839 // the node reference order for the swap is a little tricky.
1840 var p = n2.parentNode;
1841 var s = n2.nextSibling;
1842 n1.parentNode.replaceChild(n2,n1);
1843 p.insertBefore(n1,s);
1844 }
1845 };
1846
1847 /**
1848 * @private
1849 */
1850 this.getScroll = function () {
1851 var t, l;
1852 if (document.documentElement && document.documentElement.scrollTop) {
1853 t = document.documentElement.scrollTop;
1854 l = document.documentElement.scrollLeft;
1855 } else if (document.body) {
1856 t = document.body.scrollTop;
1857 l = document.body.scrollLeft;
1858 }
1859 return { top: t, left: l };
1860 };
1861
1862 /**
1863 * Returns the specified element style property
1864 * @param {HTMLElement} el the element
1865 * @param {string} styleProp the style property
1866 * @return {string} The value of the style property
1867 */
1868 this.getStyle = function(el, styleProp) {
1869 if (el.style.styleProp) {
1870 return el.style.styleProp;
1871 } else if (el.currentStyle) {
1872 return el.currentStyle[styleProp];
1873 } else if (document.defaultView) {
1874 return document.defaultView.getComputedStyle(el, null).
1875 getPropertyValue(styleProp);
1876 }
1877 };
1878
1879 /**
1880 * Gets the scrollTop
1881 *
1882 * @return {int} the document's scrollTop
1883 */
1884 this.getScrollTop = function () { return this.getScroll().top; };
1885
1886 /**
1887 * Gets the scrollLeft
1888 *
1889 * @return {int} the document's scrollTop
1890 */
1891 this.getScrollLeft = function () { return this.getScroll().left; };
1892
1893 this.moveToEl = function (moveEl, targetEl) {
1894 var aCoord = YAHOO.util.Dom.getXY(targetEl);
1895 YAHOO.util.Dom.setXY(moveEl, aCoord);
1896 };
1897
1898 /**
1899 * Gets the client height
1900 *
1901 * @return {int} client height in px
1902 */
1903 this.getClientHeight = function() {
1904 return (window.innerHeight) ? window.innerHeight :
1905 (document.documentElement && document.documentElement.clientHeight) ?
1906 document.documentElement.clientHeight : document.body.offsetHeight;
1907 };
1908
1909 /**
1910 * Gets the client width
1911 *
1912 * @return {int} client width in px
1913 */
1914 this.getClientWidth = function() {
1915 return (window.innerWidth) ? window.innerWidth :
1916 (document.documentElement && document.documentElement.clientWidth) ?
1917 document.documentElement.clientWidth : document.body.offsetWidth;
1918 };
1919
1920 /**
1921 * numeric array sort function
1922 */
1923 this.numericSort = function(a, b) { return (a - b); };
1924
1925 /**
1926 * @private
1927 */
1928 this._timeoutCount = 0;
1929
1930 /**
1931 * @private
1932 * Trying to make the load order less important. Without this we get
1933 * an error if this file is loaded before the Event Utility.
1934 */
1935 this._addListeners = function() {
1936 if ( UTIL.Event &&
1937 document &&
1938 document.body ) {
1939
1940 this._onLoad();
1941 } else {
1942 if (this._timeoutCount > 500) {
1943 } else {
1944 setTimeout("YAHOO.util.DDM._addListeners()", 10);
1945 this._timeoutCount += 1;
1946 }
1947 }
1948
1949 };
1950
1951 /**
1952 * Recursively searches the immediate parent and all child nodes for
1953 * the handle element in order to determine wheter or not it was
1954 * clicked.
1955 *
1956 * @param node the html element to inspect
1957 */
1958 this.handleWasClicked = function(node, id) {
1959 if (this.isHandle(id, node.id)) {
1960 return true;
1961 } else {
1962 // check to see if this is a text node child of the one we want
1963 var p = node.parentNode;
1964
1965 while (p) {
1966 if (this.isHandle(id, p.id)) {
1967 return true;
1968 } else {
1969 p = p.parentNode;
1970 }
1971 }
1972 }
1973
1974 return false;
1975 };
1976
1977 };
1978
1979 // shorter alias, save a few bytes
1980 YAHOO.util.DDM = YAHOO.util.DragDropMgr;
1981 YAHOO.util.DDM._addListeners();
1982
1983}
1984
1985/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
1986
1987/**
1988 * @class a DragDrop implementation where the linked element follows the
1989 * mouse cursor.
1990 *
1991 * @extends DragDrop
1992 * @constructor
1993 * @param {String} id the id of the linked element
1994 * @param {String} sGroup the group of related DragDrop items
1995 */
1996YAHOO.util.DD = function(id, sGroup) {
1997 if (id) {
1998 this.init(id, sGroup);
1999 }
2000};
2001
2002YAHOO.util.DD.prototype = new YAHOO.util.DragDrop();
2003
2004/**
2005 * Should we auto-scroll? Defaults to true
2006 *
2007 * @type boolean
2008 */
2009YAHOO.util.DD.prototype.scroll = true;
2010
2011/**
2012 * Sets the pointer offset to the distance between the linked element's top
2013 * left corner and the location the element was clicked
2014 *
2015 * @param {int} iPageX the X coordinate of the click
2016 * @param {int} iPageY the Y coordinate of the click
2017 */
2018YAHOO.util.DD.prototype.autoOffset = function(iPageX, iPageY) {
2019 var el = this.getEl();
2020 var aCoord = YAHOO.util.Dom.getXY(el);
2021 var x = iPageX - aCoord[0];
2022 var y = iPageY - aCoord[1];
2023 this.setDelta(x, y);
2024};
2025
2026/**
2027 * Sets the pointer offset. You can call this directly to force the offset to
2028 * be in a particular location (e.g., pass in 0,0 to set it to the center of the
2029 * object, as done in ygDDSliderBG)
2030 *
2031 * @param {int} iDeltaX the distance from the left
2032 * @param {int} iDeltaY the distance from the top
2033 */
2034YAHOO.util.DD.prototype.setDelta = function(iDeltaX, iDeltaY) {
2035 this.deltaX = iDeltaX;
2036 this.deltaY = iDeltaY;
2037};
2038
2039/**
2040 * Sets the drag element to the location of the mousedown or click event,
2041 * maintaining the cursor location relative to the location on the element
2042 * that was clicked. Override this if you want to place the element in a
2043 * location other than where the cursor is.
2044 *
2045 * @param {int} iPageX the X coordinate of the mousedown or drag event
2046 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2047 */
2048
2049YAHOO.util.DD.prototype.setDragElPos = function(iPageX, iPageY) {
2050 this.alignElWithMouse(this.getDragEl(), iPageX, iPageY);
2051};
2052
2053/**
2054 * Sets the element to the location of the mousedown or click event,
2055 * maintaining the cursor location relative to the location on the element
2056 * that was clicked. Override this if you want to place the element in a
2057 * location other than where the cursor is.
2058 *
2059 * @param {HTMLElement} el the element to move
2060 * @param {int} iPageX the X coordinate of the mousedown or drag event
2061 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2062 */
2063YAHOO.util.DD.prototype.alignElWithMouse = function(el, iPageX, iPageY) {
2064 var oCoord = this.getTargetCoord(iPageX, iPageY);
2065 var aCoord = [oCoord.x, oCoord.y];
2066 YAHOO.util.Dom.setXY(el, aCoord);
2067
2068 this.cachePosition(oCoord.x, oCoord.y);
2069
2070 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2071};
2072
2073/**
2074 * Saves the most recent position so that we can reset the constraints and
2075 * tick marks on-demand. We need to know this so that we can calculate the
2076 * number of pixels the element is offset from its original position.
2077 */
2078YAHOO.util.DD.prototype.cachePosition = function(iPageX, iPageY) {
2079 if (iPageX) {
2080 this.lastPageX = iPageX;
2081 this.lastPageY = iPageY;
2082 } else {
2083 var aCoord = YAHOO.util.Dom.getXY(this.getEl());
2084 this.lastPageX = aCoord[0];
2085 this.lastPageY = aCoord[1];
2086 }
2087};
2088
2089/**
2090 * Auto-scroll the window if the dragged object has been moved beyond the
2091 * visible window boundary.
2092 *
2093 * @param {int} x the drag element's x position
2094 * @param {int} y the drag element's y position
2095 * @param {int} h the height of the drag element
2096 * @param {int} w the width of the drag element
2097 * @private
2098 */
2099YAHOO.util.DD.prototype.autoScroll = function(x, y, h, w) {
2100
2101 if (this.scroll) {
2102 // The client height
2103 var clientH = this.DDM.getClientHeight();
2104
2105 // The client width
2106 var clientW = this.DDM.getClientWidth();
2107
2108 // The amt scrolled down
2109 var st = this.DDM.getScrollTop();
2110
2111 // The amt scrolled right
2112 var sl = this.DDM.getScrollLeft();
2113
2114 // Location of the bottom of the element
2115 var bot = h + y;
2116
2117 // Location of the right of the element
2118 var right = w + x;
2119
2120 // The distance from the cursor to the bottom of the visible area,
2121 // adjusted so that we don't scroll if the cursor is beyond the
2122 // element drag constraints
2123 var toBot = (clientH + st - y - this.deltaY);
2124
2125 // The distance from the cursor to the right of the visible area
2126 var toRight = (clientW + sl - x - this.deltaX);
2127
2128
2129 // How close to the edge the cursor must be before we scroll
2130 // var thresh = (document.all) ? 100 : 40;
2131 var thresh = 40;
2132
2133 // How many pixels to scroll per autoscroll op. This helps to reduce
2134 // clunky scrolling. IE is more sensitive about this ... it needs this
2135 // value to be higher.
2136 var scrAmt = (document.all) ? 80 : 30;
2137
2138 // Scroll down if we are near the bottom of the visible page and the
2139 // obj extends below the crease
2140 if ( bot > clientH && toBot < thresh ) {
2141 window.scrollTo(sl, st + scrAmt);
2142 }
2143
2144 // Scroll up if the window is scrolled down and the top of the object
2145 // goes above the top border
2146 if ( y < st && st > 0 && y - st < thresh ) {
2147 window.scrollTo(sl, st - scrAmt);
2148 }
2149
2150 // Scroll right if the obj is beyond the right border and the cursor is
2151 // near the border.
2152 if ( right > clientW && toRight < thresh ) {
2153 window.scrollTo(sl + scrAmt, st);
2154 }
2155
2156 // Scroll left if the window has been scrolled to the right and the obj
2157 // extends past the left border
2158 if ( x < sl && sl > 0 && x - sl < thresh ) {
2159 window.scrollTo(sl - scrAmt, st);
2160 }
2161 }
2162};
2163
2164/**
2165 * Finds the location the element should be placed if we want to move
2166 * it to where the mouse location less the click offset would place us.
2167 *
2168 * @param {int} iPageX the X coordinate of the click
2169 * @param {int} iPageY the Y coordinate of the click
2170 * @return an object that contains the coordinates (Object.x and Object.y)
2171 * @private
2172 */
2173YAHOO.util.DD.prototype.getTargetCoord = function(iPageX, iPageY) {
2174
2175
2176 var x = iPageX - this.deltaX;
2177 var y = iPageY - this.deltaY;
2178
2179 if (this.constrainX) {
2180 if (x < this.minX) { x = this.minX; }
2181 if (x > this.maxX) { x = this.maxX; }
2182 }
2183
2184 if (this.constrainY) {
2185 if (y < this.minY) { y = this.minY; }
2186 if (y > this.maxY) { y = this.maxY; }
2187 }
2188
2189 x = this.getTick(x, this.xTicks);
2190 y = this.getTick(y, this.yTicks);
2191
2192
2193 return {x:x, y:y};
2194};
2195
2196// overrides YAHOO.util.DragDrop
2197YAHOO.util.DD.prototype.b4MouseDown = function(e) {
2198 // this.resetConstraints();
2199 this.autoOffset(YAHOO.util.Event.getPageX(e),
2200 YAHOO.util.Event.getPageY(e));
2201};
2202
2203// overrides YAHOO.util.DragDrop
2204YAHOO.util.DD.prototype.b4Drag = function(e) {
2205 this.setDragElPos(YAHOO.util.Event.getPageX(e),
2206 YAHOO.util.Event.getPageY(e));
2207};
2208
2209///////////////////////////////////////////////////////////////////////////////
2210// Debugging ygDragDrop events that can be overridden
2211///////////////////////////////////////////////////////////////////////////////
2212/*
2213YAHOO.util.DD.prototype.startDrag = function(x, y) {
2214};
2215
2216YAHOO.util.DD.prototype.onDrag = function(e) {
2217};
2218
2219YAHOO.util.DD.prototype.onDragEnter = function(e, id) {
2220};
2221
2222YAHOO.util.DD.prototype.onDragOver = function(e, id) {
2223};
2224
2225YAHOO.util.DD.prototype.onDragOut = function(e, id) {
2226};
2227
2228YAHOO.util.DD.prototype.onDragDrop = function(e, id) {
2229};
2230
2231YAHOO.util.DD.prototype.endDrag = function(e) {
2232};
2233*/
2234
2235/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
2236
2237/**
2238 * @class a DragDrop implementation that inserts an empty, bordered div into
2239 * the document that follows the cursor during drag operations. At the time of
2240 * the click, the frame div is resized to the dimensions of the linked html
2241 * element, and moved to the exact location of the linked element.
2242 *
2243 * @extends YAHOO.util.DD
2244 * @constructor
2245 * @param {String} id the id of the linked html element
2246 * @param {String} sGroup the group of related DragDrop objects
2247 */
2248YAHOO.util.DDProxy = function(id, sGroup) {
2249 if (id) {
2250 this.init(id, sGroup);
2251 this.initFrame();
2252 }
2253};
2254
2255YAHOO.util.DDProxy.prototype = new YAHOO.util.DD();
2256
2257/**
2258 * A reference to the one div element we create for all instances of this class
2259 *
2260 * @type Object
2261 */
2262YAHOO.util.DDProxy.frameDiv = null;
2263
2264/**
2265 * the drag frame div id
2266 *
2267 * @type String
2268 */
2269YAHOO.util.DDProxy.dragElId = "ygddfdiv";
2270
2271/**
2272 * The border width of the frame. This is used when we resize the frame to
2273 * the size of the linked element. We substract the border width to make
2274 * the div the correct size.
2275 *
2276 * @TODO find a better way to handle this
2277 *
2278 * @type int
2279 */
2280YAHOO.util.DDProxy.prototype.borderWidth = 2;
2281
2282/**
2283 * By default we resize the drag frame to be the same size as the element
2284 * we want to drag (this is to get the frame effect). We can turn it off
2285 * if we want a different behavior (ex: ygDDMy2)
2286 *
2287 * @type boolean
2288 */
2289YAHOO.util.DDProxy.prototype.resizeFrame = true;
2290
2291/**
2292 * By default the frame is positioned exactly where the drag element is, so
2293 * we use the cursor offset provided by YAHOO.util.DD. Another option that works only if
2294 * you do not have constraints on the obj is to have the drag frame centered
2295 * around the cursor. Set centerFrame to true for this effect. Ex:
2296 * ygDDMy2
2297 *
2298 * @type boolean
2299 */
2300YAHOO.util.DDProxy.prototype.centerFrame = false;
2301
2302/**
2303 * Create the drag frame if needed
2304 */
2305YAHOO.util.DDProxy.createFrame = function() {
2306 var THIS = YAHOO.util.DDProxy;
2307
2308 if (!document || !document.body) {
2309 setTimeout(THIS.createFrame, 50);
2310 return;
2311 }
2312
2313 if (!THIS.frameDiv) {
2314 THIS.frameDiv = document.createElement("div");
2315 THIS.frameDiv.id = THIS.dragElId;
2316 var s = THIS.frameDiv.style;
2317 s.position = "absolute";
2318 s.visibility = "hidden";
2319 s.cursor = "move";
2320 s.border = "2px solid #aaa";
2321 s.zIndex = 999;
2322
2323 document.body.appendChild(THIS.frameDiv);
2324 }
2325};
2326
2327/**
2328 * Initialization for the drag frame element. Must be called in the
2329 * constructor of all subclasses
2330 */
2331YAHOO.util.DDProxy.prototype.initFrame = function() {
2332 YAHOO.util.DDProxy.createFrame();
2333 this.setDragElId(YAHOO.util.DDProxy.dragElId);
2334 this.useAbsMath = true;
2335};
2336
2337/**
2338 * Resizes the drag frame to the dimensions of the clicked object, positions
2339 * it over the object, and finally displays it
2340 *
2341 * @param {int} iPageX X click position
2342 * @param {int} iPageY Y click position
2343 * @private
2344 */
2345YAHOO.util.DDProxy.prototype.showFrame = function(iPageX, iPageY) {
2346 var el = this.getEl();
2347
2348 var s = this.getDragEl().style;
2349
2350 if (this.resizeFrame) {
2351 s.width = (parseInt(el.offsetWidth) - (2*this.borderWidth)) + "px";
2352 s.height = (parseInt(el.offsetHeight) - (2*this.borderWidth)) + "px";
2353 }
2354
2355 if (this.centerFrame) {
2356 this.setDelta(Math.round(parseInt(s.width)/2),
2357 Math.round(parseInt(s.width)/2));
2358 }
2359
2360 this.setDragElPos(iPageX, iPageY);
2361
2362 s.visibility = "";
2363};
2364
2365// overrides YAHOO.util.DragDrop
2366YAHOO.util.DDProxy.prototype.b4MouseDown = function(e) {
2367 var x = YAHOO.util.Event.getPageX(e);
2368 var y = YAHOO.util.Event.getPageY(e);
2369 this.autoOffset(x, y);
2370 this.setDragElPos(x, y);
2371};
2372
2373// overrides YAHOO.util.DragDrop
2374YAHOO.util.DDProxy.prototype.b4StartDrag = function(x, y) {
2375 // show the drag frame
2376 this.showFrame(x, y);
2377};
2378
2379// overrides YAHOO.util.DragDrop
2380YAHOO.util.DDProxy.prototype.b4EndDrag = function(e) {
2381
2382 // hide the drag frame
2383 var s = this.getDragEl().style;
2384 s.visibility = "hidden";
2385};
2386
2387// overrides YAHOO.util.DragDrop
2388// By default we try to move the element to the last location of the frame.
2389// This is so that the default behavior mirrors that of YAHOO.util.DD.
2390YAHOO.util.DDProxy.prototype.endDrag = function(e) {
2391 var lel = this.getEl();
2392 var del = this.getDragEl();
2393
2394 // Show the drag frame briefly so we can get its position
2395 del.style.visibility = "";
2396
2397 // Hide the linked element before the move to get around a Safari
2398 // rendering bug.
2399 lel.style.visibility = "hidden";
2400 YAHOO.util.DDM.moveToEl(lel, del);
2401 del.style.visibility = "hidden";
2402 lel.style.visibility = "";
2403};
2404
2405/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
2406
2407/**
2408 * @class a DragDrop implementation that does not move, but can be a drop
2409 * target. You would get the same result by simply omitting implementation
2410 * for the event callbacks, but this way we reduce the processing cost of the
2411 * event listener and the callbacks.
2412 *
2413 * @extends YAHOO.util.DragDrop
2414 * @constructor
2415 * @param {String} id the id of the element that is a drop target
2416 * @param {String} sGroup the group of related DragDrop objects
2417 */
2418
2419YAHOO.util.DDTarget = function(id, sGroup) {
2420 if (id) {
2421 this.initTarget(id, sGroup);
2422 }
2423};
2424
2425YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
2426
Note: See TracBrowser for help on using the repository browser.