@21epub/epub-thirdparty
Version:
epub-thirdparty
168 lines (167 loc) • 6.13 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const MAX_FOLDING_REGIONS = 0xFFFF;
export const MAX_LINE_NUMBER = 0xFFFFFF;
const MASK_INDENT = 0xFF000000;
export class FoldingRegions {
constructor(startIndexes, endIndexes, types) {
if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) {
throw new Error('invalid startIndexes or endIndexes size');
}
this._startIndexes = startIndexes;
this._endIndexes = endIndexes;
this._collapseStates = new Uint32Array(Math.ceil(startIndexes.length / 32));
this._types = types;
this._parentsComputed = false;
}
ensureParentIndices() {
if (!this._parentsComputed) {
this._parentsComputed = true;
let parentIndexes = [];
let isInsideLast = (startLineNumber, endLineNumber) => {
let index = parentIndexes[parentIndexes.length - 1];
return this.getStartLineNumber(index) <= startLineNumber && this.getEndLineNumber(index) >= endLineNumber;
};
for (let i = 0, len = this._startIndexes.length; i < len; i++) {
let startLineNumber = this._startIndexes[i];
let endLineNumber = this._endIndexes[i];
if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
throw new Error('startLineNumber or endLineNumber must not exceed ' + MAX_LINE_NUMBER);
}
while (parentIndexes.length > 0 && !isInsideLast(startLineNumber, endLineNumber)) {
parentIndexes.pop();
}
let parentIndex = parentIndexes.length > 0 ? parentIndexes[parentIndexes.length - 1] : -1;
parentIndexes.push(i);
this._startIndexes[i] = startLineNumber + ((parentIndex & 0xFF) << 24);
this._endIndexes[i] = endLineNumber + ((parentIndex & 0xFF00) << 16);
}
}
}
get length() {
return this._startIndexes.length;
}
getStartLineNumber(index) {
return this._startIndexes[index] & MAX_LINE_NUMBER;
}
getEndLineNumber(index) {
return this._endIndexes[index] & MAX_LINE_NUMBER;
}
getType(index) {
return this._types ? this._types[index] : undefined;
}
hasTypes() {
return !!this._types;
}
isCollapsed(index) {
let arrayIndex = (index / 32) | 0;
let bit = index % 32;
return (this._collapseStates[arrayIndex] & (1 << bit)) !== 0;
}
setCollapsed(index, newState) {
let arrayIndex = (index / 32) | 0;
let bit = index % 32;
let value = this._collapseStates[arrayIndex];
if (newState) {
this._collapseStates[arrayIndex] = value | (1 << bit);
}
else {
this._collapseStates[arrayIndex] = value & ~(1 << bit);
}
}
setCollapsedAllOfType(type, newState) {
let hasChanged = false;
if (this._types) {
for (let i = 0; i < this._types.length; i++) {
if (this._types[i] === type) {
this.setCollapsed(i, newState);
hasChanged = true;
}
}
}
return hasChanged;
}
toRegion(index) {
return new FoldingRegion(this, index);
}
getParentIndex(index) {
this.ensureParentIndices();
let parent = ((this._startIndexes[index] & MASK_INDENT) >>> 24) + ((this._endIndexes[index] & MASK_INDENT) >>> 16);
if (parent === MAX_FOLDING_REGIONS) {
return -1;
}
return parent;
}
contains(index, line) {
return this.getStartLineNumber(index) <= line && this.getEndLineNumber(index) >= line;
}
findIndex(line) {
let low = 0, high = this._startIndexes.length;
if (high === 0) {
return -1; // no children
}
while (low < high) {
let mid = Math.floor((low + high) / 2);
if (line < this.getStartLineNumber(mid)) {
high = mid;
}
else {
low = mid + 1;
}
}
return low - 1;
}
findRange(line) {
let index = this.findIndex(line);
if (index >= 0) {
let endLineNumber = this.getEndLineNumber(index);
if (endLineNumber >= line) {
return index;
}
index = this.getParentIndex(index);
while (index !== -1) {
if (this.contains(index, line)) {
return index;
}
index = this.getParentIndex(index);
}
}
return -1;
}
toString() {
let res = [];
for (let i = 0; i < this.length; i++) {
res[i] = `[${this.isCollapsed(i) ? '+' : '-'}] ${this.getStartLineNumber(i)}/${this.getEndLineNumber(i)}`;
}
return res.join(', ');
}
}
export class FoldingRegion {
constructor(ranges, index) {
this.ranges = ranges;
this.index = index;
}
get startLineNumber() {
return this.ranges.getStartLineNumber(this.index);
}
get endLineNumber() {
return this.ranges.getEndLineNumber(this.index);
}
get regionIndex() {
return this.index;
}
get parentIndex() {
return this.ranges.getParentIndex(this.index);
}
get isCollapsed() {
return this.ranges.isCollapsed(this.index);
}
containedBy(range) {
return range.startLineNumber <= this.startLineNumber && range.endLineNumber >= this.endLineNumber;
}
containsLine(lineNumber) {
return this.startLineNumber <= lineNumber && lineNumber <= this.endLineNumber;
}
}