select-animation 1.3.0 → 1.5.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 (2) hide show
  1. package/package.json +1 -1
  2. package/select-animation.js +99 -61
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "select-animation",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "High-performance JavaScript animation engine for organic & fluid motion",
5
5
  "main": "select-animation.js",
6
6
  "scripts": {
@@ -100,13 +100,37 @@
100
100
  // Returns flat array of matched elements given one or more selectors
101
101
  // Usage: select('.class', '#id')
102
102
  // -----------------------------
103
- const selectDom = function(...selectors) {
104
- const all = [];
105
- selectors.forEach(selector => {
106
- const nodeList = document.querySelectorAll(selector);
107
- all.push(...nodeList);
108
- });
109
- return all;
103
+ // --- Enhanced DOM Selection Utility with Explicit Error Reporting ---
104
+ const selectDom = function (selector) {
105
+ // 1. Check if the input is a DOM element directly
106
+ if (isElement(selector)) return [selector];
107
+
108
+ // 2. Handle string selectors (e.g., ".class", "#id")
109
+ if (typeof selector === "string") {
110
+ const nodes = document.querySelectorAll(selector);
111
+ if (nodes.length === 0) {
112
+ // Warning if the selector is valid string but matches nothing
113
+ console.warn(`Select-Animation: No elements found matching the selector "${selector}".`);
114
+ }
115
+ return Array.from(nodes);
116
+ }
117
+
118
+ // 3. Handle arrays or collections
119
+ if (Array.isArray(selector) || (selector && typeof selector.length === "number")) {
120
+ return Array.from(selector).filter(isElement);
121
+ }
122
+
123
+ // 4. CRITICAL: Handle invalid types (like numbers, null, undefined)
124
+ if (selector !== undefined && selector !== null) {
125
+ // Throwing a console error to stop the developer and force a fix
126
+ console.error(
127
+ `Select-Animation ERROR: Invalid input passed to selectDom().\n` +
128
+ `Expected: String (selector), HTMLElement, or Array.\n` +
129
+ `Received: ${typeof selector} (${selector})`
130
+ );
131
+ }
132
+
133
+ return [];
110
134
  };
111
135
 
112
136
  // -----------------------------
@@ -139,49 +163,65 @@
139
163
  }
140
164
  expectNextGroup = false;
141
165
  }
