PageRenderTime 113ms CodeModel.GetById 39ms RepoModel.GetById 1ms app.codeStats 0ms

/web-app/qooxdoo/application/portal/source/class/portal/dragdrop/Manager.js

https://github.com/flomotlik/grails-qooxdoo
JavaScript | 453 lines | 267 code | 67 blank | 119 comment | 53 complexity | bb23480ab26419b59818c4b971bfdb7e MD5 | raw file
Possible License(s): Unlicense, CC-BY-SA-3.0
  1. /* ************************************************************************
  2. qooxdoo - the new era of web development
  3. http://qooxdoo.org
  4. Copyright:
  5. 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
  6. License:
  7. LGPL: http://www.gnu.org/licenses/lgpl.html
  8. EPL: http://www.eclipse.org/org/documents/epl-v10.php
  9. See the LICENSE file in the project's top-level directory for details.
  10. Authors:
  11. * Alexander Back (aback)
  12. * David Werner (psycledw)
  13. ************************************************************************ */
  14. /**
  15. * Manager class for drag and drop of container
  16. */
  17. qx.Class.define("portal.dragdrop.Manager",
  18. {
  19. type : "singleton",
  20. extend : qx.core.Object,
  21. construct : function()
  22. {
  23. this.base(arguments);
  24. this.__activeBoxInfo = { top : null, height : null };
  25. this.__positions = { top : 0, left : 0 };
  26. },
  27. /* ******************************************************
  28. * PROPERTIES
  29. * ******************************************************/
  30. properties :
  31. {
  32. /** The current active draggable box. This assumes a drag session is starting */
  33. activeBox :
  34. {
  35. check : "portal.box.Box",
  36. init : null,
  37. nullable : true
  38. },
  39. /** Indicates whether a drag and drop session is currently active */
  40. sessionActive :
  41. {
  42. check : "Boolean",
  43. init : false
  44. }
  45. },
  46. /* ******************************************************
  47. * MEMBERS
  48. * ******************************************************/
  49. members :
  50. {
  51. __activeBoxInfo : null,
  52. __currentGroupBox : null,
  53. __groupBoxChange : false,
  54. __positions : null,
  55. __groupBoxInfos : null,
  56. __ghost : null,
  57. /**
  58. * Starts a drag and drop session
  59. *
  60. * @param activeBox {portal.box.Box} active box instance
  61. * @return {void}
  62. */
  63. startSession : function(activeBox)
  64. {
  65. // set session active
  66. this.setSessionActive(true);
  67. // set the active draggable
  68. this.setActiveBox(activeBox);
  69. // set the start groupBox
  70. this.__currentGroupBox = activeBox.getGroupBoxId();
  71. // ghost element
  72. this.__createGhostElement();
  73. // create groupBox coords map (if not available)
  74. if (this.__groupBoxInfos == null)
  75. {
  76. this.__groupBoxInfos = {};
  77. var groupBoxes = portal.box.Manager.getInstance().getGroupBoxes();
  78. for (var i=0, j=groupBoxes.length; i<j; i++)
  79. {
  80. this.__groupBoxInfos[groupBoxes[i].element.id] = {
  81. left : qx.bom.element.Location.getLeft(groupBoxes[i].element),
  82. right : qx.bom.element.Location.getLeft(groupBoxes[i].element) + qx.bom.element.Dimension.getWidth(groupBoxes[i].element)
  83. };
  84. }
  85. }
  86. },
  87. /**
  88. * Creates the ghost element which is moved during the drag&drop session
  89. *
  90. * @return {void}
  91. */
  92. __createGhostElement : function()
  93. {
  94. // create ghost element
  95. if (!this.__ghost) {
  96. this.__ghost = qx.bom.Element.create("div");
  97. }
  98. // use the computedStyle rather than the "clientWidth" or "clientHeight"
  99. // to ensure the padding is included for Firefox
  100. var element = this.getActiveBox().getElement();
  101. var dimension = portal.box.Util.getComputedDimension(element);
  102. // get all elements of box
  103. this.__switchParent(element, this.__ghost);
  104. // style the ghost element
  105. qx.bom.element.Class.add(this.__ghost, qx.bom.element.Class.get(element));
  106. if (qx.core.Variant.isSet("qx.client", "mshtml"))
  107. {
  108. var top = qx.bom.element.Location.getTop(element, "margin") -
  109. parseInt(qx.bom.element.Style.get(element, "paddingTop")) -
  110. parseInt(qx.bom.element.Style.get(element, "borderTopWidth"));
  111. }
  112. else if (qx.core.Variant.isSet("qx.client", "webkit"))
  113. {
  114. var top = qx.bom.element.Location.getTop(element, "margin");
  115. }
  116. else
  117. {
  118. var top = qx.bom.element.Location.getTop(element);
  119. }
  120. var css = "opacity:0.5;margin:0;position:absolute;" +
  121. "top:" + top + "px;" +
  122. "left:" + qx.bom.element.Location.getLeft(element) + "px;" +
  123. "width:" + dimension.width + "px;" +
  124. "height:" + dimension.height + "px";
  125. qx.bom.element.Style.setCss(this.__ghost, css);
  126. // set a new border for the box element - this element actually gets moved around
  127. qx.bom.element.Style.set(element, "border", "1px dashed red");
  128. // insert the ghost element and bring it to the front
  129. qx.dom.Node.getBodyElement(element).appendChild(this.__ghost);
  130. portal.box.Util.bringToFront(this.__ghost);
  131. },
  132. /**
  133. * Add the children elements of the source node to the target node.
  134. *
  135. * @param source {Node} source DOM node
  136. * @param target {Node} target DOM node
  137. * @return {void}
  138. */
  139. __switchParent : function(source, target)
  140. {
  141. while (source.firstChild) {
  142. target.appendChild(source.firstChild);
  143. }
  144. },
  145. /**
  146. * Stops a drag and drop session.
  147. *
  148. * @param e {qx.event.type.Drag} Drag event instance
  149. * @lint ignoreDeprecated(_applyActive)
  150. * @return {void}
  151. */
  152. stopSession : function(e)
  153. {
  154. // set session inactive
  155. this.setSessionActive(false);
  156. var activeBox = this.getActiveBox();
  157. var element = activeBox.getElement();
  158. if (qx.core.Variant.isSet("qx.client", "mshtml"))
  159. {
  160. var top = qx.bom.element.Location.getTop(element, "margin") - parseInt(qx.bom.element.Style.get(element, "paddingTop")) - parseInt(qx.bom.element.Style.get(element, "paddingBottom"));
  161. var left = qx.bom.element.Location.getLeft(element, "margin") - parseInt(qx.bom.element.Style.get(element, "paddingLeft"));
  162. }
  163. else if (qx.core.Variant.isSet("qx.client", "webkit"))
  164. {
  165. var top = qx.bom.element.Location.getTop(element, "margin");
  166. var left = qx.bom.element.Location.getLeft(element, "margin");
  167. }
  168. else
  169. {
  170. var top = qx.bom.element.Location.getTop(element);
  171. var left = qx.bom.element.Location.getLeft(element);
  172. }
  173. // inform the box manager about the update
  174. portal.box.Manager.getInstance().updateGroupBoxMembers(activeBox.getGroupBoxId(), this.__currentGroupBox, activeBox);
  175. // store the new groupBox id
  176. activeBox.setGroupBoxId(this.__currentGroupBox);
  177. // animation setup
  178. var animMove = new qx.fx.effect.core.Move(this.__ghost);
  179. animMove.set(
  180. {
  181. x : left,
  182. y : top,
  183. mode : "absolute",
  184. duration : 0.5,
  185. transition : "spring"
  186. });
  187. // start the animation
  188. animMove.start();
  189. // listener for animation end
  190. var that = this;
  191. animMove.addListener("finish", function()
  192. {
  193. // switch back
  194. that.__switchParent(that.__ghost, element);
  195. // reset the border
  196. qx.bom.element.Style.reset(element, "border");
  197. // remove ghost
  198. that.__ghost.parentNode.removeChild(that.__ghost);
  199. // check if the box is already the active one
  200. if (activeBox.isActive())
  201. {
  202. // if active call the apply to be sure about the state
  203. activeBox._applyActive(true);
  204. }
  205. else
  206. {
  207. // otherwise call the manager to make box active
  208. portal.box.Manager.getInstance().setActiveBox(activeBox);
  209. }
  210. });
  211. },
  212. /**
  213. * Listener method for "dragmove" events
  214. *
  215. * @param top {Integer} top coordinate of the drag event
  216. * @param left {Integer} left coordinate of the drag event
  217. * @return {void}
  218. */
  219. onDragMove : function(top, left)
  220. {
  221. // set the new left coord (if changed)
  222. if (this.__positions.left != left)
  223. {
  224. qx.bom.element.Style.set(this.__ghost, "left", left + "px");
  225. }
  226. // set the new top coord (if changed)
  227. if (this.__positions.top != top)
  228. {
  229. qx.bom.element.Style.set(this.__ghost, "top", top + "px");
  230. }
  231. // get the element of the dragged box and cache top position and height
  232. var activeBoxElement = this.getActiveBox().getElement();
  233. if (this.__activeBoxInfo.top == null)
  234. {
  235. this.__activeBoxInfo.top = qx.bom.element.Location.getTop(activeBoxElement);
  236. }
  237. if (this.__activeBoxInfo.height == null)
  238. {
  239. this.__activeBoxInfo.height = qx.bom.element.Dimension.getHeight(activeBoxElement);
  240. }
  241. // if the mouse pointer is moved over the placeholder nothing has to be done
  242. if (this.__groupBoxChange == false && top >= this.__activeBoxInfo.top &&
  243. top <= (this.__activeBoxInfo.top + this.__activeBoxInfo.height))
  244. {
  245. return;
  246. }
  247. var nextBox;
  248. /*
  249. * Special case: box is dragged to a new groupBox
  250. */
  251. if (this.__groupBoxChange)
  252. {
  253. // get the first element of the groupBox as start point
  254. nextBox = qx.dom.Hierarchy.getFirstDescendant(document.getElementById(this.__currentGroupBox));
  255. // iterate over all elements to check where to insert the placeholder element
  256. while (nextBox != null)
  257. {
  258. if (this.__checkInsert(true, top, activeBoxElement, nextBox))
  259. {
  260. this.__groupBoxChange = false;
  261. return;
  262. }
  263. nextBox = qx.dom.Hierarchy.getNextElementSibling(nextBox);
  264. }
  265. }
  266. else
  267. {
  268. nextBox = activeBoxElement;
  269. }
  270. // which direction to check?
  271. if (top - this.__positions.top > 0)
  272. {
  273. // down
  274. nextBox = qx.dom.Hierarchy.getNextElementSibling(nextBox);
  275. while(nextBox != null)
  276. {
  277. if (nextBox) {
  278. if (this.__checkInsert(true, top, activeBoxElement, nextBox))
  279. {
  280. return;
  281. }
  282. }
  283. nextBox = qx.dom.Hierarchy.getNextElementSibling(nextBox);
  284. }
  285. }
  286. else
  287. {
  288. // up
  289. var nextBox = qx.dom.Hierarchy.getPreviousElementSibling(nextBox);
  290. while(nextBox != null)
  291. {
  292. if (nextBox) {
  293. if (this.__checkInsert(false, top, activeBoxElement, nextBox))
  294. {
  295. return;
  296. }
  297. }
  298. nextBox = qx.dom.Hierarchy.getPreviousElementSibling(nextBox);
  299. }
  300. }
  301. // store the current offsets to check against at the next cycle
  302. this.__positions.left = left;
  303. this.__positions.top = top;
  304. },
  305. /**
  306. * Helper method to check where or if at all to insert the dragged box
  307. *
  308. * @param downwards {Boolean} drag direction
  309. * @param top {Integer} top coordinate
  310. * @param activeBoxElement {Element} element of the dragged box
  311. * @param nextBox {Element} element to check against (for possible insertion)
  312. * @return {Boolean} whether the element was inserted
  313. */
  314. __checkInsert : function(downwards, top, activeBoxElement, nextBox)
  315. {
  316. var nextBoxTop = qx.bom.element.Location.getTop(nextBox);
  317. var nextBoxPaddingTop = parseInt(qx.bom.element.Style.get(nextBox, "paddingTop"));
  318. var nextBoxPaddingBottom = parseInt(qx.bom.element.Style.get(nextBox, "paddingBottom"));
  319. var nextBoxHeight = qx.bom.element.Dimension.getContentHeight(nextBox) +
  320. nextBoxPaddingBottom + nextBoxPaddingTop;
  321. var sibling;
  322. if (downwards) {
  323. if (top >= (nextBoxTop + (nextBoxHeight / 3))) {
  324. sibling = qx.dom.Hierarchy.getNextElementSibling(nextBox);
  325. if (sibling != activeBoxElement || sibling == null) {
  326. qx.dom.Element.insertAfter(activeBoxElement, nextBox);
  327. this.__activeBoxInfo.top = qx.bom.element.Location.getTop(activeBoxElement);
  328. }
  329. return true;
  330. }
  331. }
  332. else {
  333. if (top <= (nextBoxTop + nextBoxHeight * 2 / 3)) {
  334. sibling = qx.dom.Hierarchy.getPreviousElementSibling(nextBox);
  335. if (sibling != activeBoxElement || sibling == null) {
  336. qx.dom.Element.insertBefore(activeBoxElement, nextBox);
  337. this.__activeBoxInfo.top = qx.bom.element.Location.getTop(activeBoxElement);
  338. }
  339. return true;
  340. }
  341. }
  342. return false;
  343. },
  344. /**
  345. * Checks over which groupBox the dragged box is and sets the current
  346. * groupBox.
  347. *
  348. * @param left {Integer} current x coordinate
  349. * @return {void}
  350. */
  351. checkGroupBox : function(left)
  352. {
  353. // check at first the current groupBox
  354. if (this.__groupBoxInfos[this.__currentGroupBox].left <= left && this.__groupBoxInfos[this.__currentGroupBox].right >= left)
  355. {
  356. return;
  357. }
  358. for (var info in this.__groupBoxInfos)
  359. {
  360. if (info != this.__currentGroupBox)
  361. {
  362. if (this.__groupBoxInfos[info].left <= left && this.__groupBoxInfos[info].right >= left)
  363. {
  364. this.__currentGroupBox = info;
  365. this.__groupBoxChange = true;
  366. return;
  367. }
  368. }
  369. }
  370. }
  371. },
  372. /* ******************************************************
  373. * DESTRUCT
  374. * ******************************************************/
  375. destruct : function()
  376. {
  377. this.__activeBoxInfo = this.__positions = this.__currentGroupBox =
  378. this.__currentBoxElement = this.__ghost = this.__groupBoxInfos = null;
  379. }
  380. });