ad372ed3780a3c4b55ca8f0cfb136cc01688d0d62ba5d64d8c9161f51ce76918dddf75e951b827a74af8272c24a17b6681b529191bb1ee7a98c30569acea46 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. import { getScrollbarWidth, getScrollTop, getStyle, offset, outerHeight, outerWidth } from './../../../helpers/dom/element';
  4. import { objectEach } from './../../../helpers/object';
  5. import EventManager from './../../../eventManager';
  6. import ViewportColumnsCalculator from './calculator/viewportColumns';
  7. import ViewportRowsCalculator from './calculator/viewportRows';
  8. /**
  9. * @class Viewport
  10. */
  11. var Viewport = function () {
  12. /**
  13. * @param wotInstance
  14. */
  15. function Viewport(wotInstance) {
  16. var _this = this;
  17. _classCallCheck(this, Viewport);
  18. this.wot = wotInstance;
  19. // legacy support
  20. this.instance = this.wot;
  21. this.oversizedRows = [];
  22. this.oversizedColumnHeaders = [];
  23. this.hasOversizedColumnHeadersMarked = {};
  24. this.clientHeight = 0;
  25. this.containerWidth = NaN;
  26. this.rowHeaderWidth = NaN;
  27. this.rowsVisibleCalculator = null;
  28. this.columnsVisibleCalculator = null;
  29. this.eventManager = new EventManager(this.wot);
  30. this.eventManager.addEventListener(window, 'resize', function () {
  31. _this.clientHeight = _this.getWorkspaceHeight();
  32. });
  33. }
  34. /**
  35. * @returns {number}
  36. */
  37. _createClass(Viewport, [{
  38. key: 'getWorkspaceHeight',
  39. value: function getWorkspaceHeight() {
  40. var trimmingContainer = this.instance.wtOverlays.topOverlay.trimmingContainer;
  41. var elemHeight = void 0;
  42. var height = 0;
  43. if (trimmingContainer === window) {
  44. height = document.documentElement.clientHeight;
  45. } else {
  46. elemHeight = outerHeight(trimmingContainer);
  47. // returns height without DIV scrollbar
  48. height = elemHeight > 0 && trimmingContainer.clientHeight > 0 ? trimmingContainer.clientHeight : Infinity;
  49. }
  50. return height;
  51. }
  52. }, {
  53. key: 'getWorkspaceWidth',
  54. value: function getWorkspaceWidth() {
  55. var width = void 0;
  56. var totalColumns = this.wot.getSetting('totalColumns');
  57. var trimmingContainer = this.instance.wtOverlays.leftOverlay.trimmingContainer;
  58. var overflow = void 0;
  59. var stretchSetting = this.wot.getSetting('stretchH');
  60. var docOffsetWidth = document.documentElement.offsetWidth;
  61. var preventOverflow = this.wot.getSetting('preventOverflow');
  62. if (preventOverflow) {
  63. return outerWidth(this.instance.wtTable.wtRootElement);
  64. }
  65. if (this.wot.getSetting('freezeOverlays')) {
  66. width = Math.min(docOffsetWidth - this.getWorkspaceOffset().left, docOffsetWidth);
  67. } else {
  68. width = Math.min(this.getContainerFillWidth(), docOffsetWidth - this.getWorkspaceOffset().left, docOffsetWidth);
  69. }
  70. if (trimmingContainer === window && totalColumns > 0 && this.sumColumnWidths(0, totalColumns - 1) > width) {
  71. // in case sum of column widths is higher than available stylesheet width, let's assume using the whole window
  72. // otherwise continue below, which will allow stretching
  73. // this is used in `scroll_window.html`
  74. // TODO test me
  75. return document.documentElement.clientWidth;
  76. }
  77. if (trimmingContainer !== window) {
  78. overflow = getStyle(this.instance.wtOverlays.leftOverlay.trimmingContainer, 'overflow');
  79. if (overflow == 'scroll' || overflow == 'hidden' || overflow == 'auto') {
  80. // this is used in `scroll.html`
  81. // TODO test me
  82. return Math.max(width, trimmingContainer.clientWidth);
  83. }
  84. }
  85. if (stretchSetting === 'none' || !stretchSetting) {
  86. // if no stretching is used, return the maximum used workspace width
  87. return Math.max(width, outerWidth(this.instance.wtTable.TABLE));
  88. }
  89. // if stretching is used, return the actual container width, so the columns can fit inside it
  90. return width;
  91. }
  92. /**
  93. * Checks if viewport has vertical scroll
  94. *
  95. * @returns {Boolean}
  96. */
  97. }, {
  98. key: 'hasVerticalScroll',
  99. value: function hasVerticalScroll() {
  100. return this.getWorkspaceActualHeight() > this.getWorkspaceHeight();
  101. }
  102. /**
  103. * Checks if viewport has horizontal scroll
  104. *
  105. * @returns {Boolean}
  106. */
  107. }, {
  108. key: 'hasHorizontalScroll',
  109. value: function hasHorizontalScroll() {
  110. return this.getWorkspaceActualWidth() > this.getWorkspaceWidth();
  111. }
  112. /**
  113. * @param from
  114. * @param length
  115. * @returns {Number}
  116. */
  117. }, {
  118. key: 'sumColumnWidths',
  119. value: function sumColumnWidths(from, length) {
  120. var sum = 0;
  121. while (from < length) {
  122. sum += this.wot.wtTable.getColumnWidth(from);
  123. from++;
  124. }
  125. return sum;
  126. }
  127. /**
  128. * @returns {Number}
  129. */
  130. }, {
  131. key: 'getContainerFillWidth',
  132. value: function getContainerFillWidth() {
  133. if (this.containerWidth) {
  134. return this.containerWidth;
  135. }
  136. var mainContainer = this.instance.wtTable.holder;
  137. var fillWidth = void 0;
  138. var dummyElement = void 0;
  139. dummyElement = document.createElement('div');
  140. dummyElement.style.width = '100%';
  141. dummyElement.style.height = '1px';
  142. mainContainer.appendChild(dummyElement);
  143. fillWidth = dummyElement.offsetWidth;
  144. this.containerWidth = fillWidth;
  145. mainContainer.removeChild(dummyElement);
  146. return fillWidth;
  147. }
  148. /**
  149. * @returns {Number}
  150. */
  151. }, {
  152. key: 'getWorkspaceOffset',
  153. value: function getWorkspaceOffset() {
  154. return offset(this.wot.wtTable.TABLE);
  155. }
  156. /**
  157. * @returns {Number}
  158. */
  159. }, {
  160. key: 'getWorkspaceActualHeight',
  161. value: function getWorkspaceActualHeight() {
  162. return outerHeight(this.wot.wtTable.TABLE);
  163. }
  164. /**
  165. * @returns {Number}
  166. */
  167. }, {
  168. key: 'getWorkspaceActualWidth',
  169. value: function getWorkspaceActualWidth() {
  170. return outerWidth(this.wot.wtTable.TABLE) || outerWidth(this.wot.wtTable.TBODY) || outerWidth(this.wot.wtTable.THEAD); // IE8 reports 0 as <table> offsetWidth;
  171. }
  172. /**
  173. * @returns {Number}
  174. */
  175. }, {
  176. key: 'getColumnHeaderHeight',
  177. value: function getColumnHeaderHeight() {
  178. if (isNaN(this.columnHeaderHeight)) {
  179. this.columnHeaderHeight = outerHeight(this.wot.wtTable.THEAD);
  180. }
  181. return this.columnHeaderHeight;
  182. }
  183. /**
  184. * @returns {Number}
  185. */
  186. }, {
  187. key: 'getViewportHeight',
  188. value: function getViewportHeight() {
  189. var containerHeight = this.getWorkspaceHeight();
  190. var columnHeaderHeight = void 0;
  191. if (containerHeight === Infinity) {
  192. return containerHeight;
  193. }
  194. columnHeaderHeight = this.getColumnHeaderHeight();
  195. if (columnHeaderHeight > 0) {
  196. containerHeight -= columnHeaderHeight;
  197. }
  198. return containerHeight;
  199. }
  200. /**
  201. * @returns {Number}
  202. */
  203. }, {
  204. key: 'getRowHeaderWidth',
  205. value: function getRowHeaderWidth() {
  206. var rowHeadersHeightSetting = this.instance.getSetting('rowHeaderWidth');
  207. var rowHeaders = this.instance.getSetting('rowHeaders');
  208. if (rowHeadersHeightSetting) {
  209. this.rowHeaderWidth = 0;
  210. for (var i = 0, len = rowHeaders.length; i < len; i++) {
  211. this.rowHeaderWidth += rowHeadersHeightSetting[i] || rowHeadersHeightSetting;
  212. }
  213. }
  214. if (this.wot.cloneSource) {
  215. return this.wot.cloneSource.wtViewport.getRowHeaderWidth();
  216. }
  217. if (isNaN(this.rowHeaderWidth)) {
  218. if (rowHeaders.length) {
  219. var TH = this.instance.wtTable.TABLE.querySelector('TH');
  220. this.rowHeaderWidth = 0;
  221. for (var _i = 0, _len = rowHeaders.length; _i < _len; _i++) {
  222. if (TH) {
  223. this.rowHeaderWidth += outerWidth(TH);
  224. TH = TH.nextSibling;
  225. } else {
  226. // yes this is a cheat but it worked like that before, just taking assumption from CSS instead of measuring.
  227. // TODO: proper fix
  228. this.rowHeaderWidth += 50;
  229. }
  230. }
  231. } else {
  232. this.rowHeaderWidth = 0;
  233. }
  234. }
  235. this.rowHeaderWidth = this.instance.getSetting('onModifyRowHeaderWidth', this.rowHeaderWidth) || this.rowHeaderWidth;
  236. return this.rowHeaderWidth;
  237. }
  238. /**
  239. * @returns {Number}
  240. */
  241. }, {
  242. key: 'getViewportWidth',
  243. value: function getViewportWidth() {
  244. var containerWidth = this.getWorkspaceWidth();
  245. var rowHeaderWidth = void 0;
  246. if (containerWidth === Infinity) {
  247. return containerWidth;
  248. }
  249. rowHeaderWidth = this.getRowHeaderWidth();
  250. if (rowHeaderWidth > 0) {
  251. return containerWidth - rowHeaderWidth;
  252. }
  253. return containerWidth;
  254. }
  255. /**
  256. * Creates:
  257. * - rowsRenderCalculator (before draw, to qualify rows for rendering)
  258. * - rowsVisibleCalculator (after draw, to measure which rows are actually visible)
  259. *
  260. * @returns {ViewportRowsCalculator}
  261. */
  262. }, {
  263. key: 'createRowsCalculator',
  264. value: function createRowsCalculator() {
  265. var _this2 = this;
  266. var visible = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  267. var height = void 0;
  268. var pos = void 0;
  269. var fixedRowsTop = void 0;
  270. var scrollbarHeight = void 0;
  271. var fixedRowsBottom = void 0;
  272. var fixedRowsHeight = void 0;
  273. var totalRows = void 0;
  274. this.rowHeaderWidth = NaN;
  275. if (this.wot.wtSettings.settings.renderAllRows) {
  276. height = Infinity;
  277. } else {
  278. height = this.getViewportHeight();
  279. }
  280. pos = this.wot.wtOverlays.topOverlay.getScrollPosition() - this.wot.wtOverlays.topOverlay.getTableParentOffset();
  281. if (pos < 0) {
  282. pos = 0;
  283. }
  284. fixedRowsTop = this.wot.getSetting('fixedRowsTop');
  285. fixedRowsBottom = this.wot.getSetting('fixedRowsBottom');
  286. totalRows = this.wot.getSetting('totalRows');
  287. if (fixedRowsTop) {
  288. fixedRowsHeight = this.wot.wtOverlays.topOverlay.sumCellSizes(0, fixedRowsTop);
  289. pos += fixedRowsHeight;
  290. height -= fixedRowsHeight;
  291. }
  292. if (fixedRowsBottom && this.wot.wtOverlays.bottomOverlay.clone) {
  293. fixedRowsHeight = this.wot.wtOverlays.bottomOverlay.sumCellSizes(totalRows - fixedRowsBottom, totalRows);
  294. height -= fixedRowsHeight;
  295. }
  296. if (this.wot.wtTable.holder.clientHeight === this.wot.wtTable.holder.offsetHeight) {
  297. scrollbarHeight = 0;
  298. } else {
  299. scrollbarHeight = getScrollbarWidth();
  300. }
  301. return new ViewportRowsCalculator(height, pos, this.wot.getSetting('totalRows'), function (sourceRow) {
  302. return _this2.wot.wtTable.getRowHeight(sourceRow);
  303. }, visible ? null : this.wot.wtSettings.settings.viewportRowCalculatorOverride, visible, scrollbarHeight);
  304. }
  305. /**
  306. * Creates:
  307. * - columnsRenderCalculator (before draw, to qualify columns for rendering)
  308. * - columnsVisibleCalculator (after draw, to measure which columns are actually visible)
  309. *
  310. * @returns {ViewportRowsCalculator}
  311. */
  312. }, {
  313. key: 'createColumnsCalculator',
  314. value: function createColumnsCalculator() {
  315. var _this3 = this;
  316. var visible = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  317. var width = this.getViewportWidth();
  318. var pos = void 0;
  319. var fixedColumnsLeft = void 0;
  320. this.columnHeaderHeight = NaN;
  321. pos = this.wot.wtOverlays.leftOverlay.getScrollPosition() - this.wot.wtOverlays.leftOverlay.getTableParentOffset();
  322. if (pos < 0) {
  323. pos = 0;
  324. }
  325. fixedColumnsLeft = this.wot.getSetting('fixedColumnsLeft');
  326. if (fixedColumnsLeft) {
  327. var fixedColumnsWidth = this.wot.wtOverlays.leftOverlay.sumCellSizes(0, fixedColumnsLeft);
  328. pos += fixedColumnsWidth;
  329. width -= fixedColumnsWidth;
  330. }
  331. if (this.wot.wtTable.holder.clientWidth !== this.wot.wtTable.holder.offsetWidth) {
  332. width -= getScrollbarWidth();
  333. }
  334. return new ViewportColumnsCalculator(width, pos, this.wot.getSetting('totalColumns'), function (sourceCol) {
  335. return _this3.wot.wtTable.getColumnWidth(sourceCol);
  336. }, visible ? null : this.wot.wtSettings.settings.viewportColumnCalculatorOverride, visible, this.wot.getSetting('stretchH'), function (stretchedWidth, column) {
  337. return _this3.wot.getSetting('onBeforeStretchingColumnWidth', stretchedWidth, column);
  338. });
  339. }
  340. /**
  341. * Creates rowsRenderCalculator and columnsRenderCalculator (before draw, to determine what rows and
  342. * cols should be rendered)
  343. *
  344. * @param fastDraw {Boolean} If `true`, will try to avoid full redraw and only update the border positions.
  345. * If `false` or `undefined`, will perform a full redraw
  346. * @returns fastDraw {Boolean} The fastDraw value, possibly modified
  347. */
  348. }, {
  349. key: 'createRenderCalculators',
  350. value: function createRenderCalculators() {
  351. var fastDraw = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  352. if (fastDraw) {
  353. var proposedRowsVisibleCalculator = this.createRowsCalculator(true);
  354. var proposedColumnsVisibleCalculator = this.createColumnsCalculator(true);
  355. if (!(this.areAllProposedVisibleRowsAlreadyRendered(proposedRowsVisibleCalculator) && this.areAllProposedVisibleColumnsAlreadyRendered(proposedColumnsVisibleCalculator))) {
  356. fastDraw = false;
  357. }
  358. }
  359. if (!fastDraw) {
  360. this.rowsRenderCalculator = this.createRowsCalculator();
  361. this.columnsRenderCalculator = this.createColumnsCalculator();
  362. }
  363. // delete temporarily to make sure that renderers always use rowsRenderCalculator, not rowsVisibleCalculator
  364. this.rowsVisibleCalculator = null;
  365. this.columnsVisibleCalculator = null;
  366. return fastDraw;
  367. }
  368. /**
  369. * Creates rowsVisibleCalculator and columnsVisibleCalculator (after draw, to determine what are
  370. * the actually visible rows and columns)
  371. */
  372. }, {
  373. key: 'createVisibleCalculators',
  374. value: function createVisibleCalculators() {
  375. this.rowsVisibleCalculator = this.createRowsCalculator(true);
  376. this.columnsVisibleCalculator = this.createColumnsCalculator(true);
  377. }
  378. /**
  379. * Returns information whether proposedRowsVisibleCalculator viewport
  380. * is contained inside rows rendered in previous draw (cached in rowsRenderCalculator)
  381. *
  382. * @param {Object} proposedRowsVisibleCalculator
  383. * @returns {Boolean} Returns `true` if all proposed visible rows are already rendered (meaning: redraw is not needed).
  384. * Returns `false` if at least one proposed visible row is not already rendered (meaning: redraw is needed)
  385. */
  386. }, {
  387. key: 'areAllProposedVisibleRowsAlreadyRendered',
  388. value: function areAllProposedVisibleRowsAlreadyRendered(proposedRowsVisibleCalculator) {
  389. if (this.rowsVisibleCalculator) {
  390. if (proposedRowsVisibleCalculator.startRow < this.rowsRenderCalculator.startRow || proposedRowsVisibleCalculator.startRow === this.rowsRenderCalculator.startRow && proposedRowsVisibleCalculator.startRow > 0) {
  391. return false;
  392. } else if (proposedRowsVisibleCalculator.endRow > this.rowsRenderCalculator.endRow || proposedRowsVisibleCalculator.endRow === this.rowsRenderCalculator.endRow && proposedRowsVisibleCalculator.endRow < this.wot.getSetting('totalRows') - 1) {
  393. return false;
  394. }
  395. return true;
  396. }
  397. return false;
  398. }
  399. /**
  400. * Returns information whether proposedColumnsVisibleCalculator viewport
  401. * is contained inside column rendered in previous draw (cached in columnsRenderCalculator)
  402. *
  403. * @param {Object} proposedColumnsVisibleCalculator
  404. * @returns {Boolean} Returns `true` if all proposed visible columns are already rendered (meaning: redraw is not needed).
  405. * Returns `false` if at least one proposed visible column is not already rendered (meaning: redraw is needed)
  406. */
  407. }, {
  408. key: 'areAllProposedVisibleColumnsAlreadyRendered',
  409. value: function areAllProposedVisibleColumnsAlreadyRendered(proposedColumnsVisibleCalculator) {
  410. if (this.columnsVisibleCalculator) {
  411. if (proposedColumnsVisibleCalculator.startColumn < this.columnsRenderCalculator.startColumn || proposedColumnsVisibleCalculator.startColumn === this.columnsRenderCalculator.startColumn && proposedColumnsVisibleCalculator.startColumn > 0) {
  412. return false;
  413. } else if (proposedColumnsVisibleCalculator.endColumn > this.columnsRenderCalculator.endColumn || proposedColumnsVisibleCalculator.endColumn === this.columnsRenderCalculator.endColumn && proposedColumnsVisibleCalculator.endColumn < this.wot.getSetting('totalColumns') - 1) {
  414. return false;
  415. }
  416. return true;
  417. }
  418. return false;
  419. }
  420. /**
  421. * Resets values in keys of the hasOversizedColumnHeadersMarked object after updateSettings.
  422. */
  423. }, {
  424. key: 'resetHasOversizedColumnHeadersMarked',
  425. value: function resetHasOversizedColumnHeadersMarked() {
  426. objectEach(this.hasOversizedColumnHeadersMarked, function (value, key, object) {
  427. object[key] = void 0;
  428. });
  429. }
  430. }]);
  431. return Viewport;
  432. }();
  433. export default Viewport;