/javascripts/lib/src/dd/DDCore.js
JavaScript | 3158 lines | 1132 code | 380 blank | 1646 comment | 232 complexity | 6352ccaa30807bab7b5f15d830e833c0 MD5 | raw file
Possible License(s): GPL-3.0
Large files files are truncated, but you can click here to view the full file
1/*! 2 * Ext JS Library 3.2.1 3 * Copyright(c) 2006-2010 Ext JS, Inc. 4 * licensing@extjs.com 5 * http://www.extjs.com/license 6 */ 7/* 8 * These classes are derivatives of the similarly named classes in the YUI Library. 9 * The original license: 10 * Copyright (c) 2006, Yahoo! Inc. All rights reserved. 11 * Code licensed under the BSD License: 12 * http://developer.yahoo.net/yui/license.txt 13 */ 14 15(function() { 16 17var Event=Ext.EventManager; 18var Dom=Ext.lib.Dom; 19 20/** 21 * @class Ext.dd.DragDrop 22 * Defines the interface and base operation of items that that can be 23 * dragged or can be drop targets. It was designed to be extended, overriding 24 * the event handlers for startDrag, onDrag, onDragOver and onDragOut. 25 * Up to three html elements can be associated with a DragDrop instance: 26 * <ul> 27 * <li>linked element: the element that is passed into the constructor. 28 * This is the element which defines the boundaries for interaction with 29 * other DragDrop objects.</li> 30 * <li>handle element(s): The drag operation only occurs if the element that 31 * was clicked matches a handle element. By default this is the linked 32 * element, but there are times that you will want only a portion of the 33 * linked element to initiate the drag operation, and the setHandleElId() 34 * method provides a way to define this.</li> 35 * <li>drag element: this represents the element that would be moved along 36 * with the cursor during a drag operation. By default, this is the linked 37 * element itself as in {@link Ext.dd.DD}. setDragElId() lets you define 38 * a separate element that would be moved, as in {@link Ext.dd.DDProxy}. 39 * </li> 40 * </ul> 41 * This class should not be instantiated until the onload event to ensure that 42 * the associated elements are available. 43 * The following would define a DragDrop obj that would interact with any 44 * other DragDrop obj in the "group1" group: 45 * <pre> 46 * dd = new Ext.dd.DragDrop("div1", "group1"); 47 * </pre> 48 * Since none of the event handlers have been implemented, nothing would 49 * actually happen if you were to run the code above. Normally you would 50 * override this class or one of the default implementations, but you can 51 * also override the methods you want on an instance of the class... 52 * <pre> 53 * dd.onDragDrop = function(e, id) { 54 * alert("dd was dropped on " + id); 55 * } 56 * </pre> 57 * @constructor 58 * @param {String} id of the element that is linked to this instance 59 * @param {String} sGroup the group of related DragDrop objects 60 * @param {object} config an object containing configurable attributes 61 * Valid properties for DragDrop: 62 * padding, isTarget, maintainOffset, primaryButtonOnly 63 */ 64Ext.dd.DragDrop = function(id, sGroup, config) { 65 if(id) { 66 this.init(id, sGroup, config); 67 } 68}; 69 70Ext.dd.DragDrop.prototype = { 71 72 /** 73 * Set to false to enable a DragDrop object to fire drag events while dragging 74 * over its own Element. Defaults to true - DragDrop objects do not by default 75 * fire drag events to themselves. 76 * @property ignoreSelf 77 * @type Boolean 78 */ 79 80 /** 81 * The id of the element associated with this object. This is what we 82 * refer to as the "linked element" because the size and position of 83 * this element is used to determine when the drag and drop objects have 84 * interacted. 85 * @property id 86 * @type String 87 */ 88 id: null, 89 90 /** 91 * Configuration attributes passed into the constructor 92 * @property config 93 * @type object 94 */ 95 config: null, 96 97 /** 98 * The id of the element that will be dragged. By default this is same 99 * as the linked element, but could be changed to another element. Ex: 100 * Ext.dd.DDProxy 101 * @property dragElId 102 * @type String 103 * @private 104 */ 105 dragElId: null, 106 107 /** 108 * The ID of the element that initiates the drag operation. By default 109 * this is the linked element, but could be changed to be a child of this 110 * element. This lets us do things like only starting the drag when the 111 * header element within the linked html element is clicked. 112 * @property handleElId 113 * @type String 114 * @private 115 */ 116 handleElId: null, 117 118 /** 119 * An object who's property names identify HTML tags to be considered invalid as drag handles. 120 * A non-null property value identifies the tag as invalid. Defaults to the 121 * following value which prevents drag operations from being initiated by <a> elements:<pre><code> 122{ 123 A: "A" 124}</code></pre> 125 * @property invalidHandleTypes 126 * @type Object 127 */ 128 invalidHandleTypes: null, 129 130 /** 131 * An object who's property names identify the IDs of elements to be considered invalid as drag handles. 132 * A non-null property value identifies the ID as invalid. For example, to prevent 133 * dragging from being initiated on element ID "foo", use:<pre><code> 134{ 135 foo: true 136}</code></pre> 137 * @property invalidHandleIds 138 * @type Object 139 */ 140 invalidHandleIds: null, 141 142 /** 143 * An Array of CSS class names for elements to be considered in valid as drag handles. 144 * @property invalidHandleClasses 145 * @type Array 146 */ 147 invalidHandleClasses: null, 148 149 /** 150 * The linked element's absolute X position at the time the drag was 151 * started 152 * @property startPageX 153 * @type int 154 * @private 155 */ 156 startPageX: 0, 157 158 /** 159 * The linked element's absolute X position at the time the drag was 160 * started 161 * @property startPageY 162 * @type int 163 * @private 164 */ 165 startPageY: 0, 166 167 /** 168 * The group defines a logical collection of DragDrop objects that are 169 * related. Instances only get events when interacting with other 170 * DragDrop object in the same group. This lets us define multiple 171 * groups using a single DragDrop subclass if we want. 172 * @property groups 173 * @type object An object in the format {'group1':true, 'group2':true} 174 */ 175 groups: null, 176 177 /** 178 * Individual drag/drop instances can be locked. This will prevent 179 * onmousedown start drag. 180 * @property locked 181 * @type boolean 182 * @private 183 */ 184 locked: false, 185 186 /** 187 * Lock this instance 188 * @method lock 189 */ 190 lock: function() { 191 this.locked = true; 192 }, 193 194 /** 195 * When set to true, other DD objects in cooperating DDGroups do not receive 196 * notification events when this DD object is dragged over them. Defaults to false. 197 * @property moveOnly 198 * @type boolean 199 */ 200 moveOnly: false, 201 202 /** 203 * Unlock this instace 204 * @method unlock 205 */ 206 unlock: function() { 207 this.locked = false; 208 }, 209 210 /** 211 * By default, all instances can be a drop target. This can be disabled by 212 * setting isTarget to false. 213 * @property isTarget 214 * @type boolean 215 */ 216 isTarget: true, 217 218 /** 219 * The padding configured for this drag and drop object for calculating 220 * the drop zone intersection with this object. 221 * @property padding 222 * @type int[] An array containing the 4 padding values: [top, right, bottom, left] 223 */ 224 padding: null, 225 226 /** 227 * Cached reference to the linked element 228 * @property _domRef 229 * @private 230 */ 231 _domRef: null, 232 233 /** 234 * Internal typeof flag 235 * @property __ygDragDrop 236 * @private 237 */ 238 __ygDragDrop: true, 239 240 /** 241 * Set to true when horizontal contraints are applied 242 * @property constrainX 243 * @type boolean 244 * @private 245 */ 246 constrainX: false, 247 248 /** 249 * Set to true when vertical contraints are applied 250 * @property constrainY 251 * @type boolean 252 * @private 253 */ 254 constrainY: false, 255 256 /** 257 * The left constraint 258 * @property minX 259 * @type int 260 * @private 261 */ 262 minX: 0, 263 264 /** 265 * The right constraint 266 * @property maxX 267 * @type int 268 * @private 269 */ 270 maxX: 0, 271 272 /** 273 * The up constraint 274 * @property minY 275 * @type int 276 * @private 277 */ 278 minY: 0, 279 280 /** 281 * The down constraint 282 * @property maxY 283 * @type int 284 * @private 285 */ 286 maxY: 0, 287 288 /** 289 * Maintain offsets when we resetconstraints. Set to true when you want 290 * the position of the element relative to its parent to stay the same 291 * when the page changes 292 * 293 * @property maintainOffset 294 * @type boolean 295 */ 296 maintainOffset: false, 297 298 /** 299 * Array of pixel locations the element will snap to if we specified a 300 * horizontal graduation/interval. This array is generated automatically 301 * when you define a tick interval. 302 * @property xTicks 303 * @type int[] 304 */ 305 xTicks: null, 306 307 /** 308 * Array of pixel locations the element will snap to if we specified a 309 * vertical graduation/interval. This array is generated automatically 310 * when you define a tick interval. 311 * @property yTicks 312 * @type int[] 313 */ 314 yTicks: null, 315 316 /** 317 * By default the drag and drop instance will only respond to the primary 318 * button click (left button for a right-handed mouse). Set to true to 319 * allow drag and drop to start with any mouse click that is propogated 320 * by the browser 321 * @property primaryButtonOnly 322 * @type boolean 323 */ 324 primaryButtonOnly: true, 325 326 /** 327 * The available property is false until the linked dom element is accessible. 328 * @property available 329 * @type boolean 330 */ 331 available: false, 332 333 /** 334 * By default, drags can only be initiated if the mousedown occurs in the 335 * region the linked element is. This is done in part to work around a 336 * bug in some browsers that mis-report the mousedown if the previous 337 * mouseup happened outside of the window. This property is set to true 338 * if outer handles are defined. 339 * 340 * @property hasOuterHandles 341 * @type boolean 342 * @default false 343 */ 344 hasOuterHandles: false, 345 346 /** 347 * Code that executes immediately before the startDrag event 348 * @method b4StartDrag 349 * @private 350 */ 351 b4StartDrag: function(x, y) { }, 352 353 /** 354 * Abstract method called after a drag/drop object is clicked 355 * and the drag or mousedown time thresholds have beeen met. 356 * @method startDrag 357 * @param {int} X click location 358 * @param {int} Y click location 359 */ 360 startDrag: function(x, y) { /* override this */ }, 361 362 /** 363 * Code that executes immediately before the onDrag event 364 * @method b4Drag 365 * @private 366 */ 367 b4Drag: function(e) { }, 368 369 /** 370 * Abstract method called during the onMouseMove event while dragging an 371 * object. 372 * @method onDrag 373 * @param {Event} e the mousemove event 374 */ 375 onDrag: function(e) { /* override this */ }, 376 377 /** 378 * Abstract method called when this element fist begins hovering over 379 * another DragDrop obj 380 * @method onDragEnter 381 * @param {Event} e the mousemove event 382 * @param {String|DragDrop[]} id In POINT mode, the element 383 * id this is hovering over. In INTERSECT mode, an array of one or more 384 * dragdrop items being hovered over. 385 */ 386 onDragEnter: function(e, id) { /* override this */ }, 387 388 /** 389 * Code that executes immediately before the onDragOver event 390 * @method b4DragOver 391 * @private 392 */ 393 b4DragOver: function(e) { }, 394 395 /** 396 * Abstract method called when this element is hovering over another 397 * DragDrop obj 398 * @method onDragOver 399 * @param {Event} e the mousemove event 400 * @param {String|DragDrop[]} id In POINT mode, the element 401 * id this is hovering over. In INTERSECT mode, an array of dd items 402 * being hovered over. 403 */ 404 onDragOver: function(e, id) { /* override this */ }, 405 406 /** 407 * Code that executes immediately before the onDragOut event 408 * @method b4DragOut 409 * @private 410 */ 411 b4DragOut: function(e) { }, 412 413 /** 414 * Abstract method called when we are no longer hovering over an element 415 * @method onDragOut 416 * @param {Event} e the mousemove event 417 * @param {String|DragDrop[]} id In POINT mode, the element 418 * id this was hovering over. In INTERSECT mode, an array of dd items 419 * that the mouse is no longer over. 420 */ 421 onDragOut: function(e, id) { /* override this */ }, 422 423 /** 424 * Code that executes immediately before the onDragDrop event 425 * @method b4DragDrop 426 * @private 427 */ 428 b4DragDrop: function(e) { }, 429 430 /** 431 * Abstract method called when this item is dropped on another DragDrop 432 * obj 433 * @method onDragDrop 434 * @param {Event} e the mouseup event 435 * @param {String|DragDrop[]} id In POINT mode, the element 436 * id this was dropped on. In INTERSECT mode, an array of dd items this 437 * was dropped on. 438 */ 439 onDragDrop: function(e, id) { /* override this */ }, 440 441 /** 442 * Abstract method called when this item is dropped on an area with no 443 * drop target 444 * @method onInvalidDrop 445 * @param {Event} e the mouseup event 446 */ 447 onInvalidDrop: function(e) { /* override this */ }, 448 449 /** 450 * Code that executes immediately before the endDrag event 451 * @method b4EndDrag 452 * @private 453 */ 454 b4EndDrag: function(e) { }, 455 456 /** 457 * Fired when we are done dragging the object 458 * @method endDrag 459 * @param {Event} e the mouseup event 460 */ 461 endDrag: function(e) { /* override this */ }, 462 463 /** 464 * Code executed immediately before the onMouseDown event 465 * @method b4MouseDown 466 * @param {Event} e the mousedown event 467 * @private 468 */ 469 b4MouseDown: function(e) { }, 470 471 /** 472 * Event handler that fires when a drag/drop obj gets a mousedown 473 * @method onMouseDown 474 * @param {Event} e the mousedown event 475 */ 476 onMouseDown: function(e) { /* override this */ }, 477 478 /** 479 * Event handler that fires when a drag/drop obj gets a mouseup 480 * @method onMouseUp 481 * @param {Event} e the mouseup event 482 */ 483 onMouseUp: function(e) { /* override this */ }, 484 485 /** 486 * Override the onAvailable method to do what is needed after the initial 487 * position was determined. 488 * @method onAvailable 489 */ 490 onAvailable: function () { 491 }, 492 493 /** 494 * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}). 495 * @type Object 496 */ 497 defaultPadding : {left:0, right:0, top:0, bottom:0}, 498 499 /** 500 * Initializes the drag drop object's constraints to restrict movement to a certain element. 501 * 502 * Usage: 503 <pre><code> 504 var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest", 505 { dragElId: "existingProxyDiv" }); 506 dd.startDrag = function(){ 507 this.constrainTo("parent-id"); 508 }; 509 </code></pre> 510 * Or you can initalize it using the {@link Ext.Element} object: 511 <pre><code> 512 Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, { 513 startDrag : function(){ 514 this.constrainTo("parent-id"); 515 } 516 }); 517 </code></pre> 518 * @param {Mixed} constrainTo The element to constrain to. 519 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints, 520 * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or 521 * an object containing the sides to pad. For example: {right:10, bottom:10} 522 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders) 523 */ 524 constrainTo : function(constrainTo, pad, inContent){ 525 if(Ext.isNumber(pad)){ 526 pad = {left: pad, right:pad, top:pad, bottom:pad}; 527 } 528 pad = pad || this.defaultPadding; 529 var b = Ext.get(this.getEl()).getBox(), 530 ce = Ext.get(constrainTo), 531 s = ce.getScroll(), 532 c, 533 cd = ce.dom; 534 if(cd == document.body){ 535 c = { x: s.left, y: s.top, width: Ext.lib.Dom.getViewWidth(), height: Ext.lib.Dom.getViewHeight()}; 536 }else{ 537 var xy = ce.getXY(); 538 c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight}; 539 } 540 541 542 var topSpace = b.y - c.y, 543 leftSpace = b.x - c.x; 544 545 this.resetConstraints(); 546 this.setXConstraint(leftSpace - (pad.left||0), // left 547 c.width - leftSpace - b.width - (pad.right||0), //right 548 this.xTickSize 549 ); 550 this.setYConstraint(topSpace - (pad.top||0), //top 551 c.height - topSpace - b.height - (pad.bottom||0), //bottom 552 this.yTickSize 553 ); 554 }, 555 556 /** 557 * Returns a reference to the linked element 558 * @method getEl 559 * @return {HTMLElement} the html element 560 */ 561 getEl: function() { 562 if (!this._domRef) { 563 this._domRef = Ext.getDom(this.id); 564 } 565 566 return this._domRef; 567 }, 568 569 /** 570 * Returns a reference to the actual element to drag. By default this is 571 * the same as the html element, but it can be assigned to another 572 * element. An example of this can be found in Ext.dd.DDProxy 573 * @method getDragEl 574 * @return {HTMLElement} the html element 575 */ 576 getDragEl: function() { 577 return Ext.getDom(this.dragElId); 578 }, 579 580 /** 581 * Sets up the DragDrop object. Must be called in the constructor of any 582 * Ext.dd.DragDrop subclass 583 * @method init 584 * @param id the id of the linked element 585 * @param {String} sGroup the group of related items 586 * @param {object} config configuration attributes 587 */ 588 init: function(id, sGroup, config) { 589 this.initTarget(id, sGroup, config); 590 Event.on(this.id, "mousedown", this.handleMouseDown, this); 591 // Event.on(this.id, "selectstart", Event.preventDefault); 592 }, 593 594 /** 595 * Initializes Targeting functionality only... the object does not 596 * get a mousedown handler. 597 * @method initTarget 598 * @param id the id of the linked element 599 * @param {String} sGroup the group of related items 600 * @param {object} config configuration attributes 601 */ 602 initTarget: function(id, sGroup, config) { 603 604 // configuration attributes 605 this.config = config || {}; 606 607 // create a local reference to the drag and drop manager 608 this.DDM = Ext.dd.DDM; 609 // initialize the groups array 610 this.groups = {}; 611 612 // assume that we have an element reference instead of an id if the 613 // parameter is not a string 614 if (typeof id !== "string") { 615 id = Ext.id(id); 616 } 617 618 // set the id 619 this.id = id; 620 621 // add to an interaction group 622 this.addToGroup((sGroup) ? sGroup : "default"); 623 624 // We don't want to register this as the handle with the manager 625 // so we just set the id rather than calling the setter. 626 this.handleElId = id; 627 628 // the linked element is the element that gets dragged by default 629 this.setDragElId(id); 630 631 // by default, clicked anchors will not start drag operations. 632 this.invalidHandleTypes = { A: "A" }; 633 this.invalidHandleIds = {}; 634 this.invalidHandleClasses = []; 635 636 this.applyConfig(); 637 638 this.handleOnAvailable(); 639 }, 640 641 /** 642 * Applies the configuration parameters that were passed into the constructor. 643 * This is supposed to happen at each level through the inheritance chain. So 644 * a DDProxy implentation will execute apply config on DDProxy, DD, and 645 * DragDrop in order to get all of the parameters that are available in 646 * each object. 647 * @method applyConfig 648 */ 649 applyConfig: function() { 650 651 // configurable properties: 652 // padding, isTarget, maintainOffset, primaryButtonOnly 653 this.padding = this.config.padding || [0, 0, 0, 0]; 654 this.isTarget = (this.config.isTarget !== false); 655 this.maintainOffset = (this.config.maintainOffset); 656 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false); 657 658 }, 659 660 /** 661 * Executed when the linked element is available 662 * @method handleOnAvailable 663 * @private 664 */ 665 handleOnAvailable: function() { 666 this.available = true; 667 this.resetConstraints(); 668 this.onAvailable(); 669 }, 670 671 /** 672 * Configures the padding for the target zone in px. Effectively expands 673 * (or reduces) the virtual object size for targeting calculations. 674 * Supports css-style shorthand; if only one parameter is passed, all sides 675 * will have that padding, and if only two are passed, the top and bottom 676 * will have the first param, the left and right the second. 677 * @method setPadding 678 * @param {int} iTop Top pad 679 * @param {int} iRight Right pad 680 * @param {int} iBot Bot pad 681 * @param {int} iLeft Left pad 682 */ 683 setPadding: function(iTop, iRight, iBot, iLeft) { 684 // this.padding = [iLeft, iRight, iTop, iBot]; 685 if (!iRight && 0 !== iRight) { 686 this.padding = [iTop, iTop, iTop, iTop]; 687 } else if (!iBot && 0 !== iBot) { 688 this.padding = [iTop, iRight, iTop, iRight]; 689 } else { 690 this.padding = [iTop, iRight, iBot, iLeft]; 691 } 692 }, 693 694 /** 695 * Stores the initial placement of the linked element. 696 * @method setInitPosition 697 * @param {int} diffX the X offset, default 0 698 * @param {int} diffY the Y offset, default 0 699 */ 700 setInitPosition: function(diffX, diffY) { 701 var el = this.getEl(); 702 703 if (!this.DDM.verifyEl(el)) { 704 return; 705 } 706 707 var dx = diffX || 0; 708 var dy = diffY || 0; 709 710 var p = Dom.getXY( el ); 711 712 this.initPageX = p[0] - dx; 713 this.initPageY = p[1] - dy; 714 715 this.lastPageX = p[0]; 716 this.lastPageY = p[1]; 717 718 this.setStartPosition(p); 719 }, 720 721 /** 722 * Sets the start position of the element. This is set when the obj 723 * is initialized, the reset when a drag is started. 724 * @method setStartPosition 725 * @param pos current position (from previous lookup) 726 * @private 727 */ 728 setStartPosition: function(pos) { 729 var p = pos || Dom.getXY( this.getEl() ); 730 this.deltaSetXY = null; 731 732 this.startPageX = p[0]; 733 this.startPageY = p[1]; 734 }, 735 736 /** 737 * Add this instance to a group of related drag/drop objects. All 738 * instances belong to at least one group, and can belong to as many 739 * groups as needed. 740 * @method addToGroup 741 * @param sGroup {string} the name of the group 742 */ 743 addToGroup: function(sGroup) { 744 this.groups[sGroup] = true; 745 this.DDM.regDragDrop(this, sGroup); 746 }, 747 748 /** 749 * Remove's this instance from the supplied interaction group 750 * @method removeFromGroup 751 * @param {string} sGroup The group to drop 752 */ 753 removeFromGroup: function(sGroup) { 754 if (this.groups[sGroup]) { 755 delete this.groups[sGroup]; 756 } 757 758 this.DDM.removeDDFromGroup(this, sGroup); 759 }, 760 761 /** 762 * Allows you to specify that an element other than the linked element 763 * will be moved with the cursor during a drag 764 * @method setDragElId 765 * @param id {string} the id of the element that will be used to initiate the drag 766 */ 767 setDragElId: function(id) { 768 this.dragElId = id; 769 }, 770 771 /** 772 * Allows you to specify a child of the linked element that should be 773 * used to initiate the drag operation. An example of this would be if 774 * you have a content div with text and links. Clicking anywhere in the 775 * content area would normally start the drag operation. Use this method 776 * to specify that an element inside of the content div is the element 777 * that starts the drag operation. 778 * @method setHandleElId 779 * @param id {string} the id of the element that will be used to 780 * initiate the drag. 781 */ 782 setHandleElId: function(id) { 783 if (typeof id !== "string") { 784 id = Ext.id(id); 785 } 786 this.handleElId = id; 787 this.DDM.regHandle(this.id, id); 788 }, 789 790 /** 791 * Allows you to set an element outside of the linked element as a drag 792 * handle 793 * @method setOuterHandleElId 794 * @param id the id of the element that will be used to initiate the drag 795 */ 796 setOuterHandleElId: function(id) { 797 if (typeof id !== "string") { 798 id = Ext.id(id); 799 } 800 Event.on(id, "mousedown", 801 this.handleMouseDown, this); 802 this.setHandleElId(id); 803 804 this.hasOuterHandles = true; 805 }, 806 807 /** 808 * Remove all drag and drop hooks for this element 809 * @method unreg 810 */ 811 unreg: function() { 812 Event.un(this.id, "mousedown", 813 this.handleMouseDown); 814 this._domRef = null; 815 this.DDM._remove(this); 816 }, 817 818 destroy : function(){ 819 this.unreg(); 820 }, 821 822 /** 823 * Returns true if this instance is locked, or the drag drop mgr is locked 824 * (meaning that all drag/drop is disabled on the page.) 825 * @method isLocked 826 * @return {boolean} true if this obj or all drag/drop is locked, else 827 * false 828 */ 829 isLocked: function() { 830 return (this.DDM.isLocked() || this.locked); 831 }, 832 833 /** 834 * Fired when this object is clicked 835 * @method handleMouseDown 836 * @param {Event} e 837 * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj) 838 * @private 839 */ 840 handleMouseDown: function(e, oDD){ 841 if (this.primaryButtonOnly && e.button != 0) { 842 return; 843 } 844 845 if (this.isLocked()) { 846 return; 847 } 848 849 this.DDM.refreshCache(this.groups); 850 851 var pt = new Ext.lib.Point(Ext.lib.Event.getPageX(e), Ext.lib.Event.getPageY(e)); 852 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) { 853 } else { 854 if (this.clickValidator(e)) { 855 856 // set the initial element position 857 this.setStartPosition(); 858 859 this.b4MouseDown(e); 860 this.onMouseDown(e); 861 862 this.DDM.handleMouseDown(e, this); 863 864 this.DDM.stopEvent(e); 865 } else { 866 867 868 } 869 } 870 }, 871 872 clickValidator: function(e) { 873 var target = e.getTarget(); 874 return ( this.isValidHandleChild(target) && 875 (this.id == this.handleElId || 876 this.DDM.handleWasClicked(target, this.id)) ); 877 }, 878 879 /** 880 * Allows you to specify a tag name that should not start a drag operation 881 * when clicked. This is designed to facilitate embedding links within a 882 * drag handle that do something other than start the drag. 883 * @method addInvalidHandleType 884 * @param {string} tagName the type of element to exclude 885 */ 886 addInvalidHandleType: function(tagName) { 887 var type = tagName.toUpperCase(); 888 this.invalidHandleTypes[type] = type; 889 }, 890 891 /** 892 * Lets you to specify an element id for a child of a drag handle 893 * that should not initiate a drag 894 * @method addInvalidHandleId 895 * @param {string} id the element id of the element you wish to ignore 896 */ 897 addInvalidHandleId: function(id) { 898 if (typeof id !== "string") { 899 id = Ext.id(id); 900 } 901 this.invalidHandleIds[id] = id; 902 }, 903 904 /** 905 * Lets you specify a css class of elements that will not initiate a drag 906 * @method addInvalidHandleClass 907 * @param {string} cssClass the class of the elements you wish to ignore 908 */ 909 addInvalidHandleClass: function(cssClass) { 910 this.invalidHandleClasses.push(cssClass); 911 }, 912 913 /** 914 * Unsets an excluded tag name set by addInvalidHandleType 915 * @method removeInvalidHandleType 916 * @param {string} tagName the type of element to unexclude 917 */ 918 removeInvalidHandleType: function(tagName) { 919 var type = tagName.toUpperCase(); 920 // this.invalidHandleTypes[type] = null; 921 delete this.invalidHandleTypes[type]; 922 }, 923 924 /** 925 * Unsets an invalid handle id 926 * @method removeInvalidHandleId 927 * @param {string} id the id of the element to re-enable 928 */ 929 removeInvalidHandleId: function(id) { 930 if (typeof id !== "string") { 931 id = Ext.id(id); 932 } 933 delete this.invalidHandleIds[id]; 934 }, 935 936 /** 937 * Unsets an invalid css class 938 * @method removeInvalidHandleClass 939 * @param {string} cssClass the class of the element(s) you wish to 940 * re-enable 941 */ 942 removeInvalidHandleClass: function(cssClass) { 943 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) { 944 if (this.invalidHandleClasses[i] == cssClass) { 945 delete this.invalidHandleClasses[i]; 946 } 947 } 948 }, 949 950 /** 951 * Checks the tag exclusion list to see if this click should be ignored 952 * @method isValidHandleChild 953 * @param {HTMLElement} node the HTMLElement to evaluate 954 * @return {boolean} true if this is a valid tag type, false if not 955 */ 956 isValidHandleChild: function(node) { 957 958 var valid = true; 959 // var n = (node.nodeName == "#text") ? node.parentNode : node; 960 var nodeName; 961 try { 962 nodeName = node.nodeName.toUpperCase(); 963 } catch(e) { 964 nodeName = node.nodeName; 965 } 966 valid = valid && !this.invalidHandleTypes[nodeName]; 967 valid = valid && !this.invalidHandleIds[node.id]; 968 969 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) { 970 valid = !Ext.fly(node).hasClass(this.invalidHandleClasses[i]); 971 } 972 973 974 return valid; 975 976 }, 977 978 /** 979 * Create the array of horizontal tick marks if an interval was specified 980 * in setXConstraint(). 981 * @method setXTicks 982 * @private 983 */ 984 setXTicks: function(iStartX, iTickSize) { 985 this.xTicks = []; 986 this.xTickSize = iTickSize; 987 988 var tickMap = {}; 989 990 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) { 991 if (!tickMap[i]) { 992 this.xTicks[this.xTicks.length] = i; 993 tickMap[i] = true; 994 } 995 } 996 997 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) { 998 if (!tickMap[i]) { 999 this.xTicks[this.xTicks.length] = i; 1000 tickMap[i] = true; 1001 } 1002 } 1003 1004 this.xTicks.sort(this.DDM.numericSort) ; 1005 }, 1006 1007 /** 1008 * Create the array of vertical tick marks if an interval was specified in 1009 * setYConstraint(). 1010 * @method setYTicks 1011 * @private 1012 */ 1013 setYTicks: function(iStartY, iTickSize) { 1014 this.yTicks = []; 1015 this.yTickSize = iTickSize; 1016 1017 var tickMap = {}; 1018 1019 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) { 1020 if (!tickMap[i]) { 1021 this.yTicks[this.yTicks.length] = i; 1022 tickMap[i] = true; 1023 } 1024 } 1025 1026 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) { 1027 if (!tickMap[i]) { 1028 this.yTicks[this.yTicks.length] = i; 1029 tickMap[i] = true; 1030 } 1031 } 1032 1033 this.yTicks.sort(this.DDM.numericSort) ; 1034 }, 1035 1036 /** 1037 * By default, the element can be dragged any place on the screen. Use 1038 * this method to limit the horizontal travel of the element. Pass in 1039 * 0,0 for the parameters if you want to lock the drag to the y axis. 1040 * @method setXConstraint 1041 * @param {int} iLeft the number of pixels the element can move to the left 1042 * @param {int} iRight the number of pixels the element can move to the 1043 * right 1044 * @param {int} iTickSize optional parameter for specifying that the 1045 * element 1046 * should move iTickSize pixels at a time. 1047 */ 1048 setXConstraint: function(iLeft, iRight, iTickSize) { 1049 this.leftConstraint = iLeft; 1050 this.rightConstraint = iRight; 1051 1052 this.minX = this.initPageX - iLeft; 1053 this.maxX = this.initPageX + iRight; 1054 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); } 1055 1056 this.constrainX = true; 1057 }, 1058 1059 /** 1060 * Clears any constraints applied to this instance. Also clears ticks 1061 * since they can't exist independent of a constraint at this time. 1062 * @method clearConstraints 1063 */ 1064 clearConstraints: function() { 1065 this.constrainX = false; 1066 this.constrainY = false; 1067 this.clearTicks(); 1068 }, 1069 1070 /** 1071 * Clears any tick interval defined for this instance 1072 * @method clearTicks 1073 */ 1074 clearTicks: function() { 1075 this.xTicks = null; 1076 this.yTicks = null; 1077 this.xTickSize = 0; 1078 this.yTickSize = 0; 1079 }, 1080 1081 /** 1082 * By default, the element can be dragged any place on the screen. Set 1083 * this to limit the vertical travel of the element. Pass in 0,0 for the 1084 * parameters if you want to lock the drag to the x axis. 1085 * @method setYConstraint 1086 * @param {int} iUp the number of pixels the element can move up 1087 * @param {int} iDown the number of pixels the element can move down 1088 * @param {int} iTickSize optional parameter for specifying that the 1089 * element should move iTickSize pixels at a time. 1090 */ 1091 setYConstraint: function(iUp, iDown, iTickSize) { 1092 this.topConstraint = iUp; 1093 this.bottomConstraint = iDown; 1094 1095 this.minY = this.initPageY - iUp; 1096 this.maxY = this.initPageY + iDown; 1097 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); } 1098 1099 this.constrainY = true; 1100 1101 }, 1102 1103 /** 1104 * resetConstraints must be called if you manually reposition a dd element. 1105 * @method resetConstraints 1106 * @param {boolean} maintainOffset 1107 */ 1108 resetConstraints: function() { 1109 // Maintain offsets if necessary 1110 if (this.initPageX || this.initPageX === 0) { 1111 // figure out how much this thing has moved 1112 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0; 1113 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0; 1114 1115 this.setInitPosition(dx, dy); 1116 1117 // This is the first time we have detected the element's position 1118 } else { 1119 this.setInitPosition(); 1120 } 1121 1122 if (this.constrainX) { 1123 this.setXConstraint( this.leftConstraint, 1124 this.rightConstraint, 1125 this.xTickSize ); 1126 } 1127 1128 if (this.constrainY) { 1129 this.setYConstraint( this.topConstraint, 1130 this.bottomConstraint, 1131 this.yTickSize ); 1132 } 1133 }, 1134 1135 /** 1136 * Normally the drag element is moved pixel by pixel, but we can specify 1137 * that it move a number of pixels at a time. This method resolves the 1138 * location when we have it set up like this. 1139 * @method getTick 1140 * @param {int} val where we want to place the object 1141 * @param {int[]} tickArray sorted array of valid points 1142 * @return {int} the closest tick 1143 * @private 1144 */ 1145 getTick: function(val, tickArray) { 1146 if (!tickArray) { 1147 // If tick interval is not defined, it is effectively 1 pixel, 1148 // so we return the value passed to us. 1149 return val; 1150 } else if (tickArray[0] >= val) { 1151 // The value is lower than the first tick, so we return the first 1152 // tick. 1153 return tickArray[0]; 1154 } else { 1155 for (var i=0, len=tickArray.length; i<len; ++i) { 1156 var next = i + 1; 1157 if (tickArray[next] && tickArray[next] >= val) { 1158 var diff1 = val - tickArray[i]; 1159 var diff2 = tickArray[next] - val; 1160 return (diff2 > diff1) ? tickArray[i] : tickArray[next]; 1161 } 1162 } 1163 1164 // The value is larger than the last tick, so we return the last 1165 // tick. 1166 return tickArray[tickArray.length - 1]; 1167 } 1168 }, 1169 1170 /** 1171 * toString method 1172 * @method toString 1173 * @return {string} string representation of the dd obj 1174 */ 1175 toString: function() { 1176 return ("DragDrop " + this.id); 1177 } 1178 1179}; 1180 1181})(); 1182/* 1183 * The drag and drop utility provides a framework for building drag and drop 1184 * applications. In addition to enabling drag and drop for specific elements, 1185 * the drag and drop elements are tracked by the manager class, and the 1186 * interactions between the various elements are tracked during the drag and 1187 * the implementing code is notified about these important moments. 1188 */ 1189 1190// Only load the library once. Rewriting the manager class would orphan 1191// existing drag and drop instances. 1192if (!Ext.dd.DragDropMgr) { 1193 1194/** 1195 * @class Ext.dd.DragDropMgr 1196 * DragDropMgr is a singleton that tracks the element interaction for 1197 * all DragDrop items in the window. Generally, you will not call 1198 * this class directly, but it does have helper methods that could 1199 * be useful in your DragDrop implementations. 1200 * @singleton 1201 */ 1202Ext.dd.DragDropMgr = function() { 1203 1204 var Event = Ext.EventManager; 1205 1206 return { 1207 1208 /** 1209 * Two dimensional Array of registered DragDrop objects. The first 1210 * dimension is the DragDrop item group, the second the DragDrop 1211 * object. 1212 * @property ids 1213 * @type String[] 1214 * @private 1215 * @static 1216 */ 1217 ids: {}, 1218 1219 /** 1220 * Array of element ids defined as drag handles. Used to determine 1221 * if the element that generated the mousedown event is actually the 1222 * handle and not the html element itself. 1223 * @property handleIds 1224 * @type String[] 1225 * @private 1226 * @static 1227 */ 1228 handleIds: {}, 1229 1230 /** 1231 * the DragDrop object that is currently being dragged 1232 * @property dragCurrent 1233 * @type DragDrop 1234 * @private 1235 * @static 1236 **/ 1237 dragCurrent: null, 1238 1239 /** 1240 * the DragDrop object(s) that are being hovered over 1241 * @property dragOvers 1242 * @type Array 1243 * @private 1244 * @static 1245 */ 1246 dragOvers: {}, 1247 1248 /** 1249 * the X distance between the cursor and the object being dragged 1250 * @property deltaX 1251 * @type int 1252 * @private 1253 * @static 1254 */ 1255 deltaX: 0, 1256 1257 /** 1258 * the Y distance between the cursor and the object being dragged 1259 * @property deltaY 1260 * @type int 1261 * @private 1262 * @static 1263 */ 1264 deltaY: 0, 1265 1266 /** 1267 * Flag to determine if we should prevent the default behavior of the 1268 * events we define. By default this is true, but this can be set to 1269 * false if you need the default behavior (not recommended) 1270 * @property preventDefault 1271 * @type boolean 1272 * @static 1273 */ 1274 preventDefault: true, 1275 1276 /** 1277 * Flag to determine if we should stop the propagation of the events 1278 * we generate. This is true by default but you may want to set it to 1279 * false if the html element contains other features that require the 1280 * mouse click. 1281 * @property stopPropagation 1282 * @type boolean 1283 * @static 1284 */ 1285 stopPropagation: true, 1286 1287 /** 1288 * Internal flag that is set to true when drag and drop has been 1289 * intialized 1290 * @property initialized 1291 * @private 1292 * @static 1293 */ 1294 initialized: false, 1295 1296 /** 1297 * All drag and drop can be disabled. 1298 * @property locked 1299 * @private 1300 * @static 1301 */ 1302 locked: false, 1303 1304 /** 1305 * Called the first time an element is registered. 1306 * @method init 1307 * @private 1308 * @static 1309 */ 1310 init: function() { 1311 this.initialized = true; 1312 }, 1313 1314 /** 1315 * In point mode, drag and drop interaction is defined by the 1316 * location of the cursor during the drag/drop 1317 * @property POINT 1318 * @type int 1319 * @static 1320 */ 1321 POINT: 0, 1322 1323 /** 1324 * In intersect mode, drag and drop interaction is defined by the 1325 * overlap of two or more drag and drop objects. 1326 * @property INTERSECT 1327 * @type int 1328 * @static 1329 */ 1330 INTERSECT: 1, 1331 1332 /** 1333 * The current drag and drop mode. Default: POINT 1334 * @property mode 1335 * @type int 1336 * @static 1337 */ 1338 mode: 0, 1339 1340 /** 1341 * Runs method on all drag and drop objects 1342 * @method _execOnAll 1343 * @private 1344 * @static 1345 */ 1346 _execOnAll: function(sMethod, args) { 1347 for (var i in this.ids) { 1348 for (var j in this.ids[i]) { 1349 var oDD = this.ids[i][j]; 1350 if (! this.isTypeOfDD(oDD)) { 1351 continue; 1352 } 1353 oDD[sMethod].apply(oDD, args); 1354 } 1355 } 1356 }, 1357 1358 /** 1359 * Drag and drop initialization. Sets up the global event handlers 1360 * @method _onLoad 1361 * @private 1362 * @static 1363 */ 1364 _onLoad: function() { 1365 1366 this.init(); 1367 1368 1369 Event.on(document, "mouseup", this.handleMouseUp, this, true); 1370 Event.on(document, "mousemove", this.handleMouseMove, this, true); 1371 Event.on(window, "unload", this._onUnload, this, true); 1372 Event.on(window, "resize", this._onResize, this, true); 1373 // Event.on(window, "mouseout", this._test); 1374 1375 }, 1376 1377 /** 1378 * Reset constraints on all drag and drop objs 1379 * @method _onResize 1380 * @private 1381 * @static 1382 */ 1383 _onResize: function(e) { 1384 this._execOnAll("resetConstraints", []); 1385 }, 1386 1387 /** 1388 * Lock all drag and drop functionality 1389 * @method lock 1390 * @static 1391 */ 1392 lock: function() { this.locked = true; }, 1393 1394 /** 1395 * Unlock all drag and drop functionality 1396 * @method unlock 1397 * @static 1398 */ 1399 unlock: function() { this.locked = false; }, 1400 1401 /** 1402 * Is drag and drop locked? 1403 * @method isLocked 1404 * @return {boolean} True if drag and drop is locked, false otherwise. 1405 * @static 1406 */ 1407 isLocked: function() { return this.locked; }, 1408 1409 /** 1410 * Location cache that is set for all drag drop objects when a drag is 1411 * initiated, cleared when the drag is finished. 1412 * @property locationCache 1413 * @private 1414 * @static 1415 */ 1416 locationCache: {}, 1417 1418 /** 1419 * Set useCache to false if you want to force object the lookup of each 1420 * drag and drop linked element constantly during a drag. 1421 * @property useCache 1422 * @type boolean 1423 * @static 1424 */ 1425 useCache: true, 1426 1427 /** 1428 * The number of pixels that the mouse needs to move after the 1429 * mousedown before the drag is initiated. Default=3; 1430 * @property clickPixelThresh 1431 * @type int 1432 * @static 1433 */ 1434 clickPixelThresh: 3, 1435 1436 /** 1437 * The number of milliseconds after the mousedown event to initiate the 1438 * drag if we don't get a mouseup event. Default=350 1439 * @property clickTimeThresh 1440 * @type int 1441 * @static 1442 */ 1443 clickTimeThresh: 350, 1444 1445 /** 1446 * Flag that indicates that either the drag pixel threshold or the 1447 * mousdown time threshold has been met 1448 * @property dragThreshMet 1449 * @type boolean 1450 * @private 1451 * @static 1452 */ 1453 dragThreshMet: false, 1454 1455 /** 1456 * Timeout used for the click time threshold 1457 * @property clickTimeout 1458 * @type Object 1459 * @private 1460 * @static 1461 */ 1462 clickTimeout: null, 1463 1464 /** 1465 * The X position of the mousedown event stored for later use when a 1466 * drag threshold is met. 1467 * @property startX 1468 * @type int 1469 * @private 1470 * @static 1471 */ 1472 startX: 0, 1473 1474 /** 1475 * The Y position of the mousedown event stored for later use when a 1476 * drag threshold is met. 1477 * @property startY 1478 * @type int 1479 * @private 1480 * @static 1481 */ 1482 startY: 0, 1483 1484 /** 1485 * Each DragDrop instance must be registered with the DragDropMgr. 1486 * This is executed in DragDrop.init() 1487 * @method regDragDrop 1488 * @param {DragDrop} oDD the DragDrop object to register 1489 * @param {String} sGroup the name of the group this element belongs to 1490 * @static 1491 */ 1492 regDragDrop: function(oDD, sGroup) { 1493 if (!this.initialized) { this.init(); } 1494 1495 if (!this.ids[sGroup]) { 1496 this.ids[sGroup] = {}; 1497 } 1498 this.ids[sGroup][oDD.id] = oDD; 1499 }, 1500 1501 /** 1502 * Removes the supplied dd instance from the supplied group. Executed 1503 * by DragDrop.removeFromGroup, so don't call this function directly. 1504 * @method removeDDFromGroup 1505 * @private 1506 * @static 1507 */ 1508 removeDDFromGroup: function(oDD, sGroup) { 1509 if (!this.ids[sGroup]) { 1510 this.ids[sGroup] = {}; 1511 } 1512 1513 var obj = this.ids[sGroup]; 1514 if (obj && obj[oDD.id]) { 1515 delete obj[oDD.id]; 1516 } 1517 }, 1518 1519 /** 1520 * Unregisters a drag and drop item. This is executed in 1521 * DragDrop.unreg, use that method instead of calling this directly. 1522 * @method _remove 1523 * @private 1524 * @static 1525 */ 1526 _remove: function(oDD) { 1527 for (var g in oDD.groups) { 1528 if (g && this.ids[g] && this.ids[g][oDD.id]) { 1529 delete this.ids[g][oDD.id]; 1530 } 1531 } 1532 delete this.handleIds[oDD.id]; 1533 }, 1534 1535 /** 1536 * Each DragDrop handle element must be registered. This is done 1537 * automatically when executing DragDrop.setHandleElId() 1538 * @method regHandle 1539 * @param {String} sDDId the DragDrop id this element is a handle for 1540 * @param {String} sHandleId the id of the element that is the drag 1541 * handle 1542 * @static 1543 */ 1544 regHandle: function(sDDId, sHandleId) { 1545 if (!this.handleIds[sDDId]) { 1546 this.handleIds[sDDId] = {}; 1547 } 1548 this.handleIds[sDDId][sHandleId] = sHandleId; 1549 }, 1550 1551 /** 1552 * Utility function to determine if a given element has been 1553 * registered as a drag drop item. 1554 * @method isDragDrop 1555 * @param {String} id the element id to check 1556 * @return {boolean} true if this element is a DragDrop item, 1557 * false otherwise 1558 * @static 1559 */ 1560 isDragDrop: function(id) { 1561 return ( this.getDDById(id) ) ? true : false; 1562 }, 1563 1564 /** 1565 * Returns the drag and drop instances that are in all groups the 1566 * passed in instance belongs to. 1567 * @method getRelated 1568 * @param {DragDrop} p_oDD the obj to get related data for 1569 * @param {boolean} bTargetsOnly if true, only return targetable objs 1570 * @return {DragDrop[]} the related instances 1571 * @static 1572 */ 1573 getRelated: function(p_oDD, bTargetsOnly) { 1574 var oDDs = []; 1575 for (var i in p_oDD.groups) { 1576 for (var j in this.ids[i]) { 1577 var dd = this.ids[i][j]; 1578 if (! this.isTypeOfDD(dd)) { 1579 continue; 1580 } 1581 if (!bTargetsOnly || dd.isTarget) { 1582 oDDs[oDDs.length] = dd; 1583 } 1584 } 1585 } 1586 1587 return oDDs; 1588 }, 1589 1590 /** 1591 * Returns true if the specified dd target is a legal target for 1592 * the specifice drag obj 1593 * @method isLegalTarget 1594 * @param {DragDrop} oDD the drag obj 1595 * @param {DragDrop} oTargetDD the target 1596 * @return {boolean} true if the target is a legal target for the 1597 * dd obj 1598 * @static 1599 */ 1600 isLegalTarget: function (oDD, oTargetDD) { 1601 var targets = this.getRelated(oDD, true); 1602 for (var i=0, len=targets.length;i<len;++i) { 1603 if (targets[i].id == oTargetDD.id) { 1604 return true; 1605 } 1606 } 1607 1608 return false; 1609 }, 1610 1611 /** 1612 * My goal is to be able to transparently determine if an object is 1613 * typeof DragDrop, and the exact subclass of DragDrop. typeof 1614 * returns "object", oDD.constructor.toString() always returns 1615 * "DragDrop" and not the name of the subclass. So for now it just 1616 * evaluates a well-known variable in DragDrop. 1617 * @method isTypeOfDD 1618 * @param {Object} the object to evaluate 1619 * @return {boolean} true if typeof oDD = DragDrop 1620 * @static 1621 */ 1622 isTypeOfDD: function (oDD) { 1623 return (oDD && oDD.__ygDragDrop); 1624 }, 1625 1626 /** 1627 * Utility function to determine if a given element has been 1628 * registered as a drag drop handle for the given Drag Drop object. 1629 * @method isHandle 1630 * @param {String} id the element id to check 1631 * @return {boolean} …
Large files files are truncated, but you can click here to view the full file