DragDrop.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. /*
  2. * This is a derivative of the similarly named class in the YUI Library.
  3. * The original license:
  4. * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
  5. * Code licensed under the BSD License:
  6. * http://developer.yahoo.net/yui/license.txt
  7. */
  8. /**
  9. * Defines the interface and base operation of items that that can be
  10. * dragged or can be drop targets. It was designed to be extended, overriding
  11. * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
  12. * Up to three html elements can be associated with a DragDrop instance:
  13. *
  14. * - linked element: the element that is passed into the constructor.
  15. * This is the element which defines the boundaries for interaction with
  16. * other DragDrop objects.
  17. *
  18. * - handle element(s): The drag operation only occurs if the element that
  19. * was clicked matches a handle element. By default this is the linked
  20. * element, but there are times that you will want only a portion of the
  21. * linked element to initiate the drag operation, and the setHandleElId()
  22. * method provides a way to define this.
  23. *
  24. * - drag element: this represents the element that would be moved along
  25. * with the cursor during a drag operation. By default, this is the linked
  26. * element itself as in {@link Ext.dd.DD}. setDragElId() lets you define
  27. * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
  28. *
  29. * This class should not be instantiated until the onload event to ensure that
  30. * the associated elements are available.
  31. * The following would define a DragDrop obj that would interact with any
  32. * other DragDrop obj in the "group1" group:
  33. *
  34. * dd = new Ext.dd.DragDrop("div1", "group1");
  35. *
  36. * Since none of the event handlers have been implemented, nothing would
  37. * actually happen if you were to run the code above. Normally you would
  38. * override this class or one of the default implementations, but you can
  39. * also override the methods you want on an instance of the class...
  40. *
  41. * dd.onDragDrop = function(e, id) {
  42. * alert("dd was dropped on " + id);
  43. * }
  44. *
  45. */
  46. Ext.define('Ext.dd.DragDrop', {
  47. requires: ['Ext.dd.DragDropManager'],
  48. /**
  49. * Creates new DragDrop.
  50. * @param {String} id of the element that is linked to this instance
  51. * @param {String} sGroup the group of related DragDrop objects
  52. * @param {Object} config an object containing configurable attributes.
  53. * Valid properties for DragDrop:
  54. *
  55. * - padding
  56. * - isTarget
  57. * - maintainOffset
  58. * - primaryButtonOnly
  59. */
  60. constructor: function(id, sGroup, config) {
  61. if(id) {
  62. this.init(id, sGroup, config);
  63. }
  64. },
  65. /**
  66. * @property {Boolean} ignoreSelf
  67. * Set to false to enable a DragDrop object to fire drag events while dragging
  68. * over its own Element. Defaults to true - DragDrop objects do not by default
  69. * fire drag events to themselves.
  70. */
  71. /**
  72. * @property {String} id
  73. * The id of the element associated with this object. This is what we
  74. * refer to as the "linked element" because the size and position of
  75. * this element is used to determine when the drag and drop objects have
  76. * interacted.
  77. */
  78. id: null,
  79. /**
  80. * @property {Object} config
  81. * Configuration attributes passed into the constructor
  82. */
  83. config: null,
  84. /**
  85. * @property {String} dragElId
  86. * The id of the element that will be dragged. By default this is same
  87. * as the linked element, but could be changed to another element. Ex:
  88. * Ext.dd.DDProxy
  89. * @private
  90. */
  91. dragElId: null,
  92. /**
  93. * @property {String} handleElId
  94. * The ID of the element that initiates the drag operation. By default
  95. * this is the linked element, but could be changed to be a child of this
  96. * element. This lets us do things like only starting the drag when the
  97. * header element within the linked html element is clicked.
  98. * @private
  99. */
  100. handleElId: null,
  101. /**
  102. * @property {Object} invalidHandleTypes
  103. * An object who's property names identify HTML tags to be considered invalid as drag handles.
  104. * A non-null property value identifies the tag as invalid. Defaults to the
  105. * following value which prevents drag operations from being initiated by `<a>` elements:
  106. *
  107. * {
  108. * A: "A"
  109. * }
  110. */
  111. invalidHandleTypes: null,
  112. /**
  113. * @property {Object} invalidHandleIds
  114. * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
  115. * A non-null property value identifies the ID as invalid. For example, to prevent
  116. * dragging from being initiated on element ID "foo", use:
  117. *
  118. * {
  119. * foo: true
  120. * }
  121. */
  122. invalidHandleIds: null,
  123. /**
  124. * @property {String[]} invalidHandleClasses
  125. * An Array of CSS class names for elements to be considered in valid as drag handles.
  126. */
  127. invalidHandleClasses: null,
  128. /**
  129. * @property {Number} startPageX
  130. * The linked element's absolute X position at the time the drag was
  131. * started
  132. * @private
  133. */
  134. startPageX: 0,
  135. /**
  136. * @property {Number} startPageY
  137. * The linked element's absolute X position at the time the drag was
  138. * started
  139. * @private
  140. */
  141. startPageY: 0,
  142. /**
  143. * @property {Object} groups
  144. * The group defines a logical collection of DragDrop objects that are
  145. * related. Instances only get events when interacting with other
  146. * DragDrop object in the same group. This lets us define multiple
  147. * groups using a single DragDrop subclass if we want.
  148. *
  149. * An object in the format {'group1':true, 'group2':true}
  150. */
  151. groups: null,
  152. /**
  153. * @property {Boolean} locked
  154. * Individual drag/drop instances can be locked. This will prevent
  155. * onmousedown start drag.
  156. * @private
  157. */
  158. locked: false,
  159. /**
  160. * Locks this instance
  161. */
  162. lock: function() {
  163. this.locked = true;
  164. },
  165. /**
  166. * @property {Boolean} moveOnly
  167. * When set to true, other DD objects in cooperating DDGroups do not receive
  168. * notification events when this DD object is dragged over them.
  169. */
  170. moveOnly: false,
  171. /**
  172. * Unlocks this instace
  173. */
  174. unlock: function() {
  175. this.locked = false;
  176. },
  177. /**
  178. * @property {Boolean} isTarget
  179. * By default, all instances can be a drop target. This can be disabled by
  180. * setting isTarget to false.
  181. */
  182. isTarget: true,
  183. /**
  184. * @property {Number[]} padding
  185. * The padding configured for this drag and drop object for calculating
  186. * the drop zone intersection with this object.
  187. * An array containing the 4 padding values: [top, right, bottom, left]
  188. */
  189. padding: null,
  190. /**
  191. * @property _domRef
  192. * Cached reference to the linked element
  193. * @private
  194. */
  195. _domRef: null,
  196. /**
  197. * @property __ygDragDrop
  198. * Internal typeof flag
  199. * @private
  200. */
  201. __ygDragDrop: true,
  202. /**
  203. * @property {Boolean} constrainX
  204. * Set to true when horizontal contraints are applied
  205. * @private
  206. */
  207. constrainX: false,
  208. /**
  209. * @property {Boolean} constrainY
  210. * Set to true when vertical contraints are applied
  211. * @private
  212. */
  213. constrainY: false,
  214. /**
  215. * @property {Number} minX
  216. * The left constraint
  217. * @private
  218. */
  219. minX: 0,
  220. /**
  221. * @property {Number} maxX
  222. * The right constraint
  223. * @private
  224. */
  225. maxX: 0,
  226. /**
  227. * @property {Number} minY
  228. * The up constraint
  229. * @private
  230. */
  231. minY: 0,
  232. /**
  233. * @property {Number} maxY
  234. * The down constraint
  235. * @private
  236. */
  237. maxY: 0,
  238. /**
  239. * @property {Boolean} maintainOffset
  240. * Maintain offsets when we resetconstraints. Set to true when you want
  241. * the position of the element relative to its parent to stay the same
  242. * when the page changes
  243. */
  244. maintainOffset: false,
  245. /**
  246. * @property {Number[]} xTicks
  247. * Array of pixel locations the element will snap to if we specified a
  248. * horizontal graduation/interval. This array is generated automatically
  249. * when you define a tick interval.
  250. */
  251. xTicks: null,
  252. /**
  253. * @property {Number[]} yTicks
  254. * Array of pixel locations the element will snap to if we specified a
  255. * vertical graduation/interval. This array is generated automatically
  256. * when you define a tick interval.
  257. */
  258. yTicks: null,
  259. /**
  260. * @property {Boolean} primaryButtonOnly
  261. * By default the drag and drop instance will only respond to the primary
  262. * button click (left button for a right-handed mouse). Set to true to
  263. * allow drag and drop to start with any mouse click that is propogated
  264. * by the browser
  265. */
  266. primaryButtonOnly: true,
  267. /**
  268. * @property {Boolean} available
  269. * The available property is false until the linked dom element is accessible.
  270. */
  271. available: false,
  272. /**
  273. * @property {Boolean} hasOuterHandles
  274. * By default, drags can only be initiated if the mousedown occurs in the
  275. * region the linked element is. This is done in part to work around a
  276. * bug in some browsers that mis-report the mousedown if the previous
  277. * mouseup happened outside of the window. This property is set to true
  278. * if outer handles are defined. Defaults to false.
  279. */
  280. hasOuterHandles: false,
  281. /**
  282. * Code that executes immediately before the startDrag event
  283. * @private
  284. */
  285. b4StartDrag: function(x, y) { },
  286. /**
  287. * Abstract method called after a drag/drop object is clicked
  288. * and the drag or mousedown time thresholds have beeen met.
  289. * @param {Number} X click location
  290. * @param {Number} Y click location
  291. */
  292. startDrag: function(x, y) { /* override this */ },
  293. /**
  294. * Code that executes immediately before the onDrag event
  295. * @private
  296. */
  297. b4Drag: function(e) { },
  298. /**
  299. * Abstract method called during the onMouseMove event while dragging an
  300. * object.
  301. * @param {Event} e the mousemove event
  302. */
  303. onDrag: function(e) { /* override this */ },
  304. /**
  305. * Abstract method called when this element fist begins hovering over
  306. * another DragDrop obj
  307. * @param {Event} e the mousemove event
  308. * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element
  309. * id this is hovering over. In INTERSECT mode, an array of one or more
  310. * dragdrop items being hovered over.
  311. */
  312. onDragEnter: function(e, id) { /* override this */ },
  313. /**
  314. * Code that executes immediately before the onDragOver event
  315. * @private
  316. */
  317. b4DragOver: function(e) { },
  318. /**
  319. * Abstract method called when this element is hovering over another
  320. * DragDrop obj
  321. * @param {Event} e the mousemove event
  322. * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element
  323. * id this is hovering over. In INTERSECT mode, an array of dd items
  324. * being hovered over.
  325. */
  326. onDragOver: function(e, id) { /* override this */ },
  327. /**
  328. * Code that executes immediately before the onDragOut event
  329. * @private
  330. */
  331. b4DragOut: function(e) { },
  332. /**
  333. * Abstract method called when we are no longer hovering over an element
  334. * @param {Event} e the mousemove event
  335. * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element
  336. * id this was hovering over. In INTERSECT mode, an array of dd items
  337. * that the mouse is no longer over.
  338. */
  339. onDragOut: function(e, id) { /* override this */ },
  340. /**
  341. * Code that executes immediately before the onDragDrop event
  342. * @private
  343. */
  344. b4DragDrop: function(e) { },
  345. /**
  346. * Abstract method called when this item is dropped on another DragDrop
  347. * obj
  348. * @param {Event} e the mouseup event
  349. * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element
  350. * id this was dropped on. In INTERSECT mode, an array of dd items this
  351. * was dropped on.
  352. */
  353. onDragDrop: function(e, id) { /* override this */ },
  354. /**
  355. * Abstract method called when this item is dropped on an area with no
  356. * drop target
  357. * @param {Event} e the mouseup event
  358. */
  359. onInvalidDrop: function(e) { /* override this */ },
  360. /**
  361. * Code that executes immediately before the endDrag event
  362. * @private
  363. */
  364. b4EndDrag: function(e) { },
  365. /**
  366. * Called when we are done dragging the object
  367. * @param {Event} e the mouseup event
  368. */
  369. endDrag: function(e) { /* override this */ },
  370. /**
  371. * Code executed immediately before the onMouseDown event
  372. * @param {Event} e the mousedown event
  373. * @private
  374. */
  375. b4MouseDown: function(e) { },
  376. /**
  377. * Called when a drag/drop obj gets a mousedown
  378. * @param {Event} e the mousedown event
  379. */
  380. onMouseDown: function(e) { /* override this */ },
  381. /**
  382. * Called when a drag/drop obj gets a mouseup
  383. * @param {Event} e the mouseup event
  384. */
  385. onMouseUp: function(e) { /* override this */ },
  386. /**
  387. * Override the onAvailable method to do what is needed after the initial
  388. * position was determined.
  389. */
  390. onAvailable: function () {
  391. },
  392. /**
  393. * @property {Object} defaultPadding
  394. * Provides default constraint padding to "constrainTo" elements.
  395. */
  396. defaultPadding: {
  397. left: 0,
  398. right: 0,
  399. top: 0,
  400. bottom: 0
  401. },
  402. /**
  403. * Initializes the drag drop object's constraints to restrict movement to a certain element.
  404. *
  405. * Usage:
  406. *
  407. * var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
  408. * { dragElId: "existingProxyDiv" });
  409. * dd.startDrag = function(){
  410. * this.constrainTo("parent-id");
  411. * };
  412. *
  413. * Or you can initalize it using the {@link Ext.Element} object:
  414. *
  415. * Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
  416. * startDrag : function(){
  417. * this.constrainTo("parent-id");
  418. * }
  419. * });
  420. *
  421. * @param {String/HTMLElement/Ext.Element} constrainTo The element or element ID to constrain to.
  422. * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
  423. * and can be either a number for symmetrical padding (4 would be equal to `{left:4, right:4, top:4, bottom:4}`) or
  424. * an object containing the sides to pad. For example: `{right:10, bottom:10}`
  425. * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
  426. */
  427. constrainTo : function(constrainTo, pad, inContent){
  428. if(Ext.isNumber(pad)){
  429. pad = {left: pad, right:pad, top:pad, bottom:pad};
  430. }
  431. pad = pad || this.defaultPadding;
  432. var b = Ext.get(this.getEl()).getBox(),
  433. ce = Ext.get(constrainTo),
  434. s = ce.getScroll(),
  435. c,
  436. cd = ce.dom,
  437. xy,
  438. topSpace,
  439. leftSpace;
  440. if(cd == document.body){
  441. c = { x: s.left, y: s.top, width: Ext.Element.getViewWidth(), height: Ext.Element.getViewHeight()};
  442. }else{
  443. xy = ce.getXY();
  444. c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
  445. }
  446. topSpace = b.y - c.y;
  447. leftSpace = b.x - c.x;
  448. this.resetConstraints();
  449. this.setXConstraint(leftSpace - (pad.left||0), // left
  450. c.width - leftSpace - b.width - (pad.right||0), //right
  451. this.xTickSize
  452. );
  453. this.setYConstraint(topSpace - (pad.top||0), //top
  454. c.height - topSpace - b.height - (pad.bottom||0), //bottom
  455. this.yTickSize
  456. );
  457. },
  458. /**
  459. * Returns a reference to the linked element
  460. * @return {HTMLElement} the html element
  461. */
  462. getEl: function() {
  463. if (!this._domRef) {
  464. this._domRef = Ext.getDom(this.id);
  465. }
  466. return this._domRef;
  467. },
  468. /**
  469. * Returns a reference to the actual element to drag. By default this is
  470. * the same as the html element, but it can be assigned to another
  471. * element. An example of this can be found in Ext.dd.DDProxy
  472. * @return {HTMLElement} the html element
  473. */
  474. getDragEl: function() {
  475. return Ext.getDom(this.dragElId);
  476. },
  477. /**
  478. * Sets up the DragDrop object. Must be called in the constructor of any
  479. * Ext.dd.DragDrop subclass
  480. * @param {String} id the id of the linked element
  481. * @param {String} sGroup the group of related items
  482. * @param {Object} config configuration attributes
  483. */
  484. init: function(id, sGroup, config) {
  485. this.initTarget(id, sGroup, config);
  486. Ext.EventManager.on(this.id, "mousedown", this.handleMouseDown, this);
  487. // Ext.EventManager.on(this.id, "selectstart", Event.preventDefault);
  488. },
  489. /**
  490. * Initializes Targeting functionality only... the object does not
  491. * get a mousedown handler.
  492. * @param {String} id the id of the linked element
  493. * @param {String} sGroup the group of related items
  494. * @param {Object} config configuration attributes
  495. */
  496. initTarget: function(id, sGroup, config) {
  497. // configuration attributes
  498. this.config = config || {};
  499. // create a local reference to the drag and drop manager
  500. this.DDMInstance = Ext.dd.DragDropManager;
  501. // initialize the groups array
  502. this.groups = {};
  503. // assume that we have an element reference instead of an id if the
  504. // parameter is not a string
  505. if (typeof id !== "string") {
  506. id = Ext.id(id);
  507. }
  508. // set the id
  509. this.id = id;
  510. // add to an interaction group
  511. this.addToGroup((sGroup) ? sGroup : "default");
  512. // We don't want to register this as the handle with the manager
  513. // so we just set the id rather than calling the setter.
  514. this.handleElId = id;
  515. // the linked element is the element that gets dragged by default
  516. this.setDragElId(id);
  517. // by default, clicked anchors will not start drag operations.
  518. this.invalidHandleTypes = { A: "A" };
  519. this.invalidHandleIds = {};
  520. this.invalidHandleClasses = [];
  521. this.applyConfig();
  522. this.handleOnAvailable();
  523. },
  524. /**
  525. * Applies the configuration parameters that were passed into the constructor.
  526. * This is supposed to happen at each level through the inheritance chain. So
  527. * a DDProxy implentation will execute apply config on DDProxy, DD, and
  528. * DragDrop in order to get all of the parameters that are available in
  529. * each object.
  530. */
  531. applyConfig: function() {
  532. // configurable properties:
  533. // padding, isTarget, maintainOffset, primaryButtonOnly
  534. this.padding = this.config.padding || [0, 0, 0, 0];
  535. this.isTarget = (this.config.isTarget !== false);
  536. this.maintainOffset = (this.config.maintainOffset);
  537. this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
  538. },
  539. /**
  540. * Executed when the linked element is available
  541. * @private
  542. */
  543. handleOnAvailable: function() {
  544. this.available = true;
  545. this.resetConstraints();
  546. this.onAvailable();
  547. },
  548. /**
  549. * Configures the padding for the target zone in px. Effectively expands
  550. * (or reduces) the virtual object size for targeting calculations.
  551. * Supports css-style shorthand; if only one parameter is passed, all sides
  552. * will have that padding, and if only two are passed, the top and bottom
  553. * will have the first param, the left and right the second.
  554. * @param {Number} iTop Top pad
  555. * @param {Number} iRight Right pad
  556. * @param {Number} iBot Bot pad
  557. * @param {Number} iLeft Left pad
  558. */
  559. setPadding: function(iTop, iRight, iBot, iLeft) {
  560. // this.padding = [iLeft, iRight, iTop, iBot];
  561. if (!iRight && 0 !== iRight) {
  562. this.padding = [iTop, iTop, iTop, iTop];
  563. } else if (!iBot && 0 !== iBot) {
  564. this.padding = [iTop, iRight, iTop, iRight];
  565. } else {
  566. this.padding = [iTop, iRight, iBot, iLeft];
  567. }
  568. },
  569. /**
  570. * Stores the initial placement of the linked element.
  571. * @param {Number} diffX the X offset, default 0
  572. * @param {Number} diffY the Y offset, default 0
  573. */
  574. setInitPosition: function(diffX, diffY) {
  575. var el = this.getEl(),
  576. dx, dy, p;
  577. if (!this.DDMInstance.verifyEl(el)) {
  578. return;
  579. }
  580. dx = diffX || 0;
  581. dy = diffY || 0;
  582. p = Ext.Element.getXY( el );
  583. this.initPageX = p[0] - dx;
  584. this.initPageY = p[1] - dy;
  585. this.lastPageX = p[0];
  586. this.lastPageY = p[1];
  587. this.setStartPosition(p);
  588. },
  589. /**
  590. * Sets the start position of the element. This is set when the obj
  591. * is initialized, the reset when a drag is started.
  592. * @param pos current position (from previous lookup)
  593. * @private
  594. */
  595. setStartPosition: function(pos) {
  596. var p = pos || Ext.Element.getXY( this.getEl() );
  597. this.deltaSetXY = null;
  598. this.startPageX = p[0];
  599. this.startPageY = p[1];
  600. },
  601. /**
  602. * Adds this instance to a group of related drag/drop objects. All
  603. * instances belong to at least one group, and can belong to as many
  604. * groups as needed.
  605. * @param {String} sGroup the name of the group
  606. */
  607. addToGroup: function(sGroup) {
  608. this.groups[sGroup] = true;
  609. this.DDMInstance.regDragDrop(this, sGroup);
  610. },
  611. /**
  612. * Removes this instance from the supplied interaction group
  613. * @param {String} sGroup The group to drop
  614. */
  615. removeFromGroup: function(sGroup) {
  616. if (this.groups[sGroup]) {
  617. delete this.groups[sGroup];
  618. }
  619. this.DDMInstance.removeDDFromGroup(this, sGroup);
  620. },
  621. /**
  622. * Allows you to specify that an element other than the linked element
  623. * will be moved with the cursor during a drag
  624. * @param {String} id the id of the element that will be used to initiate the drag
  625. */
  626. setDragElId: function(id) {
  627. this.dragElId = id;
  628. },
  629. /**
  630. * Allows you to specify a child of the linked element that should be
  631. * used to initiate the drag operation. An example of this would be if
  632. * you have a content div with text and links. Clicking anywhere in the
  633. * content area would normally start the drag operation. Use this method
  634. * to specify that an element inside of the content div is the element
  635. * that starts the drag operation.
  636. * @param {String} id the id of the element that will be used to
  637. * initiate the drag.
  638. */
  639. setHandleElId: function(id) {
  640. if (typeof id !== "string") {
  641. id = Ext.id(id);
  642. }
  643. this.handleElId = id;
  644. this.DDMInstance.regHandle(this.id, id);
  645. },
  646. /**
  647. * Allows you to set an element outside of the linked element as a drag
  648. * handle
  649. * @param {String} id the id of the element that will be used to initiate the drag
  650. */
  651. setOuterHandleElId: function(id) {
  652. if (typeof id !== "string") {
  653. id = Ext.id(id);
  654. }
  655. Ext.EventManager.on(id, "mousedown", this.handleMouseDown, this);
  656. this.setHandleElId(id);
  657. this.hasOuterHandles = true;
  658. },
  659. /**
  660. * Removes all drag and drop hooks for this element
  661. */
  662. unreg: function() {
  663. Ext.EventManager.un(this.id, "mousedown", this.handleMouseDown, this);
  664. this._domRef = null;
  665. this.DDMInstance._remove(this);
  666. },
  667. destroy : function(){
  668. this.unreg();
  669. },
  670. /**
  671. * Returns true if this instance is locked, or the drag drop mgr is locked
  672. * (meaning that all drag/drop is disabled on the page.)
  673. * @return {Boolean} true if this obj or all drag/drop is locked, else
  674. * false
  675. */
  676. isLocked: function() {
  677. return (this.DDMInstance.isLocked() || this.locked);
  678. },
  679. /**
  680. * Called when this object is clicked
  681. * @param {Event} e
  682. * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
  683. * @private
  684. */
  685. handleMouseDown: function(e, oDD){
  686. if (this.primaryButtonOnly && e.button != 0) {
  687. return;
  688. }
  689. if (this.isLocked()) {
  690. return;
  691. }
  692. this.DDMInstance.refreshCache(this.groups);
  693. if (this.hasOuterHandles || this.DDMInstance.isOverTarget(e.getPoint(), this) ) {
  694. if (this.clickValidator(e)) {
  695. // set the initial element position
  696. this.setStartPosition();
  697. this.b4MouseDown(e);
  698. this.onMouseDown(e);
  699. this.DDMInstance.handleMouseDown(e, this);
  700. this.DDMInstance.stopEvent(e);
  701. }
  702. }
  703. },
  704. clickValidator: function(e) {
  705. var target = e.getTarget();
  706. return ( this.isValidHandleChild(target) &&
  707. (this.id == this.handleElId ||
  708. this.DDMInstance.handleWasClicked(target, this.id)) );
  709. },
  710. /**
  711. * Allows you to specify a tag name that should not start a drag operation
  712. * when clicked. This is designed to facilitate embedding links within a
  713. * drag handle that do something other than start the drag.
  714. * @method addInvalidHandleType
  715. * @param {String} tagName the type of element to exclude
  716. */
  717. addInvalidHandleType: function(tagName) {
  718. var type = tagName.toUpperCase();
  719. this.invalidHandleTypes[type] = type;
  720. },
  721. /**
  722. * Lets you to specify an element id for a child of a drag handle
  723. * that should not initiate a drag
  724. * @method addInvalidHandleId
  725. * @param {String} id the element id of the element you wish to ignore
  726. */
  727. addInvalidHandleId: function(id) {
  728. if (typeof id !== "string") {
  729. id = Ext.id(id);
  730. }
  731. this.invalidHandleIds[id] = id;
  732. },
  733. /**
  734. * Lets you specify a css class of elements that will not initiate a drag
  735. * @param {String} cssClass the class of the elements you wish to ignore
  736. */
  737. addInvalidHandleClass: function(cssClass) {
  738. this.invalidHandleClasses.push(cssClass);
  739. },
  740. /**
  741. * Unsets an excluded tag name set by addInvalidHandleType
  742. * @param {String} tagName the type of element to unexclude
  743. */
  744. removeInvalidHandleType: function(tagName) {
  745. var type = tagName.toUpperCase();
  746. // this.invalidHandleTypes[type] = null;
  747. delete this.invalidHandleTypes[type];
  748. },
  749. /**
  750. * Unsets an invalid handle id
  751. * @param {String} id the id of the element to re-enable
  752. */
  753. removeInvalidHandleId: function(id) {
  754. if (typeof id !== "string") {
  755. id = Ext.id(id);
  756. }
  757. delete this.invalidHandleIds[id];
  758. },
  759. /**
  760. * Unsets an invalid css class
  761. * @param {String} cssClass the class of the element(s) you wish to
  762. * re-enable
  763. */
  764. removeInvalidHandleClass: function(cssClass) {
  765. for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
  766. if (this.invalidHandleClasses[i] == cssClass) {
  767. delete this.invalidHandleClasses[i];
  768. }
  769. }
  770. },
  771. /**
  772. * Checks the tag exclusion list to see if this click should be ignored
  773. * @param {HTMLElement} node the HTMLElement to evaluate
  774. * @return {Boolean} true if this is a valid tag type, false if not
  775. */
  776. isValidHandleChild: function(node) {
  777. var valid = true,
  778. nodeName,
  779. i, len;
  780. // var n = (node.nodeName == "#text") ? node.parentNode : node;
  781. try {
  782. nodeName = node.nodeName.toUpperCase();
  783. } catch(e) {
  784. nodeName = node.nodeName;
  785. }
  786. valid = valid && !this.invalidHandleTypes[nodeName];
  787. valid = valid && !this.invalidHandleIds[node.id];
  788. for (i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
  789. valid = !Ext.fly(node).hasCls(this.invalidHandleClasses[i]);
  790. }
  791. return valid;
  792. },
  793. /**
  794. * Creates the array of horizontal tick marks if an interval was specified
  795. * in setXConstraint().
  796. * @private
  797. */
  798. setXTicks: function(iStartX, iTickSize) {
  799. this.xTicks = [];
  800. this.xTickSize = iTickSize;
  801. var tickMap = {},
  802. i;
  803. for (i = this.initPageX; i >= this.minX; i = i - iTickSize) {
  804. if (!tickMap[i]) {
  805. this.xTicks[this.xTicks.length] = i;
  806. tickMap[i] = true;
  807. }
  808. }
  809. for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
  810. if (!tickMap[i]) {
  811. this.xTicks[this.xTicks.length] = i;
  812. tickMap[i] = true;
  813. }
  814. }
  815. Ext.Array.sort(this.xTicks, this.DDMInstance.numericSort);
  816. },
  817. /**
  818. * Creates the array of vertical tick marks if an interval was specified in
  819. * setYConstraint().
  820. * @private
  821. */
  822. setYTicks: function(iStartY, iTickSize) {
  823. this.yTicks = [];
  824. this.yTickSize = iTickSize;
  825. var tickMap = {},
  826. i;
  827. for (i = this.initPageY; i >= this.minY; i = i - iTickSize) {
  828. if (!tickMap[i]) {
  829. this.yTicks[this.yTicks.length] = i;
  830. tickMap[i] = true;
  831. }
  832. }
  833. for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
  834. if (!tickMap[i]) {
  835. this.yTicks[this.yTicks.length] = i;
  836. tickMap[i] = true;
  837. }
  838. }
  839. Ext.Array.sort(this.yTicks, this.DDMInstance.numericSort);
  840. },
  841. /**
  842. * By default, the element can be dragged any place on the screen. Use
  843. * this method to limit the horizontal travel of the element. Pass in
  844. * 0,0 for the parameters if you want to lock the drag to the y axis.
  845. * @param {Number} iLeft the number of pixels the element can move to the left
  846. * @param {Number} iRight the number of pixels the element can move to the
  847. * right
  848. * @param {Number} iTickSize (optional) parameter for specifying that the
  849. * element should move iTickSize pixels at a time.
  850. */
  851. setXConstraint: function(iLeft, iRight, iTickSize) {
  852. this.leftConstraint = iLeft;
  853. this.rightConstraint = iRight;
  854. this.minX = this.initPageX - iLeft;
  855. this.maxX = this.initPageX + iRight;
  856. if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
  857. this.constrainX = true;
  858. },
  859. /**
  860. * Clears any constraints applied to this instance. Also clears ticks
  861. * since they can't exist independent of a constraint at this time.
  862. */
  863. clearConstraints: function() {
  864. this.constrainX = false;
  865. this.constrainY = false;
  866. this.clearTicks();
  867. },
  868. /**
  869. * Clears any tick interval defined for this instance
  870. */
  871. clearTicks: function() {
  872. this.xTicks = null;
  873. this.yTicks = null;
  874. this.xTickSize = 0;
  875. this.yTickSize = 0;
  876. },
  877. /**
  878. * By default, the element can be dragged any place on the screen. Set
  879. * this to limit the vertical travel of the element. Pass in 0,0 for the
  880. * parameters if you want to lock the drag to the x axis.
  881. * @param {Number} iUp the number of pixels the element can move up
  882. * @param {Number} iDown the number of pixels the element can move down
  883. * @param {Number} iTickSize (optional) parameter for specifying that the
  884. * element should move iTickSize pixels at a time.
  885. */
  886. setYConstraint: function(iUp, iDown, iTickSize) {
  887. this.topConstraint = iUp;
  888. this.bottomConstraint = iDown;
  889. this.minY = this.initPageY - iUp;
  890. this.maxY = this.initPageY + iDown;
  891. if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
  892. this.constrainY = true;
  893. },
  894. /**
  895. * Must be called if you manually reposition a dd element.
  896. * @param {Boolean} maintainOffset
  897. */
  898. resetConstraints: function() {
  899. // Maintain offsets if necessary
  900. if (this.initPageX || this.initPageX === 0) {
  901. // figure out how much this thing has moved
  902. var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0,
  903. dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
  904. this.setInitPosition(dx, dy);
  905. // This is the first time we have detected the element's position
  906. } else {
  907. this.setInitPosition();
  908. }
  909. if (this.constrainX) {
  910. this.setXConstraint( this.leftConstraint,
  911. this.rightConstraint,
  912. this.xTickSize );
  913. }
  914. if (this.constrainY) {
  915. this.setYConstraint( this.topConstraint,
  916. this.bottomConstraint,
  917. this.yTickSize );
  918. }
  919. },
  920. /**
  921. * Normally the drag element is moved pixel by pixel, but we can specify
  922. * that it move a number of pixels at a time. This method resolves the
  923. * location when we have it set up like this.
  924. * @param {Number} val where we want to place the object
  925. * @param {Number[]} tickArray sorted array of valid points
  926. * @return {Number} the closest tick
  927. * @private
  928. */
  929. getTick: function(val, tickArray) {
  930. if (!tickArray) {
  931. // If tick interval is not defined, it is effectively 1 pixel,
  932. // so we return the value passed to us.
  933. return val;
  934. } else if (tickArray[0] >= val) {
  935. // The value is lower than the first tick, so we return the first
  936. // tick.
  937. return tickArray[0];
  938. } else {
  939. var i, len, next, diff1, diff2;
  940. for (i=0, len=tickArray.length; i<len; ++i) {
  941. next = i + 1;
  942. if (tickArray[next] && tickArray[next] >= val) {
  943. diff1 = val - tickArray[i];
  944. diff2 = tickArray[next] - val;
  945. return (diff2 > diff1) ? tickArray[i] : tickArray[next];
  946. }
  947. }
  948. // The value is larger than the last tick, so we return the last
  949. // tick.
  950. return tickArray[tickArray.length - 1];
  951. }
  952. },
  953. /**
  954. * toString method
  955. * @return {String} string representation of the dd obj
  956. */
  957. toString: function() {
  958. return ("DragDrop " + this.id);
  959. }
  960. });