eyjs 6.0.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.
package/src/eye.js ADDED
@@ -0,0 +1,1143 @@
1
+ /**
2
+ * @typedef {Object} AttrMap
3
+ * @property {HTMLDivElement} parent - append newly created element to a parent
4
+ * @property {Array<String>|String} class - class or array of classes to set
5
+ * @property {String} id - set element ID
6
+ * @property {Object} data - dataset values to set
7
+ * @property {String} text - set element text
8
+ * @property {String} html - set element html
9
+ */
10
+
11
+ /**
12
+ * Returns the associated class event of that event
13
+ * example: for click event it returns new MouseEvent("click")
14
+ * @param {string} ev
15
+ */
16
+ function getEvent(type, options = {}) {
17
+ switch (type) {
18
+ // Mouse Events
19
+ case 'click':
20
+ case 'dblclick':
21
+ case 'mousedown':
22
+ case 'mouseup':
23
+ case 'mousemove':
24
+ case 'mouseenter':
25
+ case 'mouseleave':
26
+ case 'mouseover':
27
+ case 'mouseout':
28
+ return new MouseEvent(type, options);
29
+
30
+ // Pointer Events
31
+ case 'pointerdown':
32
+ case 'pointerup':
33
+ case 'pointermove':
34
+ case 'pointerenter':
35
+ case 'pointerleave':
36
+ case 'pointerover':
37
+ case 'pointerout':
38
+ return new PointerEvent(type, options);
39
+
40
+ // Keyboard Events
41
+ case 'keydown':
42
+ case 'keyup':
43
+ case 'keypress':
44
+ return new KeyboardEvent(type, options);
45
+
46
+ // Focus Events
47
+ case 'focus':
48
+ case 'blur':
49
+ case 'focusin':
50
+ case 'focusout':
51
+ return new FocusEvent(type, options);
52
+
53
+ // Input & Form Events
54
+ case 'input':
55
+ case 'change':
56
+ case 'submit':
57
+ case 'reset':
58
+ return new Event(type, options);
59
+
60
+ // Wheel
61
+ case 'wheel':
62
+ return new WheelEvent(type, options);
63
+
64
+ // Clipboard
65
+ case 'copy':
66
+ case 'cut':
67
+ case 'paste':
68
+ return new ClipboardEvent(type, options);
69
+
70
+ // UI
71
+ case 'scroll':
72
+ case 'resize':
73
+ return new UIEvent(type, options);
74
+
75
+ // Default: fallback to generic Event
76
+ default:
77
+ return new CustomEvent(type, options);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * @typedef {EyeElement & {
83
+ * refresh: (attrs: AttrMap) => ModelEyeElement
84
+ * }} ModelEyeElement eye element definition for model elements
85
+ */
86
+
87
+ const events = [
88
+ // Mouse Events
89
+ "click",
90
+ "dblclick",
91
+ "mousedown",
92
+ "mouseup",
93
+ "mousemove",
94
+ "mouseenter",
95
+ "mouseleave",
96
+ "mouseover",
97
+ "mouseout",
98
+ "contextmenu",
99
+
100
+ // Keyboard Events
101
+ "keydown",
102
+ "keypress", // Deprecated
103
+ "keyup",
104
+
105
+ // Focus Events
106
+ "focus",
107
+ "blur",
108
+ "focusin",
109
+ "focusout",
110
+
111
+ // Form Events
112
+ "submit",
113
+ "change",
114
+ "input",
115
+ "reset",
116
+ "select",
117
+
118
+ // Touch Events (for mobile)
119
+ "touchstart",
120
+ "touchend",
121
+ "touchmove",
122
+ "touchcancel",
123
+
124
+ // Pointer Events
125
+ "pointerdown",
126
+ "pointerup",
127
+ "pointermove",
128
+ "pointerenter",
129
+ "pointerleave",
130
+ "pointercancel",
131
+
132
+ // Drag and Drop Events
133
+ "dragstart",
134
+ "dragend",
135
+ "dragenter",
136
+ "dragover",
137
+ "dragleave",
138
+ "drop",
139
+
140
+ // Window/Document Events
141
+ "resize",
142
+ "scroll",
143
+ "load",
144
+ "beforeunload",
145
+ "unload",
146
+
147
+ // Media Events
148
+ "play",
149
+ "pause",
150
+ "ended",
151
+ "volumechange",
152
+ "timeupdate",
153
+
154
+ // Clipboard Events
155
+ "copy",
156
+ "cut",
157
+ "paste",
158
+
159
+ // Animation and Transition Events
160
+ "animationstart",
161
+ "animationend",
162
+ "animationiteration",
163
+ "transitionstart",
164
+ "transitionend",
165
+
166
+ // Mutation Events
167
+ "DOMSubtreeModified",
168
+ "DOMNodeInserted",
169
+ "DOMNodeRemoved",
170
+
171
+ // Other Events
172
+ "error",
173
+ "hashchange",
174
+ "popstate",
175
+ ];
176
+
177
+ const htmlElements = [
178
+ // Metadata
179
+ "<base>",
180
+ "<head>",
181
+ "<link>",
182
+ "<meta>",
183
+ "<style>",
184
+ "<title>",
185
+
186
+ // Sections
187
+ "<body>",
188
+ "<address>",
189
+ "<article>",
190
+ "<aside>",
191
+ "<footer>",
192
+ "<header>",
193
+ "<h1>",
194
+ "<h2>",
195
+ "<h3>",
196
+ "<h4>",
197
+ "<h5>",
198
+ "<h6>",
199
+ "<main>",
200
+ "<nav>",
201
+ "<section>",
202
+
203
+ // Text content
204
+ "<blockquote>",
205
+ "<dd>",
206
+ "<div>",
207
+ "<dl>",
208
+ "<dt>",
209
+ "<figcaption>",
210
+ "<figure>",
211
+ "<hr>",
212
+ "<li>",
213
+ "<ol>",
214
+ "<p>",
215
+ "<pre>",
216
+ "<ul>",
217
+
218
+ // Inline text semantics
219
+ "<a>",
220
+ "<abbr>",
221
+ "<b>",
222
+ "<bdi>",
223
+ "<bdo>",
224
+ "<br>",
225
+ "<cite>",
226
+ "<code>",
227
+ "<data>",
228
+ "<dfn>",
229
+ "<em>",
230
+ "<i>",
231
+ "<kbd>",
232
+ "<mark>",
233
+ "<q>",
234
+ "<rp>",
235
+ "<rt>",
236
+ "<ruby>",
237
+ "<s>",
238
+ "<samp>",
239
+ "<small>",
240
+ "<span>",
241
+ "<strong>",
242
+ "<sub>",
243
+ "<sup>",
244
+ "<time>",
245
+ "<u>",
246
+ "<var>",
247
+ "<wbr>",
248
+
249
+ // Image and multimedia
250
+ "<area>",
251
+ "<audio>",
252
+ "<img>",
253
+ "<map>",
254
+ "<track>",
255
+ "<video>",
256
+
257
+ // Embedded content
258
+ "<embed>",
259
+ "<iframe>",
260
+ "<object>",
261
+ "<picture>",
262
+ "<portal>",
263
+ "<source>",
264
+
265
+ // Scripting
266
+ "<canvas>",
267
+ "<noscript>",
268
+ "<script>",
269
+
270
+ // Demarcating edits
271
+ "<del>",
272
+ "<ins>",
273
+
274
+ // Table content
275
+ "<caption>",
276
+ "<col>",
277
+ "<colgroup>",
278
+ "<table>",
279
+ "<tbody>",
280
+ "<td>",
281
+ "<tfoot>",
282
+ "<th>",
283
+ "<thead>",
284
+ "<tr>",
285
+
286
+ // Forms
287
+ "<button>",
288
+ "<datalist>",
289
+ "<fieldset>",
290
+ "<form>",
291
+ "<input>",
292
+ "<label>",
293
+ "<legend>",
294
+ "<meter>",
295
+ "<optgroup>",
296
+ "<option>",
297
+ "<output>",
298
+ "<progress>",
299
+ "<select>",
300
+ "<textarea>",
301
+
302
+ // Interactive elements
303
+ "<details>",
304
+ "<dialog>",
305
+ "<summary>",
306
+
307
+ // Web components / scripting base
308
+ "<slot>",
309
+ "<template>",
310
+ ];
311
+
312
+ function flat(word) {
313
+ let n = "";
314
+ for (let i = 0; i < word.length; i++) {
315
+ const t = word[i];
316
+ if (t === t.toUpperCase() && t !== t.toLowerCase()) n += "-" + t;
317
+ else n += t;
318
+ }
319
+ return n.toLowerCase();
320
+ }
321
+
322
+ const localdata = new WeakMap();
323
+
324
+ /**
325
+ * cmcl stands for Create Model Children Layers, recursively creates model layers one by one
326
+ * @param {EyeElement} parent
327
+ * @param {Object} layer
328
+ * @returns {Array<{name: string,set: (parent: EyeElement, value: String) =>}>}
329
+ */
330
+ function cmcl(parent, layer) {
331
+ let obj = [];
332
+ for (const key in layer) {
333
+ const subcontent = layer[key];
334
+ const [def, _set] = key.split(":");
335
+ const [tagName, ...cls] = def.split(".");
336
+ let [_set_name = null, _set_default = null] = (_set || "")
337
+ .split("-")
338
+ .map((a) => a.trim());
339
+
340
+ let elm = e(tagName.trim(), {
341
+ class: cls,
342
+ parent,
343
+ data: _set ? { value: _set_name } : undefined,
344
+ });
345
+
346
+ if (_set && _set_name) {
347
+ obj.push({
348
+ name: _set_name,
349
+ set(parent, value) {
350
+ let elm = parent.find(`[data-value="${_set_name}"]`);
351
+ elm.textContent = value ?? _set_default;
352
+ }
353
+ });
354
+ }
355
+
356
+ // recursive
357
+ if (
358
+ subcontent &&
359
+ typeof subcontent === "object" &&
360
+ !(subcontent instanceof Array)
361
+ )
362
+ obj = obj.concat(cmcl(elm, subcontent));
363
+ }
364
+ return obj;
365
+ }
366
+
367
+ let delegationEvents = ["click", "submit", "input", "change", "keydown", "keyup", "keypress", "focusin", "focusout", "mouseover", "mouseout"];
368
+ let normalSetterGetter = (action, v, elm) => v;
369
+
370
+ /**
371
+ * Eye wrapper offers a subset of functions that ease DOM minipulation! Power of JQuery with
372
+ * some a modern design and a bunch of new functions.
373
+ * @author Yousef Neji
374
+ */
375
+ export class EyeElement {
376
+ /**
377
+ * Raw html element
378
+ * @type {HTMLElement}
379
+ */
380
+ #raw = null;
381
+
382
+ /**
383
+ * Used to store delegated events listeners
384
+ * @type {Map<String,Set<{callback, target: string}>>}
385
+ */
386
+ #dlgListeners = new Map();
387
+
388
+ /**
389
+ * Custom way or modifier that redefine the way you set/get
390
+ * this element `textContent` or `value`:
391
+ * - access this feature from `.redefine` method.
392
+ */
393
+ #customSet = {
394
+ value: normalSetterGetter,
395
+ text: normalSetterGetter
396
+ };
397
+
398
+ /**
399
+ * Called internally to initiate the main EyeElement functionalities
400
+ * @method EyeElement#init
401
+ * @param {string|HTMLElement} selector
402
+ * @param {AttrMap} attrs
403
+ * @param {Object} css
404
+ * @returns {EyeElement}
405
+ */
406
+ constructor(selector, attrs, css) {
407
+ let _this = this;
408
+ if (selector instanceof HTMLElement) {
409
+ this.#raw = selector;
410
+ } else if (htmlElements.includes(selector)) {
411
+ // creating a new element
412
+ this.#raw = document.createElement(selector.substring(1, selector.length - 1));
413
+ } else {
414
+ // selecting
415
+ let s = selector.slice(-1) === "!";
416
+ this.#raw = document.querySelectorAll(s ? selector.slice(0, -1) : selector);
417
+
418
+ if (this.#raw.length == 0) return null; // we stop everything here
419
+ if (this.length == 1 || s) this.#raw = this.#raw.item(0);
420
+ }
421
+
422
+ /**
423
+ * Handler used to integrate delegation concept/algorithme
424
+ * @param {Event} e
425
+ */
426
+ function handler(e) {
427
+ let name = e.type,
428
+ listeners = _this.#dlgListeners,
429
+ _etarget = e.target,
430
+ me = this; // refers to the element being listening to the event
431
+
432
+ if (listeners.has(name)) {
433
+ let cbs = listeners.get(name);
434
+ cbs?.forEach(({ callback, target }) => {
435
+ if (_etarget.closest(target)) {
436
+ // we hitting the target
437
+ callback(e, me);
438
+ }
439
+ })
440
+ }
441
+ }
442
+
443
+ this.each((elm, idx) => {
444
+ let parentElm = null;
445
+ if (attrs)
446
+ for (const key in attrs) {
447
+ const value = attrs[key];
448
+ if (key == "class")
449
+ elm.classList.add.apply(
450
+ elm.classList,
451
+ (value instanceof Array ? value : value.split(" ")).filter(a => a != "")
452
+ );
453
+ else if (key == "text") elm.textContent = value;
454
+ else if (key == "html") elm.innerHTML = value;
455
+ else if (key == "data") for (const k in value) elm.dataset[k] = value[k];
456
+ else if (key == "parent") parentElm = value;
457
+ else if (key in elm) elm[key] = value;
458
+ else if (key[0] != "_") elm.setAttribute(key, elm); // we must ignore _ started keys 'cause they are used by models
459
+ }
460
+ if (css)
461
+ for (const key in css)
462
+ if (key.indexOf("-") != -1) elm.style[key] = css[key];
463
+ else elm.style[flat(key)] = css[key];
464
+ if (parentElm instanceof EyeElement || parentElm instanceof HTMLElement) parentElm.append(elm);
465
+
466
+ // creating the delegation handling model
467
+ delegationEvents.forEach(ev => {
468
+ elm.addEventListener(ev, handler);
469
+ })
470
+ })
471
+
472
+ // creating/initiating events functions
473
+ events.forEach(ev => {
474
+ _this[ev] = function (cb) {
475
+ if (cb) {
476
+ if (typeof cb == "function") _this.on(ev, cb);
477
+ } else _this.trigger(ev);
478
+
479
+ return _this;
480
+ }
481
+ })
482
+
483
+
484
+ return this;
485
+ }
486
+
487
+ /**
488
+ * Length of current selection
489
+ * @type {Number}
490
+ */
491
+ get length() {
492
+ return this.#raw instanceof NodeList ? this.#raw.length : 1;
493
+ }
494
+
495
+ /**
496
+ * Raw html element
497
+ * @type {HTMLElement}
498
+ */
499
+ get raw() {
500
+ return this.#raw;
501
+ }
502
+
503
+ /**
504
+ * Run(loop) through selected NodeList, or run a single call for one single element
505
+ * @method EyeElement#each
506
+ * @param {(elm: HTMLElement, index: number, current: EyeElement)=>} cb
507
+ * @returns {EyeElement}
508
+ */
509
+ each(cb) {
510
+ let _this = this;
511
+ (this.#raw instanceof NodeList ? [...this.#raw.entries()] : [[0, this.#raw]]).forEach(([idx, elm]) => {
512
+ cb(elm, idx, this);
513
+ })
514
+ return this;
515
+ }
516
+ /**
517
+ * Set or get element html
518
+ * @method EyeElement#html
519
+ * @param {string} [html]
520
+ * @returns {EyeElement|string}
521
+ */
522
+ html(html) {
523
+ let out = undefined;
524
+ this.each((elm, idx) => {
525
+ if (html === undefined) return out = elm.innerHTML;// getting the first one and exiting
526
+ elm.innerHTML = html;
527
+ })
528
+ return out != undefined ? out : this;
529
+ }
530
+ /**
531
+ * Set or get element text
532
+ * @method EyeElement#text
533
+ * @param {string} [text]
534
+ * @returns {EyeElement|string}
535
+ */
536
+ text(text) {
537
+ let out = undefined;
538
+ this.each((elm, idx) => {
539
+ if (text === undefined) return out = this.#customSet.text("get", elm.textContent, elm);
540
+ elm.textContent = this.#customSet.text("set", text, elm);
541
+ })
542
+ return out != undefined ? out : this;
543
+ }
544
+ /**
545
+ * Set or get element's data values
546
+ * @method EyeElement#data
547
+ * @param {string} key
548
+ * @param {*} [value]
549
+ * @returns {EyeElement|string}
550
+ */
551
+ data(key, value) {
552
+ if (!localdata.has(this)) localdata.set(this, {});
553
+ if (key) {
554
+ if (value != undefined) localdata.get(this)[key] = value;
555
+ else return localdata.get(this)[key];
556
+ }
557
+ return this;
558
+ }
559
+
560
+ /**
561
+ * Set or get an attribute value
562
+ * @method EyeElement#attr
563
+ * @param {string} name
564
+ * @param {*} [value]
565
+ * @returns {EyeElement|string}
566
+ */
567
+ attr(name, value) {
568
+ let out = "";
569
+ this.each((elm, idx) => {
570
+ if (name.indexOf("data-") === 0) {
571
+ let [key, val] = name.split("-").map((a) => a.trim());
572
+ // modify data
573
+ if (value == undefined) return out = elm.dataset[val];
574
+ elm.dataset[val] = value;
575
+ } else {
576
+ if (name in elm) {
577
+ if (value == undefined) return out = elm[name];
578
+ elm[name] = value;
579
+ } else if (name[0] != "_") {
580
+ if (value == undefined) return out = elm.getAttribute(name)
581
+ elm.setAttribute(name, value);
582
+ }
583
+ }
584
+ })
585
+ return out ? out : this;
586
+ }
587
+ /**
588
+ * Removing attribute of this element
589
+ * @type {string} attrName
590
+ * @returns {EyeElement}
591
+ */
592
+ rAttr(attrName) {
593
+ if (attrName) {
594
+ this.each((elm, idx) => {
595
+ elm.removeAttribute(attrName);
596
+ })
597
+ }
598
+ return this;
599
+ }
600
+ /**
601
+ * Super fancy class function that allows to modify class with different methods in one as follow:
602
+ * - `"classname"`: add classname to the element.
603
+ * - `"-classname"`: remove classname from class list.
604
+ * - `"%classname"`: toggle classname existing.
605
+ * - `"?classname"`: check classname existing in class list.
606
+ * - `"classnameA/classnameB"`: replace classnameA by classnameB
607
+ * @method EyeElement#class
608
+ * @param {string} actions
609
+ * @returns {EyeElement|string}
610
+ */
611
+ class(actions) {
612
+ let out = undefined;
613
+ this.each((elm, idx) => {
614
+ if (typeof actions === "number") return out = elm.classList.item(actions);
615
+
616
+ actions.split(" ").forEach((action) => {
617
+ if (action[0] == "-") {
618
+ elm.classList.remove(action.substring(1, action.length));
619
+ } else if (action[0] == "%") {
620
+ elm.classList.toggle(action.substring(1, action.length));
621
+ } else if (action[0] == "?") {
622
+ out = elm.classList.contains(action.substring(1, action.length));
623
+ } else if (action.indexOf("/") != -1) {
624
+ [v1, v2] = action.split("/");
625
+ elm.classList.replace(v1, v2);
626
+ } else {
627
+ elm.classList.add(action.substring(1, action.length));
628
+ }
629
+ });
630
+ if (out) return;
631
+ })
632
+
633
+ return out != undefined ? out : this;
634
+ }
635
+ /**
636
+ * Show/display the element
637
+ * @method EyeElement#show
638
+ * @param {string} cls
639
+ * @returns {EyeElement}
640
+ */
641
+ show(cls) {
642
+ this.each((elm, idx) => {
643
+ elm.style.display = cls ?? "inline-block";
644
+ })
645
+ return this;
646
+ }
647
+ /**
648
+ * Hide the element
649
+ * @method EyeElement#hide
650
+ * @param {boolean} opacity whether to hide by making invisible?
651
+ * @returns {EyeElement}
652
+ */
653
+ hide(opacity) {
654
+ this.each((elm, idx) => {
655
+ if (opacity) elm.style.opacity = 0;
656
+ else elm.style.display = "none";
657
+ })
658
+ return this;
659
+ }
660
+ /**
661
+ * Append one or more elements to the current element
662
+ * @method EyeElement#append
663
+ * @param {HTMLElement|Array<Node|EyeElement>} elm
664
+ * @param {"next" | "after" | "previous" | "before" | "first" | "afterbegin" | "last" | "beforeend"} [pos] [optional]
665
+ * @returns {EyeElement}
666
+ */
667
+ append(elm, pos) {
668
+ let nodes = [];
669
+ (Array.isArray(elm) ? elm : [elm]).forEach(item => {
670
+ if (item instanceof EyeElement) nodes.push(item.#raw);
671
+ else if (item instanceof HTMLElement) nodes.push(item)
672
+ })
673
+ if (this.#raw instanceof NodeList) {
674
+ console.warn(`[EyeJS] beware while using .append with multi selected elements`);
675
+ this.#raw.forEach((itm, idx) => {
676
+ if (!nodes[idx]) return;
677
+ itm.append(nodes[idx]);
678
+ })
679
+ return this;
680
+ }
681
+ if (!pos) this.#raw.append(...nodes);
682
+ else
683
+ switch (pos) {
684
+ case "next":
685
+ case "after":
686
+ this.#raw.after(...nodes);
687
+ break;
688
+ case "previous":
689
+ case "before":
690
+ this.#raw.before(...nodes);
691
+ break;
692
+ case "afterbegin":
693
+ case "first":
694
+ this.#raw.prepend(...nodes);
695
+ break;
696
+ case "beforeend":
697
+ case "last":
698
+ this.#raw.append(...nodes);
699
+
700
+ }
701
+ return this;
702
+ }
703
+ /**
704
+ * Insert element after this one
705
+ * @method EyeElement#after
706
+ * @param {EyeElement|HTMLElement} elm
707
+ * @returns {EyeElement}
708
+ */
709
+ after(elm) {
710
+ (this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw).after(elm instanceof EyeElement ? elm.raw : elm);
711
+ return this;
712
+ }
713
+ /**
714
+ * Insert element before this one
715
+ * @method EyeElement#before
716
+ * @param {EyeElement|HTMLElement} elm
717
+ * @returns {EyeElement}
718
+ */
719
+ before(elm) {
720
+ (this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw).before(elm instanceof EyeElement ? elm.raw : elm);
721
+ return this;
722
+ }
723
+ /**
724
+ * Replace current element with the new element, or multiple elements with multiple selected elements
725
+ * @method EyeElement#replaceWith
726
+ * @param {...HTMLElement|EyeElement} elms
727
+ * @param {string} [pos] [optional]
728
+ * @returns {EyeElement}
729
+ */
730
+ replaceWith(...elms) {
731
+ let nodes = [];
732
+ (Array.isArray(elms) ? elms : [elms]).forEach(item => {
733
+ if (item instanceof EyeElement) nodes.push(item.#raw);
734
+ else if (item instanceof HTMLElement) nodes.push(item)
735
+ })
736
+ if (this.#raw instanceof NodeList) {
737
+ [...this.#raw.entries()].forEach(([idx, elm]) => {
738
+ if (!nodes[idx]) return;
739
+ elm.replaceWith(nodes[idx]);
740
+ })
741
+ } else {
742
+ this.#raw.replaceWith(...nodes);
743
+ }
744
+ return this;
745
+ }
746
+ /**
747
+ * Get current element parent or append it to one
748
+ * @method EyeElement#parent
749
+ * @param {HTMLElement|EyeElement} par
750
+ * @returns {EyeElement}
751
+ */
752
+ parent(par) {
753
+ if (par) {
754
+ if (!(par instanceof HTMLElement) && !(par instanceof EyeElement))
755
+ throw new Error(
756
+ "[EyeJS] Unable to append current element to parent because it's not HTMLElement"
757
+ );
758
+ this.each((elm, idx) => {
759
+ par.append(elm);
760
+ })
761
+ return this;
762
+ }
763
+ return e(this.#raw instanceof NodeList ? this.#raw.item(0).parentElement : this.#raw.parentElement);
764
+ }
765
+ /**
766
+ * Returns whether current node is the same/equal(depending on `check`) as the passed node or not
767
+ * @method EyeElement#is
768
+ * @param {HTMLElement|EyeElement} node
769
+ * @param {"connected" | "same" | "equal"} [check] check type `same`, `equal`
770
+ * @returns {boolean}
771
+ */
772
+ is(node, check) {
773
+ node = node instanceof EyeElement ? node.#raw : node;
774
+ if (this.#raw instanceof NodeList) {
775
+ console.warn(`[EyeJS] .is is not functional with multi selected elements`);
776
+ return this;
777
+ }
778
+ if (node === "connected") return this.#raw.isConnected;
779
+ switch (check) {
780
+ case "same":
781
+ return this.#raw.isSameNode(node);
782
+ case "equal":
783
+ return this.#raw.isEqualNode(node);
784
+ default:
785
+ console.error(
786
+ `[EyeJS] Unknown check "${check}", possible values are ["same","equal","connected"]`
787
+ );
788
+ return false;
789
+ }
790
+ }
791
+ /**
792
+ * Set or get a css attribute
793
+ * @method EyeElement#css
794
+ * @param {string} attr
795
+ * @param {string|number} [value]
796
+ * @returns {EyeElement|string}
797
+ */
798
+ css(attr, value) {
799
+ if (attr) {
800
+ let out = undefined;
801
+ attr = flat(attr);
802
+ this.each((elm, idx) => {
803
+ if (value === undefined) return out = elm.style[attr];
804
+ elm.style[attr] = value;
805
+ })
806
+ return out != undefined ? out : this;
807
+ } else return console.error(`[EyeJS] mission argument "attr" in function .css`);
808
+ }
809
+ /**
810
+ * Remove current element
811
+ * @method EyeElement#remove
812
+ * @returns {EyeElement}
813
+ */
814
+ remove() {
815
+ this.each((elm, idx) => {
816
+ elm.remove();
817
+ })
818
+ return this;
819
+ }
820
+ /**
821
+ * @overload
822
+ * @param {string} ev
823
+ * @param {function} cb
824
+ */
825
+ /**
826
+ * @overload
827
+ * @param {string} ev
828
+ * @param {string} trg optionally define a target element for the event
829
+ * @param {function} cb
830
+ *
831
+ */
832
+ /**
833
+ * Attach an listener/handler to specific event or events
834
+ * @method EyeElement#on
835
+ * @param {string} ev may contain multiple events separated by " "(space)
836
+ * @param {string|function} arg2
837
+ * @param {function} [arg3]
838
+ * @returns {EyeElement|void}
839
+ */
840
+ on(ev, arg2, arg3) {
841
+ ev = ev.split(" ");
842
+ let target = typeof arg2 === "string" ? arg2 : null;
843
+ let cb = typeof arg2 === "function" ? arg2 : arg3;
844
+ let _this = this;
845
+ if (typeof cb !== "function")
846
+ return console.error(
847
+ "[EyeJS] .on method is missing the actuall callback `cb` or not of type function"
848
+ );
849
+
850
+ let elms = (this.#raw instanceof NodeList ? [...this.#raw.entries()] : [[0, this.#raw]])
851
+
852
+ let outsider = null;
853
+ ev.forEach(evt => {
854
+ if (target) {
855
+ if (!delegationEvents.includes(evt))
856
+ return outsider = evt; // outsider events
857
+
858
+ if (!_this.#dlgListeners.has(evt))
859
+ _this.#dlgListeners.set(evt, new Set());
860
+ _this.#dlgListeners.get(evt).add({ callback: cb, target });
861
+ } else {
862
+ elms.forEach(([idx, elm]) => {
863
+ elm.addEventListener(evt, cb);
864
+ })
865
+ }
866
+ })
867
+
868
+ if (outsider !== null)
869
+ return console.error(`[EyeJS] trying to use delegation for an inappropriate event "${outsider}"`);
870
+
871
+ return this;
872
+ }
873
+ /**
874
+ * Remove event listener of a specific event
875
+ * @method EyeElement#off
876
+ * @param {string} ev
877
+ * @param {function} cb
878
+ * @returns {EyeElement|void}
879
+ */
880
+ off(ev, cb) {
881
+ let _this = this,
882
+ listeners = _this.#dlgListeners;
883
+ if (typeof cb != "function")
884
+ return console.error(
885
+ "[EyeJS] .off method is missing the actuall callback `cb` or not of type function"
886
+ );
887
+ ev = ev.split(" ");
888
+
889
+ this.each((elm, idx) => {
890
+ ev.forEach(evt => elm.removeEventListener(evt, cb));
891
+ })
892
+ // now delegated events
893
+ ev.forEach(evt => {
894
+ if (listeners.has(evt)) {
895
+ let set = listeners.get(evt);
896
+ for (const item of set) {
897
+ if (cb === item.callback) {
898
+ // found it & remove it
899
+ set.delete(item);
900
+ }
901
+ }
902
+ }
903
+ })
904
+ }
905
+ /**
906
+ * Trigger specific event for this element
907
+ * @method EyeElement#trigger
908
+ * @param {string} ev
909
+ * @returns {EyeElement}
910
+ */
911
+ trigger(ev) {
912
+ this.each((elm, idx) => {
913
+ elm.dispatchEvent(getEvent(ev));
914
+ })
915
+ return this;
916
+ }
917
+ /**
918
+ * Find one or multiple child elements by `selector`
919
+ * @method EyeElement#find
920
+ * @param {string} selector
921
+ */
922
+ find(selector) {
923
+ let found = [];
924
+ this.each((elm, idx) => {
925
+ elm.querySelectorAll(selector).forEach(res => found.push(res));
926
+ })
927
+ return found.length == 1 ? found[0] : found;
928
+ }
929
+ /**
930
+ * Returns a clone of current selected element/s
931
+ * @method EyeElement#clone
932
+ * @param {HTMLElement} [parent] optionally append new clone to a parent
933
+ * @returns {Array<EyeElement>|EyeElement}
934
+ */
935
+ clone(parent) {
936
+ if (this.#raw instanceof NodeList) {
937
+ let list = [];
938
+ this.#raw.forEach(nd => {
939
+ list.push(nd.cloneNode(true));
940
+ })
941
+ if (parent instanceof HTMLElement || parent instanceof EyeElement) list.forEach(el => parent.append(el));
942
+ return list;
943
+ } else {
944
+ let clone = this.#raw.cloneNode(true);
945
+ if (parent instanceof HTMLElement || parent instanceof EyeElement) parent.append(clone);
946
+ return clone;
947
+ }
948
+ }
949
+ /**
950
+ * Compute DOMRect or style declaration of current element
951
+ * @method EyeElement#compute
952
+ * @param {"bounds" | "style"} type
953
+ * @returns {DOMRect|CSSStyleDeclaration}
954
+ */
955
+ compute(type) {
956
+ type = type || "bounds";
957
+ if (type === "bounds")
958
+ return (this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw).getBoundingClientRect();
959
+ else if (type == "style")
960
+ return getComputedStyle(this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw)
961
+ console.error(`[EyeJS] unkown type "${type}" in function .compute, possible values are "bounds" "style"`);
962
+ }
963
+
964
+ /**
965
+ * Get specific client informations.
966
+ * @param {"width" | "height" | "left" | "top"} attr
967
+ * @returns {EyeElement}
968
+ */
969
+ client(attr) {
970
+ if (['width', 'height', 'left', 'top'].includes(attr))
971
+ return (this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw)[`client${attr[0].toUpperCase()}${attr.substring(1, attr.length)}`];
972
+ else console.error(`[EyeJS] Unknown client* attribute "${attr}" in .client(attr)`);
973
+ return this;
974
+ }
975
+ /**
976
+ * Activate/disactive different pointer features such as PointerLock, pointerCapture...
977
+ * @method EyeElement#pointer
978
+ * @param {"capture" | "lock"} action
979
+ * @param {boolean} status
980
+ * @param {string} [pid]
981
+ * @returns {EyeElement}
982
+ */
983
+ pointer(action, status, pid) {
984
+ this.each((elm, idx) => {
985
+ if (action === "capture") {
986
+ if (status === true) elm.setPointerCapture(pid);
987
+ else elm.releasePointerCapture(pid);
988
+ } else if (action === "lock") {
989
+ if (status === true) elm.requestPointerLock();
990
+ else document.exitPointerLock();
991
+ }
992
+ });
993
+ return this;
994
+ }
995
+ /**
996
+ * Returns the count of children for this element
997
+ * @type {number}
998
+ */
999
+ get childlen() {
1000
+ return (this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw)?.children.length;
1001
+ }
1002
+ /**
1003
+ * Select a child of this element
1004
+ * @method EyeElement#child
1005
+ * @param {number} index
1006
+ * @returns {EyeElement|null}
1007
+ */
1008
+ child(index) {
1009
+ let it = (this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw);
1010
+ if (index === undefined) return it.children.length;
1011
+ if (it.children[index]) return e(it.children[index]);
1012
+ return null;
1013
+ }
1014
+ /**
1015
+ * Set/get the value of the current element
1016
+ * @method EyeElement#val
1017
+ * @param {*} value
1018
+ * @returns
1019
+ */
1020
+ val(value) {
1021
+ if (value != undefined) (this.#raw instanceof NodeList ? [...this.#raw.entries()] : [[0, this.#raw]]).forEach(([idx, a]) => a.value = this.#customSet.value("set", value, a));
1022
+ else {
1023
+ let it = (this.#raw instanceof NodeList ? this.#raw.item(0) : this.#raw);
1024
+ return this.#customSet.value("get", it.value, it);
1025
+ }
1026
+ return this;
1027
+ }
1028
+ /**
1029
+ * Serialize this element to send it over network, returns 3 formats `json`, `url` & `fd`(formData)
1030
+ * @method EyeElement#serialize
1031
+ * @param {{inputs: Array<string>}} opts
1032
+ * @returns {{json: Object, url: String, fd: FormData}}
1033
+ */
1034
+ serialize(opts) {
1035
+ opts = opts || {};
1036
+ let {
1037
+ inputs = ["input", "textarea", "select"],
1038
+ } = opts;
1039
+ if (this.#raw instanceof HTMLElement) {
1040
+ let out = {
1041
+ json: {},
1042
+ url: "",
1043
+ fd: new FormData()
1044
+ };
1045
+ this.#raw.querySelectorAll(inputs.join(','))
1046
+ .forEach((inp, i) => {
1047
+ let name = inp.name || inp.dataset.name;
1048
+ let value = inp.value || inp.textContent;
1049
+ if (typeof opts[name] === "function") value = opts[name](inp);
1050
+
1051
+ if (inp.type == "file")
1052
+ inp.files.forEach(file => {
1053
+ out.fd.append(name, file);
1054
+ })
1055
+ else {
1056
+ out.json[name] = value;
1057
+ out.fd.append(name, value);
1058
+ }
1059
+ })
1060
+
1061
+ out.url = new URLSearchParams(out.json).toString();
1062
+ return out;
1063
+ } else console.warn(`[EyeJS] this is a multi selection, it's not serializable!`);
1064
+ }
1065
+ /**
1066
+ * Redefine the way `.text` or `.val` set or get data to and from this element.
1067
+ * @method EyeElement#redefine
1068
+ * @param {"text" | "value"} type
1069
+ * @param {(action: "set" | "get", value: *, elm: EyeElement) => *} process
1070
+ * @returns {EyeElement}
1071
+ */
1072
+ redefine(type, process) {
1073
+ if (["text", "value"].includes(type) && typeof process == "function")
1074
+ this.#customSet[type] = process;
1075
+ return this;
1076
+ }
1077
+ /**
1078
+ * Animate current object
1079
+ * @method EyeElement#animate
1080
+ * @param {Array<Keyframe>} keyframes
1081
+ * @param {KeyframeAnimationOptions} opts
1082
+ * @returns {Array<Animation>|Animation}
1083
+ */
1084
+ animate(keyframes, opts) {
1085
+ /**
1086
+ * @type {Array<Animation>}
1087
+ */
1088
+ let anmts = [];
1089
+ opts.duration = opts.duration || 1000;
1090
+ this.each((elm, i) => {
1091
+ anmts.push(elm.animate(keyframes, opts));
1092
+ })
1093
+ return anmts.length == 1 ? anmts[0] : anmts;
1094
+ }
1095
+ };
1096
+
1097
+ /**
1098
+ * Creates or select nodes using css selectors, offering a pack of useful functions to use around your code!
1099
+ * @param {String} tag
1100
+ * @param {AttrMap} attrs
1101
+ * @param {Object} css CSS styles to be applied to the element.
1102
+ * @returns {EyeElement}
1103
+ */
1104
+ function e(tag, attrs, css) {
1105
+ if (typeof tag === "string" && tag.indexOf("model:") === 0 || tag === "model") {
1106
+ if (!attrs) return console.error("[EyeJS] Model creation requires parameter 'attr' as prototype, none delivered!");
1107
+
1108
+ tag = tag.split(":");
1109
+ let cls = ["eye-model"];
1110
+ if (tag[1])
1111
+ cls = cls.concat(tag[1].split(" ").filter(a => a != ""));
1112
+ // creating a model
1113
+ let model = e("<div>", {
1114
+ class: cls.join(" "),
1115
+ });
1116
+
1117
+ let sets = cmcl(model, attrs);
1118
+
1119
+ /**
1120
+ * @param {string} attrs
1121
+ * @returns {ModelEyeElement}
1122
+ */
1123
+ return (attrs) => {
1124
+ let copy = e(model.clone(attrs?.parent));
1125
+ // define & attach the refresh function
1126
+ copy.refresh = function (attrs = {}) {
1127
+ let def = attrs.default === false ? false : true;
1128
+ sets.forEach((item) => {
1129
+ if (def === true || (!def && attrs.hasOwnProperty(item.name)))
1130
+ item.set(copy, attrs[item.name]);
1131
+ });
1132
+ return this;
1133
+ };
1134
+ return copy.refresh(attrs);
1135
+ };
1136
+ } else return new EyeElement(tag, attrs, css);
1137
+ }
1138
+
1139
+
1140
+ // gloablly exposed
1141
+ window.e = e;
1142
+ window.EyeElement = EyeElement;
1143
+ export default e;