common.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. export function sleep(delay = 100) {
  2. return Promise.resolve({
  3. then: function(resolve) {
  4. setTimeout(resolve, delay);
  5. }
  6. });
  7. };
  8. export function hot() {
  9. return spec().$container.data('handsontable');
  10. };
  11. export function handsontable(options) {
  12. var currentSpec = spec();
  13. currentSpec.$container.handsontable(options);
  14. currentSpec.$container[0].focus(); // otherwise TextEditor tests do not pass in IE8
  15. return currentSpec.$container.data('handsontable');
  16. };
  17. /**
  18. * As for v. 0.11 the only scrolling method is native scroll, which creates copies of main htCore table inside of the container.
  19. * Therefore, simple $(".htCore") will return more than one object. Most of the time, you're interested in the original
  20. * htCore, not the copies made by native scroll.
  21. *
  22. * This method returns the original htCore object
  23. *
  24. * @returns {jqObject} reference to the original htCore
  25. */
  26. export function getHtCore() {
  27. return spec().$container.find('.htCore').first();
  28. };
  29. export function getTopClone() {
  30. return spec().$container.find('.ht_clone_top');
  31. };
  32. export function getTopLeftClone() {
  33. return spec().$container.find('.ht_clone_top_left_corner');
  34. };
  35. // for compatybility
  36. // var getCornerClone = getTopLeftClone;
  37. export function getLeftClone() {
  38. return spec().$container.find('.ht_clone_left');
  39. };
  40. export function getBottomClone() {
  41. return spec().$container.find('.ht_clone_bottom');
  42. };
  43. export function getBottomLeftClone() {
  44. return spec().$container.find('.ht_clone_bottom_left_corner');
  45. };
  46. // Rename me to countTD
  47. export function countCells() {
  48. return getHtCore().find('tbody td').length;
  49. };
  50. export function isEditorVisible() {
  51. return !!(keyProxy().is(':visible') && keyProxy().parent().is(':visible') && !keyProxy().parent().is('.htHidden'));
  52. };
  53. export function isFillHandleVisible() {
  54. return !!spec().$container.find('.wtBorder.corner:visible').length;
  55. };
  56. export function getCorrespondingOverlay(cell, container) {
  57. var overlay = $(cell).parents('.handsontable');
  58. if (overlay[0] == container[0]) {
  59. return $('.ht_master');
  60. }
  61. return $(overlay[0]);
  62. };
  63. /**
  64. * Shows context menu
  65. */
  66. export function contextMenu(cell) {
  67. var hot = spec().$container.data('handsontable');
  68. var selected = hot.getSelected();
  69. if (!selected) {
  70. hot.selectCell(0, 0);
  71. selected = hot.getSelected();
  72. }
  73. if (!cell) {
  74. cell = getCell(selected[0], selected[1]);
  75. }
  76. var cellOffset = $(cell).offset();
  77. $(cell).simulate('contextmenu', {
  78. clientX: cellOffset.left - Handsontable.dom.getWindowScrollLeft(),
  79. clientY: cellOffset.top - Handsontable.dom.getWindowScrollTop(),
  80. });
  81. };
  82. export function closeContextMenu() {
  83. $(document).simulate('mousedown');
  84. // $(document).trigger('mousedown');
  85. };
  86. /**
  87. * Shows dropdown menu
  88. */
  89. export function dropdownMenu(columnIndex) {
  90. var hot = spec().$container.data('handsontable');
  91. var th = hot.view.wt.wtTable.getColumnHeader(columnIndex || 0);
  92. var button = th.querySelector('.changeType');
  93. if (button) {
  94. $(button).simulate('mousedown');
  95. $(button).simulate('click');
  96. }
  97. };
  98. export function closeDropdownMenu() {
  99. $(document).simulate('mousedown');
  100. };
  101. export function dropdownMenuRootElement() {
  102. var plugin = hot().getPlugin('dropdownMenu');
  103. var root;
  104. if (plugin && plugin.menu) {
  105. root = plugin.menu.container;
  106. }
  107. return root;
  108. };
  109. /**
  110. * Returns a function that triggers a mouse event
  111. * @param {String} type Event type
  112. * @return {Function}
  113. */
  114. export function handsontableMouseTriggerFactory(type, button) {
  115. return function(element) {
  116. if (!(element instanceof jQuery)) {
  117. element = $(element);
  118. }
  119. var ev = $.Event(type);
  120. ev.which = button || 1; // left click by default
  121. element.simulate(type, ev);
  122. };
  123. };
  124. export const mouseDown = handsontableMouseTriggerFactory('mousedown');
  125. export const mouseMove = handsontableMouseTriggerFactory('mousemove');
  126. export const mouseOver = handsontableMouseTriggerFactory('mouseover');
  127. export const mouseUp = handsontableMouseTriggerFactory('mouseup');
  128. export function mouseDoubleClick(element) {
  129. mouseDown(element);
  130. mouseUp(element);
  131. mouseDown(element);
  132. mouseUp(element);
  133. };
  134. export const mouseRightDown = handsontableMouseTriggerFactory('mousedown', 3);
  135. export const mouseRightUp = handsontableMouseTriggerFactory('mouseup', 3);
  136. /**
  137. * Returns a function that triggers a key event
  138. * @param {String} type Event type
  139. * @return {Function}
  140. */
  141. export function handsontableKeyTriggerFactory(type) {
  142. return function(key, extend) {
  143. var ev = {}; // $.Event(type);
  144. if (typeof key === 'string') {
  145. if (key.indexOf('shift+') > -1) {
  146. key = key.substring(6);
  147. ev.shiftKey = true;
  148. }
  149. if (key.indexOf('ctrl+') > -1) {
  150. key = key.substring(5);
  151. ev.ctrlKey = true;
  152. ev.metaKey = true;
  153. }
  154. switch (key) {
  155. case 'tab':
  156. ev.keyCode = 9;
  157. break;
  158. case 'enter':
  159. ev.keyCode = 13;
  160. break;
  161. case 'esc':
  162. ev.keyCode = 27;
  163. break;
  164. case 'f2':
  165. ev.keyCode = 113;
  166. break;
  167. case 'arrow_left':
  168. ev.keyCode = 37;
  169. break;
  170. case 'arrow_up':
  171. ev.keyCode = 38;
  172. break;
  173. case 'arrow_right':
  174. ev.keyCode = 39;
  175. break;
  176. case 'arrow_down':
  177. ev.keyCode = 40;
  178. break;
  179. case 'ctrl':
  180. ev.keyCode = 17;
  181. break;
  182. case 'shift':
  183. ev.keyCode = 16;
  184. break;
  185. case 'backspace':
  186. ev.keyCode = 8;
  187. break;
  188. case 'delete':
  189. ev.keyCode = 46;
  190. break;
  191. case 'space':
  192. ev.keyCode = 32;
  193. break;
  194. case 'x':
  195. ev.keyCode = 88;
  196. break;
  197. case 'c':
  198. ev.keyCode = 67;
  199. break;
  200. case 'v':
  201. ev.keyCode = 86;
  202. break;
  203. default:
  204. throw new Error(`Unrecognised key name: ${key}`);
  205. }
  206. } else if (typeof key === 'number') {
  207. ev.keyCode = key;
  208. }
  209. // ev.originalEvent = {}; //needed as long Handsontable searches for event.originalEvent
  210. $.extend(ev, extend);
  211. $(document.activeElement).simulate(type, ev);
  212. };
  213. };
  214. export const keyDown = handsontableKeyTriggerFactory('keydown');
  215. export const keyUp = handsontableKeyTriggerFactory('keyup');
  216. /**
  217. * Presses keyDown, then keyUp
  218. */
  219. export function keyDownUp(key, extend) {
  220. if (typeof key === 'string' && key.indexOf('shift+') > -1) {
  221. keyDown('shift');
  222. }
  223. keyDown(key, extend);
  224. keyUp(key, extend);
  225. if (typeof key === 'string' && key.indexOf('shift+') > -1) {
  226. keyUp('shift');
  227. }
  228. };
  229. /**
  230. * Returns current value of the keyboard proxy textarea
  231. * @return {String}
  232. */
  233. export function keyProxy() {
  234. return spec().$container.find('textarea.handsontableInput');
  235. };
  236. export function serveImmediatePropagation(event) {
  237. if (event != null && event.isImmediatePropagationEnabled == null) {
  238. event.stopImmediatePropagation = function() {
  239. this.isImmediatePropagationEnabled = false;
  240. this.cancelBubble = true;
  241. };
  242. event.isImmediatePropagationEnabled = true;
  243. event.isImmediatePropagationStopped = function() {
  244. return !this.isImmediatePropagationEnabled;
  245. };
  246. }
  247. return event;
  248. };
  249. export function autocompleteEditor() {
  250. return spec().$container.find('.handsontableInput');
  251. };
  252. /**
  253. * Sets text cursor inside keyboard proxy
  254. */
  255. export function setCaretPosition(pos) {
  256. var el = keyProxy()[0];
  257. if (el.setSelectionRange) {
  258. el.focus();
  259. el.setSelectionRange(pos, pos);
  260. } else if (el.createTextRange) {
  261. var range = el.createTextRange();
  262. range.collapse(true);
  263. range.moveEnd('character', pos);
  264. range.moveStart('character', pos);
  265. range.select();
  266. }
  267. };
  268. /**
  269. * Returns autocomplete instance
  270. */
  271. export function autocomplete() {
  272. return spec().$container.find('.autocompleteEditor');
  273. };
  274. /**
  275. * Triggers paste string on current selection
  276. */
  277. export function triggerPaste(str) {
  278. spec().$container.data('handsontable').copyPaste.triggerPaste(null, str);
  279. };
  280. /**
  281. * Calls a method in current Handsontable instance, returns its output
  282. * @param method
  283. * @return {Function}
  284. */
  285. export function handsontableMethodFactory(method) {
  286. return function() {
  287. var instance;
  288. try {
  289. instance = spec().$container.handsontable('getInstance');
  290. } catch (err) {
  291. console.error(err);
  292. }
  293. if (instance) {
  294. if (method === 'destroy') {
  295. spec().$container.removeData();
  296. }
  297. } else {
  298. if (method === 'destroy') {
  299. return; // we can forgive this... maybe it was destroyed in the test
  300. }
  301. throw new Error('Something wrong with the test spec: Handsontable instance not found');
  302. }
  303. return instance[method](...arguments);
  304. };
  305. };
  306. export const addHook = handsontableMethodFactory('addHook');
  307. export const alter = handsontableMethodFactory('alter');
  308. export const colToProp = handsontableMethodFactory('colToProp');
  309. export const countCols = handsontableMethodFactory('countCols');
  310. export const countRows = handsontableMethodFactory('countRows');
  311. export const deselectCell = handsontableMethodFactory('deselectCell');
  312. export const destroy = handsontableMethodFactory('destroy');
  313. export const destroyEditor = handsontableMethodFactory('destroyEditor');
  314. export const getActiveEditor = handsontableMethodFactory('getActiveEditor');
  315. export const getCell = handsontableMethodFactory('getCell');
  316. export const getCellEditor = handsontableMethodFactory('getCellEditor');
  317. export const getCellMeta = handsontableMethodFactory('getCellMeta');
  318. export const getCellMetaAtRow = handsontableMethodFactory('getCellMetaAtRow');
  319. export const getCellRenderer = handsontableMethodFactory('getCellRenderer');
  320. export const getCellsMeta = handsontableMethodFactory('getCellsMeta');
  321. export const getCellValidator = handsontableMethodFactory('getCellValidator');
  322. export const getColHeader = handsontableMethodFactory('getColHeader');
  323. export const getCopyableData = handsontableMethodFactory('getCopyableData');
  324. export const getCopyableText = handsontableMethodFactory('getCopyableText');
  325. export const getData = handsontableMethodFactory('getData');
  326. export const getDataAtCell = handsontableMethodFactory('getDataAtCell');
  327. export const getDataAtCol = handsontableMethodFactory('getDataAtCol');
  328. export const getDataAtRow = handsontableMethodFactory('getDataAtRow');
  329. export const getDataAtRowProp = handsontableMethodFactory('getDataAtRowProp');
  330. export const getDataType = handsontableMethodFactory('getDataType');
  331. export const getInstance = handsontableMethodFactory('getInstance');
  332. export const getRowHeader = handsontableMethodFactory('getRowHeader');
  333. export const getSelected = handsontableMethodFactory('getSelected');
  334. export const getSourceData = handsontableMethodFactory('getSourceData');
  335. export const getSourceDataArray = handsontableMethodFactory('getSourceDataArray');
  336. export const getSourceDataAtCell = handsontableMethodFactory('getSourceDataAtCell');
  337. export const getSourceDataAtCol = handsontableMethodFactory('getSourceDataAtCol');
  338. export const getSourceDataAtRow = handsontableMethodFactory('getSourceDataAtRow');
  339. export const getValue = handsontableMethodFactory('getValue');
  340. export const loadData = handsontableMethodFactory('loadData');
  341. export const populateFromArray = handsontableMethodFactory('populateFromArray');
  342. export const propToCol = handsontableMethodFactory('propToCol');
  343. export const removeCellMeta = handsontableMethodFactory('removeCellMeta');
  344. export const render = handsontableMethodFactory('render');
  345. export const selectCell = handsontableMethodFactory('selectCell');
  346. export const setCellMeta = handsontableMethodFactory('setCellMeta');
  347. export const setDataAtCell = handsontableMethodFactory('setDataAtCell');
  348. export const setDataAtRowProp = handsontableMethodFactory('setDataAtRowProp');
  349. export const spliceCellsMeta = handsontableMethodFactory('spliceCellsMeta');
  350. export const spliceCol = handsontableMethodFactory('spliceCol');
  351. export const spliceRow = handsontableMethodFactory('spliceRow');
  352. export const updateSettings = handsontableMethodFactory('updateSettings');
  353. export const countSourceRows = handsontableMethodFactory('countSourceRows');
  354. export const countSourceCols = handsontableMethodFactory('countSourceCols');
  355. export const countEmptyRows = handsontableMethodFactory('countEmptyRows');
  356. export const countEmptyCols = handsontableMethodFactory('countEmptyCols');
  357. /**
  358. * Returns column width for HOT container
  359. * @param $elem
  360. * @param col
  361. * @returns {Number}
  362. */
  363. export function colWidth($elem, col) {
  364. var TR = $elem[0].querySelector('TBODY TR');
  365. var cell;
  366. if (TR) {
  367. cell = TR.querySelectorAll('TD')[col];
  368. } else {
  369. cell = $elem[0].querySelector('THEAD TR').querySelectorAll('TH')[col];
  370. }
  371. if (!cell) {
  372. throw new Error(`Cannot find table column of index '${col}'`);
  373. }
  374. return cell.offsetWidth;
  375. }
  376. /**
  377. * Returns row height for HOT container
  378. * @param $elem
  379. * @param row
  380. * @returns {Number}
  381. */
  382. export function rowHeight($elem, row) {
  383. var TD;
  384. if (row >= 0) {
  385. TD = $elem[0].querySelector(`tbody tr:nth-child(${row + 1}) td`);
  386. } else {
  387. TD = $elem[0].querySelector(`thead tr:nth-child(${Math.abs(row)})`);
  388. }
  389. if (!TD) {
  390. throw new Error(`Cannot find table row of index '${row}'`);
  391. }
  392. return Handsontable.dom.outerHeight(TD);
  393. }
  394. /**
  395. * Returns value that has been rendered in table cell
  396. * @param {Number} trIndex
  397. * @param {Number} tdIndex
  398. * @returns {String}
  399. */
  400. export function getRenderedValue(trIndex, tdIndex) {
  401. return spec().$container.find('tbody tr').eq(trIndex).find('td').eq(tdIndex).html();
  402. }
  403. /**
  404. * Returns nodes that have been rendered in table cell
  405. * @param {Number} trIndex
  406. * @param {Number} tdIndex
  407. * @returns {String}
  408. */
  409. export function getRenderedContent(trIndex, tdIndex) {
  410. return spec().$container.find('tbody tr').eq(trIndex).find('td').eq(tdIndex).children();
  411. }
  412. /**
  413. * Create numerical data values for the table
  414. * @param rowCount
  415. * @param colCount
  416. * @returns {Array}
  417. */
  418. export function createNumericData(rowCount, colCount) {
  419. rowCount = typeof rowCount === 'number' ? rowCount : 100;
  420. colCount = typeof colCount === 'number' ? colCount : 4;
  421. var
  422. rows = [],
  423. i,
  424. j;
  425. for (i = 0; i < rowCount; i++) {
  426. var row = [];
  427. for (j = 0; j < colCount; j++) {
  428. row.push((i + 1));
  429. }
  430. rows.push(row);
  431. }
  432. return rows;
  433. }
  434. /**
  435. * Model factory, which creates object with private properties, accessible by setters and getters.
  436. * Created for the purpose of testing HOT with Backbone-like Models
  437. * @param opts
  438. * @returns {{}}
  439. * @constructor
  440. */
  441. export function Model(opts) {
  442. var obj = {};
  443. var _data = $.extend({
  444. id: undefined,
  445. name: undefined,
  446. address: undefined
  447. }, opts);
  448. obj.attr = function(name, value) {
  449. if (typeof value === 'undefined') {
  450. return this.get(name);
  451. }
  452. return this.set(name, value);
  453. };
  454. obj.get = function(name) {
  455. return _data[name];
  456. };
  457. obj.set = function(name, value) {
  458. _data[name] = value;
  459. return this;
  460. };
  461. return obj;
  462. }
  463. /**
  464. * Factory which produces an accessor for objects of type "Model" (see above).
  465. * This function should be used to create accessor for a given property name and pass it as `data` option in column
  466. * configuration.
  467. *
  468. * @param name - name of the property for which an accessor function will be created
  469. * @returns {Function}
  470. */
  471. export function createAccessorForProperty(name) {
  472. return function(obj, value) {
  473. return obj.attr(name, value);
  474. };
  475. }
  476. export function resizeColumn(displayedColumnIndex, width) {
  477. var $container = spec().$container;
  478. var $th = $container.find(`thead tr:eq(0) th:eq(${displayedColumnIndex})`);
  479. $th.simulate('mouseover');
  480. var $resizer = $container.find('.manualColumnResizer');
  481. var resizerPosition = $resizer.position();
  482. $resizer.simulate('mousedown', {
  483. clientX: resizerPosition.left,
  484. });
  485. var delta = width - $th.width() - 2;
  486. var newPosition = resizerPosition.left + delta;
  487. $resizer.simulate('mousemove', {
  488. clientX: newPosition
  489. });
  490. $resizer.simulate('mouseup');
  491. }
  492. export function resizeRow(displayedRowIndex, height) {
  493. var $container = spec().$container;
  494. var $th = $container.find(`tbody tr:eq(${displayedRowIndex}) th:eq(0)`);
  495. $th.simulate('mouseover');
  496. var $resizer = $container.find('.manualRowResizer');
  497. var resizerPosition = $resizer.position();
  498. $resizer.simulate('mousedown', {
  499. clientY: resizerPosition.top
  500. });
  501. var delta = height - $th.height() - 2;
  502. if (delta < 0) {
  503. delta = 0;
  504. }
  505. $resizer.simulate('mousemove', {
  506. clientY: resizerPosition.top + delta
  507. });
  508. $resizer.simulate('mouseup');
  509. }
  510. export function moveSecondDisplayedRowBeforeFirstRow(container, secondDisplayedRowIndex) {
  511. var
  512. $mainContainer = container.parents('.handsontable').not('[class*=clone]').not('[class*=master]').first(),
  513. $rowHeaders = container.find('tbody tr th'),
  514. $firstRowHeader = $rowHeaders.eq(secondDisplayedRowIndex - 1),
  515. $secondRowHeader = $rowHeaders.eq(secondDisplayedRowIndex);
  516. $secondRowHeader.simulate('mouseover');
  517. var $manualRowMover = $mainContainer.find('.manualRowMover');
  518. if ($manualRowMover.length) {
  519. $manualRowMover.simulate('mousedown', {
  520. clientY: $manualRowMover[0].getBoundingClientRect().top
  521. });
  522. $manualRowMover.simulate('mousemove', {
  523. clientY: $manualRowMover[0].getBoundingClientRect().top - 20
  524. });
  525. $firstRowHeader.simulate('mouseover');
  526. $secondRowHeader.simulate('mouseup');
  527. }
  528. }
  529. export function moveFirstDisplayedRowAfterSecondRow(container, firstDisplayedRowIndex) {
  530. var
  531. $mainContainer = container.parents('.handsontable').not('[class*=clone]').not('[class*=master]').first(),
  532. $rowHeaders = container.find('tbody tr th'),
  533. $firstRowHeader = $rowHeaders.eq(firstDisplayedRowIndex),
  534. $secondRowHeader = $rowHeaders.eq(firstDisplayedRowIndex + 1);
  535. $secondRowHeader.simulate('mouseover');
  536. var $manualRowMover = $mainContainer.find('.manualRowMover');
  537. if ($manualRowMover.length) {
  538. $manualRowMover.simulate('mousedown', {
  539. clientY: $manualRowMover[0].getBoundingClientRect().top
  540. });
  541. $manualRowMover.simulate('mousemove', {
  542. clientY: $manualRowMover[0].getBoundingClientRect().top + 20
  543. });
  544. $firstRowHeader.simulate('mouseover');
  545. $secondRowHeader.simulate('mouseup');
  546. }
  547. }
  548. export function swapDisplayedColumns(container, from, to) {
  549. var $mainContainer = container.parents('.handsontable').not('[class*=clone]').not('[class*=master]').first();
  550. var $colHeaders = container.find('thead tr:eq(0) th');
  551. var $to = $colHeaders.eq(to);
  552. var $from = $colHeaders.eq(from);
  553. // Enter the second column header
  554. $from.simulate('mouseover');
  555. var $manualColumnMover = $mainContainer.find('.manualColumnMover');
  556. // Grab the second column
  557. $manualColumnMover.simulate('mousedown', {
  558. pageX: $manualColumnMover[0].getBoundingClientRect().left,
  559. });
  560. // Drag the second column over the first column
  561. $manualColumnMover.simulate('mousemove', {
  562. pageX: $manualColumnMover[0].getBoundingClientRect().left - 20,
  563. });
  564. $to.simulate('mouseover');
  565. // Drop the second column
  566. $from.simulate('mouseup');
  567. }
  568. export function triggerTouchEvent(type, target, pageX, pageY) {
  569. var e = document.createEvent('TouchEvent');
  570. var targetCoords = target.getBoundingClientRect();
  571. var touches;
  572. var targetTouches;
  573. var changedTouches;
  574. if (!pageX && !pageY) {
  575. pageX = parseInt(targetCoords.left + 3, 10);
  576. pageY = parseInt(targetCoords.top + 3, 10);
  577. }
  578. var touch = document.createTouch(window, target, 0, pageX, pageY, pageX, pageY);
  579. if (type == 'touchend') {
  580. touches = document.createTouchList();
  581. targetTouches = document.createTouchList();
  582. changedTouches = document.createTouchList(touch);
  583. } else {
  584. touches = document.createTouchList(touch);
  585. targetTouches = document.createTouchList(touch);
  586. changedTouches = document.createTouchList(touch);
  587. }
  588. e.initTouchEvent(type, true, true, window, null, 0, 0, 0, 0, false, false, false, false, touches, targetTouches, changedTouches, 1, 0);
  589. target.dispatchEvent(e);
  590. };