billy-herrington-utils 2.0.7 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +360 -62
  2. package/dist/billy-herrington-utils.es.js +275 -6251
  3. package/dist/billy-herrington-utils.es.js.map +1 -1
  4. package/dist/billy-herrington-utils.umd.js +321 -6253
  5. package/dist/billy-herrington-utils.umd.js.map +1 -1
  6. package/dist/index.d.ts +69 -190
  7. package/package.json +18 -7
  8. package/src/index.ts +1 -44
  9. package/src/types/index.ts +7 -0
  10. package/src/utils/arrays/index.ts +11 -5
  11. package/src/utils/async/index.ts +0 -88
  12. package/src/utils/dom/{observers.ts → dom-observers.ts} +3 -3
  13. package/src/utils/dom/index.ts +97 -16
  14. package/src/utils/events/index.ts +2 -37
  15. package/src/utils/events/on-hover.ts +42 -0
  16. package/src/utils/events/tick.ts +27 -0
  17. package/src/utils/fetch/index.ts +30 -28
  18. package/src/utils/index.ts +39 -0
  19. package/src/utils/objects/index.ts +19 -18
  20. package/src/utils/objects/memoize.ts +25 -0
  21. package/src/utils/observers/index.ts +4 -28
  22. package/src/utils/observers/lazy-image-loader.ts +27 -0
  23. package/src/utils/parsers/index.ts +2 -20
  24. package/src/utils/parsers/time-parser.ts +28 -0
  25. package/src/utils/strings/regexes.ts +12 -8
  26. package/src/types/globals.d.ts +0 -0
  27. package/src/userscripts/data-manager/data-filter.ts +0 -110
  28. package/src/userscripts/data-manager/index.ts +0 -141
  29. package/src/userscripts/infinite-scroll/index.ts +0 -106
  30. package/src/userscripts/pagination-parsing/index.ts +0 -55
  31. package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategy.ts +0 -42
  32. package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategyDataParams.ts +0 -66
  33. package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategyPathnameParams.ts +0 -77
  34. package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategySearchParams.ts +0 -56
  35. package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategyTrash.ts +0 -33
  36. package/src/userscripts/pagination-parsing/pagination-strategies/index.ts +0 -5
  37. package/src/userscripts/pagination-parsing/pagination-utils/index.ts +0 -69
  38. package/src/userscripts/router/router.ts +0 -71
  39. package/src/userscripts/rules/index.ts +0 -241
  40. package/src/userscripts/types/index.ts +0 -19
  41. package/src/utils/device/index.ts +0 -3
  42. package/src/utils/userscript/index.ts +0 -10
