tiny-essentials 1.12.2 → 1.13.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.
@@ -0,0 +1,174 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * A basic function that performs a task when the system is ready.
5
+ * Used for handlers in the readiness queue.
6
+ *
7
+ * @typedef {() => void} Fn
8
+ */
9
+
10
+ /**
11
+ * A function that determines whether a specific handler should be executed.
12
+ * Should return `true` to allow execution, or `false` to skip the handler.
13
+ *
14
+ * @typedef {() => boolean} FnFilter
15
+ */
16
+
17
+ /**
18
+ * @typedef {Object} Handler
19
+ * @property {Fn} fn - Function to execute when ready.
20
+ * @property {boolean} once - Whether to execute only once.
21
+ * @property {number} priority - Execution order (higher priority runs first).
22
+ * @property {FnFilter|null} filter - Optional filter function to determine execution.
23
+ * @property {boolean} domOnly - Whether to run as soon as DOM is ready (before full readiness).
24
+ */
25
+
26
+ class TinyDomReadyManager {
27
+ /** @type {Handler[]} */
28
+ #handlers = [];
29
+
30
+ /** @type {boolean} */
31
+ #isDomReady = false;
32
+
33
+ /** @type {boolean} */
34
+ #isFullyReady = false;
35
+
36
+ /** @type {Promise<any>[]} */
37
+ #promises = [];
38
+
39
+ /**
40
+ * Checks if the DOM is ready and if all Promises have been resolved.
41
+ */
42
+ #checkAllReady() {
43
+ if (this.#isDomReady) {
44
+ Promise.all(this.#promises)
45
+ .then(() => {
46
+ this.#isFullyReady = true;
47
+ this.#runHandlers(false); // run non-domOnly
48
+ })
49
+ .catch((err) => {
50
+ console.error('[TinyDomReadyManager] Promise rejected:', err);
51
+ });
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Executes handlers by filtering them by `domOnly` flag and sorting by priority.
57
+ * @param {boolean} domOnlyOnly - Whether to run only `domOnly` handlers.
58
+ */
59
+ #runHandlers(domOnlyOnly) {
60
+ this.#handlers
61
+ .filter((h) => h.domOnly === domOnlyOnly)
62
+ .sort((a, b) => b.priority - a.priority)
63
+ .forEach((handler) => this.#invokeHandler(handler));
64
+
65
+ this.#handlers = this.#handlers.filter((h) => !(h.once && (domOnlyOnly ? h.domOnly : true)));
66
+ }
67
+
68
+ /**
69
+ * Executes a handler if its filter passes.
70
+ * @param {Handler} handler
71
+ */
72
+ #invokeHandler(handler) {
73
+ if (typeof handler.filter === 'function') {
74
+ try {
75
+ if (!handler.filter()) return;
76
+ } catch (err) {
77
+ console.warn('[TinyDomReadyManager] Filter error:', err);
78
+ return;
79
+ }
80
+ }
81
+
82
+ try {
83
+ handler.fn();
84
+ } catch (err) {
85
+ console.error('[TinyDomReadyManager] Handler error:', err);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Marks the system as DOM-ready and runs DOM-only handlers.
91
+ * @private
92
+ */
93
+ _markDomReady() {
94
+ this.#isDomReady = true;
95
+ this.#runHandlers(true); // Run domOnly
96
+ this.#checkAllReady(); // Then check for full readiness
97
+ }
98
+
99
+ /**
100
+ * Initializes the manager using `DOMContentLoaded`.
101
+ */
102
+ init() {
103
+ if (this.#isDomReady) throw new Error('[TinyDomReadyManager] init() has already been called.');
104
+
105
+ if (document.readyState === 'interactive' || document.readyState === 'complete') {
106
+ this._markDomReady();
107
+ } else {
108
+ document.addEventListener('DOMContentLoaded', () => this._markDomReady());
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Adds a Promise to delay full readiness.
114
+ * @param {Promise<any>} promise
115
+ * @throws {TypeError}
116
+ */
117
+ addPromise(promise) {
118
+ if (!(promise instanceof Promise))
119
+ throw new TypeError('[TinyDomReadyManager] promise must be a valid Promise.');
120
+
121
+ if (this.#isFullyReady) return;
122
+ this.#promises.push(promise);
123
+ if (this.#isDomReady) this.#checkAllReady();
124
+ }
125
+
126
+ /**
127
+ * Registers a handler to run either after DOM is ready or after full readiness.
128
+ *
129
+ * @param {Fn} fn - Function to execute.
130
+ * @param {Object} [options]
131
+ * @param {boolean} [options.once=true] - Execute only once.
132
+ * @param {number} [options.priority=0] - Higher priority runs first.
133
+ * @param {FnFilter|null} [options.filter=null] - Optional filter function.
134
+ * @param {boolean} [options.domOnly=false] - If true, executes after DOM ready only.
135
+ * @throws {TypeError} If fn is not a function.
136
+ */
137
+ onReady(fn, { once = true, priority = 0, filter = null, domOnly = false } = {}) {
138
+ if (typeof fn !== 'function')
139
+ throw new TypeError('[TinyDomReadyManager] fn must be a function.');
140
+
141
+ const handler = { fn, once, priority, filter, domOnly };
142
+
143
+ if (domOnly && this.#isDomReady) {
144
+ this.#invokeHandler(handler);
145
+ if (!once) this.#handlers.push(handler);
146
+ return;
147
+ }
148
+
149
+ if (!domOnly && this.#isFullyReady) {
150
+ this.#invokeHandler(handler);
151
+ } else {
152
+ this.#handlers.push(handler);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Returns whether the system is fully ready (DOM + Promises).
158
+ * @returns {boolean}
159
+ */
160
+ isReady() {
161
+ return this.#isFullyReady;
162
+ }
163
+
164
+ /**
165
+ * Returns whether the DOM is ready (DOMContentLoaded has fired).
166
+ * Does not wait for promises.
167
+ * @returns {boolean}
168
+ */
169
+ isDomReady() {
170
+ return this.#isDomReady;
171
+ }
172
+ }
173
+
174
+ module.exports = TinyDomReadyManager;
@@ -0,0 +1,100 @@
1
+ export default TinyDomReadyManager;
2
+ /**
3
+ * A basic function that performs a task when the system is ready.
4
+ * Used for handlers in the readiness queue.
5
+ */
6
+ export type Fn = () => void;
7
+ /**
8
+ * A function that determines whether a specific handler should be executed.
9
+ * Should return `true` to allow execution, or `false` to skip the handler.
10
+ */
11
+ export type FnFilter = () => boolean;
12
+ export type Handler = {
13
+ /**
14
+ * - Function to execute when ready.
15
+ */
16
+ fn: Fn;
17
+ /**
18
+ * - Whether to execute only once.
19
+ */
20
+ once: boolean;
21
+ /**
22
+ * - Execution order (higher priority runs first).
23
+ */
24
+ priority: number;
25
+ /**
26
+ * - Optional filter function to determine execution.
27
+ */
28
+ filter: FnFilter | null;
29
+ /**
30
+ * - Whether to run as soon as DOM is ready (before full readiness).
31
+ */
32
+ domOnly: boolean;
33
+ };
34
+ /**
35
+ * A basic function that performs a task when the system is ready.
36
+ * Used for handlers in the readiness queue.
37
+ *
38
+ * @typedef {() => void} Fn
39
+ */
40
+ /**
41
+ * A function that determines whether a specific handler should be executed.
42
+ * Should return `true` to allow execution, or `false` to skip the handler.
43
+ *
44
+ * @typedef {() => boolean} FnFilter
45
+ */
46
+ /**
47
+ * @typedef {Object} Handler
48
+ * @property {Fn} fn - Function to execute when ready.
49
+ * @property {boolean} once - Whether to execute only once.
50
+ * @property {number} priority - Execution order (higher priority runs first).
51
+ * @property {FnFilter|null} filter - Optional filter function to determine execution.
52
+ * @property {boolean} domOnly - Whether to run as soon as DOM is ready (before full readiness).
53
+ */
54
+ declare class TinyDomReadyManager {
55
+ /**
56
+ * Marks the system as DOM-ready and runs DOM-only handlers.
57
+ * @private
58
+ */
59
+ private _markDomReady;
60
+ /**
61
+ * Initializes the manager using `DOMContentLoaded`.
62
+ */
63
+ init(): void;
64
+ /**
65
+ * Adds a Promise to delay full readiness.
66
+ * @param {Promise<any>} promise
67
+ * @throws {TypeError}
68
+ */
69
+ addPromise(promise: Promise<any>): void;
70
+ /**
71
+ * Registers a handler to run either after DOM is ready or after full readiness.
72
+ *
73
+ * @param {Fn} fn - Function to execute.
74
+ * @param {Object} [options]
75
+ * @param {boolean} [options.once=true] - Execute only once.
76
+ * @param {number} [options.priority=0] - Higher priority runs first.
77
+ * @param {FnFilter|null} [options.filter=null] - Optional filter function.
78
+ * @param {boolean} [options.domOnly=false] - If true, executes after DOM ready only.
79
+ * @throws {TypeError} If fn is not a function.
80
+ */
81
+ onReady(fn: Fn, { once, priority, filter, domOnly }?: {
82
+ once?: boolean | undefined;
83
+ priority?: number | undefined;
84
+ filter?: FnFilter | null | undefined;
85
+ domOnly?: boolean | undefined;
86
+ }): void;
87
+ /**
88
+ * Returns whether the system is fully ready (DOM + Promises).
89
+ * @returns {boolean}
90
+ */
91
+ isReady(): boolean;
92
+ /**
93
+ * Returns whether the DOM is ready (DOMContentLoaded has fired).
94
+ * Does not wait for promises.
95
+ * @returns {boolean}
96
+ */
97
+ isDomReady(): boolean;
98
+ #private;
99
+ }
100
+ //# sourceMappingURL=TinyDomReadyManager.d.mts.map
@@ -0,0 +1,158 @@
1
+ /**
2
+ * A basic function that performs a task when the system is ready.
3
+ * Used for handlers in the readiness queue.
4
+ *
5
+ * @typedef {() => void} Fn
6
+ */
7
+ /**
8
+ * A function that determines whether a specific handler should be executed.
9
+ * Should return `true` to allow execution, or `false` to skip the handler.
10
+ *
11
+ * @typedef {() => boolean} FnFilter
12
+ */
13
+ /**
14
+ * @typedef {Object} Handler
15
+ * @property {Fn} fn - Function to execute when ready.
16
+ * @property {boolean} once - Whether to execute only once.
17
+ * @property {number} priority - Execution order (higher priority runs first).
18
+ * @property {FnFilter|null} filter - Optional filter function to determine execution.
19
+ * @property {boolean} domOnly - Whether to run as soon as DOM is ready (before full readiness).
20
+ */
21
+ class TinyDomReadyManager {
22
+ /** @type {Handler[]} */
23
+ #handlers = [];
24
+ /** @type {boolean} */
25
+ #isDomReady = false;
26
+ /** @type {boolean} */
27
+ #isFullyReady = false;
28
+ /** @type {Promise<any>[]} */
29
+ #promises = [];
30
+ /**
31
+ * Checks if the DOM is ready and if all Promises have been resolved.
32
+ */
33
+ #checkAllReady() {
34
+ if (this.#isDomReady) {
35
+ Promise.all(this.#promises)
36
+ .then(() => {
37
+ this.#isFullyReady = true;
38
+ this.#runHandlers(false); // run non-domOnly
39
+ })
40
+ .catch((err) => {
41
+ console.error('[TinyDomReadyManager] Promise rejected:', err);
42
+ });
43
+ }
44
+ }
45
+ /**
46
+ * Executes handlers by filtering them by `domOnly` flag and sorting by priority.
47
+ * @param {boolean} domOnlyOnly - Whether to run only `domOnly` handlers.
48
+ */
49
+ #runHandlers(domOnlyOnly) {
50
+ this.#handlers
51
+ .filter((h) => h.domOnly === domOnlyOnly)
52
+ .sort((a, b) => b.priority - a.priority)
53
+ .forEach((handler) => this.#invokeHandler(handler));
54
+ this.#handlers = this.#handlers.filter((h) => !(h.once && (domOnlyOnly ? h.domOnly : true)));
55
+ }
56
+ /**
57
+ * Executes a handler if its filter passes.
58
+ * @param {Handler} handler
59
+ */
60
+ #invokeHandler(handler) {
61
+ if (typeof handler.filter === 'function') {
62
+ try {
63
+ if (!handler.filter())
64
+ return;
65
+ }
66
+ catch (err) {
67
+ console.warn('[TinyDomReadyManager] Filter error:', err);
68
+ return;
69
+ }
70
+ }
71
+ try {
72
+ handler.fn();
73
+ }
74
+ catch (err) {
75
+ console.error('[TinyDomReadyManager] Handler error:', err);
76
+ }
77
+ }
78
+ /**
79
+ * Marks the system as DOM-ready and runs DOM-only handlers.
80
+ * @private
81
+ */
82
+ _markDomReady() {
83
+ this.#isDomReady = true;
84
+ this.#runHandlers(true); // Run domOnly
85
+ this.#checkAllReady(); // Then check for full readiness
86
+ }
87
+ /**
88
+ * Initializes the manager using `DOMContentLoaded`.
89
+ */
90
+ init() {
91
+ if (this.#isDomReady)
92
+ throw new Error('[TinyDomReadyManager] init() has already been called.');
93
+ if (document.readyState === 'interactive' || document.readyState === 'complete') {
94
+ this._markDomReady();
95
+ }
96
+ else {
97
+ document.addEventListener('DOMContentLoaded', () => this._markDomReady());
98
+ }
99
+ }
100
+ /**
101
+ * Adds a Promise to delay full readiness.
102
+ * @param {Promise<any>} promise
103
+ * @throws {TypeError}
104
+ */
105
+ addPromise(promise) {
106
+ if (!(promise instanceof Promise))
107
+ throw new TypeError('[TinyDomReadyManager] promise must be a valid Promise.');
108
+ if (this.#isFullyReady)
109
+ return;
110
+ this.#promises.push(promise);
111
+ if (this.#isDomReady)
112
+ this.#checkAllReady();
113
+ }
114
+ /**
115
+ * Registers a handler to run either after DOM is ready or after full readiness.
116
+ *
117
+ * @param {Fn} fn - Function to execute.
118
+ * @param {Object} [options]
119
+ * @param {boolean} [options.once=true] - Execute only once.
120
+ * @param {number} [options.priority=0] - Higher priority runs first.
121
+ * @param {FnFilter|null} [options.filter=null] - Optional filter function.
122
+ * @param {boolean} [options.domOnly=false] - If true, executes after DOM ready only.
123
+ * @throws {TypeError} If fn is not a function.
124
+ */
125
+ onReady(fn, { once = true, priority = 0, filter = null, domOnly = false } = {}) {
126
+ if (typeof fn !== 'function')
127
+ throw new TypeError('[TinyDomReadyManager] fn must be a function.');
128
+ const handler = { fn, once, priority, filter, domOnly };
129
+ if (domOnly && this.#isDomReady) {
130
+ this.#invokeHandler(handler);
131
+ if (!once)
132
+ this.#handlers.push(handler);
133
+ return;
134
+ }
135
+ if (!domOnly && this.#isFullyReady) {
136
+ this.#invokeHandler(handler);
137
+ }
138
+ else {
139
+ this.#handlers.push(handler);
140
+ }
141
+ }
142
+ /**
143
+ * Returns whether the system is fully ready (DOM + Promises).
144
+ * @returns {boolean}
145
+ */
146
+ isReady() {
147
+ return this.#isFullyReady;
148
+ }
149
+ /**
150
+ * Returns whether the DOM is ready (DOMContentLoaded has fired).
151
+ * Does not wait for promises.
152
+ * @returns {boolean}
153
+ */
154
+ isDomReady() {
155
+ return this.#isDomReady;
156
+ }
157
+ }
158
+ export default TinyDomReadyManager;
package/docs/v1/README.md CHANGED
@@ -31,6 +31,7 @@ This folder contains the core scripts we have worked on so far. Each file is a m
31
31
  - 📥 **[TinyDragDropDetector](./libs/TinyDragDropDetector.md)** — A lightweight drag-and-drop detector for files, handling the full drag lifecycle (`enter`, `over`, `leave`, `drop`) with CSS hover management and safe event handling on any DOM element or the full page.
32
32
  * 📂 **[TinyUploadClicker](./libs/TinyUploadClicker.md)** — A minimal utility to bind any clickable element to a hidden file input, offering full control over styling, behavior, and upload event hooks.
33
33
  * 🧲 **[TinyDragger](./libs/TinyDragger.md)** — A flexible drag-and-drop manager with collision detection, jail constraints, vibration feedback, visual proxies, revert-on-drop, and full custom event support.
34
+ * 🕒 **[TinyDomReadyManager](./libs/TinyDomReadyManager.md)** — A readiness manager for DOM and async conditions, supporting prioritized callbacks, custom filters, and event-based or promise-based bootstrapping.
34
35
 
35
36
  ### 3. **`fileManager/`**
36
37
  * 📁 **[main](./fileManager/main.md)** — A Node.js file/directory utility module with support for JSON, backups, renaming, size analysis, and more.
@@ -0,0 +1,198 @@
1
+ # 🕒 TinyDomReadyManager
2
+
3
+ A flexible, customizable JavaScript class to execute code **only when the DOM is ready**, or when additional asynchronous requirements (like Promises or asset loads) are met.
4
+ Supports **prioritized callbacks**, **filters**, and now even **DOM-only fast execution**!
5
+
6
+ ---
7
+
8
+ ## 🧠 Type Definitions
9
+
10
+ ### `Fn` 🛠️
11
+
12
+ ```ts
13
+ type Fn = () => void;
14
+ ```
15
+
16
+ A basic function to be executed once the system is ready.
17
+
18
+ ---
19
+
20
+ ### `FnFilter` 🔍
21
+
22
+ ```ts
23
+ type FnFilter = () => boolean;
24
+ ```
25
+
26
+ A conditional function that decides whether a handler should be executed.
27
+ Returns `true` to allow execution, or `false` to skip.
28
+
29
+ ---
30
+
31
+ ### `Handler` 🧩
32
+
33
+ ```ts
34
+ interface Handler {
35
+ fn: Fn;
36
+ once: boolean;
37
+ priority: number;
38
+ filter: FnFilter | null;
39
+ domOnly: boolean;
40
+ }
41
+ ```
42
+
43
+ Describes a registered handler and how it should be treated during readiness flow.
44
+
45
+ ---
46
+
47
+ ## 🚀 Class: `TinyDomReadyManager`
48
+
49
+ ### `init()` 🕓
50
+
51
+ ```ts
52
+ init(): void
53
+ ```
54
+
55
+ Initializes the manager and waits for the `DOMContentLoaded` event.
56
+ If the DOM is already ready, it triggers immediately.
57
+
58
+ * ⚠️ Throws an `Error` if called more than once.
59
+
60
+ ---
61
+
62
+ ### `addPromise(promise)` ⏳
63
+
64
+ ```ts
65
+ addPromise(promise: Promise<any>): void
66
+ ```
67
+
68
+ Adds a custom `Promise` that delays full readiness.
69
+ This allows tasks like `fetch()` or image preloading to block late-stage handlers.
70
+
71
+ * ⚠️ Throws a `TypeError` if `promise` is not a real `Promise`.
72
+
73
+ ---
74
+
75
+ ### `onReady(fn, options?)` ✅
76
+
77
+ ```ts
78
+ onReady(fn: Fn, options?: {
79
+ once?: boolean;
80
+ priority?: number;
81
+ filter?: FnFilter | null;
82
+ domOnly?: boolean;
83
+ }): void
84
+ ```
85
+
86
+ Registers a function to run at the right moment:
87
+
88
+ | Option | Type | Default | Description |
89
+ | ---------- | ------------------ | ------- | ------------------------------------------------------------------------ |
90
+ | `once` | `boolean` | `true` | Whether to execute only once |
91
+ | `priority` | `number` | `0` | Higher values run earlier |
92
+ | `filter` | `FnFilter \| null` | `null` | Optional condition before executing |
93
+ | `domOnly` | `boolean` | `false` | 🚀 If `true`, runs immediately after **DOM is ready**, skipping promises |
94
+
95
+ * ⚠️ Throws a `TypeError` if `fn` is not a function.
96
+
97
+ ---
98
+
99
+ ### `isReady()` 📡
100
+
101
+ ```ts
102
+ isReady(): boolean
103
+ ```
104
+
105
+ Returns `true` only after both the DOM is ready **and** all added promises have resolved.
106
+
107
+ ---
108
+
109
+ ### `isDomReady()` 🧱
110
+
111
+ ```ts
112
+ isDomReady(): boolean
113
+ ```
114
+
115
+ Returns `true` if the DOM is already ready (i.e., `DOMContentLoaded` has fired).
116
+ Returns `false` if the DOM is still loading.
117
+
118
+ > ✅ Useful when you want to know *just* when the document is interactive, regardless of async operations.
119
+
120
+ ---
121
+
122
+ ## 🧪 Readiness Phases
123
+
124
+ | Phase | Description |
125
+ | --------------- | ---------------------------------------- |
126
+ | **DOM Ready** | Triggers `domOnly: true` handlers |
127
+ | **Fully Ready** | Triggers all normal `onReady()` handlers |
128
+
129
+ ---
130
+
131
+ ## 💡 Example Usage
132
+
133
+ ### Basic Usage
134
+
135
+ ```js
136
+ import TinyDomReadyManager from './TinyDomReadyManager.js';
137
+
138
+ const manager = new TinyDomReadyManager();
139
+
140
+ manager.onReady(() => {
141
+ console.log('Ready after DOM + Promises!');
142
+ });
143
+
144
+ manager.addPromise(fetch('/config.json'));
145
+ manager.init();
146
+ ```
147
+
148
+ ---
149
+
150
+ ### DOM-Only Handler (Faster)
151
+
152
+ ```js
153
+ manager.onReady(() => {
154
+ console.log('DOM is ready, skipping Promises!');
155
+ }, { domOnly: true, priority: 100 });
156
+ ```
157
+
158
+ ---
159
+
160
+ ### Conditional + Prioritized
161
+
162
+ ```js
163
+ manager.onReady(() => {
164
+ console.log('Only runs if in dark mode');
165
+ }, {
166
+ priority: 5,
167
+ filter: () => document.documentElement.classList.contains('dark')
168
+ });
169
+ ```
170
+
171
+ ---
172
+
173
+ ### Mixing Both Types
174
+
175
+ ```js
176
+ manager.onReady(() => {
177
+ console.log('DOM-only logic A');
178
+ }, { domOnly: true, priority: 10 });
179
+
180
+ manager.onReady(() => {
181
+ console.log('DOM-only logic B');
182
+ }, { domOnly: true, priority: 5 });
183
+
184
+ manager.onReady(() => {
185
+ console.log('Waits for API call');
186
+ });
187
+
188
+ manager.addPromise(fetch('/api/user'));
189
+ manager.init();
190
+ ```
191
+
192
+ ---
193
+
194
+ ## 📌 Notes
195
+
196
+ * `domOnly` handlers are ideal for UI setup or event binding.
197
+ * Regular handlers are best for things that depend on data loading or external states.
198
+ * All handlers are executed safely, and exceptions are caught with warning logs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-essentials",
3
- "version": "1.12.2",
3
+ "version": "1.13.0",
4
4
  "description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
5
5
  "scripts": {
6
6
  "test": "npm run test:mjs && npm run test:cjs && npm run test:js",