abstractdragdrop_test.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.fx.AbstractDragDropTest');
  15. goog.setTestOnly('goog.fx.AbstractDragDropTest');
  16. goog.require('goog.array');
  17. goog.require('goog.dom');
  18. goog.require('goog.dom.TagName');
  19. goog.require('goog.events');
  20. goog.require('goog.events.EventType');
  21. goog.require('goog.functions');
  22. goog.require('goog.fx.AbstractDragDrop');
  23. goog.require('goog.fx.DragDropItem');
  24. goog.require('goog.math.Box');
  25. goog.require('goog.math.Coordinate');
  26. goog.require('goog.style');
  27. goog.require('goog.testing.events');
  28. goog.require('goog.testing.events.Event');
  29. goog.require('goog.testing.jsunit');
  30. var targets = [
  31. {box_: new goog.math.Box(0, 3, 1, 1)}, {box_: new goog.math.Box(0, 7, 2, 6)},
  32. {box_: new goog.math.Box(2, 2, 3, 1)}, {box_: new goog.math.Box(4, 1, 6, 1)},
  33. {box_: new goog.math.Box(4, 9, 7, 6)},
  34. {box_: new goog.math.Box(9, 9, 10, 1)}
  35. ];
  36. var targets2 = [
  37. {box_: new goog.math.Box(10, 50, 20, 10)},
  38. {box_: new goog.math.Box(20, 50, 30, 10)},
  39. {box_: new goog.math.Box(60, 50, 70, 10)},
  40. {box_: new goog.math.Box(70, 50, 80, 10)}
  41. ];
  42. var targets3 = [
  43. {box_: new goog.math.Box(0, 4, 1, 1)}, {box_: new goog.math.Box(1, 6, 4, 5)},
  44. {box_: new goog.math.Box(5, 5, 6, 2)}, {box_: new goog.math.Box(2, 1, 5, 0)}
  45. ];
  46. /**
  47. * Test the utility function which tells how two one dimensional ranges
  48. * overlap.
  49. */
  50. function testRangeOverlap() {
  51. assertEquals(RangeOverlap.LEFT, rangeOverlap(1, 2, 3, 4));
  52. assertEquals(RangeOverlap.LEFT, rangeOverlap(2, 3, 3, 4));
  53. assertEquals(RangeOverlap.LEFT_IN, rangeOverlap(1, 3, 2, 4));
  54. assertEquals(RangeOverlap.IN, rangeOverlap(1, 3, 1, 4));
  55. assertEquals(RangeOverlap.IN, rangeOverlap(2, 3, 1, 4));
  56. assertEquals(RangeOverlap.IN, rangeOverlap(3, 4, 1, 4));
  57. assertEquals(RangeOverlap.RIGHT_IN, rangeOverlap(2, 4, 1, 3));
  58. assertEquals(RangeOverlap.RIGHT, rangeOverlap(2, 3, 1, 2));
  59. assertEquals(RangeOverlap.RIGHT, rangeOverlap(3, 4, 1, 2));
  60. assertEquals(RangeOverlap.CONTAINS, rangeOverlap(1, 4, 2, 3));
  61. }
  62. /**
  63. * An enum describing how two ranges overlap (non-symmetrical relation).
  64. * @enum {number}
  65. */
  66. RangeOverlap = {
  67. LEFT: 1, // First range is placed to the left of the second.
  68. LEFT_IN: 2, // First range overlaps on the left side of the second.
  69. IN: 3, // First range is completely contained in the second.
  70. RIGHT_IN: 4, // First range overlaps on the right side of the second.
  71. RIGHT: 5, // First range is placed to the right side of the second.
  72. CONTAINS: 6 // First range contains the second.
  73. };
  74. /**
  75. * Computes how two one dimensional ranges overlap.
  76. *
  77. * @param {number} left1 Left inclusive bound of the first range.
  78. * @param {number} right1 Right exclusive bound of the first range.
  79. * @param {number} left2 Left inclusive bound of the second range.
  80. * @param {number} right2 Right exclusive bound of the second range.
  81. * @return {RangeOverlap} The enum value describing the type of the overlap.
  82. */
  83. function rangeOverlap(left1, right1, left2, right2) {
  84. if (right1 <= left2) return RangeOverlap.LEFT;
  85. if (left1 >= right2) return RangeOverlap.RIGHT;
  86. var leftIn = left1 >= left2;
  87. var rightIn = right1 <= right2;
  88. if (leftIn && rightIn) return RangeOverlap.IN;
  89. if (leftIn) return RangeOverlap.RIGHT_IN;
  90. if (rightIn) return RangeOverlap.LEFT_IN;
  91. return RangeOverlap.CONTAINS;
  92. }
  93. /**
  94. * Tells whether two boxes overlap.
  95. *
  96. * @param {goog.math.Box} box1 First box in question.
  97. * @param {goog.math.Box} box2 Second box in question.
  98. * @return {boolean} Whether boxes overlap in any way.
  99. */
  100. function boxOverlaps(box1, box2) {
  101. var horizontalOverlap =
  102. rangeOverlap(box1.left, box1.right, box2.left, box2.right);
  103. var verticalOverlap =
  104. rangeOverlap(box1.top, box1.bottom, box2.top, box2.bottom);
  105. return horizontalOverlap != RangeOverlap.LEFT &&
  106. horizontalOverlap != RangeOverlap.RIGHT &&
  107. verticalOverlap != RangeOverlap.LEFT &&
  108. verticalOverlap != RangeOverlap.RIGHT;
  109. }
  110. /**
  111. * Tests if the utility function to compute box overlapping functions properly.
  112. */
  113. function testBoxOverlaps() {
  114. // Overlapping tests.
  115. var box2 = new goog.math.Box(1, 4, 4, 1);
  116. // Corner overlaps.
  117. assertTrue('NW overlap', boxOverlaps(new goog.math.Box(0, 2, 2, 0), box2));
  118. assertTrue('NE overlap', boxOverlaps(new goog.math.Box(0, 5, 2, 3), box2));
  119. assertTrue('SE overlap', boxOverlaps(new goog.math.Box(3, 5, 5, 3), box2));
  120. assertTrue('SW overlap', boxOverlaps(new goog.math.Box(3, 2, 5, 0), box2));
  121. // Inside.
  122. assertTrue(
  123. 'Inside overlap', boxOverlaps(new goog.math.Box(2, 3, 3, 2), box2));
  124. // Around.
  125. assertTrue(
  126. 'Outside overlap', boxOverlaps(new goog.math.Box(0, 5, 5, 0), box2));
  127. // Edge overlaps.
  128. assertTrue('N overlap', boxOverlaps(new goog.math.Box(0, 3, 2, 2), box2));
  129. assertTrue('E overlap', boxOverlaps(new goog.math.Box(2, 5, 3, 3), box2));
  130. assertTrue('S overlap', boxOverlaps(new goog.math.Box(3, 3, 5, 2), box2));
  131. assertTrue('W overlap', boxOverlaps(new goog.math.Box(2, 2, 3, 0), box2));
  132. assertTrue('N-in overlap', boxOverlaps(new goog.math.Box(0, 5, 2, 0), box2));
  133. assertTrue('E-in overlap', boxOverlaps(new goog.math.Box(0, 5, 5, 3), box2));
  134. assertTrue('S-in overlap', boxOverlaps(new goog.math.Box(3, 5, 5, 0), box2));
  135. assertTrue('W-in overlap', boxOverlaps(new goog.math.Box(0, 2, 5, 0), box2));
  136. // Does not overlap.
  137. var box2 = new goog.math.Box(3, 6, 6, 3);
  138. // Along the edge - shorter.
  139. assertFalse(
  140. 'N-in no overlap', boxOverlaps(new goog.math.Box(1, 5, 2, 4), box2));
  141. assertFalse(
  142. 'E-in no overlap', boxOverlaps(new goog.math.Box(4, 8, 5, 7), box2));
  143. assertFalse(
  144. 'S-in no overlap', boxOverlaps(new goog.math.Box(7, 5, 8, 4), box2));
  145. assertFalse(
  146. 'N-in no overlap', boxOverlaps(new goog.math.Box(4, 2, 5, 1), box2));
  147. // By the corner.
  148. assertFalse(
  149. 'NE no overlap', boxOverlaps(new goog.math.Box(1, 8, 2, 7), box2));
  150. assertFalse(
  151. 'SE no overlap', boxOverlaps(new goog.math.Box(7, 8, 8, 7), box2));
  152. assertFalse(
  153. 'SW no overlap', boxOverlaps(new goog.math.Box(7, 2, 8, 1), box2));
  154. assertFalse(
  155. 'NW no overlap', boxOverlaps(new goog.math.Box(1, 2, 2, 1), box2));
  156. // Perpendicular to an edge.
  157. assertFalse(
  158. 'NNE no overlap', boxOverlaps(new goog.math.Box(1, 7, 2, 5), box2));
  159. assertFalse(
  160. 'NEE no overlap', boxOverlaps(new goog.math.Box(2, 8, 4, 7), box2));
  161. assertFalse(
  162. 'SEE no overlap', boxOverlaps(new goog.math.Box(5, 8, 7, 7), box2));
  163. assertFalse(
  164. 'SSE no overlap', boxOverlaps(new goog.math.Box(7, 7, 8, 5), box2));
  165. assertFalse(
  166. 'SSW no overlap', boxOverlaps(new goog.math.Box(7, 4, 8, 2), box2));
  167. assertFalse(
  168. 'SWW no overlap', boxOverlaps(new goog.math.Box(5, 2, 7, 1), box2));
  169. assertFalse(
  170. 'NWW no overlap', boxOverlaps(new goog.math.Box(2, 2, 4, 1), box2));
  171. assertFalse(
  172. 'NNW no overlap', boxOverlaps(new goog.math.Box(1, 4, 2, 2), box2));
  173. // Along the edge - longer.
  174. assertFalse('N no overlap', boxOverlaps(new goog.math.Box(0, 7, 1, 2), box2));
  175. assertFalse('E no overlap', boxOverlaps(new goog.math.Box(2, 9, 7, 8), box2));
  176. assertFalse('S no overlap', boxOverlaps(new goog.math.Box(8, 7, 9, 2), box2));
  177. assertFalse('W no overlap', boxOverlaps(new goog.math.Box(2, 1, 7, 0), box2));
  178. }
  179. /**
  180. * Checks whether a given box overlaps any of given DnD target boxes.
  181. *
  182. * @param {goog.math.Box} box The box to check.
  183. * @param {Array<Object>} targets The array of targets with boxes to check
  184. * if they overlap with the given box.
  185. * @return {boolean} Whether the box overlaps any of the target boxes.
  186. */
  187. function boxOverlapsTargets(box, targets) {
  188. return goog.array.some(
  189. targets, function(target) { return boxOverlaps(box, target.box_); });
  190. }
  191. function testMaybeCreateDummyTargetForPosition() {
  192. var testGroup = new goog.fx.AbstractDragDrop();
  193. testGroup.targetList_ = targets;
  194. testGroup.targetBox_ = new goog.math.Box(0, 9, 10, 1);
  195. var target = testGroup.maybeCreateDummyTargetForPosition_(3, 3);
  196. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  197. assertTrue(testGroup.isInside(3, 3, target.box_));
  198. target = testGroup.maybeCreateDummyTargetForPosition_(2, 4);
  199. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  200. assertTrue(testGroup.isInside(2, 4, target.box_));
  201. target = testGroup.maybeCreateDummyTargetForPosition_(2, 7);
  202. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  203. assertTrue(testGroup.isInside(2, 7, target.box_));
  204. testGroup.targetList_.push({box_: new goog.math.Box(5, 6, 6, 0)});
  205. target = testGroup.maybeCreateDummyTargetForPosition_(3, 3);
  206. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  207. assertTrue(testGroup.isInside(3, 3, target.box_));
  208. target = testGroup.maybeCreateDummyTargetForPosition_(2, 7);
  209. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  210. assertTrue(testGroup.isInside(2, 7, target.box_));
  211. target = testGroup.maybeCreateDummyTargetForPosition_(6, 3);
  212. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  213. assertTrue(testGroup.isInside(6, 3, target.box_));
  214. target = testGroup.maybeCreateDummyTargetForPosition_(0, 3);
  215. assertNull(target);
  216. target = testGroup.maybeCreateDummyTargetForPosition_(9, 0);
  217. assertNull(target);
  218. }
  219. function testMaybeCreateDummyTargetForPosition2() {
  220. var testGroup = new goog.fx.AbstractDragDrop();
  221. testGroup.targetList_ = targets2;
  222. testGroup.targetBox_ = new goog.math.Box(10, 50, 80, 10);
  223. var target = testGroup.maybeCreateDummyTargetForPosition_(30, 40);
  224. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  225. assertTrue(testGroup.isInside(30, 40, target.box_));
  226. target = testGroup.maybeCreateDummyTargetForPosition_(45, 40);
  227. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  228. assertTrue(testGroup.isInside(45, 40, target.box_));
  229. testGroup.targetList_.push({box_: new goog.math.Box(40, 50, 50, 40)});
  230. target = testGroup.maybeCreateDummyTargetForPosition_(30, 40);
  231. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  232. target = testGroup.maybeCreateDummyTargetForPosition_(45, 35);
  233. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  234. }
  235. function testMaybeCreateDummyTargetForPosition3BoxHasDecentSize() {
  236. var testGroup = new goog.fx.AbstractDragDrop();
  237. testGroup.targetList_ = targets3;
  238. testGroup.targetBox_ = new goog.math.Box(0, 6, 6, 0);
  239. var target = testGroup.maybeCreateDummyTargetForPosition_(3, 3);
  240. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  241. assertTrue(testGroup.isInside(3, 3, target.box_));
  242. assertEquals('(1t, 5r, 5b, 1l)', target.box_.toString());
  243. }
  244. function testMaybeCreateDummyTargetForPosition4() {
  245. var testGroup = new goog.fx.AbstractDragDrop();
  246. testGroup.targetList_ = targets;
  247. testGroup.targetBox_ = new goog.math.Box(0, 9, 10, 1);
  248. for (var x = testGroup.targetBox_.left; x < testGroup.targetBox_.right; x++) {
  249. for (var y = testGroup.targetBox_.top; y < testGroup.targetBox_.bottom;
  250. y++) {
  251. var inRealTarget = false;
  252. for (var i = 0; i < testGroup.targetList_.length; i++) {
  253. if (testGroup.isInside(x, y, testGroup.targetList_[i].box_)) {
  254. inRealTarget = true;
  255. break;
  256. }
  257. }
  258. if (!inRealTarget) {
  259. var target = testGroup.maybeCreateDummyTargetForPosition_(x, y);
  260. if (target) {
  261. assertFalse(
  262. 'Fake target for point(' + x + ',' + y + ') should ' +
  263. 'not overlap any real targets.',
  264. boxOverlapsTargets(target.box_, testGroup.targetList_));
  265. assertTrue(testGroup.isInside(x, y, target.box_));
  266. }
  267. }
  268. }
  269. }
  270. }
  271. function testMaybeCreateDummyTargetForPosition_NegativePositions() {
  272. var negTargets = [
  273. {box_: new goog.math.Box(-20, 10, -5, 1)},
  274. {box_: new goog.math.Box(20, 10, 30, 1)}
  275. ];
  276. var testGroup = new goog.fx.AbstractDragDrop();
  277. testGroup.targetList_ = negTargets;
  278. testGroup.targetBox_ = new goog.math.Box(-20, 10, 30, 1);
  279. var target = testGroup.maybeCreateDummyTargetForPosition_(1, 5);
  280. assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
  281. assertTrue(testGroup.isInside(1, 5, target.box_));
  282. }
  283. function testMaybeCreateDummyTargetOutsideScrollableContainer() {
  284. var targets = [
  285. {box_: new goog.math.Box(0, 3, 10, 1)},
  286. {box_: new goog.math.Box(20, 3, 30, 1)}
  287. ];
  288. var target = targets[0];
  289. var testGroup = new goog.fx.AbstractDragDrop();
  290. testGroup.targetList_ = targets;
  291. testGroup.targetBox_ = new goog.math.Box(0, 3, 30, 1);
  292. testGroup.addScrollableContainer(document.getElementById('container1'));
  293. var container = testGroup.scrollableContainers_[0];
  294. container.containedTargets_.push(target);
  295. container.box_ = new goog.math.Box(0, 3, 5, 1); // shorter than target
  296. target.scrollableContainer_ = container;
  297. // mouse cursor is below scrollable target but not the actual target
  298. var dummyTarget = testGroup.maybeCreateDummyTargetForPosition_(2, 7);
  299. // dummy target should not overlap the scrollable container
  300. assertFalse(boxOverlaps(dummyTarget.box_, container.box_));
  301. // but should overlap the actual target, since not all of it is visible
  302. assertTrue(boxOverlaps(dummyTarget.box_, target.box_));
  303. }
  304. function testMaybeCreateDummyTargetInsideScrollableContainer() {
  305. var targets = [
  306. {box_: new goog.math.Box(0, 3, 10, 1)},
  307. {box_: new goog.math.Box(20, 3, 30, 1)}
  308. ];
  309. var target = targets[0];
  310. var testGroup = new goog.fx.AbstractDragDrop();
  311. testGroup.targetList_ = targets;
  312. testGroup.targetBox_ = new goog.math.Box(0, 3, 30, 1);
  313. testGroup.addScrollableContainer(document.getElementById('container1'));
  314. var container = testGroup.scrollableContainers_[0];
  315. container.containedTargets_.push(target);
  316. container.box_ = new goog.math.Box(0, 3, 20, 1); // longer than target
  317. target.scrollableContainer_ = container;
  318. // mouse cursor is below both the scrollable and the actual target
  319. var dummyTarget = testGroup.maybeCreateDummyTargetForPosition_(2, 15);
  320. // dummy target should overlap the scrollable container
  321. assertTrue(boxOverlaps(dummyTarget.box_, container.box_));
  322. // but not overlap the actual target
  323. assertFalse(boxOverlaps(dummyTarget.box_, target.box_));
  324. }
  325. function testCalculateTargetBox() {
  326. var testGroup = new goog.fx.AbstractDragDrop();
  327. testGroup.targetList_ = [];
  328. goog.array.forEach(targets, function(target) {
  329. testGroup.targetList_.push(target);
  330. testGroup.calculateTargetBox_(target.box_);
  331. });
  332. assertTrue(
  333. goog.math.Box.equals(
  334. testGroup.targetBox_, new goog.math.Box(0, 9, 10, 1)));
  335. testGroup = new goog.fx.AbstractDragDrop();
  336. testGroup.targetList_ = [];
  337. goog.array.forEach(targets2, function(target) {
  338. testGroup.targetList_.push(target);
  339. testGroup.calculateTargetBox_(target.box_);
  340. });
  341. assertTrue(
  342. goog.math.Box.equals(
  343. testGroup.targetBox_, new goog.math.Box(10, 50, 80, 10)));
  344. testGroup = new goog.fx.AbstractDragDrop();
  345. testGroup.targetList_ = [];
  346. goog.array.forEach(targets3, function(target) {
  347. testGroup.targetList_.push(target);
  348. testGroup.calculateTargetBox_(target.box_);
  349. });
  350. assertTrue(
  351. goog.math.Box.equals(
  352. testGroup.targetBox_, new goog.math.Box(0, 6, 6, 0)));
  353. }
  354. function testIsInside() {
  355. var add = new goog.fx.AbstractDragDrop();
  356. // The box in question.
  357. // 10,20+++++20,20
  358. // + |
  359. // 10,30-----20,30
  360. var box = new goog.math.Box(20, 20, 30, 10);
  361. assertTrue(
  362. 'A point somewhere in the middle of the box should be inside.',
  363. add.isInside(15, 25, box));
  364. assertTrue(
  365. 'A point in top-left corner should be inside the box.',
  366. add.isInside(10, 20, box));
  367. assertTrue(
  368. 'A point on top border should be inside the box.',
  369. add.isInside(15, 20, box));
  370. assertFalse(
  371. 'A point in top-right corner should be outside the box.',
  372. add.isInside(20, 20, box));
  373. assertFalse(
  374. 'A point on right border should be outside the box.',
  375. add.isInside(20, 25, box));
  376. assertFalse(
  377. 'A point in bottom-right corner should be outside the box.',
  378. add.isInside(20, 30, box));
  379. assertFalse(
  380. 'A point on bottom border should be outside the box.',
  381. add.isInside(15, 30, box));
  382. assertFalse(
  383. 'A point in bottom-left corner should be outside the box.',
  384. add.isInside(10, 30, box));
  385. assertTrue(
  386. 'A point on left border should be inside the box.',
  387. add.isInside(10, 25, box));
  388. add.dispose();
  389. }
  390. function testAddingRemovingScrollableContainers() {
  391. var group = new goog.fx.AbstractDragDrop();
  392. var el1 = goog.dom.createElement(goog.dom.TagName.DIV);
  393. var el2 = goog.dom.createElement(goog.dom.TagName.DIV);
  394. assertEquals(0, group.scrollableContainers_.length);
  395. group.addScrollableContainer(el1);
  396. assertEquals(1, group.scrollableContainers_.length);
  397. group.addScrollableContainer(el2);
  398. assertEquals(2, group.scrollableContainers_.length);
  399. group.removeAllScrollableContainers();
  400. assertEquals(0, group.scrollableContainers_.length);
  401. }
  402. function testScrollableContainersCalculation() {
  403. var group = new goog.fx.AbstractDragDrop();
  404. var target = new goog.fx.AbstractDragDrop();
  405. group.addTarget(target);
  406. group.addScrollableContainer(document.getElementById('container1'));
  407. var container = group.scrollableContainers_[0];
  408. var item1 = new goog.fx.DragDropItem(document.getElementById('child1'));
  409. var item2 = new goog.fx.DragDropItem(document.getElementById('child2'));
  410. target.items_.push(item1);
  411. group.recalculateDragTargets();
  412. group.recalculateScrollableContainers();
  413. assertEquals(1, container.containedTargets_.length);
  414. assertEquals(container, group.targetList_[0].scrollableContainer_);
  415. target.items_.push(item2);
  416. group.recalculateDragTargets();
  417. assertEquals(1, container.containedTargets_.length);
  418. assertNull(group.targetList_[0].scrollableContainer_);
  419. group.recalculateScrollableContainers();
  420. assertEquals(2, container.containedTargets_.length);
  421. assertEquals(container, group.targetList_[1].scrollableContainer_);
  422. }
  423. function testMouseDownEventDefaultAction() {
  424. var group = new goog.fx.AbstractDragDrop();
  425. var target = new goog.fx.AbstractDragDrop();
  426. group.addTarget(target);
  427. var item1 = new goog.fx.DragDropItem(document.getElementById('child1'));
  428. group.items_.push(item1);
  429. item1.setParent(group);
  430. group.init();
  431. var mousedownDefaultPrevented =
  432. !goog.testing.events.fireMouseDownEvent(item1.element);
  433. assertFalse(
  434. 'Default action of mousedown event should not be cancelled.',
  435. mousedownDefaultPrevented);
  436. }
  437. // See http://b/7494613.
  438. function testMouseUpOutsideElement() {
  439. var group = new goog.fx.AbstractDragDrop();
  440. var target = new goog.fx.AbstractDragDrop();
  441. group.addTarget(target);
  442. var item1 = new goog.fx.DragDropItem(document.getElementById('child1'));
  443. group.items_.push(item1);
  444. item1.setParent(group);
  445. group.init();
  446. group.startDrag = goog.functions.error('startDrag should not be called.');
  447. goog.testing.events.fireMouseDownEvent(item1.element);
  448. goog.testing.events.fireMouseUpEvent(item1.element.parentNode);
  449. // This should have no effect (not start a drag) since the previous event
  450. // should have cleared the listeners.
  451. goog.testing.events.fireMouseOutEvent(item1.element);
  452. group.dispose();
  453. target.dispose();
  454. }
  455. function testScrollBeforeMoveDrag() {
  456. var group = new goog.fx.AbstractDragDrop();
  457. var target = new goog.fx.AbstractDragDrop();
  458. group.addTarget(target);
  459. var container = document.getElementById('container1');
  460. group.addScrollableContainer(container);
  461. var childEl = document.getElementById('child1');
  462. var item = new goog.fx.DragDropItem(childEl);
  463. item.currentDragElement_ = childEl;
  464. target.items_.push(item);
  465. group.recalculateDragTargets();
  466. group.recalculateScrollableContainers();
  467. // Simulare starting a drag.
  468. var moveEvent = {
  469. 'clientX': 8,
  470. 'clientY': 10,
  471. 'type': goog.events.EventType.MOUSEMOVE,
  472. 'relatedTarget': childEl,
  473. 'preventDefault': function() {}
  474. };
  475. group.startDrag(moveEvent, item);
  476. // Simulate scrolling before the first move drag event.
  477. var scrollEvent = {'target': container};
  478. assertNotThrows(goog.bind(group.containerScrollHandler_, group, scrollEvent));
  479. }
  480. function testMouseMove_mouseOutBeforeThreshold() {
  481. // Setup dragdrop and item
  482. var itemEl = goog.dom.createElement(goog.dom.TagName.DIV);
  483. var childEl = goog.dom.createElement(goog.dom.TagName.DIV);
  484. itemEl.appendChild(childEl);
  485. var add = new goog.fx.AbstractDragDrop();
  486. var item = new goog.fx.DragDropItem(itemEl);
  487. item.setParent(add);
  488. add.items_.push(item);
  489. // Simulate maybeStartDrag
  490. item.startPosition_ = new goog.math.Coordinate(10, 10);
  491. item.currentDragElement_ = itemEl;
  492. // Test
  493. var draggedItem = null;
  494. add.startDrag = function(event, item) { draggedItem = item; };
  495. var event =
  496. new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, childEl);
  497. // Drag distance is only 2.
  498. event.clientX = 8;
  499. event.clientY = 10;
  500. item.mouseMove_(event);
  501. assertEquals(
  502. 'DragStart should not be fired for mouseout on child element.', null,
  503. draggedItem);
  504. var event =
  505. new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, itemEl);
  506. // Drag distance is only 2.
  507. event.clientX = 8;
  508. event.clientY = 10;
  509. item.mouseMove_(event);
  510. assertEquals(
  511. 'DragStart should be fired for mouseout on main element.', item,
  512. draggedItem);
  513. }
  514. function testGetDragElementPosition() {
  515. var testGroup = new goog.fx.AbstractDragDrop();
  516. var sourceEl = goog.dom.createElement(goog.dom.TagName.DIV);
  517. document.body.appendChild(sourceEl);
  518. var pageOffset = goog.style.getPageOffset(sourceEl);
  519. var pos = testGroup.getDragElementPosition(sourceEl);
  520. assertEquals(
  521. 'Drag element position should be source element page offset',
  522. pageOffset.x, pos.x);
  523. assertEquals(
  524. 'Drag element position should be source element page offset',
  525. pageOffset.y, pos.y);
  526. sourceEl.style.marginLeft = '5px';
  527. sourceEl.style.marginTop = '7px';
  528. pageOffset = goog.style.getPageOffset(sourceEl);
  529. pos = testGroup.getDragElementPosition(sourceEl);
  530. assertEquals(
  531. 'Drag element position should be adjusted for source element ' +
  532. 'margins',
  533. pageOffset.x - 10, pos.x);
  534. assertEquals(
  535. 'Drag element position should be adjusted for source element ' +
  536. 'margins',
  537. pageOffset.y - 14, pos.y);
  538. }
  539. function testDragEndEvent() {
  540. function testDragEndEventInternal(shouldContainItemData) {
  541. var testGroup = new goog.fx.AbstractDragDrop();
  542. var childEl = document.getElementById('child1');
  543. var item = new goog.fx.DragDropItem(childEl);
  544. item.currentDragElement_ = childEl;
  545. testGroup.items_.push(item);
  546. testGroup.recalculateDragTargets();
  547. // Simulate starting a drag
  548. var startEvent = {
  549. 'clientX': 0,
  550. 'clientY': 0,
  551. 'type': goog.events.EventType.MOUSEMOVE,
  552. 'relatedTarget': childEl,
  553. 'preventDefault': function() {}
  554. };
  555. testGroup.startDrag(startEvent, item);
  556. testGroup.activeTarget_ = new goog.fx.ActiveDropTarget_(
  557. new goog.math.Box(0, 0, 0, 0), testGroup, item, childEl);
  558. goog.events.listen(
  559. testGroup, goog.fx.AbstractDragDrop.EventType.DRAGEND, function(event) {
  560. if (shouldContainItemData) {
  561. assertEquals(
  562. 'The drag end event should contain a drop target', testGroup,
  563. event.dropTarget);
  564. assertEquals(
  565. 'The drag end event should contain a drop target item', item,
  566. event.dropTargetItem);
  567. assertEquals(
  568. 'The drag end event should contain a drop target element',
  569. childEl, event.dropTargetElement);
  570. } else {
  571. assertUndefined(
  572. 'The drag end event shouldn\'t contain a drop target',
  573. event.dropTarget);
  574. assertUndefined(
  575. 'The drag end event shouldn\'t contain a drop target item',
  576. event.dropTargetItem);
  577. assertUndefined(
  578. 'The drag end event shouldn\'t contain a drop target element',
  579. event.dropTargetElement);
  580. }
  581. });
  582. testGroup.endDrag(
  583. {'clientX': 0, 'clientY': 0, 'dragCanceled': !shouldContainItemData});
  584. testGroup.dispose();
  585. item.dispose();
  586. }
  587. testDragEndEventInternal(false);
  588. testDragEndEventInternal(true);
  589. }
  590. function testDropEventHasBrowserEvent() {
  591. var testGroup = new goog.fx.AbstractDragDrop();
  592. var childEl = document.getElementById('child1');
  593. var item = new goog.fx.DragDropItem(childEl);
  594. item.currentDragElement_ = childEl;
  595. testGroup.items_.push(item);
  596. testGroup.recalculateDragTargets();
  597. // Simulate starting a drag
  598. var startBrowserEvent = {
  599. 'clientX': 0,
  600. 'clientY': 0,
  601. 'type': goog.events.EventType.MOUSEMOVE,
  602. 'relatedTarget': childEl,
  603. 'preventDefault': function() {},
  604. };
  605. testGroup.startDrag(startBrowserEvent, item);
  606. testGroup.activeTarget_ = new goog.fx.ActiveDropTarget_(
  607. new goog.math.Box(0, 0, 0, 0), testGroup, item, childEl);
  608. var endBrowserEvent = {
  609. 'clientX': 0,
  610. 'clientY': 0,
  611. 'type': goog.events.EventType.MOUSEUP,
  612. 'ctrlKey': false,
  613. 'altKey': true
  614. };
  615. goog.events.listen(
  616. testGroup, goog.fx.AbstractDragDrop.EventType.DROP, function(event) {
  617. var browserEvent = event.browserEvent;
  618. assertEquals(
  619. 'The drop event should contain the browser event', endBrowserEvent,
  620. browserEvent);
  621. });
  622. testGroup.endDrag({
  623. 'clientX': 0,
  624. 'clientY': 0,
  625. 'dragCanceled': false,
  626. 'browserEvent': endBrowserEvent
  627. });
  628. testGroup.dispose();
  629. item.dispose();
  630. }
  631. // Helper function for manual debugging.
  632. function drawTargets(targets, multiplier) {
  633. var colors = ['green', 'blue', 'red', 'lime', 'pink', 'silver', 'orange'];
  634. var cont = document.getElementById('cont');
  635. cont.innerHTML = '';
  636. for (var i = 0; i < targets.length; i++) {
  637. var box = targets[i].box_;
  638. var el = goog.dom.createElement(goog.dom.TagName.DIV);
  639. el.style.top = (box.top * multiplier) + 'px';
  640. el.style.left = (box.left * multiplier) + 'px';
  641. el.style.width = ((box.right - box.left) * multiplier) + 'px';
  642. el.style.height = ((box.bottom - box.top) * multiplier) + 'px';
  643. el.style.backgroundColor = colors[i];
  644. cont.appendChild(el);
  645. }
  646. }