billy-herrington-utils 2.0.6 → 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 -6267
  3. package/dist/billy-herrington-utils.es.js.map +1 -1
  4. package/dist/billy-herrington-utils.umd.js +321 -6269
  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,69 +0,0 @@
1
- export function parseURL(s: HTMLAnchorElement | Location | URL | string): URL {
2
- if (typeof s === 'string') return new URL(s);
3
- return new URL(s.href);
4
- }
5
-
6
- export function getPaginationLinks(
7
- doc: Element | HTMLElement | Document = document,
8
- url: Location | URL | string = location.href,
9
- pathnameSelector = /\/(page\/)?\d+\/?$/,
10
- ): string[] {
11
- const currentUrl = parseURL(url);
12
- currentUrl.pathname = currentUrl.pathname.replace(pathnameSelector, '/');
13
- const pathnameStrict = doc instanceof Document;
14
-
15
- const pageLinks = Array.from(
16
- (doc.querySelectorAll('a[href]') as NodeListOf<HTMLAnchorElement>) || [],
17
- (a) => a.href,
18
- ).filter((h) => {
19
- try {
20
- const linkUrl = new URL(h.replace(/#\w*$/, ''), doc.baseURI || currentUrl.origin);
21
- return (
22
- linkUrl.hostname === currentUrl.hostname &&
23
- (pathnameStrict ? linkUrl.pathname.startsWith(currentUrl.pathname) : true)
24
- );
25
- } catch {
26
- return false;
27
- }
28
- });
29
- return pageLinks;
30
- }
31
-
32
- /**
33
- * @description
34
- * curr: website.com, links: [webiste.com/new/23] => wegsite.com/new
35
- */
36
- export function upgradePathname(curr: URL, links: URL[]): URL {
37
- if (/\/(page\/)?\d+\/?$/.test(curr.pathname) || links.length < 1) return curr;
38
- const linksDepaginated = links.map((l) => {
39
- l.pathname = l.pathname.replace(/\/(page\/)?\d+\/?$/, '/');
40
- return l;
41
- });
42
- if (linksDepaginated.some((l) => l.pathname === curr.pathname)) return curr;
43
- const last = linksDepaginated.at(-1) as URL;
44
- if (last.pathname !== curr.pathname) curr.pathname = last.pathname;
45
- return curr;
46
- }
47
-
48
- /**
49
- * @description
50
- * website.com/search => website.com/search+word1...+-word2
51
- */
52
- export function applyURLLevelSearchFilters(
53
- searchFilter: string,
54
- queryType: keyof Pick<Location, 'pathname' | 'search'>,
55
- ): void {
56
- const wordsToFilter =
57
- searchFilter.replace(/f:/g, '').match(/(?<!user:)\b\w+\b(?!\s*:)/g) || [];
58
-
59
- if (!wordsToFilter.some((w) => !location.href.includes(w))) return;
60
-
61
- let query = location[queryType];
62
-
63
- wordsToFilter.forEach((w) => {
64
- if (query.includes(w)) return;
65
- query += `+-${w.trim()}`;
66
- });
67
-
68
- window.location[queryType] = query;
69
- }
@@ -1,71 +0,0 @@
1
- /*
2
-
3
- At this point I have no idea what I'm doing
4
- ...
5
-
6
- */
7
-
8
- // import { JabronioGUI, JabronioStore, setupScheme } from 'jabroni-outfit';
9
- // import { DataManager } from '../data-manager';
10
- // import { InfiniteScroller } from '../infinite-scroll';
11
- // import { RulesGlobal } from '../rules';
12
-
13
- // import type { NonFunctionProperties } from '../types';
14
-
15
- // // const store = new JabroniOutfitStore(defaultStateWithDurationAndPrivacy);
16
- // // const { state } = store;
17
-
18
- // type RouterProp = NonFunctionProperties<Router>;
19
-
20
- // export class Router {
21
- // constructor(
22
- // public rules: typeof RulesGlobal,
23
- // public scheme: SchemeInput
24
-
25
- // ) {
26
- // this.rules = new RulesGlobal();
27
- // }
28
-
29
- // public defaultRouterRules: typeof this.routerRules = {
30
- // initStore: ({ rules }) => {
31
- // this.store = new JabronioStore();
32
- // this.dataManager = new DataManager(rules, this.store.state);
33
- // this.store.subscribe(() => this.dataManager?.applyFilters());
34
- // },
35
- // initUI: () => {
36
- // this.ui = new JabronioGUI([], this.store as JabronioStore);
37
- // },
38
- // grabInit: ({ rules, dataManager }) => {
39
- // if (!rules.container) return;
40
- // // if find many containers > then for each
41
- // dataManager?.parseData(rules.container, rules.container);
42
- // },
43
- // initInfiniteScroll: ({ rules, dataManager, store }) => {
44
- // if (rules.paginationStrategy.hasPagination) {
45
- // InfiniteScroller.create(
46
- // store as JabronioStore,
47
- // (dataManager as DataManager)?.parseData,
48
- // rules,
49
- // );
50
- // rules.animatePreview?.();
51
- // }
52
- // },
53
- // };
54
-
55
- // public routerRules: Record<string, (args: RouterProp) => void> = {};
56
-
57
- // // you are a tricky motherfucker aren't you?
58
- // public add<O extends typeof this.routerRules, A extends keyof O, B extends O[string]>(
59
- // name: A,
60
- // fn: B,
61
- // ) {
62
- // Object.assign(this.routerRules, { [name]: fn });
63
- // }
64
-
65
- // public run() {
66
- // Object.assign(this.routerRules, this.defaultRouterRules);
67
- // Object.entries(this.routerRules).forEach(([_, f]) => {
68
- // f(this);
69
- // });
70
- // }
71
- // }
@@ -1,241 +0,0 @@
1
- import { JabronioGUI, JabronioStore, type JabroniTypes, setupScheme } from 'jabroni-outfit';
2
- import {
3
- getAllUniqueParents,
4
- querySelectorText,
5
- waitForElementToDisappear,
6
- } from '../../utils/dom';
7
- import { timeToSeconds } from '../../utils/parsers';
8
- import { sanitizeStr } from '../../utils/strings';
9
- import { DataManager } from '../data-manager';
10
- import type { CustomSelector } from '../data-manager/data-filter';
11
- import { InfiniteScroller, type OffsetGenerator } from '../infinite-scroll/';
12
- import { getPaginationStrategy } from '../pagination-parsing';
13
- import type { PaginationStrategy } from '../pagination-parsing/pagination-strategies';
14
-
15
- type ThumbData = { title: string; duration?: number };
16
-
17
- export class RulesGlobal {
18
- public delay?: number;
19
-
20
- public alternativeGenerator?: () => OffsetGenerator;
21
-
22
- public getThumbUrl(thumb: HTMLElement | HTMLAnchorElement) {
23
- return ((thumb.querySelector('a[href]') || thumb) as HTMLAnchorElement).href;
24
- }
25
-
26
- public titleSelector: undefined | string;
27
- public uploaderSelector: undefined | string;
28
- public durationSelector: undefined | string;
29
- public getThumbDataStrategy: 'default' | 'auto-select' | 'auto-text' = 'default';
30
- // durationParseStrategy: 'default' | 'auto' = 'default';
31
- // timeToSeconds(thumb.innerText.match(/\d+m/)?.[0]);
32
-
33
- public getThumbDataCallback?: (thumb: HTMLElement, thumbData: ThumbData) => void;
34
-
35
- public getThumbData(thumb: HTMLElement): ThumbData {
36
- let { titleSelector, uploaderSelector, durationSelector } = this;
37
- const thumbData: ThumbData = { title: '' };
38
-
39
- if (this.getThumbDataStrategy === 'auto-text') {
40
- const text = sanitizeStr(thumb.innerText);
41
- thumbData.title = text;
42
- thumbData.duration = timeToSeconds(text.match(/\d+m|\d+:\d+/)?.[0] || '');
43
- return thumbData;
44
- }
45
-
46
- /* experimental */
47
- if (this.getThumbDataStrategy === 'auto-select') {
48
- // CamWhores, Ebalka, Erome, EropProfile, Javhdporn, Motherless
49
- titleSelector = '[class *= title],[title]';
50
- // CamWhores, Javhdporn,
51
- durationSelector = '[class *= duration]';
52
- // Erome, Eporner, Motherless
53
- uploaderSelector = '[class *= uploader], [class *= user], [class *= name]';
54
- }
55
-
56
- const title = querySelectorText(thumb, titleSelector);
57
-
58
- if (uploaderSelector) {
59
- const uploader = querySelectorText(thumb, uploaderSelector);
60
- thumbData.title = `${title} user:${uploader}`;
61
- }
62
-
63
- if (durationSelector) {
64
- const duration = timeToSeconds(querySelectorText(thumb, durationSelector));
65
- thumbData.duration = duration;
66
- }
67
-
68
- this.getThumbDataCallback?.(thumb, thumbData);
69
-
70
- return thumbData;
71
- }
72
-
73
- public getThumbImgDataAttrSelector: string | undefined;
74
- public getThumbImgDataStrategy: 'default' | 'auto' = 'default';
75
- // getThumbImgDataRemLazy: boolean | string = false;
76
-
77
- public getThumbImgData(thumb: HTMLElement) {
78
- const result: { img?: HTMLImageElement; imgSrc?: string } = {};
79
-
80
- if (this.getThumbImgDataStrategy === 'auto') {
81
- const img = thumb.querySelector<HTMLImageElement>('img');
82
- if (!img) return {};
83
-
84
- result.img = img;
85
-
86
- const possibleAttrs = this.getThumbImgDataAttrSelector
87
- ? [this.getThumbImgDataAttrSelector]
88
- : ['data-src', 'src'];
89
-
90
- for (const attr of possibleAttrs) {
91
- const imgSrc = img.getAttribute(attr);
92
- if (imgSrc) {
93
- result.imgSrc = imgSrc;
94
- img.removeAttribute(attr);
95
- break;
96
- }
97
- }
98
-
99
- // remove lazy- attrs and so on
100
-
101
- // img.naturalWidth === 0
102
- // if (img.complete && img.getAttribute('src') && !img.src.includes('data:image')) {
103
- // return {};
104
- // }
105
- }
106
-
107
- return result;
108
- }
109
-
110
- public containerSelector: string | (() => HTMLElement) = '.container';
111
- public intersectionObservable?: HTMLElement;
112
-
113
- get container() {
114
- if (typeof this.containerSelector === 'string') {
115
- return document.querySelector<HTMLElement>(this.containerSelector) as HTMLElement;
116
- }
117
- return this.containerSelector();
118
- }
119
-
120
- public thumbsSelector = '.thumb';
121
- public getThumbsStrategy: 'default' | 'auto' = 'default';
122
-
123
- public getThumbs(html: HTMLElement): HTMLElement[] {
124
- if (this.getThumbsStrategy === 'auto') {
125
- if (typeof this.containerSelector !== 'string') return [];
126
- const container = html.querySelector(this.containerSelector);
127
- const thumbs = [...(container?.children || [])] as HTMLElement[];
128
- return thumbs;
129
- }
130
-
131
- const thumbs = Array.from<HTMLElement>(html.querySelectorAll(this.thumbsSelector));
132
- return thumbs;
133
- }
134
-
135
- public paginationStrategyOptions: Partial<PaginationStrategy> = {};
136
- public paginationStrategy: PaginationStrategy;
137
-
138
- // get paginationElement() {
139
- // return this.paginationStrategy.getPaginationElement();
140
- // }
141
-
142
- // get paginationOffset() {
143
- // return this.paginaCustomSelectorNametionStrategy.getPaginationOffset();
144
- // }
145
-
146
- // get paginationLast() {
147
- // return this.paginationStrategy.getPaginationLast();
148
- // }
149
-
150
- // get paginationUrlGenerator() {
151
- // return this.paginationStrategy.getPaginationUrlGenerator();
152
- // }
153
-
154
- public get observable(): HTMLElement {
155
- return (this.intersectionObservable ||
156
- this.paginationStrategy.getPaginationElement()) as HTMLElement;
157
- }
158
-
159
- mutationObservers: MutationObserver[] = [];
160
-
161
- reset() {
162
- this.mutationObservers.forEach((o) => {
163
- o.disconnect();
164
- });
165
- this.resetInfiniteScroller();
166
- this.resetOn();
167
- }
168
-
169
- public resetOnPaginationOrContainerDeath = true;
170
-
171
- resetOn() {
172
- if (!this.resetOnPaginationOrContainerDeath) return;
173
-
174
- const observables = [
175
- this.container,
176
- this.intersectionObservable || this.paginationStrategy.getPaginationElement(),
177
- ];
178
-
179
- observables.forEach((o) => {
180
- const observer = waitForElementToDisappear(o as HTMLElement, () => {
181
- this.reset();
182
- });
183
- this.mutationObservers.push(observer);
184
- });
185
- }
186
-
187
- public customSelectors: Record<string, CustomSelector<any>> = {};
188
-
189
- public animatePreview?: (doc?: HTMLElement) => void;
190
-
191
- public storeOptions?: JabroniTypes.StoreStateOptions;
192
- public customScheme?: JabroniTypes.SchemeInput;
193
- public defaultSchemeOptions: Parameters<typeof setupScheme>[0] = [];
194
-
195
- public store: JabronioStore;
196
- public gui: JabronioGUI;
197
- public dataManager: DataManager;
198
-
199
- public infiniteScroller?: InfiniteScroller;
200
-
201
- private resetInfiniteScroller() {
202
- this.infiniteScroller?.dispose();
203
- if (!this.paginationStrategy.hasPagination) return;
204
- this.infiniteScroller = InfiniteScroller.create(
205
- this.store,
206
- this,
207
- this.dataManager.parseData,
208
- );
209
- }
210
-
211
- public initialGrope: 'all-in-one' | 'all-in-all' | undefined;
212
-
213
- public gropeInit() {
214
- if (!this.initialGrope) return;
215
- if (this.initialGrope === 'all-in-one') {
216
- this.dataManager?.parseData(this.container, this.container);
217
- }
218
- if (this.initialGrope === 'all-in-all') {
219
- getAllUniqueParents(this.getThumbs(document.body)).forEach((c) => {
220
- this.dataManager.parseData(c, c, true);
221
- });
222
- }
223
- }
224
-
225
- constructor(options: Partial<RulesGlobal>) {
226
- Object.assign(this, options);
227
-
228
- this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
229
-
230
- this.store = new JabronioStore(this.storeOptions);
231
- const scheme = setupScheme(this.defaultSchemeOptions, this.customScheme);
232
- this.gui = new JabronioGUI(scheme, this.store);
233
- this.dataManager = new DataManager(this, this.store.state);
234
-
235
- this.store.subscribe(() => this.dataManager.applyFilters());
236
- this.resetInfiniteScroller();
237
- this.resetOn();
238
- this.animatePreview?.();
239
- this.gropeInit();
240
- }
241
- }
@@ -1,19 +0,0 @@
1
- export interface DataFilterState {
2
- filterPublic: boolean;
3
- filterPrivate: boolean;
4
- filterHD: boolean;
5
- filterDuration: boolean;
6
- filterDurationFrom: number;
7
- filterDurationTo: number;
8
- filterExclude: boolean;
9
- filterExcludeWords: string;
10
- filterInclude: boolean;
11
- filterIncludeWords: string;
12
- infiniteScrollEnabled: true;
13
- }
14
-
15
- export type NonFunctionKeys<T> = {
16
- [K in keyof T]: T[K] extends Function ? never : K;
17
- }[keyof T];
18
-
19
- export type NonFunctionProperties<T> = Pick<T, NonFunctionKeys<T>>;
@@ -1,3 +0,0 @@
1
- export function isMob() {
2
- return /iPhone|Android/i.test(navigator.userAgent);
3
- }
@@ -1,10 +0,0 @@
1
- export function getWindows(): Window[] {
2
- return [window, unsafeWindow].filter(Boolean);
3
- }
4
-
5
- export function assignGlobals<T extends {}>(obj: T) {
6
- const windows = getWindows();
7
- windows.forEach((w: Window) => {
8
- Object.assign(w, obj);
9
- });
10
- }