UNPKG

vscroll

Version:
250 lines 8.43 kB
import { DefaultSize } from './defaultSize'; import { Direction } from '../../inputs/index'; export class ItemCache { constructor(item, saveData) { this.$index = item.$index; this.data = saveData ? item.data : null; this.size = item.size; } changeIndex(value) { this.$index = value; } } export class Cache { constructor({ itemSize, cacheData, cacheOnReload, sizeStrategy }, logger) { this.itemSize = itemSize; this.saveData = cacheData; this.cacheOnReload = cacheOnReload; this.sizeStrategy = sizeStrategy; this.logger = logger; this.items = new Map(); this.defaultSize = new DefaultSize(itemSize, sizeStrategy); this.reset(true); } reset(force) { force = force || !this.cacheOnReload; if (force) { this.minIndex = +Infinity; this.maxIndex = -Infinity; this.items.clear(); } this.defaultSize.reset(force); } get size() { return this.items.size; } get(index) { return this.items.get(index); } getSizeByIndex(index) { const item = this.get(index); return (item && item.size) || this.defaultSize.get(); } getDefaultSize() { return this.defaultSize.get(); } recalculateDefaultSize() { if (this.defaultSize.recalculate(this.size)) { this.logger.log(() => `default size has been updated: ${this.defaultSize.get()}`); return true; } return false; } /** * Adds item to Set by $index, replaces existed item if $index matches. * Maintains min/max indexes and default item size. * * @param {Item<Data>} item A Buffer item to be cached, an objects with { $index, data, size } props. * @returns {ItemCache<Data>} Cached item. */ add(item) { let itemCache = this.get(item.$index); if (itemCache) { // adding item is already cached if (this.saveData) { itemCache.data = item.data; } if (itemCache.size !== item.size) { if (itemCache.size) { this.defaultSize.setExisted(itemCache.size, item.size); } else { this.defaultSize.setNew(item.size); } itemCache.size = item.size; } } else { itemCache = new ItemCache(item, this.saveData); this.items.set(item.$index, itemCache); this.defaultSize.setNew(item.size); } if (item.$index < this.minIndex) { this.minIndex = item.$index; } if (item.$index > this.maxIndex) { this.maxIndex = item.$index; } return itemCache; } /** * Inserts items to Set, shifts $indexes of items that remain. * Replaces current Set with a new one with new regular $indexes. * Maintains min/max indexes. * * @param {Data[]} toInsert List of non-indexed items to be inserted. * @param {number} index The index before/after which the insertion is performed. * @param {Direction} direction Determines the direction of insertion. * @param {boolean} fixRight Defines indexes shifting strategy. * If false, indexes that are greater than the inserted ones are increased. * If true, indexes that are less than than the inserted ones are decreased. */ insertItems(toInsert, index, direction, fixRight) { const items = new Map(); const length = toInsert.length; let min = Infinity, max = -Infinity; const set = (item) => { items.set(item.$index, item); min = item.$index < min ? item.$index : min; max = item.$index > max ? item.$index : max; }; this.items.forEach(item => { let shift = 0; if (direction === Direction.backward) { if (item.$index < index && fixRight) { shift = -length; } else if (item.$index >= index && !fixRight) { shift = length; } } else if (direction === Direction.forward) { if (item.$index <= index && fixRight) { shift = -length; } else if (item.$index > index && !fixRight) { shift = length; } } if (shift) { item.changeIndex(item.$index + shift); } set(item); }); if (this.saveData) { // persist data with no sizes toInsert.forEach((data, i) => { const $index = index + i - (fixRight ? length : 0) + (direction === Direction.forward ? 1 : 0); const item = new ItemCache({ $index, data }, this.saveData); set(item); }); } this.items = items; this.minIndex = min; this.maxIndex = max; } /** * Removes items from Set, shifts $indexes of items that remain. * Replaces current Set with a new one with new regular $indexes. * Maintains min/max indexes and default item size. * * @param {number[]} toRemove List of indexes to be removed. * @param {boolean} fixRight Defines indexes shifting strategy. * If false, indexes that are greater than the removed ones will be decreased. * If true, indexes that are less than than the removed ones will be increased. */ removeItems(toRemove, fixRight) { const items = new Map(); let min = Infinity, max = -Infinity; this.items.forEach(item => { if (toRemove.some(index => index === item.$index)) { if (item.size) { this.defaultSize.setRemoved(item.size); } return; } const diff = fixRight ? toRemove.reduce((acc, index) => acc + (item.$index < index ? 1 : 0), 0) : toRemove.reduce((acc, index) => acc - (item.$index > index ? 1 : 0), 0); item.changeIndex(item.$index + diff); items.set(item.$index, item); min = item.$index < min ? item.$index : min; max = item.$index > max ? item.$index : max; }); this.items = items; this.minIndex = min; this.maxIndex = max; } /** * Destructively updates Set based on subset (before-after) changes. * Replaces current Set with a new one with new regular $indexes. * Maintains min/max indexes. Maintains default item size on remove only. * * @param {ItemUpdate[]} before Initial subset of items to be replaced by "after". * Each element is an object with { $index, size, toRemove } props. Must be $index-incremental. * Items to be removed must have toRemove flag: before[].toRemove = true. * @param {Item<Data>[]} after Transformed subset that replaces "before". Must be $index-incremental. * Must contain at least 1 $index from "before" or be empty. * @param {boolean} fixRight This is to fix right indexes during subset collapsing. Acts only if "after" is empty. */ updateSubset(before, after, fixRight) { if (!this.size || !before.length) { return; } const minB = before[0].$index, maxB = before[before.length - 1].$index; let leftDiff, rightDiff; if (after.length) { const minA = after[0].$index, maxA = after[after.length - 1].$index; leftDiff = minA - minB; rightDiff = maxA - maxB; } else { leftDiff = fixRight ? maxB - minB + 1 : 0; rightDiff = fixRight ? 0 : minB - maxB - 1; } const items = new Map(); this.items.forEach(item => { if (item.$index < minB) { // items to the left of the subset item.changeIndex(item.$index + leftDiff); items.set(item.$index, item); return; } else if (item.$index > maxB) { // items to the right of the subset item.changeIndex(item.$index + rightDiff); items.set(item.$index, item); return; } }); after.forEach((item // subset items ) => items.set(item.$index, new ItemCache(item, this.saveData))); before // to maintain default size on remove .filter(item => item.toRemove) .forEach(item => this.defaultSize.setRemoved(item.size)); this.minIndex += leftDiff; this.maxIndex += rightDiff; this.items = items; } /** * Shifts all indexes by some value. * Replaces current Set with a new one with new regular $indexes. * Maintains min/max indexes. * * @param {number} delta A shift value. */ shiftIndexes(delta) { const items = new Map(); let min = Infinity, max = -Infinity; this.items.forEach(item => { item.changeIndex(item.$index + delta); items.set(item.$index, item); min = item.$index < min ? item.$index : min; max = item.$index > max ? item.$index : max; }); this.items = items; this.minIndex = min; this.maxIndex = max; } } //# sourceMappingURL=cache.js.map