@@ -1,110 +0,0 @@
1
- import type { StoreState } from 'jabroni-outfit';
2
- import { RegexFilter } from '../../utils/strings/regexes';
3
- import type { RulesGlobal } from '../rules';
4
- import type { DataElement } from '.';
5
-
6
- export type CustomSelector<R> = {
7
- handle: (el: DataElement, state: StoreState, $preDefineResult?: R) => boolean;
8
- $preDefine?: (state: StoreState) => R;
9
- };
10
-
11
- interface FilterResult {
12
- tag: string;
13
- condition: boolean;
14
- }
15
-
16
- export type FilterFunction = (v: DataElement) => FilterResult;
17
-
18
- export class DataFilter {
19
- public filters = new Map<string, () => FilterFunction>();
20
-
21
- constructor(
22
- private rules: RulesGlobal,
23
- private state: StoreState,
24
- ) {
25
- this.registerFilters(DataFilter.customFiltersDefault);
26
- this.registerFilters(rules.customSelectors);
27
- this.applyCSSFilters();
28
- }
29
-
30
- public applyCSSFilters() {
31
- Object.keys(this.filters).forEach((name) => {
32
- const className = `.filter-${name.toLowerCase().slice(6)}`;
33
- GM_addStyle(`${className} { display: none !important; content-visibility: auto; }`);
34
- });
35
- }
36
-
37
- public registerFilters(customFilters: Record<string, CustomSelector<any>>) {
38
- Object.entries(customFilters).forEach(([k, v]) => {
39
- if (!Object.hasOwn(this.rules.customSelectors, k)) {
40
- this.rules.customSelectors[k] = v;
41
- }
42
- this.registerFilter(k);
43
- });
44
- }
45
-
46
- public registerFilter(CustomSelectorName: string) {
47
- const handler = this.rules.customSelectors[CustomSelectorName];
48
-
49
- const fn = (): FilterFunction => {
50
- const preDefined = handler.$preDefine?.(this.state);
51
-
52
- return (v: DataElement) => {
53
- const result = handler.handle(v, this.state, preDefined);
54
-
55
- return {
56
- condition: result,
57
- tag: `filter-${CustomSelectorName}`,
58
- };
59
- };
60
- };
61
-
62
- this.filters.set(CustomSelectorName, fn);
63
- }
64
-
65
- static customFiltersDefault: Record<string, CustomSelector<any>> = {
66
- isPrivate: {
67
- handle(el, state) {
68
- const isPrivate = !!(el.element as HTMLElement).querySelector('.private');
69
- return (state.filterPrivate as boolean) && isPrivate;
70
- },
71
- },
72
- isPublic: {
73
- handle(el, state) {
74
- const isPublic = !!(el.element as HTMLElement).querySelector('.public');
75
- return (state.filterPublic as boolean) && isPublic;
76
- },
77
- },
78
- filterDuration: {
79
- handle(el, state, preDefined) {
80
- const d = el.duration as number;
81
- const notInRange = d < (preDefined.from as number) || d > (preDefined.to as number);
82
- return (state.filterDuration as boolean) && notInRange;
83
- },
84
- $preDefine: (state) => {
85
- const f = (x: number) => (state.filterDurationMinutes ? x * 60 : x);
86
- const from = f(state.filterDurationFrom as number);
87
- const to = f(state.filterDurationTo as number);
88
- return { from, to };
89
- },
90
- },
91
- filterExclude: {
92
- handle(el, state, searchFilter) {
93
- return (
94
- (state.filterExclude as boolean) &&
95
- (searchFilter as RegexFilter).hasNone(el.title as string)
96
- );
97
- },
98
- $preDefine: (state) => new RegexFilter(state.filterExcludeWords as string),
99
- },
100
- filterInclude: {
101
- handle(el, state, searchFilter) {
102
- return (
103
- (state.filterInclude as boolean) &&
104
- (searchFilter as RegexFilter).hasEvery(el.title as string)
105
- );
106
- },
107
- $preDefine: (state) => new RegexFilter(state.filterIncludeWords as string),
108
- },
109
- };
110
- }
@@ -1,141 +0,0 @@
1
- import type { StoreState } from 'jabroni-outfit';
2
- import { LazyImgLoader } from '../../utils/observers';
3
- import { assignGlobals } from '../../utils/userscript';
4
- import type { RulesGlobal } from '../rules';
5
- import { DataFilter, type FilterFunction } from './data-filter';
6
-
7
- export type DataElement = Record<string, string | number | boolean | HTMLElement>;
8
-
9
- export class DataManager {
10
- private data = new Map<string, DataElement>();
11
- private lazyImgLoader = new LazyImgLoader(
12
- (target: Element) => !this.isFiltered(target as HTMLElement),
13
- );
14
- private dataFilter: DataFilter;
15
-
16
- constructor(
17
- private rules: RulesGlobal,
18
- private state: StoreState,
19
- ) {
20
- this.dataFilter = new DataFilter(rules, state);
21
-
22
- assignGlobals({
23
- sortByDuration: () => this.sortBy('duration'),
24
- sortByViews: () => this.sortBy('view'),
25
- });
26
- }
27
-
28
- public isFiltered(el: HTMLElement): boolean {
29
- return el.className.includes('filtered');
30
- }
31
-
32
- public applyFilters = (filters: { [key: string]: boolean } = {}, offset = 0): void => {
33
- const filtersToApply = Object.keys(filters)
34
- .filter((k) => this.dataFilter.filters.has(k))
35
- .map((k) => this.dataFilter.filters.get(k) as () => FilterFunction);
36
-
37
- if (filtersToApply.length === 0) return;
38
-
39
- const iterator = this.data.values();
40
- let currentIndex = 0;
41
-
42
- const runBatch = (deadline: IdleDeadline) => {
43
- while (currentIndex < offset) {
44
- const skip = iterator.next();
45
- if (skip.done) return;
46
- currentIndex++;
47
- }
48
-
49
- const updates: { e: HTMLElement; tag: string; condition: boolean }[] = [];
50
-
51
- while (deadline.timeRemaining() > 0) {
52
- const { value, done } = iterator.next();
53
- if (done) break;
54
-
55
- for (const f of filtersToApply) {
56
- const { tag, condition } = f()(value);
57
- updates.push({ e: value.element as HTMLElement, tag, condition });
58
- }
59
- }
60
-
61
- if (updates.length > 0) {
62
- requestAnimationFrame(() => {
63
- updates.forEach((u) => {
64
- u.e.classList.toggle(u.tag, u.condition);
65
- });
66
- });
67
- }
68
-
69
- if (currentIndex < this.data.size) {
70
- requestIdleCallback(runBatch);
71
- }
72
- };
73
-
74
- requestIdleCallback(runBatch);
75
- };
76
-
77
- public filterAll = (offset?: number): void => {
78
- const filters = Object.assign(
79
- {},
80
- ...Object.keys(this.dataFilter.filters).map((f) => ({
81
- [f]: this.state[f as keyof StoreState],
82
- })),
83
- );
84
- this.applyFilters(filters, offset);
85
- };
86
-
87
- public parseData = (
88
- html: HTMLElement,
89
- container?: HTMLElement,
90
- removeDuplicates = false,
91
- shouldLazify = true,
92
- ): void => {
93
- const thumbs = this.rules.getThumbs(html);
94
- const data_offset = this.data.size;
95
- const fragment = document.createDocumentFragment();
96
- const parent = container || this.rules.container;
97
-
98
- for (const thumbElement of thumbs) {
99
- const url = this.rules.getThumbUrl(thumbElement);
100
- if (!url || this.data.has(url) || parent.contains(thumbElement)) {
101
- if (removeDuplicates) thumbElement.remove();
102
- continue;
103
- }
104
-
105
- const data = this.rules.getThumbData(thumbElement);
106
- this.data.set(url, { element: thumbElement, ...data });
107
-
108
- if (shouldLazify) {
109
- const { img, imgSrc } = this.rules.getThumbImgData(thumbElement);
110
- this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
111
- }
112
-
113
- fragment.append(thumbElement);
114
- }
115
-
116
- requestAnimationFrame(() => {
117
- parent.appendChild(fragment);
118
- });
119
-
120
- this.filterAll(data_offset);
121
- };
122
-
123
- public sortBy<K extends keyof DataElement>(key: K): void {
124
- if (this.data.size < 2) return;
125
-
126
- const sorted: DataElement[] = Array.from(this.data.values()).sort(
127
- (a: DataElement, b: DataElement) => {
128
- return (a[key] as number) - (b[key] as number);
129
- },
130
- );
131
-
132
- const container = (sorted[0].element as HTMLElement).parentElement as HTMLElement;
133
- container.style.visibility = 'hidden';
134
-
135
- sorted.forEach((s) => {
136
- container.append(s.element as HTMLElement);
137
- });
138
-
139
- container.style.visibility = 'visible';
140
- }
141
- }
@@ -1,106 +0,0 @@
1
- import type { JabronioStore } from 'jabroni-outfit';
2
- import { fetchHtml } from '../../utils/fetch';
3
- import { Observer } from '../../utils/observers';
4
- import type { RulesGlobal } from '../rules';
5
-
6
- type InfiniteScrollerOptions = Pick<InfiniteScroller, 'rules'> & Partial<InfiniteScroller>;
7
- type GeneratorResult = { url: string; offset: number };
8
- export type OffsetGenerator = Generator<GeneratorResult> | AsyncGenerator<GeneratorResult>;
9
-
10
- export class InfiniteScroller {
11
- public enabled = true;
12
- public delay = 200;
13
- public paginationOffset = 1;
14
- public writeHistory = false;
15
- public parseData?: (document: HTMLElement) => void;
16
- public rules: RulesGlobal;
17
-
18
- private observer?: Observer;
19
- private paginationGenerator: OffsetGenerator;
20
-
21
- constructor(options: InfiniteScrollerOptions) {
22
- this.rules = options.rules;
23
- this.paginationOffset = this.rules.paginationStrategy.getPaginationOffset();
24
- Object.assign(this, options);
25
-
26
- this.paginationGenerator = this.createPaginationGenerator();
27
- this.setObserver(this.rules.observable);
28
- }
29
-
30
- public dispose() {
31
- if (this.observer) this.observer.dispose();
32
- }
33
-
34
- public setObserver(observable: HTMLElement) {
35
- if (this.observer) this.observer.dispose();
36
- this.observer = Observer.observeWhile(observable, this.generatorConsumer, this.delay);
37
- return this;
38
- }
39
-
40
- private onScrollCBs: Array<(scroller: InfiniteScroller) => void> = [];
41
-
42
- public onScroll(callback: (scroller: InfiniteScroller) => void, initCall = false) {
43
- if (initCall) callback(this);
44
- this.onScrollCBs.push(callback);
45
- return this;
46
- }
47
-
48
- private _onScroll() {
49
- this.onScrollCBs.forEach((cb) => {
50
- cb(this);
51
- });
52
- }
53
-
54
- generatorConsumer = async () => {
55
- if (!this.enabled) return false;
56
- const { value: { url, offset } = {}, done } = await this.paginationGenerator.next();
57
- if (!done) {
58
- await this.doScroll(url, offset);
59
- }
60
- return !done;
61
- };
62
-
63
- async doScroll(url: string, offset: number) {
64
- const nextPageHTML = await fetchHtml(url);
65
- const prevScrollPos = document.documentElement.scrollTop;
66
- this.paginationOffset = Math.max(this.paginationOffset, offset);
67
- this.parseData?.(nextPageHTML);
68
- this._onScroll();
69
- window.scrollTo(0, prevScrollPos);
70
- if (this.writeHistory) {
71
- history.replaceState({}, '', url);
72
- }
73
- }
74
-
75
- private *createPaginationGenerator(): OffsetGenerator {
76
- const curPage = this.rules.paginationStrategy.getPaginationOffset();
77
- const lastPage = this.rules.paginationStrategy.getPaginationLast();
78
- for (let offset = curPage + 1; offset <= lastPage; offset++) {
79
- const url = this.rules.paginationStrategy.getPaginationUrlGenerator()(offset);
80
- yield { url, offset };
81
- }
82
- }
83
-
84
- static create(
85
- store: JabronioStore,
86
- rules: RulesGlobal,
87
- parseData: (document: HTMLElement) => void,
88
- ) {
89
- const enabled = store.state.infiniteScrollEnabled as boolean;
90
-
91
- store.state.$paginationLast = rules.paginationStrategy.getPaginationLast();
92
-
93
- const infiniteScroller = new InfiniteScroller({ enabled, parseData, rules }).onScroll(
94
- ({ paginationOffset }) => {
95
- store.state.$aginationOffset = paginationOffset;
96
- },
97
- true,
98
- );
99
-
100
- store.subscribe(() => {
101
- infiniteScroller.enabled = store.state.infiniteScrollEnabled as boolean;
102
- });
103
-
104
- return infiniteScroller;
105
- }
106
- }
@@ -1,55 +0,0 @@
1
- import {
2
- PaginationStrategy,
3
- PaginationStrategyDataParams,
4
- PaginationStrategyPathnameParams,
5
- PaginationStrategySearchParams,
6
- } from './pagination-strategies';
7
- import { getPaginationLinks } from './pagination-utils';
8
-
9
- export function getPaginationStrategy(
10
- options: Partial<PaginationStrategy>,
11
- ): PaginationStrategy {
12
- const _paginationStrategy = new PaginationStrategy(options);
13
- const pagination = _paginationStrategy.getPaginationElement();
14
-
15
- Object.assign(options, { ..._paginationStrategy });
16
- const { url, searchParamSelector } = options;
17
-
18
- if (!pagination) {
19
- console.error('Found No Pagination');
20
- return _paginationStrategy;
21
- }
22
-
23
- const pageLinks = getPaginationLinks(pagination, url).map((l) => new URL(l));
24
-
25
- console.log({ pageLinks: pageLinks.map((l) => l.href) });
26
-
27
- const selectStrategy = (): typeof PaginationStrategy => {
28
- if (PaginationStrategyDataParams.testLinks(pagination)) {
29
- return PaginationStrategyDataParams;
30
- }
31
-
32
- if (PaginationStrategySearchParams.testLinks(pageLinks, searchParamSelector)) {
33
- return PaginationStrategySearchParams;
34
- }
35
-
36
- if (PaginationStrategyPathnameParams.testLinks(pageLinks, options)) {
37
- return PaginationStrategyPathnameParams;
38
- }
39
-
40
- console.error('Found No Strategy');
41
- return PaginationStrategy;
42
- };
43
-
44
- const PaginationStrategyConstructor = selectStrategy();
45
- const paginationStrategy = new PaginationStrategyConstructor(options);
46
-
47
- console.log(
48
- 'paginationStrategy:',
49
- PaginationStrategyConstructor.name,
50
- '\n',
51
- paginationStrategy,
52
- );
53
-
54
- return paginationStrategy;
55
- }
@@ -1,42 +0,0 @@
1
- import { parseURL } from '../pagination-utils';
2
-
3
- export class PaginationStrategy {
4
- public doc = document;
5
- public url: URL;
6
- public paginationSelector = '.pagination';
7
- public searchParamSelector = 'page';
8
- public static _pathnameSelector = /\/(page\/)?\d+\/?$/;
9
- public pathnameSelector = /\/(\d+)\/?$/;
10
- public dataparamSelector = '[data-parameters *= from]';
11
- public fixPaginationLast?: (n: number, offset?: number) => number;
12
- public offsetMin = 1;
13
-
14
- constructor(options?: Partial<PaginationStrategy>) {
15
- if (options) {
16
- Object.entries(options).forEach(([k, v]) => {
17
- Object.assign(this, { [k]: v });
18
- });
19
- }
20
-
21
- this.url = parseURL(options?.url || this.doc.URL);
22
- }
23
-
24
- getPaginationElement() {
25
- return this.doc.querySelector<HTMLElement>(this.paginationSelector);
26
- }
27
-
28
- get hasPagination() {
29
- return !!this.getPaginationElement();
30
- }
31
-
32
- getPaginationOffset() {
33
- return this.offsetMin;
34
- }
35
- getPaginationLast() {
36
- return this.offsetMin;
37
- }
38
-
39
- getPaginationUrlGenerator() {
40
- return (_: number) => this.url.href;
41
- }
42
- }
@@ -1,66 +0,0 @@
1
- import { parseDataParams } from '../../../utils/parsers';
2
- import { PaginationStrategy } from './PaginationStrategy';
3
-
4
- export class PaginationStrategyDataParams extends PaginationStrategy {
5
- getPaginationLast() {
6
- const links = this.getPaginationElement()?.querySelectorAll(this.dataparamSelector);
7
- const pages = Array.from(links || [], (l) => {
8
- const p = l.getAttribute('data-parameters');
9
- const v = p?.match(/from\w*:(\d+)/)?.[1] || this.offsetMin.toString();
10
- return parseInt(v);
11
- });
12
- const lastPage = Math.max(...pages, this.offsetMin);
13
- if (this.fixPaginationLast) return this.fixPaginationLast(lastPage);
14
- return lastPage;
15
- }
16
-
17
- getPaginationOffset() {
18
- const link = this.getPaginationElement()?.querySelector(
19
- '.prev[data-parameters *= from], .prev [data-parameters *= from]',
20
- );
21
- if (!link) return this.offsetMin;
22
- const p = link.getAttribute('data-parameters');
23
- const v = p?.match(/from\w*:(\d+)/)?.[1] || this.offsetMin.toString();
24
- return parseInt(v);
25
- }
26
-
27
- getPaginationUrlGenerator() {
28
- const url = new URL(this.url.href);
29
-
30
- const parametersElement = this.getPaginationElement()?.querySelector(
31
- 'a[data-block-id][data-parameters]',
32
- );
33
- const block_id = parametersElement?.getAttribute('data-block-id') || '';
34
- const parameters = parseDataParams(
35
- parametersElement?.getAttribute('data-parameters') || '',
36
- );
37
-
38
- const attrs: Record<string, string> = {
39
- block_id,
40
- function: 'get_block',
41
- mode: 'async',
42
- ...parameters,
43
- };
44
-
45
- Object.keys(attrs).forEach((k) => {
46
- url.searchParams.set(k, attrs[k]);
47
- });
48
-
49
- const paginationUrlGenerator = (n: number) => {
50
- Object.keys(attrs).forEach((k) => {
51
- k.includes('from') && url.searchParams.set(k, n.toString());
52
- });
53
- url.searchParams.set('_', Date.now().toString());
54
- return url.href;
55
- };
56
-
57
- return paginationUrlGenerator;
58
- }
59
-
60
- static testLinks(doc: HTMLElement | Document = document) {
61
- const dataParamLinks = Array.from(
62
- doc.querySelectorAll<HTMLElement>('[data-parameters *= from]'),
63
- );
64
- return dataParamLinks.length > 0;
65
- }
66
- }
@@ -1,77 +0,0 @@
1
- import { getPaginationLinks, parseURL, upgradePathname } from '../pagination-utils';
2
- import { PaginationStrategy } from './PaginationStrategy';
3
-
4
- export class PaginationStrategyPathnameParams extends PaginationStrategy {
5
- extractPage = (a: HTMLAnchorElement | Location | string): number => {
6
- const href = typeof a === 'string' ? a : a.href;
7
- const { pathname } = new URL(href, this.doc.baseURI || this.url.origin);
8
- return parseInt(
9
- pathname.match(this.pathnameSelector)?.pop() || this.offsetMin.toString(),
10
- );
11
- };
12
-
13
- static checkLink(
14
- link: URL,
15
- pathnameSelector: RegExp = PaginationStrategy._pathnameSelector,
16
- ): boolean {
17
- return pathnameSelector.test(link.pathname);
18
- }
19
-
20
- static testLinks(links: URL[], options: Partial<PaginationStrategy>): boolean {
21
- const result = links.some((h) =>
22
- PaginationStrategyPathnameParams.checkLink(h, options.pathnameSelector),
23
- );
24
-
25
- if (result) {
26
- const pathnamesMatched = links.filter((h) =>
27
- PaginationStrategyPathnameParams.checkLink(h, options.pathnameSelector),
28
- );
29
- options.url = upgradePathname(
30
- parseURL(options.url as unknown as string),
31
- pathnamesMatched,
32
- );
33
- }
34
-
35
- return result;
36
- }
37
-
38
- getPaginationLast() {
39
- const links = getPaginationLinks(
40
- (this.getPaginationElement() || document) as HTMLElement,
41
- this.url.href,
42
- this.pathnameSelector,
43
- );
44
- const pages = Array.from(links, this.extractPage);
45
- const lastPage = Math.max(...pages, this.offsetMin);
46
- if (this.fixPaginationLast) return this.fixPaginationLast(lastPage);
47
- return lastPage;
48
- }
49
-
50
- getPaginationOffset() {
51
- return this.extractPage(this.url.href);
52
- }
53
-
54
- getPaginationUrlGenerator(url_: URL = this.url) {
55
- const url = new URL(url_.href);
56
-
57
- const pathnameSelectorPlaceholder = this.pathnameSelector
58
- .toString()
59
- .replace(/[/|\\|$|?|(|)]+/g, '/');
60
-
61
- if (!this.pathnameSelector.test(url.pathname)) {
62
- url.pathname = url.pathname
63
- .concat(pathnameSelectorPlaceholder.replace(/d\+/, this.offsetMin.toString()))
64
- .replace(/\/{2,}/g, '/');
65
- }
66
-
67
- const paginationUrlGenerator = (offset: number) => {
68
- url.pathname = url.pathname.replace(
69
- this.pathnameSelector,
70
- pathnameSelectorPlaceholder.replace(/d\+/, offset.toString()),
71
- );
72
- return url.href;
73
- };
74
-
75
- return paginationUrlGenerator;
76
- }
77
- }
@@ -1,56 +0,0 @@
1
- import { getPaginationLinks } from '../pagination-utils';
2
- import { PaginationStrategy } from './PaginationStrategy';
3
-
4
- export class PaginationStrategySearchParams extends PaginationStrategy {
5
- extractPage = (a: HTMLAnchorElement | Location | URL | string): number => {
6
- const href = typeof a === 'string' ? a : a.href;
7
- const p = new URL(href).searchParams.get(this.searchParamSelector) as string;
8
- return parseInt(p) || this.offsetMin;
9
- };
10
-
11
- getPaginationLast() {
12
- const links = getPaginationLinks(
13
- (this.getPaginationElement() || document) as HTMLElement,
14
- this.url.href,
15
- ).filter((h) =>
16
- PaginationStrategySearchParams.checkLink(new URL(h), this.searchParamSelector),
17
- );
18
- const pages = links.map(this.extractPage);
19
- const lastPage = Math.max(...pages, this.offsetMin);
20
- if (this.fixPaginationLast) return this.fixPaginationLast(lastPage);
21
- return lastPage;
22
- }
23
-
24
- getPaginationOffset() {
25
- if (this.doc === document) {
26
- return this.extractPage(this.url);
27
- }
28
- const link = this.getPaginationElement()?.querySelector(
29
- `a.active[href *= "${this.searchParamSelector}="]`,
30
- ) as HTMLAnchorElement;
31
- return this.extractPage(link);
32
- }
33
-
34
- getPaginationUrlGenerator() {
35
- const url = new URL(this.url.href);
36
-
37
- const paginationUrlGenerator = (offset: number) => {
38
- url.searchParams.set(this.searchParamSelector, offset.toString());
39
- return url.href;
40
- };
41
-
42
- return paginationUrlGenerator;
43
- }
44
-
45
- static checkLink(link: URL, searchParamSelector?: string): boolean {
46
- const searchParamSelectors = ['page', 'p'];
47
- if (searchParamSelector) searchParamSelectors.push(searchParamSelector);
48
- return searchParamSelectors.some((p) => link.searchParams.get(p) !== null);
49
- }
50
-
51
- static testLinks(links: URL[], searchParamSelector?: string) {
52
- return links.some((h) =>
53
- PaginationStrategySearchParams.checkLink(h, searchParamSelector),
54
- );
55
- }
56
- }
@@ -1,33 +0,0 @@
1
- import { fetchHtml } from '../../../utils/fetch';
2
- import { PaginationStrategy } from './PaginationStrategy';
3
-
4
- export class PaginationStrategyTrash extends PaginationStrategy {
5
- getPaginationLast() {
6
- return 9999;
7
- }
8
-
9
- getPaginationOffset() {
10
- return this.offsetMin;
11
- }
12
-
13
- eHentaiNext = async () => {
14
- if (!(unsafeWindow as any).PAGINATION_NEXT) {
15
- const hrefs = [
16
- ...document.querySelectorAll('a#dnext[href]'),
17
- ] as Array<HTMLAnchorElement>;
18
- (unsafeWindow as any).PAGINATION_NEXT = hrefs.pop()?.href;
19
- }
20
- const doc = await fetchHtml((unsafeWindow as any).PAGINATION_NEXT as string);
21
- const hrefs = [...doc.querySelectorAll('a#dnext[href]')] as Array<HTMLAnchorElement>;
22
- (unsafeWindow as any).PAGINATION_NEXT = hrefs.pop()?.href;
23
- };
24
-
25
- getPaginationUrlGenerator() {
26
- const paginationUrlGenerator = (_: number) => {
27
- this.eHentaiNext();
28
- return (unsafeWindow as any).PAGINATION_NEXT as string;
29
- };
30
-
31
- return paginationUrlGenerator;
32
- }
33
- }
@@ -1,5 +0,0 @@
1
- export { PaginationStrategy } from './PaginationStrategy';
2
- export { PaginationStrategyDataParams } from './PaginationStrategyDataParams';
3
- export { PaginationStrategyPathnameParams } from './PaginationStrategyPathnameParams';
4
- export { PaginationStrategySearchParams } from './PaginationStrategySearchParams';
5
- export { PaginationStrategyTrash } from './PaginationStrategyTrash';