tableView.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
  2. import { addClass, empty, fastInnerHTML, fastInnerText, getComputedStyle, getScrollbarWidth, hasClass, isChildOf, isInput, isOutsideInput } from './helpers/dom/element';
  3. import { isChrome, isSafari } from './helpers/browser';
  4. import EventManager from './eventManager';
  5. import { stopPropagation, isImmediatePropagationStopped, isRightClick, isLeftClick } from './helpers/dom/event';
  6. import Walkontable, { CellCoords, Selection } from './3rdparty/walkontable/src';
  7. /**
  8. * Handsontable TableView constructor
  9. * @param {Object} instance
  10. */
  11. function TableView(instance) {
  12. var _this = this;
  13. var that = this;
  14. this.eventManager = new EventManager(instance);
  15. this.instance = instance;
  16. this.settings = instance.getSettings();
  17. this.selectionMouseDown = false;
  18. var originalStyle = instance.rootElement.getAttribute('style');
  19. if (originalStyle) {
  20. instance.rootElement.setAttribute('data-originalstyle', originalStyle); // needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
  21. }
  22. addClass(instance.rootElement, 'handsontable');
  23. var table = document.createElement('TABLE');
  24. addClass(table, 'htCore');
  25. if (instance.getSettings().tableClassName) {
  26. addClass(table, instance.getSettings().tableClassName);
  27. }
  28. this.THEAD = document.createElement('THEAD');
  29. table.appendChild(this.THEAD);
  30. this.TBODY = document.createElement('TBODY');
  31. table.appendChild(this.TBODY);
  32. instance.table = table;
  33. instance.container.insertBefore(table, instance.container.firstChild);
  34. this.eventManager.addEventListener(instance.rootElement, 'mousedown', function (event) {
  35. this.selectionMouseDown = true;
  36. if (!that.isTextSelectionAllowed(event.target)) {
  37. clearTextSelection();
  38. event.preventDefault();
  39. window.focus(); // make sure that window that contains HOT is active. Important when HOT is in iframe.
  40. }
  41. });
  42. this.eventManager.addEventListener(instance.rootElement, 'mouseup', function (event) {
  43. this.selectionMouseDown = false;
  44. });
  45. this.eventManager.addEventListener(instance.rootElement, 'mousemove', function (event) {
  46. if (this.selectionMouseDown && !that.isTextSelectionAllowed(event.target)) {
  47. clearTextSelection();
  48. event.preventDefault();
  49. }
  50. });
  51. this.eventManager.addEventListener(document.documentElement, 'keyup', function (event) {
  52. if (instance.selection.isInProgress() && !event.shiftKey) {
  53. instance.selection.finish();
  54. }
  55. });
  56. var isMouseDown;
  57. this.isMouseDown = function () {
  58. return isMouseDown;
  59. };
  60. this.eventManager.addEventListener(document.documentElement, 'mouseup', function (event) {
  61. if (instance.selection.isInProgress() && event.which === 1) {
  62. // is left mouse button
  63. instance.selection.finish();
  64. }
  65. isMouseDown = false;
  66. if (isOutsideInput(document.activeElement)) {
  67. instance.unlisten();
  68. }
  69. });
  70. this.eventManager.addEventListener(document.documentElement, 'mousedown', function (event) {
  71. var originalTarget = event.target;
  72. var next = event.target;
  73. var eventX = event.x || event.clientX;
  74. var eventY = event.y || event.clientY;
  75. if (isMouseDown || !instance.rootElement) {
  76. return; // it must have been started in a cell
  77. }
  78. // immediate click on "holder" means click on the right side of vertical scrollbar
  79. if (next === instance.view.wt.wtTable.holder) {
  80. var scrollbarWidth = getScrollbarWidth();
  81. if (document.elementFromPoint(eventX + scrollbarWidth, eventY) !== instance.view.wt.wtTable.holder || document.elementFromPoint(eventX, eventY + scrollbarWidth) !== instance.view.wt.wtTable.holder) {
  82. return;
  83. }
  84. } else {
  85. while (next !== document.documentElement) {
  86. if (next === null) {
  87. if (event.isTargetWebComponent) {
  88. break;
  89. }
  90. // click on something that was a row but now is detached (possibly because your click triggered a rerender)
  91. return;
  92. }
  93. if (next === instance.rootElement) {
  94. // click inside container
  95. return;
  96. }
  97. next = next.parentNode;
  98. }
  99. }
  100. // function did not return until here, we have an outside click!
  101. var outsideClickDeselects = typeof that.settings.outsideClickDeselects === 'function' ? that.settings.outsideClickDeselects(originalTarget) : that.settings.outsideClickDeselects;
  102. if (outsideClickDeselects) {
  103. instance.deselectCell();
  104. } else {
  105. instance.destroyEditor();
  106. }
  107. });
  108. this.eventManager.addEventListener(table, 'selectstart', function (event) {
  109. if (that.settings.fragmentSelection || isInput(event.target)) {
  110. return;
  111. }
  112. // https://github.com/handsontable/handsontable/issues/160
  113. // Prevent text from being selected when performing drag down.
  114. event.preventDefault();
  115. });
  116. var clearTextSelection = function clearTextSelection() {
  117. // http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
  118. if (window.getSelection) {
  119. if (window.getSelection().empty) {
  120. // Chrome
  121. window.getSelection().empty();
  122. } else if (window.getSelection().removeAllRanges) {
  123. // Firefox
  124. window.getSelection().removeAllRanges();
  125. }
  126. } else if (document.selection) {
  127. // IE?
  128. document.selection.empty();
  129. }
  130. };
  131. var selections = [new Selection({
  132. className: 'current',
  133. border: {
  134. width: 2,
  135. color: '#5292F7',
  136. // style: 'solid', // not used
  137. cornerVisible: function cornerVisible() {
  138. return that.settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple();
  139. },
  140. multipleSelectionHandlesVisible: function multipleSelectionHandlesVisible() {
  141. return !that.isCellEdited() && !instance.selection.isMultiple();
  142. }
  143. }
  144. }), new Selection({
  145. className: 'area',
  146. border: {
  147. width: 1,
  148. color: '#89AFF9',
  149. // style: 'solid', // not used
  150. cornerVisible: function cornerVisible() {
  151. return that.settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple();
  152. },
  153. multipleSelectionHandlesVisible: function multipleSelectionHandlesVisible() {
  154. return !that.isCellEdited() && instance.selection.isMultiple();
  155. }
  156. }
  157. }), new Selection({
  158. className: 'highlight',
  159. highlightHeaderClassName: that.settings.currentHeaderClassName,
  160. highlightRowClassName: that.settings.currentRowClassName,
  161. highlightColumnClassName: that.settings.currentColClassName
  162. }), new Selection({
  163. className: 'fill',
  164. border: {
  165. width: 1,
  166. color: 'red'
  167. }
  168. })];
  169. selections.current = selections[0];
  170. selections.area = selections[1];
  171. selections.highlight = selections[2];
  172. selections.fill = selections[3];
  173. var walkontableConfig = {
  174. debug: function debug() {
  175. return that.settings.debug;
  176. },
  177. externalRowCalculator: this.instance.getPlugin('autoRowSize') && this.instance.getPlugin('autoRowSize').isEnabled(),
  178. table: table,
  179. preventOverflow: function preventOverflow() {
  180. return _this.settings.preventOverflow;
  181. },
  182. stretchH: function stretchH() {
  183. return that.settings.stretchH;
  184. },
  185. data: instance.getDataAtCell,
  186. totalRows: function totalRows() {
  187. return instance.countRows();
  188. },
  189. totalColumns: function totalColumns() {
  190. return instance.countCols();
  191. },
  192. fixedColumnsLeft: function fixedColumnsLeft() {
  193. return that.settings.fixedColumnsLeft;
  194. },
  195. fixedRowsTop: function fixedRowsTop() {
  196. return that.settings.fixedRowsTop;
  197. },
  198. fixedRowsBottom: function fixedRowsBottom() {
  199. return that.settings.fixedRowsBottom;
  200. },
  201. minSpareRows: function minSpareRows() {
  202. return that.settings.minSpareRows;
  203. },
  204. renderAllRows: that.settings.renderAllRows,
  205. rowHeaders: function rowHeaders() {
  206. var headerRenderers = [];
  207. if (instance.hasRowHeaders()) {
  208. headerRenderers.push(function (row, TH) {
  209. that.appendRowHeader(row, TH);
  210. });
  211. }
  212. instance.runHooks('afterGetRowHeaderRenderers', headerRenderers);
  213. return headerRenderers;
  214. },
  215. columnHeaders: function columnHeaders() {
  216. var headerRenderers = [];
  217. if (instance.hasColHeaders()) {
  218. headerRenderers.push(function (column, TH) {
  219. that.appendColHeader(column, TH);
  220. });
  221. }
  222. instance.runHooks('afterGetColumnHeaderRenderers', headerRenderers);
  223. return headerRenderers;
  224. },
  225. columnWidth: instance.getColWidth,
  226. rowHeight: instance.getRowHeight,
  227. cellRenderer: function cellRenderer(row, col, TD) {
  228. var cellProperties = that.instance.getCellMeta(row, col);
  229. var prop = that.instance.colToProp(col);
  230. var value = that.instance.getDataAtRowProp(row, prop);
  231. if (that.instance.hasHook('beforeValueRender')) {
  232. value = that.instance.runHooks('beforeValueRender', value);
  233. }
  234. that.instance.runHooks('beforeRenderer', TD, row, col, prop, value, cellProperties);
  235. that.instance.getCellRenderer(cellProperties)(that.instance, TD, row, col, prop, value, cellProperties);
  236. that.instance.runHooks('afterRenderer', TD, row, col, prop, value, cellProperties);
  237. },
  238. selections: selections,
  239. hideBorderOnMouseDownOver: function hideBorderOnMouseDownOver() {
  240. return that.settings.fragmentSelection;
  241. },
  242. onCellMouseDown: function onCellMouseDown(event, coords, TD, wt) {
  243. var blockCalculations = {
  244. row: false,
  245. column: false,
  246. cells: false
  247. };
  248. instance.listen();
  249. that.activeWt = wt;
  250. isMouseDown = true;
  251. instance.runHooks('beforeOnCellMouseDown', event, coords, TD, blockCalculations);
  252. if (isImmediatePropagationStopped(event)) {
  253. return;
  254. }
  255. var actualSelection = instance.getSelectedRange();
  256. var selection = instance.selection;
  257. var selectedHeader = selection.selectedHeader;
  258. if (event.shiftKey && actualSelection) {
  259. if (coords.row >= 0 && coords.col >= 0 && !blockCalculations.cells) {
  260. selection.setSelectedHeaders(false, false);
  261. selection.setRangeEnd(coords);
  262. } else if ((selectedHeader.cols || selectedHeader.rows) && coords.row >= 0 && coords.col >= 0 && !blockCalculations.cells) {
  263. selection.setSelectedHeaders(false, false);
  264. selection.setRangeEnd(new CellCoords(coords.row, coords.col));
  265. } else if (selectedHeader.cols && coords.row < 0 && !blockCalculations.column) {
  266. selection.setRangeEnd(new CellCoords(actualSelection.to.row, coords.col));
  267. } else if (selectedHeader.rows && coords.col < 0 && !blockCalculations.row) {
  268. selection.setRangeEnd(new CellCoords(coords.row, actualSelection.to.col));
  269. } else if ((!selectedHeader.cols && !selectedHeader.rows && coords.col < 0 || selectedHeader.cols && coords.col < 0) && !blockCalculations.row) {
  270. selection.setSelectedHeaders(true, false);
  271. selection.setRangeStartOnly(new CellCoords(actualSelection.from.row, 0));
  272. selection.setRangeEnd(new CellCoords(coords.row, instance.countCols() - 1));
  273. } else if ((!selectedHeader.cols && !selectedHeader.rows && coords.row < 0 || selectedHeader.rows && coords.row < 0) && !blockCalculations.column) {
  274. selection.setSelectedHeaders(false, true);
  275. selection.setRangeStartOnly(new CellCoords(0, actualSelection.from.col));
  276. selection.setRangeEnd(new CellCoords(instance.countRows() - 1, coords.col));
  277. }
  278. } else {
  279. var doNewSelection = true;
  280. if (actualSelection) {
  281. var from = actualSelection.from,
  282. to = actualSelection.to;
  283. var coordsNotInSelection = !selection.inInSelection(coords);
  284. if (coords.row < 0 && selectedHeader.cols) {
  285. var start = Math.min(from.col, to.col);
  286. var end = Math.max(from.col, to.col);
  287. doNewSelection = coords.col < start || coords.col > end;
  288. } else if (coords.col < 0 && selectedHeader.rows) {
  289. var _start = Math.min(from.row, to.row);
  290. var _end = Math.max(from.row, to.row);
  291. doNewSelection = coords.row < _start || coords.row > _end;
  292. } else {
  293. doNewSelection = coordsNotInSelection;
  294. }
  295. }
  296. var rightClick = isRightClick(event);
  297. var leftClick = isLeftClick(event) || event.type === 'touchstart';
  298. // clicked row header and when some column was selected
  299. if (coords.row < 0 && coords.col >= 0 && !blockCalculations.column) {
  300. selection.setSelectedHeaders(false, true);
  301. if (leftClick || rightClick && doNewSelection) {
  302. selection.setRangeStartOnly(new CellCoords(0, coords.col));
  303. selection.setRangeEnd(new CellCoords(Math.max(instance.countRows() - 1, 0), coords.col), false);
  304. }
  305. // clicked column header and when some row was selected
  306. } else if (coords.col < 0 && coords.row >= 0 && !blockCalculations.row) {
  307. selection.setSelectedHeaders(true, false);
  308. if (leftClick || rightClick && doNewSelection) {
  309. selection.setRangeStartOnly(new CellCoords(coords.row, 0));
  310. selection.setRangeEnd(new CellCoords(coords.row, Math.max(instance.countCols() - 1, 0)), false);
  311. }
  312. } else if (coords.col >= 0 && coords.row >= 0 && !blockCalculations.cells) {
  313. if (leftClick || rightClick && doNewSelection) {
  314. selection.setSelectedHeaders(false, false);
  315. selection.setRangeStart(coords);
  316. }
  317. } else if (coords.col < 0 && coords.row < 0) {
  318. coords.row = 0;
  319. coords.col = 0;
  320. selection.setSelectedHeaders(false, false, true);
  321. selection.setRangeStart(coords);
  322. }
  323. }
  324. instance.runHooks('afterOnCellMouseDown', event, coords, TD);
  325. that.activeWt = that.wt;
  326. },
  327. onCellMouseOut: function onCellMouseOut(event, coords, TD, wt) {
  328. that.activeWt = wt;
  329. instance.runHooks('beforeOnCellMouseOut', event, coords, TD);
  330. if (isImmediatePropagationStopped(event)) {
  331. return;
  332. }
  333. instance.runHooks('afterOnCellMouseOut', event, coords, TD);
  334. that.activeWt = that.wt;
  335. },
  336. onCellMouseOver: function onCellMouseOver(event, coords, TD, wt) {
  337. var blockCalculations = {
  338. row: false,
  339. column: false,
  340. cell: false
  341. };
  342. that.activeWt = wt;
  343. instance.runHooks('beforeOnCellMouseOver', event, coords, TD, blockCalculations);
  344. if (isImmediatePropagationStopped(event)) {
  345. return;
  346. }
  347. if (event.button === 0 && isMouseDown) {
  348. if (coords.row >= 0 && coords.col >= 0) {
  349. // is not a header
  350. if (instance.selection.selectedHeader.cols && !blockCalculations.column) {
  351. instance.selection.setRangeEnd(new CellCoords(instance.countRows() - 1, coords.col), false);
  352. } else if (instance.selection.selectedHeader.rows && !blockCalculations.row) {
  353. instance.selection.setRangeEnd(new CellCoords(coords.row, instance.countCols() - 1), false);
  354. } else if (!blockCalculations.cell) {
  355. instance.selection.setRangeEnd(coords);
  356. }
  357. } else {
  358. /* eslint-disable no-lonely-if */
  359. if (instance.selection.selectedHeader.cols && !blockCalculations.column) {
  360. instance.selection.setRangeEnd(new CellCoords(instance.countRows() - 1, coords.col), false);
  361. } else if (instance.selection.selectedHeader.rows && !blockCalculations.row) {
  362. instance.selection.setRangeEnd(new CellCoords(coords.row, instance.countCols() - 1), false);
  363. } else if (!blockCalculations.cell) {
  364. instance.selection.setRangeEnd(coords);
  365. }
  366. }
  367. }
  368. instance.runHooks('afterOnCellMouseOver', event, coords, TD);
  369. that.activeWt = that.wt;
  370. },
  371. onCellMouseUp: function onCellMouseUp(event, coords, TD, wt) {
  372. that.activeWt = wt;
  373. instance.runHooks('beforeOnCellMouseUp', event, coords, TD);
  374. instance.runHooks('afterOnCellMouseUp', event, coords, TD);
  375. that.activeWt = that.wt;
  376. },
  377. onCellCornerMouseDown: function onCellCornerMouseDown(event) {
  378. event.preventDefault();
  379. instance.runHooks('afterOnCellCornerMouseDown', event);
  380. },
  381. onCellCornerDblClick: function onCellCornerDblClick(event) {
  382. event.preventDefault();
  383. instance.runHooks('afterOnCellCornerDblClick', event);
  384. },
  385. beforeDraw: function beforeDraw(force, skipRender) {
  386. that.beforeRender(force, skipRender);
  387. },
  388. onDraw: function onDraw(force) {
  389. that.onDraw(force);
  390. },
  391. onScrollVertically: function onScrollVertically() {
  392. instance.runHooks('afterScrollVertically');
  393. },
  394. onScrollHorizontally: function onScrollHorizontally() {
  395. instance.runHooks('afterScrollHorizontally');
  396. },
  397. onBeforeDrawBorders: function onBeforeDrawBorders(corners, borderClassName) {
  398. instance.runHooks('beforeDrawBorders', corners, borderClassName);
  399. },
  400. onBeforeTouchScroll: function onBeforeTouchScroll() {
  401. instance.runHooks('beforeTouchScroll');
  402. },
  403. onAfterMomentumScroll: function onAfterMomentumScroll() {
  404. instance.runHooks('afterMomentumScroll');
  405. },
  406. onBeforeStretchingColumnWidth: function onBeforeStretchingColumnWidth(stretchedWidth, column) {
  407. return instance.runHooks('beforeStretchingColumnWidth', stretchedWidth, column);
  408. },
  409. onModifyRowHeaderWidth: function onModifyRowHeaderWidth(rowHeaderWidth) {
  410. return instance.runHooks('modifyRowHeaderWidth', rowHeaderWidth);
  411. },
  412. viewportRowCalculatorOverride: function viewportRowCalculatorOverride(calc) {
  413. var rows = instance.countRows();
  414. var viewportOffset = that.settings.viewportRowRenderingOffset;
  415. if (viewportOffset === 'auto' && that.settings.fixedRowsTop) {
  416. viewportOffset = 10;
  417. }
  418. if (typeof viewportOffset === 'number') {
  419. calc.startRow = Math.max(calc.startRow - viewportOffset, 0);
  420. calc.endRow = Math.min(calc.endRow + viewportOffset, rows - 1);
  421. }
  422. if (viewportOffset === 'auto') {
  423. var center = calc.startRow + calc.endRow - calc.startRow;
  424. var offset = Math.ceil(center / rows * 12);
  425. calc.startRow = Math.max(calc.startRow - offset, 0);
  426. calc.endRow = Math.min(calc.endRow + offset, rows - 1);
  427. }
  428. instance.runHooks('afterViewportRowCalculatorOverride', calc);
  429. },
  430. viewportColumnCalculatorOverride: function viewportColumnCalculatorOverride(calc) {
  431. var cols = instance.countCols();
  432. var viewportOffset = that.settings.viewportColumnRenderingOffset;
  433. if (viewportOffset === 'auto' && that.settings.fixedColumnsLeft) {
  434. viewportOffset = 10;
  435. }
  436. if (typeof viewportOffset === 'number') {
  437. calc.startColumn = Math.max(calc.startColumn - viewportOffset, 0);
  438. calc.endColumn = Math.min(calc.endColumn + viewportOffset, cols - 1);
  439. }
  440. if (viewportOffset === 'auto') {
  441. var center = calc.startColumn + calc.endColumn - calc.startColumn;
  442. var offset = Math.ceil(center / cols * 12);
  443. calc.startRow = Math.max(calc.startColumn - offset, 0);
  444. calc.endColumn = Math.min(calc.endColumn + offset, cols - 1);
  445. }
  446. instance.runHooks('afterViewportColumnCalculatorOverride', calc);
  447. },
  448. rowHeaderWidth: function rowHeaderWidth() {
  449. return that.settings.rowHeaderWidth;
  450. },
  451. columnHeaderHeight: function columnHeaderHeight() {
  452. var columnHeaderHeight = instance.runHooks('modifyColumnHeaderHeight');
  453. return that.settings.columnHeaderHeight || columnHeaderHeight;
  454. }
  455. };
  456. instance.runHooks('beforeInitWalkontable', walkontableConfig);
  457. this.wt = new Walkontable(walkontableConfig);
  458. this.activeWt = this.wt;
  459. if (!isChrome() && !isSafari()) {
  460. this.eventManager.addEventListener(instance.rootElement, 'wheel', function (event) {
  461. event.preventDefault();
  462. var lineHeight = parseInt(getComputedStyle(document.body)['font-size'], 10);
  463. var holder = that.wt.wtOverlays.scrollableElement;
  464. var deltaY = event.wheelDeltaY || event.deltaY;
  465. var deltaX = event.wheelDeltaX || event.deltaX;
  466. switch (event.deltaMode) {
  467. case 0:
  468. holder.scrollLeft += deltaX;
  469. holder.scrollTop += deltaY;
  470. break;
  471. case 1:
  472. holder.scrollLeft += deltaX * lineHeight;
  473. holder.scrollTop += deltaY * lineHeight;
  474. break;
  475. default:
  476. break;
  477. }
  478. });
  479. }
  480. this.eventManager.addEventListener(that.wt.wtTable.spreader, 'mousedown', function (event) {
  481. // right mouse button exactly on spreader means right click on the right hand side of vertical scrollbar
  482. if (event.target === that.wt.wtTable.spreader && event.which === 3) {
  483. stopPropagation(event);
  484. }
  485. });
  486. this.eventManager.addEventListener(that.wt.wtTable.spreader, 'contextmenu', function (event) {
  487. // right mouse button exactly on spreader means right click on the right hand side of vertical scrollbar
  488. if (event.target === that.wt.wtTable.spreader && event.which === 3) {
  489. stopPropagation(event);
  490. }
  491. });
  492. this.eventManager.addEventListener(document.documentElement, 'click', function () {
  493. if (that.settings.observeDOMVisibility) {
  494. if (that.wt.drawInterrupted) {
  495. that.instance.forceFullRender = true;
  496. that.render();
  497. }
  498. }
  499. });
  500. }
  501. TableView.prototype.isTextSelectionAllowed = function (el) {
  502. if (isInput(el)) {
  503. return true;
  504. }
  505. var isChildOfTableBody = isChildOf(el, this.instance.view.wt.wtTable.spreader);
  506. if (this.settings.fragmentSelection === true && isChildOfTableBody) {
  507. return true;
  508. }
  509. if (this.settings.fragmentSelection === 'cell' && this.isSelectedOnlyCell() && isChildOfTableBody) {
  510. return true;
  511. }
  512. if (!this.settings.fragmentSelection && this.isCellEdited() && this.isSelectedOnlyCell()) {
  513. return true;
  514. }
  515. return false;
  516. };
  517. /**
  518. * Check if selected only one cell.
  519. *
  520. * @returns {Boolean}
  521. */
  522. TableView.prototype.isSelectedOnlyCell = function () {
  523. var _ref = this.instance.getSelected() || [],
  524. _ref2 = _slicedToArray(_ref, 4),
  525. row = _ref2[0],
  526. col = _ref2[1],
  527. rowEnd = _ref2[2],
  528. colEnd = _ref2[3];
  529. return row !== void 0 && row === rowEnd && col === colEnd;
  530. };
  531. TableView.prototype.isCellEdited = function () {
  532. var activeEditor = this.instance.getActiveEditor();
  533. return activeEditor && activeEditor.isOpened();
  534. };
  535. TableView.prototype.beforeRender = function (force, skipRender) {
  536. if (force) {
  537. // this.instance.forceFullRender = did Handsontable request full render?
  538. this.instance.runHooks('beforeRender', this.instance.forceFullRender, skipRender);
  539. }
  540. };
  541. TableView.prototype.onDraw = function (force) {
  542. if (force) {
  543. // this.instance.forceFullRender = did Handsontable request full render?
  544. this.instance.runHooks('afterRender', this.instance.forceFullRender);
  545. }
  546. };
  547. TableView.prototype.render = function () {
  548. this.wt.draw(!this.instance.forceFullRender);
  549. this.instance.forceFullRender = false;
  550. this.instance.renderCall = false;
  551. };
  552. /**
  553. * Returns td object given coordinates
  554. *
  555. * @param {CellCoords} coords
  556. * @param {Boolean} topmost
  557. */
  558. TableView.prototype.getCellAtCoords = function (coords, topmost) {
  559. var td = this.wt.getCell(coords, topmost);
  560. if (td < 0) {
  561. // there was an exit code (cell is out of bounds)
  562. return null;
  563. }
  564. return td;
  565. };
  566. /**
  567. * Scroll viewport to selection.
  568. *
  569. * @param {CellCoords} coords
  570. */
  571. TableView.prototype.scrollViewport = function (coords) {
  572. this.wt.scrollViewport(coords);
  573. };
  574. /**
  575. * Append row header to a TH element
  576. * @param row
  577. * @param TH
  578. */
  579. TableView.prototype.appendRowHeader = function (row, TH) {
  580. if (TH.firstChild) {
  581. var container = TH.firstChild;
  582. if (!hasClass(container, 'relative')) {
  583. empty(TH);
  584. this.appendRowHeader(row, TH);
  585. return;
  586. }
  587. this.updateCellHeader(container.querySelector('.rowHeader'), row, this.instance.getRowHeader);
  588. } else {
  589. var div = document.createElement('div');
  590. var span = document.createElement('span');
  591. div.className = 'relative';
  592. span.className = 'rowHeader';
  593. this.updateCellHeader(span, row, this.instance.getRowHeader);
  594. div.appendChild(span);
  595. TH.appendChild(div);
  596. }
  597. this.instance.runHooks('afterGetRowHeader', row, TH);
  598. };
  599. /**
  600. * Append column header to a TH element
  601. * @param col
  602. * @param TH
  603. */
  604. TableView.prototype.appendColHeader = function (col, TH) {
  605. if (TH.firstChild) {
  606. var container = TH.firstChild;
  607. if (hasClass(container, 'relative')) {
  608. this.updateCellHeader(container.querySelector('.colHeader'), col, this.instance.getColHeader);
  609. } else {
  610. empty(TH);
  611. this.appendColHeader(col, TH);
  612. }
  613. } else {
  614. var div = document.createElement('div');
  615. var span = document.createElement('span');
  616. div.className = 'relative';
  617. span.className = 'colHeader';
  618. this.updateCellHeader(span, col, this.instance.getColHeader);
  619. div.appendChild(span);
  620. TH.appendChild(div);
  621. }
  622. this.instance.runHooks('afterGetColHeader', col, TH);
  623. };
  624. /**
  625. * Update header cell content
  626. *
  627. * @since 0.15.0-beta4
  628. * @param {HTMLElement} element Element to update
  629. * @param {Number} index Row index or column index
  630. * @param {Function} content Function which should be returns content for this cell
  631. */
  632. TableView.prototype.updateCellHeader = function (element, index, content) {
  633. var renderedIndex = index;
  634. var parentOverlay = this.wt.wtOverlays.getParentOverlay(element) || this.wt;
  635. // prevent wrong calculations from SampleGenerator
  636. if (element.parentNode) {
  637. if (hasClass(element, 'colHeader')) {
  638. renderedIndex = parentOverlay.wtTable.columnFilter.sourceToRendered(index);
  639. } else if (hasClass(element, 'rowHeader')) {
  640. renderedIndex = parentOverlay.wtTable.rowFilter.sourceToRendered(index);
  641. }
  642. }
  643. if (renderedIndex > -1) {
  644. fastInnerHTML(element, content(index));
  645. } else {
  646. // workaround for https://github.com/handsontable/handsontable/issues/1946
  647. fastInnerText(element, String.fromCharCode(160));
  648. addClass(element, 'cornerHeader');
  649. }
  650. };
  651. /**
  652. * Given a element's left position relative to the viewport, returns maximum element width until the right
  653. * edge of the viewport (before scrollbar)
  654. *
  655. * @param {Number} leftOffset
  656. * @return {Number}
  657. */
  658. TableView.prototype.maximumVisibleElementWidth = function (leftOffset) {
  659. var workspaceWidth = this.wt.wtViewport.getWorkspaceWidth();
  660. var maxWidth = workspaceWidth - leftOffset;
  661. return maxWidth > 0 ? maxWidth : 0;
  662. };
  663. /**
  664. * Given a element's top position relative to the viewport, returns maximum element height until the bottom
  665. * edge of the viewport (before scrollbar)
  666. *
  667. * @param {Number} topOffset
  668. * @return {Number}
  669. */
  670. TableView.prototype.maximumVisibleElementHeight = function (topOffset) {
  671. var workspaceHeight = this.wt.wtViewport.getWorkspaceHeight();
  672. var maxHeight = workspaceHeight - topOffset;
  673. return maxHeight > 0 ? maxHeight : 0;
  674. };
  675. TableView.prototype.mainViewIsActive = function () {
  676. return this.wt === this.activeWt;
  677. };
  678. TableView.prototype.destroy = function () {
  679. this.wt.destroy();
  680. this.eventManager.destroy();
  681. };
  682. export default TableView;