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
package/README.md CHANGED
@@ -1,90 +1,388 @@
1
1
  ### _daddy told us not to be ashamed of our utils_
2
2
  ![](https://i.imgur.com/wwfRj0R.jpeg)
3
3
 
4
+ ## Installation
5
+
6
+ ```shell
7
+ npm i billy-herrington-utils
4
8
  ```
5
- <script src="https://unpkg.com/billy-herrington-utils"></script>
6
- ```
7
- ```
9
+ ```html
8
10
  <script src="https://unpkg.com/billy-herrington-utils/dist/billy-herrington-utils.umd.js"></script>
9
11
  <script>
10
12
  const { Tick } = window.bhutils;
11
13
  </script>
12
14
  ```
15
+
16
+ ## 📦 Arrays & Iterables
17
+
18
+ ### chunks
19
+
20
+ Splits an array into smaller arrays of a specified size.
21
+
22
+ * **Input**: `arr: T[]`, `size: number`
23
+ * **Output**: `T[][]`
24
+
25
+ ```typescript
26
+ const data = [1, 2, 3, 4, 5]
27
+ const result = chunks(data, 2)
28
+ console.log(result)
29
+
13
30
  ```
14
- npm i billy-herrington-utils
31
+
32
+ ### range
33
+
34
+ Generates an array of numbers starting from a specific value.
35
+
36
+ * **Input**: `size: number`, `start?: number` (default 1), `step?: number` (default 1)
37
+ * **Output**: `number[]`
38
+
39
+ ```typescript
40
+ const numbers = range(5, 0, 10)
41
+ console.log(numbers)
42
+
15
43
  ```
16
44
 
17
- **A comprehensive collection of utility 🛠️ functions to make dungeon life easier.**
45
+ ### circularShift
46
+
47
+ Performs a circular shift on a number within a specific capacity.
18
48
 
19
- **Key features:**
49
+ * **Input**: `n: number` (current index), `c?: number` (capacity, default 6), `s?: number` (shift step, default 1)
50
+ * **Output**: `number`
20
51
 
21
- * **String manipulation:** Easily parse, sanitize, and convert strings.
22
- * **Time and date:** Work with time and date values effortlessly.
23
- * **DOM manipulation:** Interact with DOM elements like a pro.
24
- * **Networking:** Make HTTP requests and handle data with ease.
25
- * **Miscellaneous:** A variety of other useful functions for common tasks.
52
+ ```typescript
53
+ const nextIndex = circularShift(5, 6, 1)
54
+ console.log(nextIndex)
26
55
 
27
- ## **Documentation**
56
+ ```
28
57
 
29
58
  ---
30
59
 
31
- ### General Utilities
32
-
33
- | Function | Short Explanation | Input Parameters | Example Input/Output or Usage |
34
- |---|---|---|---|
35
- | `splitWith(s)` | Splits a comma-separated string into an array of lowercase, trimmed words. | `s: string` | `splitWith("Hello, world!, Test_String")` -> `["hello", "world", "test_string"]` |
36
- | `sanitizeStr(s)` | Cleans up a string by removing newlines/tabs, excess spaces, and converting to lowercase. | `s: string` | `sanitizeStr(" Hello \nWorld ")` -> `"hello world"` |
37
- | `formatTimeToHHMMSS(timeString)` | Formats a time string (e.g., "1h 30min 15sec") into "HH:MM:SS". | `timeString: string` | `formatTimeToHHMMSS("1h 2min")` -> `"01:02:00"` |
38
- | `timeToSeconds(t)` | Converts a time string (e.g., "1h 30min") or "HH:MM:SS" into total seconds. | `t: string` | `timeToSeconds("1h 2min")` -> `3720` |
39
- | `parseIntegerOr(n, or)` | Parses a string into an integer, returning a default value if parsing fails. | `n: any`, `or: number` | `parseIntegerOr("100", 0)` -> `100`, `parseIntegerOr("abc", 0)` -> `0` |
40
- | `parseDataParams(str)` | Parses a string of key-value pairs (`key:value;`) into an object. | `str: string` | `parseDataParams("user:john;id:123")` -> `{user: "john", id: "123"}` |
41
- | `parseCSSUrl(s)` | Extracts the URL from a CSS `url()` string. | `s: string` | `parseCSSUrl("url(\"https://example.com/img.png\")")` -> `"https://example.com/img.png"` |
42
- | `circularShift(n, c = 6, s = 1)` | Performs a circular shift on a number within a range. | `n: number`, `c: number`, `s: number` | `circularShift(5, 6, 1)` -> `6` |
43
- | `range(size, startAt = 1, step = 1)` | Creates an array of numbers in a specified range. | `size: number`, `startAt: number`, `step: number` | `range(3, 2, 2)` -> `[2, 4, 6]` |
44
- | `chunks(arr, n)` | Splits an array into chunks of a given size. | `arr: Array`, `n: number` | `chunks([1, 2, 3, 4, 5], 2)` -> `[[1, 2], [3, 4], [5]]` |
45
- | `isMob()` | Checks if the user agent indicates a mobile device. | N/A | `isMob()` -> `true` or `false` |
46
- | `wait(milliseconds)` | Returns a Promise that resolves after a delay. | `milliseconds: number` | `await wait(1000)` waits for 1 second. |
47
- | `computeAsyncOneAtTime(iterable)` | Executes an iterable of async functions one at a time. | `iterable: Iterable<Function>` | `await computeAsyncOneAtTime([() => fetch('a'), () => fetch('b')])` |
48
- | `objectToFormData(object)` | Converts a JavaScript object into a `FormData` object. | `object: object` | `objectToFormData({ key: 'value' })` -> `FormData` object |
60
+ ## ⏱️ Async & Timing
61
+
62
+ ### wait
63
+
64
+ Returns a Promise that resolves after a specified duration.
65
+
66
+ * **Input**: `milliseconds: number`
67
+ * **Output**: `Promise<void>`
68
+
69
+ ```typescript
70
+ await wait(1000)
71
+ console.log("Done waiting")
72
+
73
+ ```
74
+
75
+ ### Tick
76
+
77
+ A class for creating repeating intervals with start/stop control and final callbacks.
78
+
79
+ * **Methods**: `start(callback, callbackFinal?)`, `stop()`
80
+
81
+ ```typescript
82
+ const ticker = new Tick(1000)
83
+ ticker.start(
84
+ () => console.log("Tick"),
85
+ () => console.log("Stopped")
86
+ )
87
+ await wait(3000)
88
+ ticker.stop()
89
+
90
+ ```
49
91
 
50
92
  ---
51
93
 
52
- ### DOM Manipulation
53
-
54
- | Function | Short Explanation | Input Parameters | Example Input/Output or Usage |
55
- |---|---|---|---|
56
- | `parseDom(html)` | Parses an HTML string into a DOM element. | `html: string` | `parseDom("<div>Hello</div>")` -> `HTMLDivElement` |
57
- | `copyAttributes(target, source)` | Copies all attributes from one element to another. | `target: HTMLElement`, `source: HTMLElement` | `copyAttributes(div1, div2)` |
58
- | `replaceElementTag(e, tagName)` | Replaces an element's tag while preserving its content and attributes. | `e: HTMLElement`, `tagName: string` | `replaceElementTag(document.querySelector('p'), 'div')` |
59
- | `getAllUniqueParents(elements)` | Returns an array of unique parent elements. | `elements: Array<HTMLElement>` | `getAllUniqueParents([el1, el2])` |
60
- | `findNextSibling(el)` | Finds the next sibling, or recursively checks parent elements. | `el: HTMLElement` | `findNextSibling(document.querySelector('li'))` |
61
- | `waitForElementToAppear(parent, selector, callback)` | Waits for an element to exist in the DOM and then runs a callback. | `parent: HTMLElement`, `selector: string`, `callback: Function` | `waitForElementToAppear(document.body, '.my-class', (el) => console.log(el))` |
62
- | `watchElementChildrenCount(element, callback)` | Observes an element for changes in its number of children. | `element: HTMLElement`, `callback: Function` | `watchElementChildrenCount(list, (obs, count) => console.log(count))` |
63
- | `watchDomChangesWithThrottle(element, callback, throttle, times, options)` | Watches for DOM changes with a throttle to prevent excessive callbacks. | `element: HTMLElement`, `callback: Function`, `throttle: number`, `times: number`, `options: object` | `watchDomChangesWithThrottle(body, () => ..., 500)` |
64
- | `downloader(options)` | Creates a button to download a video from the page. | `options: object` | `downloader({ button: '<button>Download</button>', append: 'body' })` |
65
- | `exterminateVideo(video)` | Removes a video element and stops it from loading. | `video: HTMLVideoElement` | `exterminateVideo(document.querySelector('video'))` |
66
- | `listenEvents(dom, events, callback)` | Adds multiple event listeners to a DOM element. | `dom: HTMLElement`, `events: Array<string>`, `callback: Function` | `listenEvents(btn, ['click', 'mouseover'], handler)` |
94
+ ## 📝 String & Formatting
95
+
96
+ ### splitWith
97
+
98
+ Splits a string by a delimiter, trims whitespace, and filters out empty strings.
99
+
100
+ * **Input**: `s: string`, `c?: string` (delimiter, default ",")
101
+ * **Output**: `string[]`
102
+
103
+ ```typescript
104
+ const tags = splitWith(" apple, banana , , orange ")
105
+ console.log(tags)
106
+
107
+ ```
108
+
109
+ ### sanitizeStr
110
+
111
+ Removes newlines, tabs, and excessive whitespace from a string.
112
+
113
+ * **Input**: `s: string`
114
+ * **Output**: `string`
115
+
116
+ ```typescript
117
+ const raw = " Hello \n\t World "
118
+ const clean = sanitizeStr(raw)
119
+ console.log(clean)
120
+
121
+ ```
122
+
123
+ ### timeToSeconds
124
+
125
+ Converts a time string (e.g., "1h 30min" or "01:30:00") into total seconds.
126
+
127
+ * **Input**: `timeStr: string`
128
+ * **Output**: `number`
129
+
130
+ ```typescript
131
+ const totalSeconds = timeToSeconds("1h 2min 30sec")
132
+
133
+ ```
134
+
135
+ ### formatTimeToHHMMSS
136
+
137
+ Formats a descriptive time string into a standard HH:MM:SS format.
138
+
139
+ * **Input**: `timeStr: string`
140
+ * **Output**: `string`
141
+
142
+ ```typescript
143
+ const stamp = formatTimeToHHMMSS("1h 5min")
144
+ console.log(stamp)
145
+
146
+ ```
67
147
 
68
148
  ---
69
149
 
70
- ### Network Utilities
150
+ ## 🕸️ Network
151
+
152
+ ### fetchWith
153
+
154
+ A wrapper around the native `fetch` API that supports a mobile User-Agent spoofing and automatic response parsing.
155
+
156
+ * **Input**: `input: string`, `options: { type?: 'json' | 'html' | 'text', mobile?: boolean, init?: RequestInit }`
157
+ * **Output**: `Promise<any>`
158
+
159
+ ```typescript
160
+ const data = await fetchWith("https://api.example.com/data", {
161
+ type: "json",
162
+ mobile: true
163
+ })
164
+
165
+ ```
71
166
 
72
- | Function | Short Explanation | Input Parameters | Example Input/Output or Usage |
73
- |---|---|---|---|
74
- | `fetchWith(url, options)` | A flexible wrapper for the `fetch` API with options for HTML parsing and mobile user agents. | `url: string`, `options: object` | `fetchWith('https://example.com', { html: true })` |
75
- | `fetchHtml(url)` | Fetches a URL and parses the response as HTML. | `url: string` | `fetchHtml('https://example.com/page.html')` |
76
- | `fetchText(url)` | Fetches a URL and returns the response as plain text. | `url: string` | `fetchText('https://example.com/data.txt')` |
167
+ ### fetchHtml / fetchJson / fetchText
168
+
169
+ Shorthand functions for `fetchWith` with specific return types.
170
+
171
+ * **Input**: `input: string`
172
+ * **Output**: `Promise<HTMLElement | object | string>`
173
+
174
+ ```typescript
175
+ const doc = await fetchHtml("https://example.com")
176
+ const json = await fetchJson("https://api.example.com")
177
+
178
+ ```
77
179
 
78
180
  ---
79
181
 
80
- ### Classes
81
-
82
- | Class | Short Explanation | Methods | Usage |
83
- |---|---|---|---|
84
- | `Observer` | A wrapper around the native `IntersectionObserver`. | `observe(target)`, `throttle(target, time)`, `static observeWhile(...)` | `const obs = new Observer(cb); obs.observe(target);` |
85
- | `LazyImgLoader` | Handles lazy-loading images using `IntersectionObserver`. | `lazify(_target, img, imgSrc)`, `delazify(target)` | `const loader = new LazyImgLoader(shouldLoad); loader.lazify(div, img, 'src');` |
86
- | `Tick` | A utility for creating timed intervals. | `start(callback, finalCallback)`, `stop()` | `const ticker = new Tick(1000); ticker.start(() => console.log('tick'), () => console.log('done'));` |
87
- | `AsyncPool` | Manages a pool of asynchronous tasks with a concurrency limit. | `push(task)`, `run()`, `static doNAsyncAtOnce(...)` | `const pool = new AsyncPool(2); pool.push(task1); pool.push(task2); pool.run();` |
88
- | `DataManager` | A class for managing, filtering, and sorting data from a webpage. | `applyFilters(filters, offset)`, `filterAll(offset)`, `parseData(html, container, ...)` | `const dm = new DataManager(rules, state); dm.parseData(html);` |
89
- | `InfiniteScroller` | Implements infinite scrolling by loading new content when the user reaches the end of the page. | `onScroll(callback)`, `_onScroll()` | `const iscroll = new InfiniteScroller({ ...rules });` |
90
- | `RulesHelper` | A helper class for defining website-specific rules. | `router(store, cb)`, `_IS_VIDEO_PAGE()`, `_IS_SEARCH_PAGE()` | `const rules = new RulesHelper(options); rules.router(store, cb);` |
182
+ ## 🌲 DOM Manipulation
183
+
184
+ ### parseHtml
185
+
186
+ Parses a raw HTML string and returns a DOM element (or body if multiple children exist).
187
+
188
+ * **Input**: `html: string`
189
+ * **Output**: `HTMLElement`
190
+
191
+ ```typescript
192
+ const element = parseHtml("<div><span>Hello</span></div>")
193
+ document.body.append(element)
194
+
195
+ ```
196
+
197
+ ### querySelectorLast
198
+
199
+ Selects the last element matching a selector within a root.
200
+
201
+ * **Input**: `root?: ParentNode`, `selector: string`
202
+ * **Output**: `Element | undefined`
203
+
204
+ ```typescript
205
+ const lastItem = querySelectorLast(document, ".list-item")
206
+
207
+ ```
208
+
209
+ ### querySelectorText
210
+
211
+ Safely extracts and sanitizes the inner text of an element matching the selector.
212
+
213
+ * **Input**: `e: Element`, `selector: string`
214
+ * **Output**: `string`
215
+
216
+ ```typescript
217
+ const title = querySelectorText(document.body, "h1.main-title")
218
+
219
+ ```
220
+
221
+ ### replaceElementTag
222
+
223
+ Replaces an existing DOM element with a new element of a different tag name, preserving attributes and content.
224
+
225
+ * **Input**: `e: Element`, `tagName: string`
226
+ * **Output**: `Element` (the new element)
227
+
228
+ ```typescript
229
+ const oldDiv = document.querySelector("#container")
230
+ const newSection = replaceElementTag(oldDiv, "section")
231
+
232
+ ```
233
+
234
+ ### instantiateTemplate
235
+
236
+ Creates a DOM element from a selector, updating specific attributes and text content during cloning.
237
+
238
+ * **Input**: `sourceSelector: string`, `attributeUpdates: object`, `contentUpdates: object`
239
+ * **Output**: `string` (innerHTML of the wrapper)
240
+
241
+ ```typescript
242
+ const html = instantiateTemplate(
243
+ "#card-template",
244
+ { "data-id": "123" },
245
+ { ".card-title": "New Item" }
246
+ )
247
+
248
+ ```
249
+
250
+ ---
251
+
252
+ ## 👁️ DOM Observers
253
+
254
+ ### waitForElementToAppear
255
+
256
+ Watches the DOM until an element matching the selector appears, then executes a callback.
257
+
258
+ * **Input**: `parent: Node`, `selector: string`, `callback: (el: Element) => void`
259
+ * **Output**: `MutationObserver`
260
+
261
+ ```typescript
262
+ waitForElementToAppear(document.body, ".modal-popup", (modal) => {
263
+ console.log("Modal is ready", modal)
264
+ })
265
+
266
+ ```
267
+
268
+ ### waitForElementToDisappear
269
+
270
+ Watches an element and triggers a callback when it is removed from the DOM.
271
+
272
+ * **Input**: `observable: Element`, `callback: () => void`
273
+ * **Output**: `MutationObserver`
274
+
275
+ ```typescript
276
+ const loadingSpinner = document.querySelector("#spinner")
277
+ waitForElementToDisappear(loadingSpinner, () => {
278
+ console.log("Loading finished")
279
+ })
280
+
281
+ ```
282
+
283
+ ### watchDomChangesWithThrottle
284
+
285
+ Observes DOM changes and executes a callback with a throttle (rate limit).
286
+
287
+ * **Input**: `element: Node`, `callback: () => void`, `throttle?: number`
288
+ * **Output**: `MutationObserver`
289
+
290
+ ```typescript
291
+ watchDomChangesWithThrottle(document.body, () => {
292
+ console.log("DOM changed")
293
+ }, 500)
294
+
295
+ ```
296
+
297
+ ---
298
+
299
+ ## 🧬 Objects & Logic
300
+
301
+ ### memoize
302
+
303
+ Creates a function that caches the result of calls with identical arguments.
304
+
305
+ * **Input**: `fn: Function`
306
+ * **Output**: `Function`
307
+
308
+ ```typescript
309
+ const heavyCalc = (x) => x * x
310
+ const cachedCalc = memoize(heavyCalc)
311
+ cachedCalc(5)
312
+ cachedCalc(5)
313
+
314
+ ```
315
+
316
+ ### propsDifference
317
+
318
+ Compares two objects and returns the property names that are unique to each.
319
+
320
+ * **Input**: `obj1: object`, `obj2: object`
321
+ * **Output**: `{ d1: string[], d2: string[] }`
322
+
323
+ ```typescript
324
+ const diff = propsDifference({ a: 1, b: 2 }, { b: 3, c: 4 })
325
+ console.log(diff)
326
+
327
+ ```
328
+
329
+ ### objectToFormData
330
+
331
+ Converts a plain JavaScript object into a `FormData` object.
332
+
333
+ * **Input**: `obj: Record<string, any>`
334
+ * **Output**: `FormData`
335
+
336
+ ```typescript
337
+ const form = objectToFormData({ username: "admin", file: blob })
338
+
339
+ ```
340
+
341
+ ---
342
+
343
+ ## 🛠️ Specialized Classes
344
+
345
+ ### RegexFilter
346
+
347
+ A utility to compile and test strings against complex filter queries (supports OR logic, full-word search, and regex prefixes).
348
+
349
+ * **Usage**: Create with a query string, then use `hasEvery` or `hasNone`.
350
+
351
+ ```typescript
352
+ const filter = new RegexFilter("dog, cat, f:bird")
353
+ const isMatch = filter.hasEvery("I have a dog and a bird")
354
+
355
+ ```
356
+
357
+ ### OnHover
358
+
359
+ Handles complex hover interactions, including tracking when the pointer leaves a specific subject.
360
+
361
+ * **Usage**: Instantiate with a container and a subject selector.
362
+
363
+ ```typescript
364
+ OnHover.create(
365
+ document.body,
366
+ (el) => el.classList.contains("tooltip-target"),
367
+ (target) => {
368
+ console.log("Hovering", target)
369
+ return {
370
+ onOverCallback: () => console.log("Finally block")
371
+ }
372
+ }
373
+ )
374
+
375
+ ```
376
+
377
+ ### LazyImgLoader
378
+
379
+ Manages lazy loading of images by observing intersection and swapping data attributes for source URLs.
380
+
381
+ * **Usage**: Use `lazify` to setup an image and `delazify` to load it immediately.
382
+
383
+ ```typescript
384
+ const loader = new LazyImgLoader((target) => true)
385
+ const img = document.querySelector("img")
386
+ loader.lazify(img, "https://example.com/image.jpg")
387
+
388
+ ```