row-height-cache.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /**
  2. * @fileoverview added by tsickle
  3. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  4. */
  5. /**
  6. * This object contains the cache of the various row heights that are present inside
  7. * the data table. Its based on Fenwick tree data structure that helps with
  8. * querying sums that have time complexity of log n.
  9. *
  10. * Fenwick Tree Credits: http://petr-mitrichev.blogspot.com/2013/05/fenwick-tree-range-updates.html
  11. * https://github.com/mikolalysenko/fenwick-tree
  12. *
  13. */
  14. export class RowHeightCache {
  15. constructor() {
  16. /**
  17. * Tree Array stores the cumulative information of the row heights to perform efficient
  18. * range queries and updates. Currently the tree is initialized to the base row
  19. * height instead of the detail row height.
  20. */
  21. this.treeArray = [];
  22. }
  23. /**
  24. * Clear the Tree array.
  25. * @return {?}
  26. */
  27. clearCache() {
  28. this.treeArray = [];
  29. }
  30. /**
  31. * Initialize the Fenwick tree with row Heights.
  32. *
  33. * @param {?} details
  34. * @return {?}
  35. */
  36. initCache(details) {
  37. const { rows, rowHeight, detailRowHeight, externalVirtual, rowCount, rowIndexes, rowExpansions } = details;
  38. /** @type {?} */
  39. const isFn = typeof rowHeight === 'function';
  40. /** @type {?} */
  41. const isDetailFn = typeof detailRowHeight === 'function';
  42. if (!isFn && isNaN(rowHeight)) {
  43. throw new Error(`Row Height cache initialization failed. Please ensure that 'rowHeight' is a
  44. valid number or function value: (${rowHeight}) when 'scrollbarV' is enabled.`);
  45. }
  46. // Add this additional guard in case detailRowHeight is set to 'auto' as it wont work.
  47. if (!isDetailFn && isNaN(detailRowHeight)) {
  48. throw new Error(`Row Height cache initialization failed. Please ensure that 'detailRowHeight' is a
  49. valid number or function value: (${detailRowHeight}) when 'scrollbarV' is enabled.`);
  50. }
  51. /** @type {?} */
  52. const n = externalVirtual ? rowCount : rows.length;
  53. this.treeArray = new Array(n);
  54. for (let i = 0; i < n; ++i) {
  55. this.treeArray[i] = 0;
  56. }
  57. for (let i = 0; i < n; ++i) {
  58. /** @type {?} */
  59. const row = rows[i];
  60. /** @type {?} */
  61. let currentRowHeight = rowHeight;
  62. if (isFn) {
  63. currentRowHeight = rowHeight(row);
  64. }
  65. // Add the detail row height to the already expanded rows.
  66. // This is useful for the table that goes through a filter or sort.
  67. /** @type {?} */
  68. const expanded = rowExpansions.has(row);
  69. if (row && expanded) {
  70. if (isDetailFn) {
  71. /** @type {?} */
  72. const index = rowIndexes.get(row);
  73. currentRowHeight += detailRowHeight(row, index);
  74. }
  75. else {
  76. currentRowHeight += detailRowHeight;
  77. }
  78. }
  79. this.update(i, currentRowHeight);
  80. }
  81. }
  82. /**
  83. * Given the ScrollY position i.e. sum, provide the rowIndex
  84. * that is present in the current view port. Below handles edge cases.
  85. * @param {?} scrollY
  86. * @return {?}
  87. */
  88. getRowIndex(scrollY) {
  89. if (scrollY === 0)
  90. return 0;
  91. return this.calcRowIndex(scrollY);
  92. }
  93. /**
  94. * When a row is expanded or rowHeight is changed, update the height. This can
  95. * be utilized in future when Angular Data table supports dynamic row heights.
  96. * @param {?} atRowIndex
  97. * @param {?} byRowHeight
  98. * @return {?}
  99. */
  100. update(atRowIndex, byRowHeight) {
  101. if (!this.treeArray.length) {
  102. throw new Error(`Update at index ${atRowIndex} with value ${byRowHeight} failed:
  103. Row Height cache not initialized.`);
  104. }
  105. /** @type {?} */
  106. const n = this.treeArray.length;
  107. atRowIndex |= 0;
  108. while (atRowIndex < n) {
  109. this.treeArray[atRowIndex] += byRowHeight;
  110. atRowIndex |= atRowIndex + 1;
  111. }
  112. }
  113. /**
  114. * Range Sum query from 1 to the rowIndex
  115. * @param {?} atIndex
  116. * @return {?}
  117. */
  118. query(atIndex) {
  119. if (!this.treeArray.length) {
  120. throw new Error(`query at index ${atIndex} failed: Fenwick tree array not initialized.`);
  121. }
  122. /** @type {?} */
  123. let sum = 0;
  124. atIndex |= 0;
  125. while (atIndex >= 0) {
  126. sum += this.treeArray[atIndex];
  127. atIndex = (atIndex & (atIndex + 1)) - 1;
  128. }
  129. return sum;
  130. }
  131. /**
  132. * Find the total height between 2 row indexes
  133. * @param {?} atIndexA
  134. * @param {?} atIndexB
  135. * @return {?}
  136. */
  137. queryBetween(atIndexA, atIndexB) {
  138. return this.query(atIndexB) - this.query(atIndexA - 1);
  139. }
  140. /**
  141. * Given the ScrollY position i.e. sum, provide the rowIndex
  142. * that is present in the current view port.
  143. * @private
  144. * @param {?} sum
  145. * @return {?}
  146. */
  147. calcRowIndex(sum) {
  148. if (!this.treeArray.length)
  149. return 0;
  150. /** @type {?} */
  151. let pos = -1;
  152. /** @type {?} */
  153. const dataLength = this.treeArray.length;
  154. // Get the highest bit for the block size.
  155. /** @type {?} */
  156. const highestBit = Math.pow(2, dataLength.toString(2).length - 1);
  157. for (let blockSize = highestBit; blockSize !== 0; blockSize >>= 1) {
  158. /** @type {?} */
  159. const nextPos = pos + blockSize;
  160. if (nextPos < dataLength && sum >= this.treeArray[nextPos]) {
  161. sum -= this.treeArray[nextPos];
  162. pos = nextPos;
  163. }
  164. }
  165. return pos + 1;
  166. }
  167. }
  168. if (false) {
  169. /**
  170. * Tree Array stores the cumulative information of the row heights to perform efficient
  171. * range queries and updates. Currently the tree is initialized to the base row
  172. * height instead of the detail row height.
  173. * @type {?}
  174. * @private
  175. */
  176. RowHeightCache.prototype.treeArray;
  177. }
  178. //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm93LWhlaWdodC1jYWNoZS5qcyIsInNvdXJjZVJvb3QiOiJuZzovL0Bzd2ltbGFuZS9uZ3gtZGF0YXRhYmxlLyIsInNvdXJjZXMiOlsibGliL3V0aWxzL3Jvdy1oZWlnaHQtY2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7OztBQVNBLE1BQU0sT0FBTyxjQUFjO0lBQTNCOzs7Ozs7UUFNVSxjQUFTLEdBQWEsRUFBRSxDQUFDO0lBMkluQyxDQUFDOzs7OztJQXRJQyxVQUFVO1FBQ1IsSUFBSSxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDdEIsQ0FBQzs7Ozs7OztJQVNELFNBQVMsQ0FBQyxPQUFZO2NBQ2QsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxhQUFhLEVBQUUsR0FBRyxPQUFPOztjQUNwRyxJQUFJLEdBQUcsT0FBTyxTQUFTLEtBQUssVUFBVTs7Y0FDdEMsVUFBVSxHQUFHLE9BQU8sZUFBZSxLQUFLLFVBQVU7UUFFeEQsSUFBSSxDQUFDLElBQUksSUFBSSxLQUFLLENBQUMsU0FBUyxDQUFDLEVBQUU7WUFDN0IsTUFBTSxJQUFJLEtBQUssQ0FBQzsyQ0FDcUIsU0FBUyxpQ0FBaUMsQ0FBQyxDQUFDO1NBQ2xGO1FBRUQsc0ZBQXNGO1FBQ3RGLElBQUksQ0FBQyxVQUFVLElBQUksS0FBSyxDQUFDLGVBQWUsQ0FBQyxFQUFFO1lBQ3pDLE1BQU0sSUFBSSxLQUFLLENBQUM7MkNBQ3FCLGVBQWUsaUNBQWlDLENBQUMsQ0FBQztTQUN4Rjs7Y0FFSyxDQUFDLEdBQUcsZUFBZSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNO1FBQ2xELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFOUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMs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QUFFLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztRQUVqRSxLQUFLLElBQUksU0FBUyxHQUFHLFVBQVUsRUFBRSxTQUFTLEtBQUssQ0FBQyxFQUFFLFNBQVMsS0FBSyxDQUFDLEVBQUU7O2tCQUMzRCxPQUFPLEdBQUcsR0FBRyxHQUFHLFNBQVM7WUFDL0IsSUFBSSxPQUFPLEdBQUcsVUFBVSxJQUFJLEdBQUcsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUMxRCxHQUFHLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztnQkFDL0IsR0FBRyxHQUFHLE9BQU8sQ0FBQzthQUNmO1NBQ0Y7UUFFRCxPQUFPLEdBQUcsR0FBRyxDQUFDLENBQUM7SUFDakIsQ0FBQztDQUNGOzs7Ozs7Ozs7SUEzSUMsbUNBQWlDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaGlzIG9iamVjdCBjb250YWlucyB0aGUgY2FjaGUgb2YgdGhlIHZhcmlvdXMgcm93IGhlaWdodHMgdGhhdCBhcmUgcHJlc2VudCBpbnNpZGVcbiAqIHRoZSBkYXRhIHRhYmxlLiAgIEl0cyBiYXNlZCBvbiBGZW53aWNrIHRyZWUgZGF0YSBzdHJ1Y3R1cmUgdGhhdCBoZWxwcyB3aXRoXG4gKiBxdWVyeWluZyBzdW1zIHRoYXQgaGF2ZSB0aW1lIGNvbXBsZXhpdHkgb2YgbG9nIG4uXG4gKlxuICogRmVud2ljayBUcmVlIENyZWRpdHM6IGh0dHA6Ly9wZXRyLW1pdHJpY2hldi5ibG9nc3BvdC5jb20vMjAxMy8wNS9mZW53aWNrLXRyZWUtcmFuZ2UtdXBkYXRlcy5odG1sXG4gKiBodHRwczovL2dpdGh1Yi5jb20vbWlrb2xhbHlzZW5rby9mZW53aWNrLXRyZWVcbiAqXG4gKi9cbmV4cG9ydCBjbGFzcyBSb3dIZWlnaHRDYWNoZSB7XG4gIC8qKlxuICAgKiBUcmVlIEFycmF5IHN0b3JlcyB0aGUgY3VtdWxhdGl2ZSBpbmZvcm1hdGlvbiBvZiB0aGUgcm93IGhlaWdodHMgdG8gcGVyZm9ybSBlZmZpY2llbnRcbiAgICogcmFuZ2UgcXVlcmllcyBhbmQgdXBkYXRlcy4gIEN1cnJlbnRseSB0aGUgdHJlZSBpcyBpbml0aWFsaXplZCB0byB0aGUgYmFzZSByb3dcbiAgICogaGVpZ2h0IGluc3RlYWQgb2YgdGhlIGRldGFpbCByb3cgaGVpZ2h0LlxuICAgKi9cbiAgcHJpdmF0ZSB0cmVlQXJyYXk6IG51bWJlcltdID0gW107XG5cbiAgLyoqXG4gICAqIENsZWFyIHRoZSBUcmVlIGFycmF5LlxuICAgKi9cbiAgY2xlYXJDYWNoZSgpOiB2b2lkIHtcbiAgICB0aGlzLnRyZWVBcnJheSA9IFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemUgdGhlIEZlbndpY2sgdHJlZSB3aXRoIHJvdyBIZWlnaHRzLlxuICAgKlxuICAgKiBAcGFyYW0gcm93cyBUaGUgYXJyYXkgb2Ygcm93cyB3aGljaCBjb250YWluIHRoZSBleHBhbmRlZCBzdGF0dXMuXG4gICAqIEBwYXJhbSByb3dIZWlnaHQgVGhlIHJvdyBoZWlnaHQuXG4gICAqIEBwYXJhbSBkZXRhaWxSb3dIZWlnaHQgVGhlIGRldGFpbCByb3cgaGVpZ2h0LlxuICAgKi9cbiAgaW5pdENhY2hlKGRldGFpbHM6IGFueSk6IHZvaWQge1xuICAgIGNvbnN0IHsgcm93cywgcm93SGVpZ2h0LCBkZXRhaWxSb3dIZWlnaHQsIGV4dGVybmFsVmlydHVhbCwgcm93Q291bnQsIHJvd0luZGV4ZXMsIHJvd0V4cGFuc2lvbnMgfSA9IGRldGFpbHM7XG4gICAgY29uc3QgaXNGbiA9IHR5cGVvZiByb3dIZWlnaHQgPT09ICdmdW5jdGlvbic7XG4gICAgY29uc3QgaXNEZXRhaWxGbiA9IHR5cGVvZiBkZXRhaWxSb3dIZWlnaHQgPT09ICdmdW5jdGlvbic7XG5cbiAgICBpZiAoIWlzRm4gJiYgaXNOYU4ocm93SGVpZ2h0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBSb3cgSGVpZ2h0IGNhY2hlIGluaXRpYWxpemF0aW9uIGZhaWxlZC4gUGxlYXNlIGVuc3VyZSB0aGF0ICdyb3dIZWlnaHQnIGlzIGFcbiAgICAgICAgdmFsaWQgbnVtYmVyIG9yIGZ1bmN0aW9uIHZhbHVlOiAoJHtyb3dIZWlnaHR9KSB3aGVuICdzY3JvbGxiYXJWJyBpcyBlbmFibGVkLmApO1xuICAgIH1cblxuICAgIC8vIEFkZCB0aGlzIGFkZGl0aW9uYWwgZ3VhcmQgaW4gY2FzZSBkZXRhaWxSb3dIZWlnaHQgaXMgc2V0IHRvICdhdXRvJyBhcyBpdCB3b250IHdvcmsuXG4gICAgaWYgKCFpc0RldGFpbEZuICYmIGlzTmFOKGRldGFpbFJvd0hlaWdodCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgUm93IEhlaWdodCBjYWNoZSBpbml0aWFsaXphdGlvbiBmYWlsZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCAnZGV0YWlsUm93SGVpZ2h0JyBpcyBhXG4gICAgICAgIHZhbGlkIG51bWJlciBvciBmdW5jdGlvbiB2YWx1ZTogKCR7ZGV0YWlsUm93SGVpZ2h0fSkgd2hlbiAnc2Nyb2xsYmFyVicgaXMgZW5hYmxlZC5gKTtcbiAgICB9XG5cbiAgICBjb25zdCBuID0gZXh0ZXJuYWxWaXJ0dWFsID8gcm93Q291bnQgOiByb3dzLmxlbmd0aDtcbiAgICB0aGlzLnRyZWVBcnJheSA9IG5ldyBBcnJheShuKTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgICB0aGlzLnRyZWVBcnJheVtpXSA9IDA7XG4gICAgfVxuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyArK2kpIHtcbiAgICAgIGNvbnN0IHJvdyA9IHJvd3NbaV07XG4gICAgICBsZXQgY3VycmVudFJvd0hlaWdodCA9IHJvd0hlaWdodDtcbiAgICAgIGlmIChpc0ZuKSB7XG4gICAgICAgIGN1cnJlbnRSb3dIZWlnaHQgPSByb3dIZWlnaHQocm93KTtcbiAgICAgIH1cblxuICAgICAgLy8gQWRkIHRoZSBkZXRhaWwgcm93IGhlaWdodCB0byB0aGUgYWxyZWFkeSBleHBhbmRlZCByb3dzLlxuICAgICAgLy8gVGhpcyBpcyB1c2VmdWwgZm9yIHRoZSB0YWJsZSB0aGF0IGdvZXMgdGhyb3VnaCBhIGZpbHRlciBvciBzb3J0LlxuICAgICAgY29uc3QgZXhwYW5kZWQgPSByb3dFeHBhbnNpb25zLmhhcyhyb3cpO1xuICAgICAgaWYgKHJvdyAmJiBleHBhbmRlZCkge1xuICAgICAgICBpZiAoaXNEZXRhaWxGbikge1xuICAgICAgICAgIGNvbnN0IGluZGV4ID0gcm93SW5kZXhlcy5nZXQocm93KTtcbiAgICAgICAgICBjdXJyZW50Um93SGVpZ2h0ICs9IGRldGFpbFJvd0hlaWdodChyb3csIGluZGV4KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjdXJyZW50Um93SGVpZ2h0ICs9IGRldGFpbFJvd0hlaWdodDtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICB0aGlzLnVwZGF0ZShpLCBjdXJyZW50Um93SGVpZ2h0KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2l2ZW4gdGhlIFNjcm9sbFkgcG9zaXRpb24gaS5lLiBzdW0sIHByb3ZpZGUgdGhlIHJvd0luZGV4XG4gICAqIHRoYXQgaXMgcHJlc2VudCBpbiB0aGUgY3VycmVudCB2aWV3IHBvcnQuICBCZWxvdyBoYW5kbGVzIGVkZ2UgY2FzZXMuXG4gICAqL1xuICBnZXRSb3dJbmRleChzY3JvbGxZOiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmIChzY3JvbGxZID09PSAwKSByZXR1cm4gMDtcbiAgICByZXR1cm4gdGhpcy5jYWxjUm93SW5kZXgoc2Nyb2xsWSk7XG4gIH1cblxuICAvKipcbiAgICogV2hlbiBhIHJvdyBpcyBleHBhbmRlZCBvciByb3dIZWlnaHQgaXMgY2hhbmdlZCwgdXBkYXRlIHRoZSBoZWlnaHQuICBUaGlzIGNhblxuICAgKiBiZSB1dGlsaXplZCBpbiBmdXR1cmUgd2hlbiBBbmd1bGFyIERhdGEgdGFibGUgc3VwcG9ydHMgZHluYW1pYyByb3cgaGVpZ2h0cy5cbiAgICovXG4gIHVwZGF0ZShhdFJvd0luZGV4OiBudW1iZXIsIGJ5Um93SGVpZ2h0OiBudW1iZXIpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMudHJlZUFycmF5Lmxlbmd0aCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVcGRhdGUgYXQgaW5kZXggJHthdFJvd0luZGV4fSB3aXRoIHZhbHVlICR7YnlSb3dIZWlnaHR9IGZhaWxlZDpcbiAgICAgICAgUm93IEhlaWdodCBjYWNoZSBub3QgaW5pdGlhbGl6ZWQuYCk7XG4gICAgfVxuXG4gICAgY29uc3QgbiA9IHRoaXMudHJlZUFycmF5Lmxlbmd0aDtcbiAgICBhdFJvd0luZGV4IHw9IDA7XG5cbiAgICB3aGlsZSAoYXRSb3dJbmRleCA8IG4pIHtcbiAgICAgIHRoaXMudHJlZUFycmF5W2F0Um93SW5kZXhdICs9IGJ5Um93SGVpZ2h0O1xuICAgICAgYXRSb3dJbmRleCB8PSBhdFJvd0luZGV4ICsgMTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmFuZ2UgU3VtIHF1ZXJ5IGZyb20gMSB0byB0aGUgcm93SW5kZXhcbiAgICovXG4gIHF1ZXJ5KGF0SW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKCF0aGlzLnRyZWVBcnJheS5sZW5ndGgpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgcXVlcnkgYXQgaW5kZXggJHthdEluZGV4fSBmYWlsZWQ6IEZlbndpY2sgdHJlZSBhcnJheSBub3QgaW5pdGlhbGl6ZWQuYCk7XG4gICAgfVxuXG4gICAgbGV0IHN1bSA9IDA7XG4gICAgYXRJbmRleCB8PSAwO1xuXG4gICAgd2hpbGUgKGF0SW5kZXggPj0gMCkge1xuICAgICAgc3VtICs9IHRoaXMudHJlZUFycmF5W2F0SW5kZXhdO1xuICAgICAgYXRJbmRleCA9IChhdEluZGV4ICYgKGF0SW5kZXggKyAxKSkgLSAxO1xuICAgIH1cblxuICAgIHJldHVybiBzdW07XG4gIH1cblxuICAvKipcbiAgICogRmluZCB0aGUgdG90YWwgaGVpZ2h0IGJldHdlZW4gMiByb3cgaW5kZXhlc1xuICAgKi9cbiAgcXVlcnlCZXR3ZWVuKGF0SW5kZXhBOiBudW1iZXIsIGF0SW5kZXhCOiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnF1ZXJ5KGF0SW5kZXhCKSAtIHRoaXMucXVlcnkoYXRJbmRleEEgLSAxKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHaXZlbiB0aGUgU2Nyb2xsWSBwb3NpdGlvbiBpLmUuIHN1bSwgcHJvdmlkZSB0aGUgcm93SW5kZXhcbiAgICogdGhhdCBpcyBwcmVzZW50IGluIHRoZSBjdXJyZW50IHZpZXcgcG9ydC5cbiAgICovXG4gIHByaXZhdGUgY2FsY1Jvd0luZGV4KHN1bTogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBpZiAoIXRoaXMudHJlZUFycmF5Lmxlbmd0aCkgcmV0dXJuIDA7XG5cbiAgICBsZXQgcG9zID0gLTE7XG4gICAgY29uc3QgZGF0YUxlbmd0aCA9IHRoaXMudHJlZUFycmF5Lmxlbmd0aDtcblxuICAgIC8vIEdldCB0aGUgaGlnaGVzdCBiaXQgZm9yIHRoZSBibG9jayBzaXplLlxuICAgIGNvbnN0IGhpZ2hlc3RCaXQgPSBNYXRoLnBvdygyLCBkYXRhTGVuZ3RoLnRvU3RyaW5nKDIpLmxlbmd0aCAtIDEpO1xuXG4gICAgZm9yIChsZXQgYmxvY2tTaXplID0gaGlnaGVzdEJpdDsgYmxvY2tTaXplICE9PSAwOyBibG9ja1NpemUgPj49IDEpIHtcbiAgICAgIGNvbnN0IG5leHRQb3MgPSBwb3MgKyBibG9ja1NpemU7XG4gICAgICBpZiAobmV4dFBvcyA8IGRhdGFMZW5ndGggJiYgc3VtID49IHRoaXMudHJlZUFycmF5W25leHRQb3NdKSB7XG4gICAgICAgIHN1bSAtPSB0aGlzLnRyZWVBcnJheVtuZXh0UG9zXTtcbiAgICAgICAgcG9zID0gbmV4dFBvcztcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcG9zICsgMTtcbiAgfVxufVxuIl19