142
- // If current item is a configuration object
166
+ // --- Handle Configuration Object with Validation ---
143
167
  else if (typeof defsCopy[c] === 'object' && defsCopy[c] !== null) {
144
168
  if (secondPass) {
145
- // Execute the runner for the current group and config
169
+ // Execute the animation runner for the current group
146
170
  runner(grouped, definitions, defsCopy, c)();
147
171
  } else {
148
172
  expectNextGroup = false;
173
+
174
+ // 1. Validate Duration and Animation Type
175
+ defsCopy[c].duration = (typeof defsCopy[c].duration === 'number' && defsCopy[c].duration > 0) ? defsCopy[c].duration : 1000;
149
176
 
150
- // Handle specialized animation types like vibration
151
- if (defsCopy[c].typeAnimation === "vibration" && defsCopy[c].vibrationStep === undefined) {
177
+ let requestedType = defsCopy[c].typeAnimation;
178
+
179
+ if (requestedType === "vibration" && defsCopy[c].vibrationStep === undefined) {
152
180
  defsCopy[c].vibrationStep = 6;
153
- } else {
154
- t = getNumber(defsCopy[c].typeAnimation);
181
+ } else if (requestedType) {
182
+ t = getNumber(requestedType);
155
183
  if (t && t.length === 4) {
156
184
  defsCopy[c].cubicbezier = t;
157
185
  defsCopy[c].typeAnimation = "cubicbezier";
186
+ } else {
187
+ // STOP EXECUTION: If easing is not found, do not fall back to linear
188
+ if (requestedType !== "linear" && requestedType !== "vibration" && requestedType !== "cubicbezier" && (!Easing || !Easing[requestedType])) {
189
+ // Use Error instead of warn to make it impossible to ignore
190
+ throw new Error(`Select-Animation ERROR: The easing function "${requestedType}" does not exist. Please check your spelling or definitions.`);
191
+ }
158
192
  }
159
193
  }
160
-
161
- // Set default loop behavior
162
- if (defsCopy[c].boucle && defsCopy[c].boucleType === undefined) {
163
- defsCopy[c].boucleType = "return";
164
- }
165
-
166
- // Organize properties and prepare "from" and "to" values
167
- if (!defsCopy[c].callback &&
194
+
195
+ // Double-check if the animation type is valid before processing
196
+ if (!defsCopy[c].typeAnimation) return;
197
+
198
+ // 2. Helper to sanitize input values (convert "100px" to 100)
199
+ const parseVal = (v) => {
200
+ if (typeof v === "number") return v;
201
+ let p = parseFloat(v);
202
+ return isNaN(p) ? 0 : p;
203
+ };
204
+
205
+ // Process properties and prepare "from" and "to" values
206
+ if (!defsCopy[c].callback &&
168
207
  (Array.isArray(defsCopy[c].property) || (defsCopy[c].property !== undefined && (defsCopy[c].property = [defsCopy[c].property])))) {
169
208
 
170
209
  defsCopy[c].property.forEach(function (propItem) {
171
210
  const fromIsObject = typeof defsCopy[c].from === "object";
172
211
  const toIsObject = typeof defsCopy[c].to === "object";
173
212
 
174
- if (!fromIsObject) valFrom = defsCopy[c].from;
175
- else fromItem = defsCopy[c]["from"][propIndex];
213
+ // Safely extract from/to values based on their types
214
+ if (!fromIsObject) valFrom = parseVal(defsCopy[c].from);
215
+ else fromItem = defsCopy[c]["from"][propIndex] || 0;
176
216
 
177
- if (!toIsObject) valTo = defsCopy[c].to;
178
- else toItem = defsCopy[c]["to"][propIndex];
217
+ if (!toIsObject) valTo = parseVal(defsCopy[c].to);
218
+ else toItem = defsCopy[c]["to"][propIndex] || 0;
179
219
 
180
- // Handle complex properties (Colors and Transforms)
181
220
  if (typeof propItem === "object") {
182
221
  let propName = Object.keys(propItem)[0];
183
222
  if (!Array.isArray(propItem[propName])) propItem[propName] = [propItem[propName]];
184
223
 
224
+ // Handle Colors and Transform objects
185
225
  if ((propName.toLowerCase().indexOf("color") !== -1 && (staging.color[propName] = COLOR_PROPERTIES[propName])) ||
186
226
  propName.toLowerCase().indexOf("transform") !== -1) {
187
227
 
@@ -193,66 +233,55 @@
193
233
  if (propName.toLowerCase() === "transform") staging[propName][innerProp] = 0;
194
234
  else staging.color[propName][innerProp] = 0;
195
235
 
196
- // Parse specific sub-properties (like scale, rotate, or RGB channels)
236
+ // Validate and assign nested properties
197
237
  if (fromIsObject) {
198
- if (fromItem[propName] !== undefined) {
199
- if (typeof fromItem[propName] === "number") {
200
- valFrom = staging["from"][propName][innerProp] = fromItem[propName];
201
- } else if (Array.isArray(fromItem[propName])) {
202
- valFrom = staging["from"][propName][innerProp] = fromItem[propName][inner] !== undefined ? fromItem[propName][inner] : valFrom;
203
- } else if (fromItem[propName][innerProp] !== undefined) {
204
- valFrom = staging["from"][propName][innerProp] = fromItem[propName][innerProp];
205
- }
206
- } else {
207
- staging["from"][propName][innerProp] = valFrom;
208
- }
238
+ let raw = (fromItem[propName] !== undefined) ? (Array.isArray(fromItem[propName]) ? fromItem[propName][inner] : fromItem[propName][innerProp]) : valFrom;
239
+ staging["from"][propName][innerProp] = parseVal(raw);
209
240
  } else {
210
- staging["from"][propName][innerProp] = defsCopy[c].from;
241
+ staging["from"][propName][innerProp] = parseVal(defsCopy[c].from);
211
242
  }
212
243
 
213
- // Repeat logic for "to" values
214
244
  if (toIsObject) {
215
- if (toItem[propName] !== undefined) {
216
- if (typeof toItem[propName] === "number") {
217
- valTo = staging["to"][propName][innerProp] = toItem[propName];
218
- } else if (Array.isArray(toItem[propName])) {
219
- valTo = staging["to"][propName][innerProp] = toItem[propName][inner] !== undefined ? toItem[propName][inner] : valTo;
220
- } else if (toItem[propName][innerProp] !== undefined) {
221
- valTo = staging["to"][propName][innerProp] = toItem[propName][innerProp];
222
- }
223
- } else {
224
- staging["to"][propName][innerProp] = valTo;
225
- }
245
+ let raw = (toItem[propName] !== undefined) ? (Array.isArray(toItem[propName]) ? toItem[propName][inner] : toItem[propName][innerProp]) : valTo;
246
+ staging["to"][propName][innerProp] = parseVal(raw);
226
247
  } else {
227
- staging["to"][propName][innerProp] = defsCopy[c].to;
248
+ staging["to"][propName][innerProp] = parseVal(defsCopy[c].to);
228
249
  }
229
250
  inner++;
230
251
  });
231
252
  propIndex++;
232
253
  }
233
254
  } else {
234
- // Handle simple numeric CSS properties (e.g., width, opacity)
255
+ // Handle simple numeric CSS properties (width, height, etc.)
235
256
  if (fromIsObject) {
236
- valFrom = staging["from"][propItem] = fromItem[propItem] !== undefined ? fromItem[propItem] : (fromItem !== undefined ? fromItem : valFrom);
257
+ let raw = fromItem[propItem] !== undefined ? fromItem[propItem] : (fromItem !== undefined ? fromItem : valFrom);
258
+ staging["from"][propItem] = parseVal(raw);
237
259
  } else {
238
- staging["from"][propItem] = defsCopy[c].from;
260
+ staging["from"][propItem] = parseVal(defsCopy[c].from);
239
261
  }
240
262
 
241
263
  if (toIsObject) {
242
- valTo = staging["to"][propItem] = toItem[propItem] !== undefined ? toItem[propItem] : (toItem !== undefined ? toItem : valTo);
264
+ let raw = toItem[propItem] !== undefined ? toItem[propItem] : (toItem !== undefined ? toItem : valTo);
265
+ staging["to"][propItem] = parseVal(raw);
243
266
  } else {
244
- staging["to"][propItem] = defsCopy[c].to;
267
+ staging["to"][propItem] = parseVal(defsCopy[c].to);
245
268
  }
246
269
  propIndex++;
247
270
  }
248
271
  });
249
272
  }
250
- // Save the processed plan into a store for runtime use
273
+
274
+ // 3. Callback Safety Validation
275
+ // Ensure hooks are actual functions to prevent execution errors
276
+ if (defsCopy[c].onStep && typeof defsCopy[c].onStep !== 'function') defsCopy[c].onStep = null;
277
+ if (defsCopy[c].onComplete && typeof defsCopy[c].onComplete !== 'function') defsCopy[c].onComplete = null;
278
+
279
+ // Finalize staging data and store it
251
280
  defsCopy[c].storeValueAnim = copyObj(staging);
252
281
  staging = { color: {}, transform: {}, from: {}, to: {} };
253
282
  }
254
-
255
- // Check if the next argument starts a new animation sequence
283
+
284
+ // Determine if the next item starts a new sequence group
256
285
  if (definitions[c + 1] !== undefined && (Array.isArray(definitions[c + 1]) || isElement(definitions[c + 1]))) {
257
286
  expectNextGroup = true;
258
287
  grouped = [];
@@ -767,8 +796,17 @@
767
796
  else return el.style[cssprop];
768
797
  }
769
798
 
799
+ // --- Robust DOM Element Validation ---
770
800
  function isElement(element) {
771
- return element instanceof Element || element instanceof HTMLDocument;
801
+ try {
802
+ // Check for standard DOM Element or HTMLDocument
803
+ return element instanceof Element || element instanceof HTMLDocument;
804
+ } catch (e) {
805
+ // Fallback for environments where Element is not defined
806
+ return (typeof element === "object") &&
807
+ (element.nodeType === 1) &&
808
+ (typeof element.nodeName === "string");
809
+ }
772
810
  }
773
811
 
774
812
  const copyObj = function(obj) {