Added page break methods
This commit is contained in:
parent
b7e3c1b2f5
commit
460d58d4d6
@ -4,20 +4,20 @@
|
||||
|
||||
import { CacheInterface, HTMLPagination } from "../index";
|
||||
|
||||
describe("HTMLPagination", () => {
|
||||
let hp: HTMLPagination;
|
||||
let hp: HTMLPagination;
|
||||
|
||||
const content = document.createElement("div");
|
||||
const container = document.createElement("div");
|
||||
class Cache extends CacheInterface {
|
||||
g(key: string) {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
s(key: string, value: string) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
const content = document.createElement("div");
|
||||
const container = document.createElement("div");
|
||||
class Cache extends CacheInterface {
|
||||
g(key: string) {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
s(key: string, value: string) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
describe("Text position stuff", () => {
|
||||
beforeEach(() => {
|
||||
content.innerHTML = "";
|
||||
content.innerHTML = "<div><p>aa</p><p>bb<span>cc</span>dd</p></div>";
|
||||
@ -27,22 +27,42 @@ describe("HTMLPagination", () => {
|
||||
hp = new HTMLPagination(content, container, new Cache());
|
||||
});
|
||||
|
||||
test("Computes positions for each text node", () => {
|
||||
it("computes positions for each text node", () => {
|
||||
expect(hp.elementPositions.map((el) => el[0])).toEqual([0, 2, 4, 6]);
|
||||
});
|
||||
|
||||
test("Gets element by position", () => {
|
||||
it("gets element by position", () => {
|
||||
expect(hp.getElementForPosition(0)[0]).toBe(0);
|
||||
expect(hp.getElementForPosition(1)[0]).toBe(0);
|
||||
expect(hp.getElementForPosition(2)[0]).toBe(2);
|
||||
expect(hp.getElementForPosition(3)[0]).toBe(2);
|
||||
});
|
||||
|
||||
test("Gets html content for range of text", () => {
|
||||
expect(hp.getFromRange(0, 8)).toEqual(
|
||||
it("gets html content for range of text", () => {
|
||||
expect(hp.getContentFromRange(0, 8)).toEqual(
|
||||
"<p>aa</p><p>bb<span>cc</span>dd</p>"
|
||||
);
|
||||
expect(hp.getFromRange(0, 3)).toEqual("<p>aa</p><p>b</p>");
|
||||
expect(hp.getFromRange(5, 7)).toEqual("<span>c</span>d");
|
||||
expect(hp.getContentFromRange(0, 3)).toEqual("<p>aa</p><p>b</p>");
|
||||
expect(hp.getContentFromRange(5, 7)).toEqual("<span>c</span>d");
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Add pagination tests using puppeteer
|
||||
|
||||
// let browser: puppeteer.Browser, page: puppeteer.Page;
|
||||
|
||||
// describe("Page splitting stuff", () => {
|
||||
// beforeAll(async () => {
|
||||
// browser = await puppeteer.launch({
|
||||
// defaultViewport: {
|
||||
// height: 150,
|
||||
// width: 150,
|
||||
// },
|
||||
// });
|
||||
// page = await browser.newPage();
|
||||
// });
|
||||
|
||||
// it("Inserts page break to prevent scroll", async () => {
|
||||
// await page.goto("localhost:5000");
|
||||
// });
|
||||
// });
|
||||
|
106
src/index.ts
106
src/index.ts
@ -1,12 +1,13 @@
|
||||
import { CacheInterface } from "./cache";
|
||||
import { isElementNode, isTextNode, TextNode } from "./types";
|
||||
import { binSearch } from "./utils";
|
||||
|
||||
class HTMLPagination {
|
||||
content: HTMLElement;
|
||||
container: HTMLElement;
|
||||
cache: CacheInterface;
|
||||
|
||||
elementPositions: [number, TextNode][];
|
||||
elementPositions: [number, Node][];
|
||||
idPositions: Map<string, number>;
|
||||
|
||||
/**
|
||||
@ -33,7 +34,7 @@ class HTMLPagination {
|
||||
const from = 0;
|
||||
const to = 1;
|
||||
|
||||
return this.getFromRange(from, to);
|
||||
return this.getContentFromRange(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,27 +58,106 @@ class HTMLPagination {
|
||||
recursive(0, this.content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds position for next page break
|
||||
* initialJump may be computed in a clever way
|
||||
*/
|
||||
getPageBreak(start: number, initialJump: number) {
|
||||
let previousEnd = this.getMaxPosition();
|
||||
let end = this.getNextSpaceForPosition(start + initialJump);
|
||||
|
||||
this.getContentFromRange(start, end);
|
||||
while (!this.scrollNecessary() && end < this.getMaxPosition()) {
|
||||
previousEnd = end;
|
||||
end = this.getNextSpaceForPosition(end + 1);
|
||||
this.getContentFromRange(start, end);
|
||||
}
|
||||
|
||||
while (this.scrollNecessary() && end > start) {
|
||||
previousEnd = end;
|
||||
end = this.getPreviousSpaceForPosition(end - 1);
|
||||
this.getContentFromRange(start, end);
|
||||
}
|
||||
|
||||
if (start === end) return previousEnd;
|
||||
else return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets next space or gap between elements for position
|
||||
*/
|
||||
getNextSpaceForPosition(startPos: number): number {
|
||||
const nodeIndex = this.getElementIndexForPosition(startPos);
|
||||
const [nodePosition, node] = this.elementPositions[nodeIndex];
|
||||
|
||||
let nodeOffset = startPos - nodePosition;
|
||||
const str = node.nodeValue || "";
|
||||
while (nodeOffset < str.length && str.charAt(nodeOffset) !== " ")
|
||||
nodeOffset++;
|
||||
|
||||
if (nodeOffset === str.length) {
|
||||
if (nodeIndex === this.elementPositions.length - 1)
|
||||
return this.getMaxPosition();
|
||||
else return this.elementPositions[nodeIndex + 1][0];
|
||||
} else {
|
||||
return this.elementPositions[nodeIndex][0] + nodeOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets previous space or gap between elements for position
|
||||
*/
|
||||
getPreviousSpaceForPosition(startPos: number): number {
|
||||
const nodeIndex = this.getElementIndexForPosition(startPos);
|
||||
const [nodePosition, node] = this.elementPositions[nodeIndex];
|
||||
|
||||
let nodeOffset = startPos - nodePosition;
|
||||
const str = node.nodeValue || "";
|
||||
while (nodeOffset > 0 && str.charAt(nodeOffset) !== " ") nodeOffset--;
|
||||
|
||||
return this.elementPositions[nodeIndex][0] + nodeOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if container is overflowing with content
|
||||
*/
|
||||
scrollNecessary(): boolean {
|
||||
return this.container.clientHeight < this.container.scrollHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns end position of content
|
||||
*/
|
||||
getMaxPosition(): number {
|
||||
const [offset, element] =
|
||||
this.elementPositions[this.elementPositions.length - 1];
|
||||
return offset + (element.nodeValue?.length || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for `binSearch` util to find index of element for position
|
||||
*/
|
||||
getElementIndexForPosition(pos: number): number {
|
||||
return binSearch(
|
||||
this.elementPositions,
|
||||
pos,
|
||||
(i) => this.elementPositions[i][0]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds node inside which `pos` is located. Returns node positions and itself
|
||||
*/
|
||||
getElementForPosition(pos: number): [number, Node] {
|
||||
let s = 0,
|
||||
e = this.elementPositions.length - 1;
|
||||
const elementIndex = this.getElementIndexForPosition(pos);
|
||||
|
||||
while (s <= e) {
|
||||
const c = (s + e) >> 1;
|
||||
if (pos > this.elementPositions[c][0]) s = c + 1;
|
||||
else if (pos < this.elementPositions[c][0]) e = c - 1;
|
||||
else return this.elementPositions[c];
|
||||
}
|
||||
|
||||
return this.elementPositions[s - 1];
|
||||
return this.elementPositions[elementIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets `container` element content and return as string html content between `from` and `to`
|
||||
*/
|
||||
getFromRange(from: number, to: number): string {
|
||||
getContentFromRange(from: number, to: number): string {
|
||||
this.container.innerHTML = "";
|
||||
const range = new Range();
|
||||
|
||||
|
21
src/utils.ts
Normal file
21
src/utils.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Binarycally searches element in array or last element lower than searched
|
||||
* @param getter Function to convert array element to comparable with searched element
|
||||
*/
|
||||
export const binSearch = <T>(
|
||||
arr: T[],
|
||||
el: number,
|
||||
getter: (i: number) => number
|
||||
): number => {
|
||||
let s = 0,
|
||||
e = arr.length - 1;
|
||||
|
||||
while (s <= e) {
|
||||
const c = (s + e) >> 1;
|
||||
if (el > getter(c)) s = c + 1;
|
||||
else if (el < getter(c)) e = c - 1;
|
||||
else return c;
|
||||
}
|
||||
|
||||
return s - 1;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user