rect.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global document */
  6. import global from '../../src/dom/global';
  7. import Rect from '../../src/dom/rect';
  8. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  9. testUtils.createSinonSandbox();
  10. describe( 'Rect', () => {
  11. let geometry;
  12. beforeEach( () => {
  13. geometry = {
  14. top: 10,
  15. right: 40,
  16. bottom: 30,
  17. left: 20,
  18. width: 20,
  19. height: 20
  20. };
  21. } );
  22. describe( 'constructor()', () => {
  23. it( 'should store passed object in #_obj property', () => {
  24. const obj = {};
  25. const rect = new Rect( obj );
  26. expect( rect._obj ).to.equal( obj );
  27. } );
  28. it( 'should accept HTMLElement', () => {
  29. const element = document.createElement( 'div' );
  30. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
  31. assertRect( new Rect( element ), geometry );
  32. } );
  33. it( 'should accept Range', () => {
  34. const range = document.createRange();
  35. testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( geometry );
  36. assertRect( new Rect( range ), geometry );
  37. } );
  38. it( 'should accept Rect', () => {
  39. const sourceRect = new Rect( geometry );
  40. const rect = new Rect( sourceRect );
  41. expect( rect ).to.not.equal( sourceRect );
  42. assertRect( rect, geometry );
  43. } );
  44. it( 'should accept ClientRect', () => {
  45. const clientRect = document.body.getBoundingClientRect();
  46. const { top, right, bottom, left, width, height } = clientRect;
  47. const rect = new Rect( clientRect );
  48. assertRect( rect, { top, right, bottom, left, width, height } );
  49. } );
  50. it( 'should accept geometry object', () => {
  51. assertRect( new Rect( geometry ), geometry );
  52. } );
  53. it( 'should copy the properties (Rect)', () => {
  54. const sourceGeometry = Object.assign( {}, geometry );
  55. const sourceRect = new Rect( geometry );
  56. const rect = new Rect( sourceRect );
  57. assertRect( rect, geometry );
  58. rect.top = 100;
  59. rect.width = 200;
  60. assertRect( sourceRect, sourceGeometry );
  61. } );
  62. it( 'should copy the properties (geomerty object)', () => {
  63. const sourceGeometry = Object.assign( {}, geometry );
  64. const rect = new Rect( geometry );
  65. assertRect( rect, geometry );
  66. rect.top = 100;
  67. rect.width = 200;
  68. assertRect( geometry, sourceGeometry );
  69. } );
  70. } );
  71. describe( 'clone()', () => {
  72. it( 'should clone the source rect', () => {
  73. const rect = new Rect( geometry );
  74. const clone = rect.clone();
  75. expect( clone ).to.be.instanceOf( Rect );
  76. expect( clone ).not.equal( rect );
  77. assertRect( clone, rect );
  78. } );
  79. it( 'should preserve #_obj', () => {
  80. const rect = new Rect( geometry );
  81. const clone = rect.clone();
  82. expect( clone._obj ).to.equal( rect._obj );
  83. assertRect( clone, rect );
  84. } );
  85. } );
  86. describe( 'moveTo()', () => {
  87. it( 'should return the source rect', () => {
  88. const rect = new Rect( geometry );
  89. const returned = rect.moveTo( 100, 200 );
  90. expect( returned ).to.equal( rect );
  91. } );
  92. it( 'should move the rect', () => {
  93. const rect = new Rect( geometry );
  94. rect.moveTo( 100, 200 );
  95. assertRect( rect, {
  96. top: 200,
  97. right: 120,
  98. bottom: 220,
  99. left: 100,
  100. width: 20,
  101. height: 20
  102. } );
  103. } );
  104. } );
  105. describe( 'moveBy()', () => {
  106. it( 'should return the source rect', () => {
  107. const rect = new Rect( geometry );
  108. const returned = rect.moveBy( 100, 200 );
  109. expect( returned ).to.equal( rect );
  110. } );
  111. it( 'should move the rect', () => {
  112. const rect = new Rect( geometry );
  113. rect.moveBy( 100, 200 );
  114. assertRect( rect, {
  115. top: 210,
  116. right: 140,
  117. bottom: 230,
  118. left: 120,
  119. width: 20,
  120. height: 20
  121. } );
  122. } );
  123. } );
  124. describe( 'getIntersection()', () => {
  125. it( 'should return a new rect', () => {
  126. const rect = new Rect( geometry );
  127. const insersect = rect.getIntersection( new Rect( geometry ) );
  128. expect( insersect ).to.be.instanceOf( Rect );
  129. expect( insersect ).to.not.equal( rect );
  130. } );
  131. it( 'should calculate the geometry (#1)', () => {
  132. const rectA = new Rect( {
  133. top: 0,
  134. right: 100,
  135. bottom: 100,
  136. left: 0,
  137. width: 100,
  138. height: 100
  139. } );
  140. const rectB = new Rect( {
  141. top: 50,
  142. right: 150,
  143. bottom: 150,
  144. left: 50,
  145. width: 100,
  146. height: 100
  147. } );
  148. const insersect = rectA.getIntersection( rectB );
  149. assertRect( insersect, {
  150. top: 50,
  151. right: 100,
  152. bottom: 100,
  153. left: 50,
  154. width: 50,
  155. height: 50
  156. } );
  157. } );
  158. it( 'should calculate the geometry (#2)', () => {
  159. const rectA = new Rect( {
  160. top: 0,
  161. right: 100,
  162. bottom: 100,
  163. left: 0,
  164. width: 100,
  165. height: 100
  166. } );
  167. const rectB = new Rect( {
  168. top: 0,
  169. right: 200,
  170. bottom: 100,
  171. left: 100,
  172. width: 100,
  173. height: 100
  174. } );
  175. const insersect = rectA.getIntersection( rectB );
  176. assertRect( insersect, {
  177. top: 0,
  178. right: 100,
  179. bottom: 100,
  180. left: 100,
  181. width: 0,
  182. height: 100
  183. } );
  184. } );
  185. it( 'should calculate the geometry (#3)', () => {
  186. const rectA = new Rect( {
  187. top: 0,
  188. right: 100,
  189. bottom: 100,
  190. left: 0,
  191. width: 100,
  192. height: 100
  193. } );
  194. const rectB = new Rect( {
  195. top: 100,
  196. right: 300,
  197. bottom: 200,
  198. left: 200,
  199. width: 100,
  200. height: 100
  201. } );
  202. expect( rectA.getIntersection( rectB ) ).to.be.null;
  203. } );
  204. } );
  205. describe( 'getIntersectionArea()', () => {
  206. it( 'should calculate the area (#1)', () => {
  207. const rectA = new Rect( {
  208. top: 0,
  209. right: 100,
  210. bottom: 100,
  211. left: 0,
  212. width: 100,
  213. height: 100
  214. } );
  215. const rectB = new Rect( {
  216. top: 50,
  217. right: 150,
  218. bottom: 150,
  219. left: 50,
  220. width: 100,
  221. height: 100
  222. } );
  223. expect( rectA.getIntersectionArea( rectB ) ).to.equal( 2500 );
  224. } );
  225. it( 'should calculate the area (#2)', () => {
  226. const rectA = new Rect( {
  227. top: 0,
  228. right: 100,
  229. bottom: 100,
  230. left: 0,
  231. width: 100,
  232. height: 100
  233. } );
  234. const rectB = new Rect( {
  235. top: 0,
  236. right: 200,
  237. bottom: 100,
  238. left: 100,
  239. width: 100,
  240. height: 100
  241. } );
  242. expect( rectA.getIntersectionArea( rectB ) ).to.equal( 0 );
  243. } );
  244. it( 'should calculate the area (#3)', () => {
  245. const rectA = new Rect( {
  246. top: 0,
  247. right: 100,
  248. bottom: 100,
  249. left: 0,
  250. width: 100,
  251. height: 100
  252. } );
  253. const rectB = new Rect( {
  254. top: 100,
  255. right: 300,
  256. bottom: 200,
  257. left: 200,
  258. width: 100,
  259. height: 100
  260. } );
  261. expect( rectA.getIntersectionArea( rectB ) ).to.equal( 0 );
  262. } );
  263. } );
  264. describe( 'getArea()', () => {
  265. it( 'should calculate the area', () => {
  266. const rect = new Rect( {
  267. width: 100,
  268. height: 50
  269. } );
  270. expect( rect.getArea() ).to.equal( 5000 );
  271. } );
  272. } );
  273. describe( 'getVisible()', () => {
  274. let element, range, ancestorA, ancestorB;
  275. beforeEach( () => {
  276. element = document.createElement( 'div' );
  277. range = document.createRange();
  278. ancestorA = document.createElement( 'div' );
  279. ancestorB = document.createElement( 'div' );
  280. ancestorA.append( element );
  281. ancestorB.append( ancestorA );
  282. document.body.appendChild( ancestorB );
  283. } );
  284. afterEach( () => {
  285. ancestorA.remove();
  286. ancestorB.remove();
  287. } );
  288. it( 'should return a new rect', () => {
  289. const rect = new Rect( {} );
  290. const visible = rect.getVisible();
  291. expect( visible ).to.not.equal( rect );
  292. } );
  293. it( 'should not fail when the rect is for document#body', () => {
  294. testUtils.sinon.stub( document.body, 'getBoundingClientRect' ).returns( {
  295. top: 0,
  296. right: 100,
  297. bottom: 100,
  298. left: 0,
  299. width: 100,
  300. height: 100
  301. } );
  302. assertRect( new Rect( document.body ).getVisible(), {
  303. top: 0,
  304. right: 100,
  305. bottom: 100,
  306. left: 0,
  307. width: 100,
  308. height: 100
  309. } );
  310. } );
  311. it( 'should return the visible rect (HTMLElement), partially cropped', () => {
  312. ancestorA.style.overflow = 'scroll';
  313. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  314. top: 0,
  315. right: 100,
  316. bottom: 100,
  317. left: 0,
  318. width: 100,
  319. height: 100
  320. } );
  321. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  322. top: 50,
  323. right: 150,
  324. bottom: 150,
  325. left: 50,
  326. width: 100,
  327. height: 100
  328. } );
  329. assertRect( new Rect( element ).getVisible(), {
  330. top: 50,
  331. right: 100,
  332. bottom: 100,
  333. left: 50,
  334. width: 50,
  335. height: 50
  336. } );
  337. } );
  338. it( 'should return the visible rect (HTMLElement), fully visible', () => {
  339. ancestorA.style.overflow = 'scroll';
  340. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  341. top: 0,
  342. right: 100,
  343. bottom: 100,
  344. left: 0,
  345. width: 100,
  346. height: 100
  347. } );
  348. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  349. top: 0,
  350. right: 150,
  351. bottom: 150,
  352. left: 0,
  353. width: 150,
  354. height: 150
  355. } );
  356. assertRect( new Rect( element ).getVisible(), {
  357. top: 0,
  358. right: 100,
  359. bottom: 100,
  360. left: 0,
  361. width: 100,
  362. height: 100
  363. } );
  364. } );
  365. it( 'should return the visible rect (HTMLElement), partially cropped, deep ancestor overflow', () => {
  366. ancestorB.style.overflow = 'scroll';
  367. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  368. top: 0,
  369. right: 100,
  370. bottom: 100,
  371. left: 0,
  372. width: 100,
  373. height: 100
  374. } );
  375. testUtils.sinon.stub( ancestorB, 'getBoundingClientRect' ).returns( {
  376. top: 50,
  377. right: 150,
  378. bottom: 150,
  379. left: 50,
  380. width: 100,
  381. height: 100
  382. } );
  383. assertRect( new Rect( element ).getVisible(), {
  384. top: 50,
  385. right: 100,
  386. bottom: 100,
  387. left: 50,
  388. width: 50,
  389. height: 50
  390. } );
  391. } );
  392. it( 'should return the visible rect (Range), partially cropped', () => {
  393. range.setStart( ancestorA, 0 );
  394. ancestorA.style.overflow = 'scroll';
  395. testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( {
  396. top: 0,
  397. right: 100,
  398. bottom: 100,
  399. left: 0,
  400. width: 100,
  401. height: 100
  402. } );
  403. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  404. top: 50,
  405. right: 150,
  406. bottom: 150,
  407. left: 50,
  408. width: 100,
  409. height: 100
  410. } );
  411. assertRect( new Rect( range ).getVisible(), {
  412. top: 50,
  413. right: 100,
  414. bottom: 100,
  415. left: 50,
  416. width: 50,
  417. height: 50
  418. } );
  419. } );
  420. it( 'should return null if there\'s no visible rect', () => {
  421. ancestorA.style.overflow = 'scroll';
  422. testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
  423. top: 0,
  424. right: 100,
  425. bottom: 100,
  426. left: 0,
  427. width: 100,
  428. height: 100
  429. } );
  430. testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
  431. top: 150,
  432. right: 200,
  433. bottom: 200,
  434. left: 150,
  435. width: 50,
  436. height: 50
  437. } );
  438. expect( new Rect( element ).getVisible() ).to.equal( null );
  439. } );
  440. } );
  441. describe( 'getViewportRect()', () => {
  442. it( 'should reaturn a rect', () => {
  443. expect( Rect.getViewportRect() ).to.be.instanceOf( Rect );
  444. } );
  445. it( 'should return the viewport\'s rect', () => {
  446. testUtils.sinon.stub( global, 'window', {
  447. innerWidth: 1000,
  448. innerHeight: 500,
  449. scrollX: 100,
  450. scrollY: 200
  451. } );
  452. assertRect( Rect.getViewportRect(), {
  453. top: 0,
  454. right: 1000,
  455. bottom: 500,
  456. left: 0,
  457. width: 1000,
  458. height: 500
  459. } );
  460. } );
  461. } );
  462. } );
  463. function assertRect( rect, expected ) {
  464. expect( rect ).to.deep.equal( expected );
  465. }