objs-core 1.1.1 → 2.0.1

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/objs.built.js ADDED
@@ -0,0 +1,2657 @@
1
+ /**
2
+ * @fileoverview Objs-core library
3
+ * @version 2.0
4
+ * @author Roman Torshin
5
+ * @license Apache-2.0
6
+ */
7
+ const __DEV__ = true;
8
+ const o = (query) => {
9
+ let result = {
10
+ els: [],
11
+ ie: {},
12
+ delegated: {},
13
+ parented: {},
14
+ store: {},
15
+ refs: {},
16
+ states: [],
17
+ isDebug: false,
18
+ currentState: "",
19
+ savedStates: {},
20
+ isRoot: false,
21
+ _parent: null
22
+ }, ONE = 1, TWO = 2, THREE = 3, booleanType = "boolean", objectType = "object", functionType = "function", stringType = "string", numberType = "number", notEmptyStringType = "notEmptyString", undefinedType = "undefined", _reactProp = "dangerouslySetInnerHTML", u, D = o.D, start = -1, finish = 0, select = 0, ssr = typeof process !== "undefined" || o.D === o.DocumentMVP, i = 0, j = 0;
23
+ const self = result;
24
+ const type = (obj) => typeof obj;
25
+ const cycleObj = (obj, func) => {
26
+ for (const item in obj) if (Object.hasOwn(obj, item)) func(item, obj);
27
+ };
28
+ const error = o.onError;
29
+ const typeVerify = (pairs) => o.verify(pairs);
30
+ const returner = (f, name = "") => {
31
+ return (...a) => {
32
+ if (__DEV__ && (o.debug || result.isDebug)) {
33
+ console.log(
34
+ name ? `${name}()` : f,
35
+ a.length ? "with " + a.join(", ") : "without parameters"
36
+ );
37
+ }
38
+ try {
39
+ const res = f(a[0], a[ONE], a[TWO], a[THREE]);
40
+ return res !== u ? res : result;
41
+ } catch (err) {
42
+ error(err, name);
43
+ }
44
+ };
45
+ };
46
+ const iterator = (f) => {
47
+ for (i = finish; i <= start; i++) f();
48
+ };
49
+ const toEl = (el) => {
50
+ if (el?.els) return el.el;
51
+ if (type(el) !== objectType) el = o.first(el).el;
52
+ return el;
53
+ };
54
+ const setResultVals = (clearStates = true, els = result.els) => {
55
+ const ln = els.length;
56
+ result.length = ln;
57
+ start = ln - ONE;
58
+ finish = 0;
59
+ result.el = ln ? els[0] : u;
60
+ result.last = ln ? els[start] : u;
61
+ if (clearStates) {
62
+ cycleObj(result.states, (i2, state) => {
63
+ delete result[state[i2]];
64
+ });
65
+ result.states = [];
66
+ result.ie = {};
67
+ }
68
+ };
69
+ result.reset = o;
70
+ const transform = (el, state, props) => {
71
+ cycleObj(state, (s) => {
72
+ let value = state[s];
73
+ if (type(value) === functionType) {
74
+ value = value(props);
75
+ }
76
+ if (s === "append" && type(value) === objectType) {
77
+ if (value.els) {
78
+ value = [value];
79
+ }
80
+ if (value[0]?.els) {
81
+ valueBuff = [];
82
+ cycleObj(value, (i2) => {
83
+ valueBuff.push(...value[i2].els);
84
+ });
85
+ value = valueBuff;
86
+ }
87
+ }
88
+ if (value !== u && el.getAttribute(s) !== value && ![
89
+ "tag",
90
+ "tagName",
91
+ "name",
92
+ "sample",
93
+ "state",
94
+ "events",
95
+ "ssr",
96
+ "nodeName",
97
+ "revertChildren",
98
+ "root",
99
+ "ref"
100
+ ].includes(s)) {
101
+ ["html", "innerHTML"].includes(s) ? el.innerHTML = value : (
102
+ // className alias
103
+ s === "className" ? el.setAttribute("class", value) : (
104
+ // attach dataset
105
+ s === "dataset" && type(value) === objectType ? cycleObj(value, (data) => {
106
+ el.dataset[data] = value[data];
107
+ }) : (
108
+ // classes
109
+ s === "toggleClass" ? el.classList.toggle(value) : s === "addClass" ? type(value) === objectType ? el.classList.add(...value) : el.classList.add(value) : s === "removeClass" ? el.classList.remove(value) : (
110
+ // style attribute
111
+ s === "style" && type(value) === objectType ? cycleObj(value, (data) => {
112
+ el.style[data] = value[data];
113
+ }) : (
114
+ // append DOM objects
115
+ (s === "append" || s === "children" || s === "childNodes") && type(value) === objectType ? cycleObj(value.length ? value : [value], (j2) => {
116
+ if (s === "append" || !el.childNodes[j2]) {
117
+ el.appendChild(value[j2]);
118
+ } else if (el.childNodes[j2] !== value[j2]) {
119
+ el.childNodes[j2].replaceWith(value[j2]);
120
+ }
121
+ }) : (
122
+ // set attributes
123
+ el.setAttribute(s, value)
124
+ )
125
+ )
126
+ )
127
+ )
128
+ )
129
+ );
130
+ }
131
+ });
132
+ el.dataset["oState"] = state.state;
133
+ if (o.autotag && state.name) {
134
+ el.dataset[o.autotag] = o.camelToKebab(state.name);
135
+ }
136
+ };
137
+ if (__DEV__) {
138
+ result.debug = returner(() => {
139
+ result.isDebug = true;
140
+ }, "debug ON");
141
+ }
142
+ result.saveAs = returner((key) => {
143
+ typeVerify([[key, [notEmptyStringType]]]);
144
+ if (!o.getSaved[key]) {
145
+ o.getSaved[key] = result;
146
+ } else if (o.debug || result.isDebug) {
147
+ console.warn("the key exists (not saved):" + key);
148
+ }
149
+ }, "saveAs");
150
+ result.unmount = () => {
151
+ if (o.debug || result.isDebug) {
152
+ console.log("unmount() for initID:" + result.initID);
153
+ }
154
+ if (type(result.remove) === functionType) {
155
+ result.remove();
156
+ } else if (result.els?.length) {
157
+ result.els.forEach((el) => {
158
+ if (el?.parentNode) el.parentNode.removeChild(el);
159
+ });
160
+ }
161
+ o.inits[result.initID] = void 0;
162
+ result = {};
163
+ return true;
164
+ };
165
+ result.init = returner((states) => {
166
+ typeVerify([[states, [objectType, functionType]]]);
167
+ const initN = result.initID || o.inits.length || 0;
168
+ result.initID = initN;
169
+ setResultVals();
170
+ o.inits[result.initID] = result;
171
+ if (type(states) === "function") {
172
+ result.render = returner((props) => {
173
+ const root = D.createElement("div");
174
+ setTimeout(() => {
175
+ o.reactRender(states, root, props);
176
+ });
177
+ result.add(root);
178
+ });
179
+ return;
180
+ }
181
+ if (type(states) !== objectType || states.render === u) {
182
+ states = {
183
+ render: states
184
+ };
185
+ }
186
+ cycleObj(states, (state) => {
187
+ if (result?.render && state === "render") {
188
+ return;
189
+ }
190
+ result.states.push(state);
191
+ result[state] = returner((props = [{}]) => {
192
+ result.currentState = state;
193
+ const data = states[state] || { tag: "div" };
194
+ const slice = Array.isArray(result.els) ? result.els.slice(finish, start + ONE) : [];
195
+ const els = slice.length ? slice : result.els || [];
196
+ if (type(data) === objectType) {
197
+ data.state = state;
198
+ data["data-o-init"] = initN;
199
+ }
200
+ const newEl = (n, prop = {}) => {
201
+ if (type(data) === objectType) {
202
+ return D.createElement(data.tag || data.tagName || "div");
203
+ } else {
204
+ const newElem = D.createElement("div");
205
+ newElem.innerHTML = type(data) === functionType ? data(prop) : data;
206
+ if (newElem.children.length > ONE || !newElem.firstElementChild) {
207
+ newElem.dataset.oInit = n;
208
+ return newElem;
209
+ } else {
210
+ newElem.firstElementChild.dataset.oInit = n;
211
+ return newElem.firstElementChild;
212
+ }
213
+ }
214
+ };
215
+ const rawData = props;
216
+ !props.length ? props = [props] : props;
217
+ const creation = !els[0] && state === "render";
218
+ props = props.map((prop, i2) => {
219
+ const newProp = Object.assign({}, type(prop) === objectType ? prop : {}, {
220
+ self: result,
221
+ o,
222
+ i: prop.i === u ? i2 : prop.i,
223
+ parent: result._parent,
224
+ data: Array.isArray(rawData) ? rawData[i2] : rawData
225
+ });
226
+ if (creation && (!data.ssr || ssr)) {
227
+ els.push(newEl(initN, newProp));
228
+ }
229
+ return newProp;
230
+ });
231
+ if (creation) {
232
+ result.els = els;
233
+ setResultVals(false);
234
+ }
235
+ const initSSR = () => {
236
+ cycleObj(data.events, (event) => {
237
+ result.on(event, data.events[event]);
238
+ });
239
+ };
240
+ if (els) {
241
+ j = els.length === props.length;
242
+ els.map((el, i2) => {
243
+ props[j ? i2 : 0].i = i2 + finish;
244
+ const buff = type(data) === functionType ? data(props[j ? i2 : 0]) : data;
245
+ if (type(buff) === objectType) {
246
+ buff["state"] = state;
247
+ if (creation) {
248
+ buff["data-o-init"] = initN;
249
+ buff["data-o-init-i"] = i2;
250
+ }
251
+ transform(el, buff, props[j ? i2 : 0]);
252
+ }
253
+ });
254
+ if (creation) {
255
+ result.refs = {};
256
+ result.els.forEach((el) => {
257
+ if (!el.querySelectorAll) return;
258
+ el.querySelectorAll("[ref]").forEach((refEl) => {
259
+ result.refs[refEl.getAttribute("ref")] = o(refEl);
260
+ refEl.removeAttribute("ref");
261
+ });
262
+ });
263
+ }
264
+ }
265
+ if (creation && type(data) === objectType && data.events) {
266
+ if (!ssr && !data.ssr) {
267
+ initSSR();
268
+ }
269
+ }
270
+ });
271
+ });
272
+ const renderState = states.render || states;
273
+ if (!ssr && type(renderState) === objectType && renderState.events && renderState.ssr) {
274
+ result.initSSRAfterGettingSSR = () => {
275
+ result.refs = {};
276
+ result.els.forEach((el) => {
277
+ if (!el.querySelectorAll) return;
278
+ el.querySelectorAll("[ref]").forEach((refEl) => {
279
+ result.refs[refEl.getAttribute("ref")] = o(refEl);
280
+ refEl.removeAttribute("ref");
281
+ });
282
+ });
283
+ cycleObj(renderState.events, (event) => {
284
+ result.on(event, renderState.events[event]);
285
+ });
286
+ };
287
+ }
288
+ }, "init");
289
+ result.connect = returner((loader, state = "render", fail) => {
290
+ typeVerify([
291
+ [loader, [objectType]],
292
+ [state, [notEmptyStringType]],
293
+ [fail, [stringType, undefinedType]]
294
+ ]);
295
+ loader.connect(self, state, fail);
296
+ }, "connect");
297
+ result.getSSR = returner((initId) => {
298
+ typeVerify([[initId, [numberType, undefinedType]]]);
299
+ const effectiveId = initId !== void 0 ? initId : result.initID;
300
+ if (ssr || type(initId) === undefinedType && type(result.initID) === undefinedType) {
301
+ return;
302
+ }
303
+ const ssrEls = o.D.querySelectorAll(`[data-o-init="${effectiveId}"]`);
304
+ if (ssrEls.length && !result.els.length) {
305
+ result.els = Array.from(ssrEls);
306
+ result.initID = initId;
307
+ o.inits[initId] = result;
308
+ setResultVals(false);
309
+ if (type(result.initSSRAfterGettingSSR) === functionType) {
310
+ result.initSSRAfterGettingSSR();
311
+ delete result.initSSRAfterGettingSSR;
312
+ }
313
+ }
314
+ }, "getSSR");
315
+ result.initState = returner((state, props) => {
316
+ typeVerify([
317
+ [state, [objectType]],
318
+ [props, [objectType, undefinedType]]
319
+ ]);
320
+ result.init(state).render(props);
321
+ }, "initState");
322
+ const parseState = (el, stateId, root) => {
323
+ const attrs = el.attributes;
324
+ const stateData = {
325
+ tagName: el.tagName.toLowerCase()
326
+ };
327
+ for (const attr of attrs) {
328
+ stateData[attr.nodeName] = attr.value;
329
+ }
330
+ if (root) {
331
+ stateData.innerHTML = el.innerHTML;
332
+ stateData.revertChildren = [];
333
+ const initedChildren = el.querySelectorAll("[data-o-init]");
334
+ for (const child of initedChildren) {
335
+ const initId = child.getAttribute("data-o-init");
336
+ stateData.revertChildren.push(initId);
337
+ o.inits[initId]?.saveState(stateId, false);
338
+ }
339
+ }
340
+ return stateData;
341
+ };
342
+ result.saveState = returner((stateId, root = true) => {
343
+ typeVerify([
344
+ [stateId, [notEmptyStringType, undefinedType]],
345
+ [root, [booleanType]]
346
+ ]);
347
+ if (!result.el) {
348
+ throw Error("saveState(): There are no elements to save");
349
+ }
350
+ const targetState = stateId ? stateId : "fastSavedState";
351
+ const stateRevert = { els: [], parentNode: result.el.parentNode, root };
352
+ iterator(() => {
353
+ stateRevert.els.push(parseState(result.els[i], targetState, root));
354
+ });
355
+ stateRevert.ie = Object.assign({}, result.ie);
356
+ stateRevert.delegated = Object.assign({}, result.delegated);
357
+ stateRevert.store = Object.assign({}, result.store);
358
+ result.isRoot = result.isRoot || root;
359
+ result.savedStates[targetState] = stateRevert;
360
+ }, "saveState");
361
+ result.revertState = returner((state) => {
362
+ typeVerify([[state, [notEmptyStringType, undefinedType]]]);
363
+ const targetState = state ? state : "fastSavedState";
364
+ if (!result.savedStates[targetState]) {
365
+ throw Error(
366
+ `revertState(): The state "${targetState}" should have been saved by saveState()`
367
+ );
368
+ }
369
+ const stateRevert = result.savedStates[targetState];
370
+ result.offAll();
371
+ result.offDelegate();
372
+ result.store = Object.assign({}, stateRevert.store);
373
+ stateRevert.els.forEach((elData, index) => {
374
+ if (!result.els[index]) {
375
+ const newEl = o.D.createElement(elData.tagName);
376
+ if (stateRevert.parentNode) {
377
+ if (index) {
378
+ result.els[index - 1].after(newEl);
379
+ } else {
380
+ stateRevert.parentNode.append(newEl);
381
+ }
382
+ }
383
+ result.add(newEl);
384
+ }
385
+ transform(result.els[index], elData);
386
+ });
387
+ result.delegated = Object.assign({}, stateRevert.delegated);
388
+ result.ie = Object.assign({}, stateRevert.ie);
389
+ result.onAll();
390
+ cycleObj(stateRevert.delegated, (ev) => {
391
+ stateRevert.delegated[ev].forEach((f) => {
392
+ iterator(() => {
393
+ result.els[i].addEventListener(ev, f);
394
+ });
395
+ });
396
+ });
397
+ result.currentState = targetState;
398
+ if (stateRevert.root) {
399
+ stateRevert.els.forEach(({ rootElement }) => {
400
+ rootElement.revertChildren.forEach((initId) => {
401
+ o.inits[initId]?.revertState(targetState);
402
+ o('[data-o-init="' + initId + '"]').els.forEach((el, index) => {
403
+ el.replaceWith(o.inits[initId]?.els[index]);
404
+ });
405
+ });
406
+ });
407
+ }
408
+ }, "revertState");
409
+ result.loseState = returner((stateId) => {
410
+ typeVerify([[stateId, [notEmptyStringType]]]);
411
+ if (result.savedStates[stateId]) {
412
+ delete result.savedStates[stateId];
413
+ iterator(() => {
414
+ const initedChildren = result.els[i].querySelectorAll("[data-o-init]");
415
+ for (const child of initedChildren) {
416
+ const initId = child.getAttribute("data-o-init");
417
+ o.inits[initId]?.loseState(stateId);
418
+ }
419
+ });
420
+ }
421
+ }, "sample");
422
+ result.sample = returner((state = "render") => {
423
+ typeVerify([[state, [notEmptyStringType]]]);
424
+ return { [state]: parseState(result.els[finish]) };
425
+ }, "sample");
426
+ result.select = returner((i2) => {
427
+ typeVerify([[i2, [numberType, undefinedType]]]);
428
+ if (i2 === u) {
429
+ i2 = result.length - ONE;
430
+ }
431
+ start = i2;
432
+ finish = i2;
433
+ result.el = result.els[i2];
434
+ select = ONE;
435
+ }, "select");
436
+ result.all = returner(() => {
437
+ start = result.length - ONE;
438
+ finish = 0;
439
+ result.el = result.els[0];
440
+ select = 0;
441
+ }, "all");
442
+ result.remove = returner((j2) => {
443
+ typeVerify([[j2, [numberType, undefinedType]]]);
444
+ if (j2 === u && select) {
445
+ j2 = finish;
446
+ }
447
+ if (j2 !== u) {
448
+ const el = result.els[j2];
449
+ if (el?.parentNode) {
450
+ el.parentNode.removeChild(el);
451
+ } else if (el === void 0 && j2 >= result.els.length) {
452
+ if (o.onError) o.onError("remove(" + j2 + "): index out of bounds", "remove");
453
+ }
454
+ } else {
455
+ iterator(() => {
456
+ const el = result.els[i];
457
+ if (el?.parentNode) el.parentNode.removeChild(el);
458
+ });
459
+ }
460
+ setResultVals(false);
461
+ }, "remove");
462
+ result.skip = returner((j2) => {
463
+ typeVerify([[j2, [numberType, undefinedType]]]);
464
+ if (j2 === u) {
465
+ j2 = finish;
466
+ }
467
+ result.els.splice(i, ONE);
468
+ setResultVals();
469
+ }, "skip");
470
+ result.add = returner((el) => {
471
+ typeVerify([[el, [stringType, objectType, numberType]]]);
472
+ if (result.initID !== u) {
473
+ return;
474
+ }
475
+ if (type(el) === "string" && el !== "") {
476
+ result.els.push(...Array.from(D.querySelectorAll(el)));
477
+ } else if (type(el) === objectType) {
478
+ if (el.tagName) {
479
+ result.els.push(el);
480
+ } else if (el.els) {
481
+ result.els.push(...el.els);
482
+ } else if (el.length && el[0].tagName) {
483
+ result.els.push(...el);
484
+ }
485
+ } else if (type(el) === "number" && o.inits[el]) {
486
+ result = o.inits[el];
487
+ }
488
+ setResultVals(false);
489
+ if (result.initID !== u) {
490
+ result.dataset({ oInit: result.initID });
491
+ }
492
+ }, "add");
493
+ result.appendInside = returner((el) => {
494
+ typeVerify([[el, [objectType, notEmptyStringType]]]);
495
+ if (el?.els) result._parent = el;
496
+ iterator(() => {
497
+ toEl(el).appendChild(result.els[i]);
498
+ });
499
+ }, "appendInside");
500
+ result.appendBefore = returner((el) => {
501
+ typeVerify([[el, [objectType, notEmptyStringType]]]);
502
+ iterator(() => {
503
+ toEl(el).parentNode.insertBefore(result.els[i], toEl(el));
504
+ });
505
+ }, "appendBefore");
506
+ result.appendAfter = returner((el) => {
507
+ typeVerify([[el, [objectType, notEmptyStringType]]]);
508
+ iterator(() => {
509
+ toEl(el).after(...result.els);
510
+ });
511
+ }, "appendAfter");
512
+ result.find = returner((innerQuery = "") => {
513
+ typeVerify([[innerQuery, stringType]]);
514
+ const newEls = [];
515
+ iterator(() => {
516
+ newEls.push(...Array.from(result.els[i].querySelectorAll(":scope " + innerQuery)));
517
+ });
518
+ return o(newEls);
519
+ }, "find");
520
+ result.first = returner((innerQuery = "") => {
521
+ typeVerify([[innerQuery, stringType]]);
522
+ let buff = u;
523
+ const newEls = [];
524
+ iterator(() => {
525
+ buff = result.els[i].querySelector(innerQuery);
526
+ if (buff) {
527
+ newEls.push(buff);
528
+ }
529
+ });
530
+ return o(newEls);
531
+ }, "first");
532
+ result.attr = returner((attr, val) => {
533
+ if (val !== null) {
534
+ typeVerify([
535
+ [attr, stringType],
536
+ [val, [stringType, undefinedType]]
537
+ ]);
538
+ }
539
+ if (val === u) {
540
+ const attrs = [];
541
+ iterator(() => {
542
+ attrs[i] = result.els[i].getAttribute(attr);
543
+ });
544
+ return select ? attrs[0] : attrs;
545
+ } else if (val !== null) {
546
+ iterator(() => {
547
+ result.els[i].setAttribute(attr, val);
548
+ });
549
+ } else {
550
+ iterator(() => {
551
+ result.els[i].removeAttribute(attr);
552
+ });
553
+ }
554
+ }, "attr");
555
+ result.attrs = returner(() => {
556
+ const res = [];
557
+ iterator(() => {
558
+ const obj = {};
559
+ [...result.els[i].attributes].forEach((attr) => {
560
+ obj[attr.nodeName] = attr.nodeValue;
561
+ });
562
+ res.push(obj);
563
+ });
564
+ return select ? res[0] : res;
565
+ }, "attrs");
566
+ result.dataset = returner((values) => {
567
+ typeVerify([[values, [objectType, undefinedType]]]);
568
+ if (typeof values === objectType) {
569
+ iterator(() => {
570
+ cycleObj(values, (data) => {
571
+ result.els[i].dataset[data] = values[data];
572
+ });
573
+ });
574
+ } else {
575
+ const res = [];
576
+ iterator(() => {
577
+ res.push({ ...result.els[i].dataset });
578
+ });
579
+ return select ? res[0] : res;
580
+ }
581
+ }, "dataset");
582
+ result.style = returner((val) => {
583
+ if (val !== null) typeVerify([[val, [stringType, undefinedType]]]);
584
+ result.attr("style", val);
585
+ }, "style");
586
+ result.css = returner((styles = {}) => {
587
+ if (styles === null) {
588
+ result.style(null);
589
+ return;
590
+ }
591
+ typeVerify([[styles, objectType]]);
592
+ let val = "";
593
+ cycleObj(styles, (style) => {
594
+ val += style + ":" + styles[style].replace('"', "'") + ";";
595
+ });
596
+ result.style(val || null);
597
+ }, "css");
598
+ result.setClass = returner((cl) => {
599
+ typeVerify([[cl, stringType]]);
600
+ iterator(() => {
601
+ result.els[i].setAttribute("class", cl);
602
+ });
603
+ }, "setClass");
604
+ result.addClass = returner((...cls) => {
605
+ iterator(() => {
606
+ result.els[i].classList.add(...cls);
607
+ });
608
+ }, "addClass");
609
+ result.removeClass = returner((...cls) => {
610
+ iterator(() => {
611
+ result.els[i].classList.remove(...cls);
612
+ });
613
+ }, "removeClass");
614
+ result.toggleClass = returner((cl, check) => {
615
+ typeVerify([
616
+ [cl, notEmptyStringType],
617
+ [check, [booleanType, undefinedType]]
618
+ ]);
619
+ iterator(() => {
620
+ result.els[i].classList.toggle(cl, check);
621
+ });
622
+ }, "toggleClass");
623
+ result.haveClass = (cl) => {
624
+ typeVerify([[cl, notEmptyStringType]]);
625
+ let res = true;
626
+ iterator(() => {
627
+ if (!result.els[i].classList.contains(cl)) {
628
+ res = false;
629
+ }
630
+ });
631
+ if (result.isDebug || o.debug) {
632
+ console.log("haveClass() with", cl);
633
+ }
634
+ return res;
635
+ };
636
+ result.innerHTML = returner((html) => {
637
+ typeVerify([[html, [stringType, undefinedType]]]);
638
+ if (html !== u) {
639
+ iterator(() => {
640
+ result.els[i].innerHTML = html;
641
+ });
642
+ } else {
643
+ let res = "";
644
+ iterator(() => {
645
+ res += ssr && result.els[i].innerHTML.length === 0 ? o.D.parseElement(result.els[i], false) : result.els[i].innerHTML;
646
+ });
647
+ return res;
648
+ }
649
+ }, "innerHTML");
650
+ result.innerText = returner((text) => {
651
+ typeVerify([[text, [stringType]]]);
652
+ iterator(() => {
653
+ result.els[i].innerText = text;
654
+ });
655
+ }, "innerText");
656
+ result.textContent = returner((text) => {
657
+ typeVerify([[text, [stringType]]]);
658
+ iterator(() => {
659
+ result.els[i].textContent = text;
660
+ });
661
+ }, "textContent");
662
+ result.html = returner((value) => {
663
+ typeVerify([[value, [stringType, undefinedType]]]);
664
+ if (value !== void 0) {
665
+ result.innerHTML(value);
666
+ } else {
667
+ let html = "";
668
+ iterator(() => {
669
+ html += ssr ? result.els[i].outerHTML() : result.els[i].outerHTML;
670
+ });
671
+ return html;
672
+ }
673
+ }, "html");
674
+ result.val = returner((value) => {
675
+ if (value === void 0) return result.el?.value;
676
+ iterator(() => {
677
+ result.els[i].value = value;
678
+ });
679
+ }, "val");
680
+ result.forEach = returner((f) => {
681
+ typeVerify([[f, [functionType]]]);
682
+ iterator(() => {
683
+ f({ self: result, i, o, el: result.els[i] });
684
+ });
685
+ }, "forEach");
686
+ result.prepareFor = returner((reactArg, ReactComponent) => {
687
+ typeVerify([
688
+ [reactArg, [objectType, functionType, undefinedType]],
689
+ [ReactComponent, [functionType, undefinedType]]
690
+ ]);
691
+ const isFullReact = reactArg && type(reactArg) === objectType && reactArg.createElement;
692
+ if (!isFullReact && type(reactArg) !== functionType) {
693
+ throw Error(
694
+ "prepareFor(): pass React (full object) or React.createElement as first argument"
695
+ );
696
+ }
697
+ const createElement = isFullReact ? reactArg.createElement : reactArg;
698
+ const useEffect = isFullReact ? reactArg.useEffect : void 0;
699
+ return (p) => {
700
+ if (p.ref === u) {
701
+ throw Error("No ref property to convert Objs to React");
702
+ }
703
+ const props = Object.assign({}, p);
704
+ const reactElement = createElement("div", { ref: p.ref });
705
+ delete props.ref;
706
+ useEffect(() => {
707
+ cycleObj(props, (key) => {
708
+ if (key.substring(0, 1) === "on") {
709
+ const e = o.camelToKebab(key).split("-")[1];
710
+ result.on(e, props[key]);
711
+ delete props[key];
712
+ }
713
+ });
714
+ result.render(props);
715
+ result.appendInside(reactElement.ref.current);
716
+ }, []);
717
+ return reactElement;
718
+ };
719
+ }, "prepareFor");
720
+ result.on = returner((a, b, c, d) => {
721
+ typeVerify([
722
+ [a, [notEmptyStringType]],
723
+ [b, [functionType]],
724
+ [c, [objectType, undefinedType]],
725
+ [d, [booleanType, undefinedType]]
726
+ ]);
727
+ a.split(", ").forEach((ev) => {
728
+ iterator(() => {
729
+ result.els[i].addEventListener(ev, b, c, d);
730
+ });
731
+ if (!result.ie[ev]) {
732
+ result.ie[ev] = [];
733
+ }
734
+ result.ie[ev].push([b, c, d]);
735
+ });
736
+ }, "on");
737
+ result.off = returner((a, b, c) => {
738
+ typeVerify([
739
+ [a, [notEmptyStringType]],
740
+ [b, [functionType]],
741
+ [c, [objectType, undefinedType]]
742
+ ]);
743
+ a.split(", ").forEach((ev) => {
744
+ iterator(() => {
745
+ result.els[i].removeEventListener(ev, b, c);
746
+ });
747
+ if (result.ie[ev]) {
748
+ result.ie[ev] = result.ie[ev].filter((f) => f[0] !== b);
749
+ }
750
+ });
751
+ }, "off");
752
+ result.onDelegate = returner((e, selector, f) => {
753
+ typeVerify([
754
+ [e, [notEmptyStringType]],
755
+ [selector, [notEmptyStringType]],
756
+ [f, [functionType]]
757
+ ]);
758
+ e.split(", ").forEach((ev) => {
759
+ const delegateCheck = (event) => {
760
+ const delegate = event.target.closest(selector);
761
+ if (delegate) {
762
+ event.delegate = delegate;
763
+ event.objs = result;
764
+ f(event);
765
+ }
766
+ };
767
+ iterator(() => {
768
+ result.els[i].addEventListener(ev, delegateCheck);
769
+ });
770
+ if (!result.delegated[ev]) {
771
+ result.delegated[ev] = [];
772
+ }
773
+ result.delegated[ev].push(delegateCheck);
774
+ });
775
+ }, "onDelegate");
776
+ result.offDelegate = returner((e) => {
777
+ typeVerify([[e, [notEmptyStringType]]]);
778
+ cycleObj(result.delegated, (ev) => {
779
+ if (!e || e === ev) {
780
+ while (result.delegated[ev].length) {
781
+ const f = result.delegated[ev].pop();
782
+ iterator(() => {
783
+ result.els[i].removeEventListener(ev, f);
784
+ });
785
+ }
786
+ }
787
+ });
788
+ result.delegated = {};
789
+ }, "offDelegate");
790
+ result.onParent = returner((e, selector, f) => {
791
+ typeVerify([
792
+ [e, [notEmptyStringType]],
793
+ [selector, [notEmptyStringType, objectType]],
794
+ [f, [functionType]]
795
+ ]);
796
+ const parent = type(selector) === objectType ? selector : o.D.querySelector(selector);
797
+ e.split(", ").forEach((ev) => {
798
+ const parentCheck = (event) => {
799
+ event.objs = result;
800
+ f(event);
801
+ };
802
+ parent.addEventListener(ev, parentCheck);
803
+ if (!result.parented[ev]) {
804
+ result.parented[ev] = [];
805
+ }
806
+ result.parented[ev].push(parentCheck);
807
+ });
808
+ }, "onParent");
809
+ result.offParent = returner((e, query2) => {
810
+ typeVerify([
811
+ [e, [notEmptyStringType]],
812
+ [query2, [notEmptyStringType, objectType]]
813
+ ]);
814
+ const parent = type(query2) === objectType ? query2 : o.D.querySelector(query2);
815
+ cycleObj(result.parented, (ev) => {
816
+ if (!e || e === ev) {
817
+ result.parented[ev].forEach((f) => {
818
+ parent.removeEventListener(ev, f);
819
+ });
820
+ delete result.parented[ev];
821
+ }
822
+ });
823
+ }, "offParent");
824
+ result.onAll = returner((type2, off) => {
825
+ typeVerify([
826
+ [type2, [notEmptyStringType, undefinedType]],
827
+ [off, [booleanType, undefinedType]]
828
+ ]);
829
+ cycleObj(result.ie, (ev, events) => {
830
+ if (!type2 || type2 === ev) {
831
+ events[ev].forEach((data) => {
832
+ iterator(() => {
833
+ if (off) {
834
+ result.els[i].removeEventListener(ev, data[0]);
835
+ } else {
836
+ result.els[i].addEventListener(ev, data[0], data[ONE], data[TWO]);
837
+ }
838
+ });
839
+ });
840
+ }
841
+ });
842
+ }, "onAll");
843
+ result.offAll = returner((type2) => {
844
+ typeVerify([[type2, [notEmptyStringType]]]);
845
+ result.onAll(type2, ONE);
846
+ }, "offAll");
847
+ if (query) {
848
+ result.add(query);
849
+ }
850
+ result.take = (innerQuery) => {
851
+ typeVerify([[innerQuery, [stringType, objectType, numberType]]]);
852
+ result.add(innerQuery);
853
+ if (result.el) {
854
+ const initID = result.el.dataset["oInit"];
855
+ if (initID !== u && o.inits[initID]) {
856
+ if (result.length === ONE) {
857
+ j = result.els[0];
858
+ Object.assign(result, o.inits[initID]);
859
+ result.els = [j];
860
+ } else {
861
+ result = o.inits[initID];
862
+ }
863
+ setResultVals(false, result.els);
864
+ return result;
865
+ }
866
+ }
867
+ };
868
+ return result;
869
+ };
870
+ o.first = (query) => {
871
+ o.verify([[query, ["notEmptyString"]]]);
872
+ if (__DEV__ && o.debug) {
873
+ console.log(query, " -> ", "o.first()");
874
+ }
875
+ return o(o.D.querySelector(query)).select(0);
876
+ };
877
+ o.inits = [];
878
+ o.getSaved = {};
879
+ o.errors = [];
880
+ o.showErrors = false;
881
+ o.logErrors = () => {
882
+ o.errors.length ? o.errors.forEach((e) => console.error(e)) : console.log("No errors");
883
+ };
884
+ o.onError = (e, name) => {
885
+ if (o.showErrors) {
886
+ console.error(e, name);
887
+ } else {
888
+ o.errors.push(e);
889
+ if (name) {
890
+ o.errors.push(name);
891
+ }
892
+ }
893
+ };
894
+ o.reactRender = () => new Error("React render function is not defined");
895
+ o.autotag = void 0;
896
+ o.reactQA = (name) => ({
897
+ ["data-" + (o.autotag || "qa")]: name.replace(/([A-Z])/g, (_, l) => "-" + l.toLowerCase()).replace(/^-/, "")
898
+ });
899
+ o.specialTypes = {
900
+ notEmptyString: (val, type) => {
901
+ return type === "string" && val.length;
902
+ },
903
+ array: (val) => {
904
+ return Array.isArray(val);
905
+ },
906
+ promise: (val) => {
907
+ return val instanceof Promise || Boolean(val && typeof val.then === "function");
908
+ }
909
+ };
910
+ o.verify = (pairs, safe = false) => {
911
+ for (const pair of pairs) {
912
+ const type = typeof pair[0];
913
+ let expectedTypes = Array.isArray(pair[1]) ? pair[1] : [pair[1]];
914
+ let isValid = false;
915
+ if (expectedTypes.includes(type)) {
916
+ return true;
917
+ } else {
918
+ expectedTypes = expectedTypes.filter((t) => !!o.specialTypes[t]);
919
+ }
920
+ for (const expectedType of expectedTypes) {
921
+ isValid = o.specialTypes[expectedType](pair[0], type);
922
+ if (isValid) {
923
+ return true;
924
+ }
925
+ }
926
+ }
927
+ if (safe) {
928
+ return false;
929
+ }
930
+ return new Error("Type verification failed");
931
+ };
932
+ o.safeVerify = (pairs) => {
933
+ return o.verify(pairs, true);
934
+ };
935
+ o.init = (states, reactRender) => o().init(states, reactRender);
936
+ o.initState = (state, props) => o().init(state).render(props);
937
+ o.take = (query) => o().take(query);
938
+ o.getStates = () => o.inits.reduce((acc, result) => {
939
+ acc.push(result?.states);
940
+ return acc;
941
+ }, []);
942
+ o.getStores = () => o.inits.reduce((acc, result) => {
943
+ acc.push(result?.store);
944
+ return acc;
945
+ }, []);
946
+ o.getListeners = () => o.inits.reduce((acc, result) => {
947
+ acc.push(result?.ie);
948
+ return acc;
949
+ }, []);
950
+ o.createStore = (defaults) => {
951
+ const store = Object.assign({}, defaults);
952
+ store._defaults = Object.assign({}, defaults);
953
+ store._listeners = [];
954
+ store.subscribe = function(component, stateName) {
955
+ this._listeners.push((data) => component[stateName]?.(data));
956
+ return this;
957
+ };
958
+ store.notify = function() {
959
+ this._listeners.forEach((fn) => fn(this));
960
+ };
961
+ store.reset = function() {
962
+ const skip = { _listeners: 1, subscribe: 1, notify: 1, _defaults: 1, reset: 1 };
963
+ for (const key of Object.keys(this._defaults)) {
964
+ if (!skip[key]) this[key] = this._defaults[key];
965
+ }
966
+ };
967
+ return store;
968
+ };
969
+ o.U = void 0;
970
+ o.W = 2;
971
+ o.H = 100;
972
+ o.F = false;
973
+ o.C = (a, b) => {
974
+ return Object.hasOwn(a, b);
975
+ };
976
+ o.kebabToCamel = (str) => str.replace(/-./g, (m) => m.toUpperCase()[1]);
977
+ o.camelToKebab = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
978
+ o.route = (path, task) => {
979
+ o.verify([
980
+ [path, ["notEmptyString", "function", "boolean"]],
981
+ [task, ["function", "object"]]
982
+ ]);
983
+ const result = typeof path === "function" ? path(window.location.pathname) : path;
984
+ if (result === true || window.location.pathname === result) {
985
+ if (task) {
986
+ if (typeof task === "function") {
987
+ task();
988
+ return true;
989
+ } else {
990
+ return task;
991
+ }
992
+ } else {
993
+ return o;
994
+ }
995
+ } else {
996
+ return typeof task === "function" ? false : {};
997
+ }
998
+ };
999
+ o.router = (routes = {}) => {
1000
+ o.verify([[routes, ["object"]]]);
1001
+ for (const route in routes) {
1002
+ if (o.C(routes, route)) {
1003
+ if (window.location.pathname === route) {
1004
+ if (typeof routes[route] === "function") {
1005
+ routes[route]();
1006
+ return true;
1007
+ } else {
1008
+ return routes[route];
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+ return false;
1014
+ };
1015
+ o.DocumentMVP = {
1016
+ addEventListener: () => {
1017
+ },
1018
+ parseElement: (elem, outer = true) => {
1019
+ o.verify([
1020
+ [elem, ["object", "string"]],
1021
+ [outer, ["boolean"]]
1022
+ ]);
1023
+ const attrToStr = (attrs, prefix = "") => {
1024
+ let attrStr = "";
1025
+ for (const attr in attrs) {
1026
+ if (o.C(attrs, attr)) {
1027
+ attrStr += ` ${prefix}${o.camelToKebab(attr)}="${typeof attrs[attr] !== "object" ? attrs[attr] : Object.entries(attrs[attr]).map((e) => `${e[0]}: ${e[1]};`).join(" ")}"`;
1028
+ }
1029
+ }
1030
+ return attrStr;
1031
+ };
1032
+ if (typeof elem === "string") {
1033
+ return elem;
1034
+ }
1035
+ if (outer) {
1036
+ const selfClosing = [
1037
+ "area",
1038
+ "base",
1039
+ "br",
1040
+ "col",
1041
+ "embed",
1042
+ "hr",
1043
+ "img",
1044
+ "input",
1045
+ "link",
1046
+ "meta",
1047
+ "param",
1048
+ "source",
1049
+ "track",
1050
+ "wbr"
1051
+ ];
1052
+ const tagName = elem.tagName.toLowerCase();
1053
+ const dataOInit = elem.attributes["data-o-init"];
1054
+ const dataOInitAttr = dataOInit !== void 0 ? ` data-o-init="${dataOInit}"` : "";
1055
+ return `<${tagName}${elem.className ? ` class="${elem.className}"` : ""}${attrToStr(elem.attributes)}${dataOInitAttr}${attrToStr(elem.dataset, "data-")}${selfClosing.includes(tagName) ? "/" : ""}>${selfClosing.includes(tagName) ? "" : elem.innerHTML.length ? elem.innerHTML : elem.children.map((el) => o.D.parseElement(el)).join("")}${!selfClosing.includes(tagName) ? `</${tagName}>` : ""}`;
1056
+ }
1057
+ return elem.innerHTML.length ? elem.innerHTML : elem.children.map((el) => o.D.parseElement(el)).join("");
1058
+ },
1059
+ createElement: (tag) => {
1060
+ o.verify([[tag, ["notEmptyString"]]]);
1061
+ const elem = {
1062
+ tagName: tag.toUpperCase(),
1063
+ attributes: {},
1064
+ innerHTML: "",
1065
+ children: [],
1066
+ dataset: {},
1067
+ className: "",
1068
+ classArray: [],
1069
+ style: {},
1070
+ addEventListener: () => {
1071
+ },
1072
+ removeEventListener: () => {
1073
+ }
1074
+ };
1075
+ elem.classList = {
1076
+ add: (...cl) => {
1077
+ o.verify([[cl, ["array"]]]);
1078
+ elem.classArray.push(cl);
1079
+ elem.className = elem.classArray.join(" ");
1080
+ },
1081
+ has: (cl) => {
1082
+ o.verify([[cl, ["notEmptyString"]]]);
1083
+ return elem.classArray.includes(cl);
1084
+ },
1085
+ remove: (cl) => {
1086
+ o.verify([[cl, ["notEmptyString"]]]);
1087
+ elem.classArray = elem.classArray.filter((listed) => cl !== listed);
1088
+ elem.className = elem.classArray.join(" ");
1089
+ }
1090
+ };
1091
+ elem.classList.toggle = (cl) => {
1092
+ o.verify([[cl, ["notEmptyString"]]]);
1093
+ if (elem.classList.has(cl)) {
1094
+ elem.classList.remove(cl);
1095
+ } else {
1096
+ elem.classList.add(cl);
1097
+ }
1098
+ };
1099
+ elem.setAttribute = (attr, val) => {
1100
+ o.verify([
1101
+ [attr, ["notEmptyString"]],
1102
+ [val, ["string", "number", "boolean", "undefined"]]
1103
+ ]);
1104
+ elem.attributes[attr] = val;
1105
+ };
1106
+ elem.getAttribute = (attr) => {
1107
+ o.verify([[attr, ["notEmptyString"]]]);
1108
+ return elem.attributes[attr];
1109
+ };
1110
+ elem.removeAttribute = (attr) => {
1111
+ o.verify([[attr, ["notEmptyString"]]]);
1112
+ delete elem.attributes[attr];
1113
+ };
1114
+ elem.appendChild = (el) => {
1115
+ o.verify([[el, ["object"]]]);
1116
+ elem.children.push(el);
1117
+ elem.firstElementChild = elem.children[0];
1118
+ };
1119
+ elem.outerHTML = () => o.D.parseElement(elem);
1120
+ return elem;
1121
+ }
1122
+ };
1123
+ o.D = typeof document !== "undefined" && typeof process === "undefined" ? document : o.DocumentMVP;
1124
+ o.setCookie = (title, value = "", params = {}) => {
1125
+ o.verify([
1126
+ [title, ["notEmptyString"]],
1127
+ [value, ["string", "number", "boolean"]],
1128
+ [params, ["object"]]
1129
+ ]);
1130
+ if (o.D.cookie === void 0) {
1131
+ console.log("Cookies are not supported on server side");
1132
+ return;
1133
+ }
1134
+ let str = encodeURIComponent(title) + "=" + encodeURIComponent(value);
1135
+ params = {
1136
+ path: "/",
1137
+ ...params
1138
+ };
1139
+ if (params.expires instanceof Date) {
1140
+ params.expires = params.expires.toUTCString();
1141
+ } else if (typeof params.expires === "number") {
1142
+ params.expires = new Date(params.expires).toUTCString();
1143
+ }
1144
+ for (const key in params) {
1145
+ str += "; " + key;
1146
+ const r = params[key];
1147
+ if (r !== true) {
1148
+ str += "=" + r;
1149
+ }
1150
+ }
1151
+ o.D.cookie = str;
1152
+ };
1153
+ o.getCookie = (title) => {
1154
+ o.verify([[title, ["notEmptyString"]]]);
1155
+ if (o.D.cookie === void 0) {
1156
+ console.log("Cookies are not supported on server side");
1157
+ return;
1158
+ }
1159
+ const val = o.D.cookie.match(
1160
+ RegExp("(?:^|; )" + title.replace(/([.$?*|{}()[\]\\/+^])/g, "\\$1") + "=([^;]*)")
1161
+ );
1162
+ return val ? decodeURIComponent(val[1]) : void 0;
1163
+ };
1164
+ o.deleteCookie = (title) => {
1165
+ o.verify([[title, ["notEmptyString"]]]);
1166
+ o.setCookie(title, "", { "max-age": 0 });
1167
+ };
1168
+ o.clearCookies = () => {
1169
+ if (o.D.cookie === void 0) {
1170
+ console.log("Cookies are not supported on server side");
1171
+ return;
1172
+ }
1173
+ const ca = o.D.cookie.split(";");
1174
+ while (ca.length) {
1175
+ let c = ca.pop();
1176
+ while (c.charAt(0) === " ") {
1177
+ c = c.substring(1);
1178
+ }
1179
+ const key = c.split("=")[0];
1180
+ o.deleteCookie(key);
1181
+ }
1182
+ };
1183
+ o.clearLocalStorage = (all) => {
1184
+ o.verify([[all, ["boolean", "undefined"]]]);
1185
+ if (typeof localStorage === "undefined") {
1186
+ return;
1187
+ }
1188
+ if (all) {
1189
+ localStorage.clear();
1190
+ } else {
1191
+ for (let i = localStorage.length - 1; i >= 0; i--) {
1192
+ const key = localStorage.key(i);
1193
+ if (key.indexOf("oInc-") === -1 && key.indexOf("oTest-") === -1) {
1194
+ localStorage.removeItem(key);
1195
+ }
1196
+ }
1197
+ }
1198
+ };
1199
+ o.clearSessionStorage = (onlyTests) => {
1200
+ o.verify([[onlyTests, ["boolean", "undefined"]]]);
1201
+ if (typeof sessionStorage === "undefined") {
1202
+ return;
1203
+ }
1204
+ if (!onlyTests) {
1205
+ sessionStorage.clear();
1206
+ } else {
1207
+ for (let i = sessionStorage.length - 1; i >= 0; i--) {
1208
+ const key = sessionStorage.key(i);
1209
+ if (key && key.indexOf("oTest-") === 0) {
1210
+ sessionStorage.removeItem(key);
1211
+ }
1212
+ }
1213
+ }
1214
+ };
1215
+ o.clearTestsStorage = () => {
1216
+ o.clearSessionStorage(1);
1217
+ };
1218
+ o.clearAfterTests = () => {
1219
+ o.clearCookies();
1220
+ o.clearLocalStorage(false);
1221
+ o.clearTestsStorage();
1222
+ };
1223
+ o.ajax = (url, props = {}) => {
1224
+ o.verify([
1225
+ [url, ["notEmptyString"]],
1226
+ [props, ["object"]]
1227
+ ]);
1228
+ const row = new URLSearchParams();
1229
+ if (props.data && typeof props.data === "object") {
1230
+ for (const param in props.data) {
1231
+ if (o.C(props.data, param)) {
1232
+ if (typeof props.data[param] === "object") {
1233
+ row.set(param, encodeURIComponent(JSON.stringify(props.data[param])));
1234
+ } else {
1235
+ row.set(param, props.data[param]);
1236
+ }
1237
+ }
1238
+ }
1239
+ if (props.method.toLowerCase() === "get") {
1240
+ url += "?" + row.toString();
1241
+ } else if (!props.body) {
1242
+ props.body = row;
1243
+ }
1244
+ delete props.data;
1245
+ }
1246
+ if (!props.headers) {
1247
+ props.headers = { "X-Requested-With": "XMLHttpRequest" };
1248
+ }
1249
+ return fetch(url, props);
1250
+ };
1251
+ o.get = (url, props = {}) => {
1252
+ o.verify([
1253
+ [url, ["notEmptyString"]],
1254
+ [props, ["object"]]
1255
+ ]);
1256
+ return o.ajax(url, { ...props, method: "GET" });
1257
+ };
1258
+ o.post = (url, props = {}) => {
1259
+ o.verify([
1260
+ [url, ["notEmptyString"]],
1261
+ [props, ["object"]]
1262
+ ]);
1263
+ return o.ajax(url, { ...props, method: "POST" });
1264
+ };
1265
+ o.newLoader = (promise) => {
1266
+ o.verify([[promise, ["promise", "undefined"]]]);
1267
+ let listeners = [];
1268
+ let data = null;
1269
+ let finished = false;
1270
+ let error = false;
1271
+ const reload = (p) => {
1272
+ finished = false;
1273
+ error = false;
1274
+ data = null;
1275
+ setTimeout(() => {
1276
+ p.then((response) => {
1277
+ finished = true;
1278
+ if (!response.ok && typeof response.ok !== "undefined") {
1279
+ error = true;
1280
+ listeners.forEach(([listener, _state, fail]) => {
1281
+ fail ? listener[fail](response) : "";
1282
+ });
1283
+ } else if (typeof response.json === "function") {
1284
+ response.json().then((jsonData) => {
1285
+ data = jsonData;
1286
+ listeners.forEach(([listener, state]) => {
1287
+ listener[state](data);
1288
+ });
1289
+ });
1290
+ } else {
1291
+ data = response;
1292
+ listeners.forEach(([listener, state]) => {
1293
+ listener[state](data);
1294
+ });
1295
+ }
1296
+ }).catch((err) => {
1297
+ error = true;
1298
+ listeners.forEach(([listener, _state, fail]) => {
1299
+ fail ? listener[fail](err) : "";
1300
+ });
1301
+ });
1302
+ }, 33);
1303
+ };
1304
+ if (promise) {
1305
+ reload(promise);
1306
+ }
1307
+ return {
1308
+ reload,
1309
+ isObjsLoader: true,
1310
+ listeners,
1311
+ isFinished: () => finished,
1312
+ getStore: () => data,
1313
+ connect: (listener, state = "render", fail) => {
1314
+ o.verify([
1315
+ [listener, ["object"]],
1316
+ [state, ["notEmptyString"]],
1317
+ [fail, ["string", "undefined"]]
1318
+ ]);
1319
+ if (finished) {
1320
+ if (error) {
1321
+ fail ? listener[fail]() : "";
1322
+ } else if (typeof listener[state] === "function") {
1323
+ listener[state](data);
1324
+ }
1325
+ } else {
1326
+ listeners.push([listener, state, fail]);
1327
+ }
1328
+ },
1329
+ disconnect: (listener) => {
1330
+ o.verify([[listener, ["object"]]]);
1331
+ listeners = listeners.filter(([l]) => l !== listener);
1332
+ }
1333
+ };
1334
+ };
1335
+ o.getParams = (key) => {
1336
+ o.verify([[key, ["string", "undefined"]]]);
1337
+ const params = {};
1338
+ const paramsRaw = new URLSearchParams(window.location.search).entries();
1339
+ for (const entry of paramsRaw) {
1340
+ params[entry[0]] = entry[1];
1341
+ }
1342
+ return key ? params[key] : params;
1343
+ };
1344
+ o.incCache = true;
1345
+ o.incCacheExp = 1e3 * 60 * 60 * 24;
1346
+ o.incTimeout = 6e3;
1347
+ o.incSource = "";
1348
+ o.incForce = o.F;
1349
+ o.incAsync = true;
1350
+ o.incCors = o.F;
1351
+ o.incSeparator = "?";
1352
+ o.incFns = {};
1353
+ o.incSet = [0];
1354
+ o.incReady = [0];
1355
+ o.incN = 0;
1356
+ o.incGetHash = (path) => path.split(o.incSeparator)[1] || "";
1357
+ o.incCheck = (set = 0, fnId, loaded = 0) => {
1358
+ o.verify([
1359
+ [set, ["number"]],
1360
+ [fnId, ["number", "undefined"]],
1361
+ [loaded, ["number"]]
1362
+ ]);
1363
+ if (!loaded && set && fnId === o.U && o.incReady[set]) {
1364
+ return o.incSet[set] === 1;
1365
+ }
1366
+ if (o.incReady[set] === o.U || o.incReady[set][fnId] === o.U) {
1367
+ return o.F;
1368
+ }
1369
+ o.incReady[set][fnId].loaded = loaded;
1370
+ o.incFns[o.incReady[set][fnId].name] = loaded;
1371
+ o.incReady[set][0] += loaded;
1372
+ if (set && o.incReady[set].length === o.incReady[set][0]) {
1373
+ if (typeof o.incSet[set] === "function") {
1374
+ o.incSet[set](set);
1375
+ }
1376
+ o.incSet[set] = 1;
1377
+ }
1378
+ return o.incSet[set] === 1;
1379
+ };
1380
+ o.incCacheClear = (all = o.F) => {
1381
+ o.verify([[all, ["boolean"]]]);
1382
+ for (const name in o.incFns) {
1383
+ if (o.C(o.incFns, name)) {
1384
+ localStorage.removeItem("oInc-" + name);
1385
+ localStorage.removeItem("oInc-" + name + "-expires");
1386
+ }
1387
+ }
1388
+ if (all) {
1389
+ o.incReady.forEach((val, i) => {
1390
+ if (i) {
1391
+ val.forEach((_a, j) => {
1392
+ if (j) {
1393
+ o("#oInc-" + i + "-" + j).remove();
1394
+ }
1395
+ });
1396
+ }
1397
+ });
1398
+ o.incN = 0;
1399
+ o.incFns = {};
1400
+ o.incSet = [0];
1401
+ o.incReady = [0];
1402
+ }
1403
+ return true;
1404
+ };
1405
+ o.inc = (sources, callBack, callBad) => {
1406
+ o.verify([
1407
+ [sources, ["object", "undefined"]],
1408
+ [callBack, ["function", "undefined"]],
1409
+ [callBad, ["function", "undefined"]]
1410
+ ]);
1411
+ if (typeof localStorage === "undefined") {
1412
+ return;
1413
+ }
1414
+ let sourcesN = 0, sourcesReady = 0, hash = "", preload = false;
1415
+ const f = "function", no = -1;
1416
+ if (typeof sources !== "object" || !sources) {
1417
+ return o.incSet[0];
1418
+ }
1419
+ o.incSet[0]++;
1420
+ const setN = o.incSet[0];
1421
+ o.incSet[setN] = callBack || 0;
1422
+ o.incReady[setN] = [];
1423
+ const fnsStatus = o.incReady[setN];
1424
+ fnsStatus[0] = 1;
1425
+ const fnId = {};
1426
+ for (const name in sources) {
1427
+ if (o.C(sources, name)) {
1428
+ if (name === "preload") {
1429
+ preload = true;
1430
+ continue;
1431
+ }
1432
+ hash = o.incGetHash(sources[name]);
1433
+ sourcesN++;
1434
+ o.incN++;
1435
+ const tag = sources[name].indexOf(".css") > no ? "style" : "script";
1436
+ sources[name] = (o.incSource ? o.incSource + "/" : "") + sources[name];
1437
+ if (Number.isNaN(Number(name)) && o.C(o.incFns, name) && o.incFns[name] && !o.incForce) {
1438
+ fnsStatus[sourcesN] = {
1439
+ name,
1440
+ loaded: 1
1441
+ };
1442
+ sourcesReady++;
1443
+ continue;
1444
+ }
1445
+ fnsStatus[sourcesN] = {
1446
+ name,
1447
+ loaded: 0
1448
+ };
1449
+ if (Number.isNaN(Number(name))) {
1450
+ o.incFns[name] = 0;
1451
+ }
1452
+ if (Number.isNaN(Number(name)) && o.incCache && (sources[name].substring(0, 4) !== "http" || !o.incCors) && window.location.protocol !== "file:" && (sources[name].indexOf(".css") > no || sources[name].indexOf(".js") > no)) {
1453
+ const ls = localStorage, script = ls.getItem("oInc-" + name), cacheSavedTill = ls.getItem("oInc-" + name + "-expires"), cacheHash = ls.getItem("oInc-" + name + "-hash");
1454
+ if (script && cacheSavedTill && Date.now() < cacheSavedTill && cacheHash === hash) {
1455
+ if (!preload) {
1456
+ o.initState({
1457
+ tag,
1458
+ id: "oInc-" + setN + "-" + sourcesN,
1459
+ innerHTML: script,
1460
+ "data-o-inc": setN
1461
+ }).appendInside("head");
1462
+ }
1463
+ fnsStatus[sourcesN].loaded = 1;
1464
+ o.incFns[name] = 1;
1465
+ sourcesReady++;
1466
+ } else {
1467
+ fnId[name] = sourcesN;
1468
+ o.get(sources[name], { mode: o.incCors ? "cors" : "same-origin" }).then(
1469
+ (response) => {
1470
+ if (response.status !== 200) {
1471
+ o.onError ? o.onError({
1472
+ message: o.incSource + sources[name] + " was not loaded"
1473
+ }) : "";
1474
+ return;
1475
+ }
1476
+ response.text().then((script2) => {
1477
+ ls.setItem("oInc-" + name, script2);
1478
+ ls.setItem("oInc-" + name + "-expires", Date.now() + o.incCacheExp);
1479
+ ls.setItem("oInc-" + name + "-hash", hash);
1480
+ if (!preload) {
1481
+ o.initState({
1482
+ tag,
1483
+ id: "oInc-" + setN + "-" + fnId[name],
1484
+ innerHTML: script2,
1485
+ "data-o-inc": setN
1486
+ }).appendInside("head");
1487
+ }
1488
+ o.incCheck(setN, fnId[name], 1);
1489
+ });
1490
+ }
1491
+ );
1492
+ }
1493
+ } else {
1494
+ const state = {
1495
+ tag,
1496
+ id: "oInc-" + setN + "-" + sourcesN,
1497
+ "data-o-inc": setN,
1498
+ async: o.incAsync,
1499
+ onload: "o.incCheck(" + setN + "," + sourcesN + ",1)"
1500
+ };
1501
+ if (sources[name].indexOf(".css") > no) {
1502
+ state.tag = "link";
1503
+ state.rel = "stylesheet";
1504
+ state.href = sources[name];
1505
+ } else if (sources[name].indexOf(".js") > no) {
1506
+ state.src = sources[name];
1507
+ } else {
1508
+ state.tag = "img";
1509
+ state.style = "display:none;";
1510
+ state.src = sources[name];
1511
+ }
1512
+ o.initState(state).appendInside(state.style ? "body" : "head");
1513
+ }
1514
+ }
1515
+ }
1516
+ fnsStatus[0] += sourcesReady;
1517
+ if (sourcesN !== 0) {
1518
+ if (sourcesReady === sourcesN) {
1519
+ if (typeof callBack === f) {
1520
+ callBack(setN);
1521
+ }
1522
+ } else {
1523
+ setTimeout(
1524
+ (set) => {
1525
+ if (o.incReady[set] && o.incReady[set].length < o.incReady[set][0]) {
1526
+ o.incSet[set] = 0;
1527
+ if (typeof callBad === f) {
1528
+ callBad(setN);
1529
+ }
1530
+ }
1531
+ },
1532
+ o.incTimeout,
1533
+ setN
1534
+ );
1535
+ }
1536
+ }
1537
+ return o.incSet[0];
1538
+ };
1539
+ o.connectRedux = (store, selector, component, state = "render") => {
1540
+ o.verify([
1541
+ [store, ["object"]],
1542
+ [selector, ["function"]],
1543
+ [component, ["object"]],
1544
+ [state, ["notEmptyString"]]
1545
+ ]);
1546
+ const update = () => {
1547
+ if (typeof component[state] === "function") {
1548
+ const val = selector(store.getState());
1549
+ component[state](val !== null && typeof val === "object" ? val : { value: val });
1550
+ }
1551
+ };
1552
+ update();
1553
+ return store.subscribe(update);
1554
+ };
1555
+ o.connectMobX = (mobx, observable, accessor, component, state = "render") => {
1556
+ o.verify([
1557
+ [mobx, ["object"]],
1558
+ [observable, ["object"]],
1559
+ [accessor, ["function"]],
1560
+ [component, ["object"]],
1561
+ [state, ["notEmptyString"]]
1562
+ ]);
1563
+ return mobx.autorun(() => {
1564
+ if (typeof component[state] === "function") {
1565
+ const val = accessor(observable);
1566
+ component[state](val !== null && typeof val === "object" ? val : { value: val });
1567
+ }
1568
+ });
1569
+ };
1570
+ o.ObjsContext = null;
1571
+ o.withReactContext = (React, Context, selector, component, state = "render") => {
1572
+ return function ObjsContextBridge() {
1573
+ const value = React.useContext(Context);
1574
+ React.useEffect(() => {
1575
+ if (typeof component[state] === "function") {
1576
+ const val = selector(value);
1577
+ component[state](val !== null && typeof val === "object" ? val : { value: val });
1578
+ }
1579
+ }, [value]);
1580
+ return null;
1581
+ };
1582
+ };
1583
+ if (__DEV__) {
1584
+ o.debug = false;
1585
+ }
1586
+ o.tLog = [];
1587
+ o.tRes = [];
1588
+ o.tStatus = [];
1589
+ o.tFns = [];
1590
+ o.tShowOk = o.F;
1591
+ o.tStyled = o.F;
1592
+ o.tTime = 2e3;
1593
+ o.tests = [];
1594
+ o.tAutolog = o.F;
1595
+ o.tBeforeEach = void 0;
1596
+ o.tAfterEach = void 0;
1597
+ o.addTest = (title, ...tests) => {
1598
+ o.verify([
1599
+ [title, ["notEmptyString"]],
1600
+ [tests, ["array"]]
1601
+ ]);
1602
+ let hooks = {};
1603
+ if (tests.length && typeof tests[tests.length - 1] === "object" && !Array.isArray(tests[tests.length - 1])) {
1604
+ hooks = tests.pop();
1605
+ }
1606
+ const testId = o.tests.length;
1607
+ o.tests[testId] = { title, tests, hooks };
1608
+ return {
1609
+ run: () => {
1610
+ if (typeof hooks.before === "function") {
1611
+ o.tBeforeEach = hooks.before;
1612
+ }
1613
+ if (typeof hooks.after === "function") {
1614
+ o.tAfterEach = hooks.after;
1615
+ }
1616
+ o.runTest(testId);
1617
+ },
1618
+ autorun: () => {
1619
+ if (typeof hooks.before === "function") {
1620
+ o.tBeforeEach = hooks.before;
1621
+ }
1622
+ if (typeof hooks.after === "function") {
1623
+ o.tAfterEach = hooks.after;
1624
+ }
1625
+ o.runTest(testId, true);
1626
+ },
1627
+ testId
1628
+ };
1629
+ };
1630
+ o.updateLogs = () => {
1631
+ for (let i = 0; i < o.tests.length; i++) {
1632
+ o.tLog[i] = o.tLog[testN] = sessionStorage.getItem(`oTest-Log-${testN}`) || "";
1633
+ }
1634
+ return o.tLog;
1635
+ };
1636
+ o.runTest = (testId = 0, autoRun, savePrev) => {
1637
+ o.verify([
1638
+ [testId, ["number"]],
1639
+ [autoRun, ["boolean", "undefined"]],
1640
+ [savePrev, ["boolean", "undefined"]]
1641
+ ]);
1642
+ if (!o.tests[testId]) {
1643
+ return;
1644
+ }
1645
+ if (!savePrev) {
1646
+ sessionStorage?.removeItem(`oTest-Log-${testId}`);
1647
+ sessionStorage?.removeItem(`oTest-Res-${testId}`);
1648
+ sessionStorage?.removeItem(`oTest-Status-${testId}`);
1649
+ }
1650
+ sessionStorage?.setItem(`oTest-Run`, testId);
1651
+ if (autoRun) {
1652
+ sessionStorage?.setItem(`oTest-Autorun`, autoRun);
1653
+ } else {
1654
+ sessionStorage?.removeItem(`oTest-Autorun`);
1655
+ }
1656
+ const testSession = o.tests[testId];
1657
+ let lastTest = testSession.tests.pop();
1658
+ if (typeof lastTest !== "function") {
1659
+ testSession.tests.push(lastTest);
1660
+ lastTest = () => {
1661
+ };
1662
+ }
1663
+ testSession.tests.push((testN2) => {
1664
+ lastTest(testN2);
1665
+ sessionStorage.setItem("dddd", 1);
1666
+ sessionStorage?.removeItem(`oTest-Run`);
1667
+ if (autoRun) {
1668
+ o.runTest(testId + 1, autoRun);
1669
+ }
1670
+ });
1671
+ o.test(testSession.title, ...testSession.tests);
1672
+ };
1673
+ o.tPre = '<div style="font-family:monospace;text-align:left;">';
1674
+ o.tOk = '<span style="background:#cfc;padding: 0 15px;">OK</span> ';
1675
+ o.tXx = '<div style="background:#fcc;padding:3px;">';
1676
+ o.tDc = "</div>";
1677
+ o.test = (title = "", ...tests) => {
1678
+ o.verify([
1679
+ [title, ["notEmptyString"]],
1680
+ [tests, ["array"]]
1681
+ ]);
1682
+ const testSession = sessionStorage?.getItem(`oTest-Run`);
1683
+ const testN2 = testSession || o.tLog.length;
1684
+ let waits = 0, preOk = "\u251C OK: ", preXx = "\u251C \u2718 ", posOk = "\n", posXx = "\n", row = "", num = tests.length, done = 0;
1685
+ const log = (line = "", error = false, log2 = false) => {
1686
+ if (o.tAutolog) {
1687
+ if (error) {
1688
+ console.error(line);
1689
+ } else if (o.tShowOk || log2) {
1690
+ console.log(line);
1691
+ }
1692
+ }
1693
+ };
1694
+ if (typeof tests[num - 1] === "function") {
1695
+ o.tFns[testN2] = tests[num - 1];
1696
+ num--;
1697
+ }
1698
+ if (testSession) {
1699
+ o.tLog[testN2] = sessionStorage.getItem(`oTest-Log-${testN2}`) || "";
1700
+ o.tRes[testN2] = sessionStorage.getItem(`oTest-Res-${testN2}`) || false;
1701
+ o.tStatus[testN2] = JSON.parse(
1702
+ sessionStorage.getItem(`oTest-Status-${testN2}`) || "[]"
1703
+ );
1704
+ for (let i = 0; i < o.tStatus[testN2].length; i++) {
1705
+ const s = o.tStatus[testN2][i];
1706
+ if (s === true || s === false) done++;
1707
+ }
1708
+ }
1709
+ if (o.tStyled) {
1710
+ preOk = o.tPre + o.tOk;
1711
+ preXx = o.tPre + o.tXx;
1712
+ posOk = o.tDc;
1713
+ posXx = posOk + posOk;
1714
+ }
1715
+ if (!testSession || o.tLog[testN2].length === 0) {
1716
+ log("\u2552 " + title + " #" + testN2, false, true);
1717
+ if (o.tStyled) {
1718
+ o.tLog[testN2] = "<div><b>" + title + " #" + testN2 + "</b></div>";
1719
+ } else {
1720
+ o.tLog[testN2] = "\u2552 " + title + " #" + testN2 + "\n";
1721
+ }
1722
+ o.tRes[testN2] = o.F;
1723
+ o.tStatus[testN2] = [];
1724
+ }
1725
+ for (let i = o.tStatus[testN2].length; i < num; i++) {
1726
+ const testInfo = {
1727
+ n: testN2,
1728
+ i,
1729
+ title: tests[i][0],
1730
+ tShowOk: o.tShowOk,
1731
+ tStyled: o.tStyled
1732
+ };
1733
+ let res = tests[i][1];
1734
+ if (typeof res === "undefined") {
1735
+ if (o.tStyled) {
1736
+ o.tLog[testN2] += "<div>" + testInfo.title + "</div>";
1737
+ } else {
1738
+ o.tLog[testN2] += testInfo.title + "\n";
1739
+ }
1740
+ log("\u251C " + testInfo.title, false, true);
1741
+ o.tStatus[testN2][i] = true;
1742
+ done++;
1743
+ continue;
1744
+ }
1745
+ if (typeof o.tBeforeEach === "function") {
1746
+ o.tBeforeEach(testInfo);
1747
+ }
1748
+ if (typeof res === "function") {
1749
+ try {
1750
+ res = res(testInfo);
1751
+ } catch (error) {
1752
+ res = error.message;
1753
+ if (o.onError) {
1754
+ o.onError(error);
1755
+ }
1756
+ }
1757
+ }
1758
+ if (typeof o.tAfterEach === "function") {
1759
+ o.tAfterEach(testInfo, res);
1760
+ }
1761
+ if (res && typeof res.then === "function") {
1762
+ waits++;
1763
+ const timeoutId = setTimeout(() => {
1764
+ testInfo.title += " (timeout)";
1765
+ o.testUpdate(testInfo);
1766
+ }, o.tTime);
1767
+ res.then((value) => {
1768
+ clearTimeout(timeoutId);
1769
+ const ok = value === true || value && value.ok === true;
1770
+ const msg = value && value.errors && value.errors.length ? value.errors.join("; ") : typeof value === "string" ? value : "";
1771
+ o.testUpdate(testInfo, ok, ok ? "" : msg ? ": " + msg : "");
1772
+ }).catch((err) => {
1773
+ clearTimeout(timeoutId);
1774
+ o.testUpdate(testInfo, false, err.message || "Promise rejected");
1775
+ });
1776
+ continue;
1777
+ }
1778
+ if (typeof o.tStatus[testN2][i] === "undefined") {
1779
+ o.tStatus[testN2][i] = typeof res === "string" ? o.F : res;
1780
+ } else {
1781
+ sessionStorage.setItem(`oTest-Status-${testN2}`, JSON.stringify(o.tStatus[testN2]));
1782
+ return;
1783
+ }
1784
+ if (res === true) {
1785
+ done++;
1786
+ if (o.tShowOk) {
1787
+ o.tLog[testN2] += preOk + tests[i][0] + posOk;
1788
+ log("\u251C OK: " + tests[i][0]);
1789
+ }
1790
+ } else if (res !== o.U) {
1791
+ o.tLog[testN2] += preXx + tests[i][0] + (res !== o.F ? ": " + res : "") + posXx;
1792
+ log("\u251C \u2718 " + tests[i][0] + (res !== o.F ? ": " + res : ""), true);
1793
+ } else {
1794
+ waits++;
1795
+ setTimeout(
1796
+ (info) => {
1797
+ info.title += " (timeout)";
1798
+ o.testUpdate(info);
1799
+ },
1800
+ o.tTime,
1801
+ testInfo
1802
+ );
1803
+ }
1804
+ }
1805
+ o.tRes[testN2] = done === num;
1806
+ row = waits ? "\u251C " : "\u2558 ";
1807
+ row += "DONE " + done + "/" + num + (waits ? ", waiting: " + waits : "");
1808
+ log(row, done + waits !== num);
1809
+ if (!waits) {
1810
+ log();
1811
+ }
1812
+ if (o.tStyled) {
1813
+ o.tLog[testN2] += o.tPre + '<div style="color:' + (done + waits !== num ? "red" : "green") + ';"><b>DONE ' + done + "/" + num + (waits ? ", waiting: " + waits : "") + "</b>" + o.tDc + o.tDc;
1814
+ } else {
1815
+ o.tLog[testN2] += row + "\n";
1816
+ }
1817
+ if (testSession) {
1818
+ sessionStorage.setItem(`oTest-Log-${testN2}`, o.tLog[testN2]);
1819
+ sessionStorage.setItem(`oTest-Res-${testN2}`, o.tRes[testN2]);
1820
+ sessionStorage.setItem(`oTest-Status-${testN2}`, JSON.stringify(o.tStatus[testN2]));
1821
+ }
1822
+ if (!waits && typeof o.tFns[testN2] === "function") {
1823
+ o.tFns[testN2](testN2);
1824
+ }
1825
+ return testN2;
1826
+ };
1827
+ o.testUpdate = (info, res = o.F, suff = "") => {
1828
+ o.verify([
1829
+ [info, ["object"]],
1830
+ [res, ["boolean", "string"]],
1831
+ [suff, ["string"]]
1832
+ ]);
1833
+ let row = "";
1834
+ const testN2 = info.n;
1835
+ const log = (line = "", error = false) => {
1836
+ if (o.tAutolog) {
1837
+ if (error) {
1838
+ console.error(line);
1839
+ } else {
1840
+ console.log(line);
1841
+ }
1842
+ }
1843
+ };
1844
+ if (o.tStatus[testN2][info.i] === o.U || o.tStatus[testN2][info.i] === null) {
1845
+ o.tStatus[testN2][info.i] = res === true;
1846
+ if (res === true) {
1847
+ if (info.tShowOk) {
1848
+ row = "\u251C OK: " + info.title + suff;
1849
+ log(row);
1850
+ if (info.tStyled) {
1851
+ o.tLog[testN2] += o.tPre + o.tOk + info.title + suff + o.tDc;
1852
+ } else {
1853
+ o.tLog[testN2] += row + "\n";
1854
+ }
1855
+ }
1856
+ } else {
1857
+ o.tRes[testN2] = o.F;
1858
+ row = "\u251C \u2718 " + info.title + (res ? ": " + res : "") + suff;
1859
+ log(row, true);
1860
+ if (info.tStyled) {
1861
+ o.tLog[testN2] += o.tPre + o.tXx + info.title + suff + (res ? ": " + res : "") + o.tDc + o.tDc;
1862
+ } else {
1863
+ o.tLog[testN2] += row + "\n";
1864
+ }
1865
+ }
1866
+ let fails = 0, n = 0;
1867
+ for (const s of o.tStatus[testN2]) {
1868
+ if (s === o.U || s === null) {
1869
+ return;
1870
+ }
1871
+ if (!s) {
1872
+ fails++;
1873
+ }
1874
+ n++;
1875
+ }
1876
+ if (sessionStorage?.getItem("oTest-Run") === testN2) {
1877
+ sessionStorage.setItem(`oTest-Log-${testN2}`, o.tLog[testN2]);
1878
+ sessionStorage.setItem(`oTest-Res-${testN2}`, o.tRes[testN2]);
1879
+ sessionStorage.setItem(`oTest-Status-${testN2}`, JSON.stringify(o.tStatus[testN2]));
1880
+ if (n < o.tests[testN2].tests.length) {
1881
+ return;
1882
+ }
1883
+ }
1884
+ o.tRes[testN2] = !fails;
1885
+ row = fails ? "FAILED " + fails + "/" + n : "DONE " + n + "/" + n;
1886
+ log("\u2558 " + row, Boolean(fails));
1887
+ log();
1888
+ if (info.tStyled) {
1889
+ o.tLog[testN2] += o.tPre + '<b style="color:' + (!fails ? "green" : "red") + ';">' + row + "</b>" + o.tDc;
1890
+ } else {
1891
+ o.tLog[testN2] += "\u2558 " + row + "\n";
1892
+ }
1893
+ if (typeof o.tFns[testN2] === "function") {
1894
+ o.tFns[testN2](testN2);
1895
+ }
1896
+ }
1897
+ };
1898
+ if (sessionStorage?.getItem("oTest-Run")) {
1899
+ window?.addEventListener(
1900
+ "load",
1901
+ () => {
1902
+ o.runTest(
1903
+ sessionStorage?.getItem("oTest-Run"),
1904
+ sessionStorage?.getItem("oTest-Autorun") || o.F,
1905
+ true
1906
+ );
1907
+ },
1908
+ false
1909
+ );
1910
+ }
1911
+ o.measure = (el) => {
1912
+ if (!el) {
1913
+ return {};
1914
+ }
1915
+ const rect = el.getBoundingClientRect();
1916
+ const style = window.getComputedStyle(el);
1917
+ return {
1918
+ width: rect.width,
1919
+ height: rect.height,
1920
+ top: rect.top,
1921
+ left: rect.left,
1922
+ visible: style.display !== "none" && style.visibility !== "hidden" && rect.width > 0,
1923
+ opacity: style.opacity,
1924
+ zIndex: style.zIndex
1925
+ };
1926
+ };
1927
+ o.assertVisible = (el) => {
1928
+ const m = o.measure(el);
1929
+ return Boolean(m.visible);
1930
+ };
1931
+ o.assertSize = (el, expected = {}) => {
1932
+ if (!el) return "element is null";
1933
+ const m = o.measure(el);
1934
+ if (expected.w !== void 0 && Math.round(m.width) !== expected.w) {
1935
+ return `width: expected ${expected.w}, got ${Math.round(m.width)}`;
1936
+ }
1937
+ if (expected.h !== void 0 && Math.round(m.height) !== expected.h) {
1938
+ return `height: expected ${expected.h}, got ${Math.round(m.height)}`;
1939
+ }
1940
+ const getPx = (prop) => {
1941
+ const v = window.getComputedStyle(el).getPropertyValue(prop);
1942
+ return v ? parseFloat(v) : 0;
1943
+ };
1944
+ if (expected.padding !== void 0 && getPx("padding-top") !== expected.padding) {
1945
+ return `padding: expected ${expected.padding}, got ${getPx("padding-top")}`;
1946
+ }
1947
+ if (expected.paddingTop !== void 0 && getPx("padding-top") !== expected.paddingTop) {
1948
+ return `paddingTop: expected ${expected.paddingTop}, got ${getPx("padding-top")}`;
1949
+ }
1950
+ if (expected.paddingRight !== void 0 && getPx("padding-right") !== expected.paddingRight) {
1951
+ return `paddingRight: expected ${expected.paddingRight}, got ${getPx("padding-right")}`;
1952
+ }
1953
+ if (expected.paddingBottom !== void 0 && getPx("padding-bottom") !== expected.paddingBottom) {
1954
+ return `paddingBottom: expected ${expected.paddingBottom}, got ${getPx("padding-bottom")}`;
1955
+ }
1956
+ if (expected.paddingLeft !== void 0 && getPx("padding-left") !== expected.paddingLeft) {
1957
+ return `paddingLeft: expected ${expected.paddingLeft}, got ${getPx("padding-left")}`;
1958
+ }
1959
+ if (expected.margin !== void 0 && getPx("margin-top") !== expected.margin) {
1960
+ return `margin: expected ${expected.margin}, got ${getPx("margin-top")}`;
1961
+ }
1962
+ if (expected.marginTop !== void 0 && getPx("margin-top") !== expected.marginTop) {
1963
+ return `marginTop: expected ${expected.marginTop}, got ${getPx("margin-top")}`;
1964
+ }
1965
+ if (expected.marginRight !== void 0 && getPx("margin-right") !== expected.marginRight) {
1966
+ return `marginRight: expected ${expected.marginRight}, got ${getPx("margin-right")}`;
1967
+ }
1968
+ if (expected.marginBottom !== void 0 && getPx("margin-bottom") !== expected.marginBottom) {
1969
+ return `marginBottom: expected ${expected.marginBottom}, got ${getPx("margin-bottom")}`;
1970
+ }
1971
+ if (expected.marginLeft !== void 0 && getPx("margin-left") !== expected.marginLeft) {
1972
+ return `marginLeft: expected ${expected.marginLeft}, got ${getPx("margin-left")}`;
1973
+ }
1974
+ return true;
1975
+ };
1976
+ o.recorder = {
1977
+ active: false,
1978
+ actions: [],
1979
+ mocks: {},
1980
+ initialData: {},
1981
+ assertions: [],
1982
+ observeRoot: null,
1983
+ _originalFetch: null,
1984
+ _listeners: [],
1985
+ _observer: null
1986
+ };
1987
+ o.startRecording = (observe, events, timeouts) => {
1988
+ if (o.recorder.active) {
1989
+ return;
1990
+ }
1991
+ const defaultEvents = ["click", "mouseover", "scroll", "input", "change"];
1992
+ const defaultStepDelays = {
1993
+ click: 100,
1994
+ mouseover: 50,
1995
+ scroll: 30,
1996
+ input: 50,
1997
+ change: 50
1998
+ };
1999
+ const listenEvents = events || defaultEvents;
2000
+ const stepDelays = Object.assign({}, defaultStepDelays, timeouts || {});
2001
+ const captureDebounce = { scroll: 30, mouseover: 50 };
2002
+ const rec = o.recorder;
2003
+ rec.active = true;
2004
+ rec.actions = [];
2005
+ rec.mocks = {};
2006
+ rec.stepDelays = stepDelays;
2007
+ rec.initialData = { url: window.location.href, timestamp: Date.now() };
2008
+ rec.observeRoot = observe || null;
2009
+ rec.assertions = [];
2010
+ o.inits.forEach((inst, idx) => {
2011
+ if (inst?.store) {
2012
+ rec.initialData["init_" + idx] = JSON.parse(JSON.stringify(inst.store));
2013
+ }
2014
+ });
2015
+ rec._originalFetch = window.fetch;
2016
+ window.fetch = async (url, opts = {}) => {
2017
+ const method = (opts.method || "GET").toUpperCase();
2018
+ let reqBody;
2019
+ try {
2020
+ reqBody = opts.body ? JSON.parse(opts.body) : void 0;
2021
+ } catch (_e) {
2022
+ reqBody = opts.body;
2023
+ }
2024
+ const response = await rec._originalFetch(url, opts);
2025
+ const clone = response.clone();
2026
+ let respBody;
2027
+ try {
2028
+ respBody = await clone.json();
2029
+ } catch (_e) {
2030
+ respBody = await clone.text().catch(() => null);
2031
+ }
2032
+ const key = method + ":" + url;
2033
+ rec.mocks[key] = {
2034
+ url,
2035
+ method,
2036
+ request: reqBody,
2037
+ response: respBody,
2038
+ status: response.status
2039
+ };
2040
+ return response;
2041
+ };
2042
+ const unstableDataAttrs = { "data-o-init": 1, "data-o-init-i": 1, "data-o-state": 1 };
2043
+ const qualify = (sel, fromNode) => {
2044
+ if (o.D.querySelectorAll(sel).length <= 1) return sel;
2045
+ let node = fromNode;
2046
+ while (node && node !== o.D.body) {
2047
+ let ancestorSel = node.id ? "#" + node.id : null;
2048
+ if (!ancestorSel && node.attributes) {
2049
+ const autotagAttr = o.autotag ? "data-" + o.autotag : null;
2050
+ if (autotagAttr) {
2051
+ const val = node.getAttribute(autotagAttr);
2052
+ if (val != null) ancestorSel = `[${autotagAttr}="${val}"]`;
2053
+ }
2054
+ if (!ancestorSel) {
2055
+ for (const attr of node.attributes) {
2056
+ if (attr.name.startsWith("data-") && !unstableDataAttrs[attr.name]) {
2057
+ ancestorSel = `[${attr.name}="${attr.value}"]`;
2058
+ break;
2059
+ }
2060
+ }
2061
+ }
2062
+ }
2063
+ if (ancestorSel) {
2064
+ const q = ancestorSel + " " + sel;
2065
+ if (o.D.querySelectorAll(q).length === 1) return q;
2066
+ }
2067
+ node = node.parentElement;
2068
+ }
2069
+ return sel;
2070
+ };
2071
+ const buildSelector = (node) => {
2072
+ if (!node || node.nodeType !== 1) return "";
2073
+ let sel = "";
2074
+ if (node.dataset) {
2075
+ const qaKey = o.autotag && node.dataset[o.autotag];
2076
+ if (qaKey) {
2077
+ sel = qualify(`[data-${o.autotag}="${qaKey}"]`, node.parentElement);
2078
+ }
2079
+ }
2080
+ if (!sel && node.tagName) {
2081
+ const base = node.id ? "#" + node.id : node.className ? node.tagName.toLowerCase() + "." + [...node.classList].join(".") : node.tagName.toLowerCase();
2082
+ sel = node.id ? base : qualify(base, node.parentElement);
2083
+ }
2084
+ return sel;
2085
+ };
2086
+ const observeTarget = observe && o.D.querySelector(observe) || o.D.body;
2087
+ rec._observer = new MutationObserver((mutations) => {
2088
+ const actionIdx = rec.actions.length - 1;
2089
+ if (actionIdx < 0) return;
2090
+ mutations.forEach((m) => {
2091
+ const addAssertionIndex = (sel, node) => {
2092
+ let listSelector;
2093
+ let index;
2094
+ if (sel && observeTarget) {
2095
+ const matches = observeTarget.querySelectorAll(sel);
2096
+ if (matches.length > 1) {
2097
+ let n = node;
2098
+ while (n && n !== observeTarget && n.nodeType === 1) {
2099
+ const qaAttr = o.autotag && n.dataset && n.dataset[o.autotag];
2100
+ if (qaAttr) {
2101
+ const itemSel = `[data-${o.autotag}="${qaAttr}"]`;
2102
+ const itemMatches = observeTarget.querySelectorAll(itemSel);
2103
+ if (itemMatches.length > 1) {
2104
+ const idx = [...itemMatches].indexOf(n);
2105
+ if (idx !== -1) {
2106
+ listSelector = itemSel;
2107
+ index = idx;
2108
+ break;
2109
+ }
2110
+ }
2111
+ }
2112
+ n = n.parentElement;
2113
+ }
2114
+ }
2115
+ }
2116
+ return { listSelector, index };
2117
+ };
2118
+ if (m.type === "childList") {
2119
+ m.addedNodes.forEach((node) => {
2120
+ if (node.nodeType !== 1) return;
2121
+ if (!node.offsetParent) return;
2122
+ const sel = buildSelector(node);
2123
+ if (!sel) return;
2124
+ if (rec.assertions.some(
2125
+ (a2) => a2.actionIdx === actionIdx && a2.selector === sel && a2.type === "visible"
2126
+ ))
2127
+ return;
2128
+ const textEl = node.querySelector?.(".task-text") || node;
2129
+ const text = (textEl.textContent?.trim() || node.textContent?.trim() || "").slice(0, 80) || void 0;
2130
+ const { listSelector: aListSel, index: aIdx } = addAssertionIndex(sel, node);
2131
+ const a = { actionIdx, type: "visible", selector: sel, text };
2132
+ if (aListSel != null) a.listSelector = aListSel;
2133
+ if (aIdx != null) a.index = aIdx;
2134
+ rec.assertions.push(a);
2135
+ });
2136
+ }
2137
+ if (m.type === "attributes") {
2138
+ const sel = buildSelector(m.target);
2139
+ if (!sel) return;
2140
+ if (rec.assertions.some(
2141
+ (a2) => a2.actionIdx === actionIdx && a2.selector === sel && a2.type === "class"
2142
+ ))
2143
+ return;
2144
+ const { listSelector: aListSel, index: aIdx } = addAssertionIndex(sel, m.target);
2145
+ const a = {
2146
+ actionIdx,
2147
+ type: "class",
2148
+ selector: sel,
2149
+ className: m.target.className
2150
+ };
2151
+ if (aListSel != null) a.listSelector = aListSel;
2152
+ if (aIdx != null) a.index = aIdx;
2153
+ rec.assertions.push(a);
2154
+ }
2155
+ });
2156
+ });
2157
+ rec._observer.observe(observeTarget, {
2158
+ childList: true,
2159
+ subtree: true,
2160
+ attributes: true,
2161
+ attributeFilter: [
2162
+ "class",
2163
+ "style",
2164
+ "hidden",
2165
+ "disabled",
2166
+ "aria-expanded",
2167
+ "aria-checked"
2168
+ ]
2169
+ });
2170
+ const timers = {};
2171
+ listenEvents.forEach((ev) => {
2172
+ const handler = (e) => {
2173
+ const target = e.target;
2174
+ if (observe && observeTarget && target?.nodeType === 1 && !observeTarget.contains(target)) {
2175
+ return;
2176
+ }
2177
+ let selector = "";
2178
+ if (target?.dataset) {
2179
+ const qaKey = o.autotag && target.dataset[o.autotag];
2180
+ if (qaKey) {
2181
+ selector = qualify(`[data-${o.autotag}="${qaKey}"]`, target.parentElement);
2182
+ }
2183
+ }
2184
+ if (!selector && target?.tagName) {
2185
+ const base = target.id ? "#" + target.id : target.className ? target.tagName.toLowerCase() + "." + [...target.classList].join(".") : target.tagName.toLowerCase();
2186
+ selector = target.id ? base : qualify(base, target.parentElement);
2187
+ }
2188
+ let listSelector;
2189
+ let targetIndex;
2190
+ if (selector && observeTarget) {
2191
+ const matches = observeTarget.querySelectorAll(selector);
2192
+ if (matches.length > 1) {
2193
+ let node = target;
2194
+ while (node && node !== observeTarget && node.nodeType === 1) {
2195
+ const qaAttr = o.autotag && node.dataset && node.dataset[o.autotag];
2196
+ if (qaAttr) {
2197
+ const itemSel = `[data-${o.autotag}="${qaAttr}"]`;
2198
+ const itemMatches = observeTarget.querySelectorAll(itemSel);
2199
+ if (itemMatches.length > 1) {
2200
+ const idx = [...itemMatches].indexOf(node);
2201
+ if (idx !== -1) {
2202
+ listSelector = itemSel;
2203
+ targetIndex = idx;
2204
+ break;
2205
+ }
2206
+ }
2207
+ }
2208
+ node = node.parentElement;
2209
+ }
2210
+ }
2211
+ }
2212
+ const targetType = target?.tagName ? target.tagName.toLowerCase() + (target.type ? ":" + target.type : "") : void 0;
2213
+ const scrollY = ev === "scroll" ? window.scrollY : void 0;
2214
+ const value = ev === "input" || ev === "change" ? target?.value : void 0;
2215
+ const checked = ev === "change" && (target?.type === "checkbox" || target?.type === "radio") ? target?.checked : void 0;
2216
+ const delay = stepDelays[ev] !== void 0 ? stepDelays[ev] : captureDebounce[ev] ?? 0;
2217
+ const pushAction = () => {
2218
+ const action = { type: ev, target: selector, time: Date.now() };
2219
+ if (targetType) action.targetType = targetType;
2220
+ if (scrollY !== void 0) action.scrollY = scrollY;
2221
+ if (value !== void 0) action.value = value;
2222
+ if (checked !== void 0) action.checked = checked;
2223
+ if (listSelector != null) action.listSelector = listSelector;
2224
+ if (targetIndex != null) action.targetIndex = targetIndex;
2225
+ rec.actions.push(action);
2226
+ };
2227
+ if (delay === 0) {
2228
+ pushAction();
2229
+ } else {
2230
+ clearTimeout(timers[ev]);
2231
+ timers[ev] = setTimeout(pushAction, delay);
2232
+ }
2233
+ };
2234
+ o.D.addEventListener(ev, handler, true);
2235
+ rec._listeners.push({ ev, handler });
2236
+ });
2237
+ };
2238
+ o.stopRecording = () => {
2239
+ const rec = o.recorder;
2240
+ rec.active = false;
2241
+ if (rec._originalFetch) {
2242
+ window.fetch = rec._originalFetch;
2243
+ rec._originalFetch = null;
2244
+ }
2245
+ rec._listeners.forEach(({ ev, handler }) => {
2246
+ o.D.removeEventListener(ev, handler, true);
2247
+ });
2248
+ rec._listeners = [];
2249
+ if (rec._observer) {
2250
+ rec._observer.disconnect();
2251
+ rec._observer = null;
2252
+ }
2253
+ return {
2254
+ actions: [...rec.actions],
2255
+ mocks: { ...rec.mocks },
2256
+ initialData: { ...rec.initialData },
2257
+ stepDelays: { ...rec.stepDelays },
2258
+ assertions: [...rec.assertions || []],
2259
+ observeRoot: rec.observeRoot || null
2260
+ };
2261
+ };
2262
+ o.clearRecording = (id) => {
2263
+ if (id !== void 0) {
2264
+ sessionStorage?.removeItem("oTest-Recording-" + id);
2265
+ } else {
2266
+ for (let i = sessionStorage?.length - 1; i >= 0; i--) {
2267
+ const key = sessionStorage?.key(i);
2268
+ if (key && key.indexOf("oTest-Recording-") === 0) {
2269
+ sessionStorage?.removeItem(key);
2270
+ }
2271
+ }
2272
+ }
2273
+ };
2274
+ o.exportTest = (recording) => {
2275
+ const cases = recording.actions.map((a) => {
2276
+ let body;
2277
+ if (a.type === "scroll") {
2278
+ body = ` window.scrollTo(0, ${a.scrollY || 0}); return true;
2279
+ `;
2280
+ } else if (a.type === "input" || a.type === "change") {
2281
+ body = (a.value !== void 0 ? ` el.value = ${JSON.stringify(a.value)};
2282
+ ` : "") + (a.checked !== void 0 ? ` el.checked = ${a.checked};
2283
+ ` : "") + ` el.dispatchEvent(new Event('${a.type}', {bubbles:true})); return true;
2284
+ `;
2285
+ } else {
2286
+ const useNativeClick = a.type === "click";
2287
+ body = useNativeClick ? ` el.click(); return true;
2288
+ ` : ` el.dispatchEvent(new MouseEvent('${a.type}', {bubbles:true,cancelable:true})); return true;
2289
+ `;
2290
+ }
2291
+ return ` ['${a.type} on ${a.target}', () => {
2292
+ const el = document.querySelector('${a.target}');
2293
+ if (!el) return 'element not found';
2294
+ ` + body + ` }],`;
2295
+ }).join("\n");
2296
+ const mocksStr = Object.keys(recording.mocks).length ? JSON.stringify(recording.mocks, null, 2) : "{}";
2297
+ return `// Auto-generated by o.exportTest() \u2014 review and anonymize mocks before committing
2298
+ const recordingMocks = ${mocksStr};
2299
+
2300
+ o.addTest('Recorded test', [
2301
+ ${cases}
2302
+ ], () => {
2303
+ // teardown
2304
+ });
2305
+ `;
2306
+ };
2307
+ o.exportPlaywrightTest = (recording, options = {}) => {
2308
+ const testName = options.testName || "Recorded session";
2309
+ const rawUrl = recording.initialData?.url ?? "/";
2310
+ let path = "/";
2311
+ try {
2312
+ path = new URL(rawUrl).pathname || "/";
2313
+ } catch (_e) {
2314
+ path = rawUrl;
2315
+ }
2316
+ const baseUrl = options.baseUrl || path;
2317
+ const routes = Object.values(recording.mocks).map((mock) => {
2318
+ const urlPath = mock.url.startsWith("/") ? mock.url : "/" + mock.url;
2319
+ const body = JSON.stringify(mock.response);
2320
+ return ` await page.route('**${urlPath}', async route => {
2321
+ await route.fulfill({ status: ${mock.status || 200}, contentType: 'application/json',
2322
+ body: JSON.stringify(${body}) });
2323
+ });`;
2324
+ }).join("\n");
2325
+ const sd = Object.assign(
2326
+ { click: 100, mouseover: 50, scroll: 30, input: 50, change: 50 },
2327
+ recording.stepDelays || {}
2328
+ );
2329
+ const steps = recording.actions.map((action, i) => {
2330
+ const loc = action.listSelector != null && action.targetIndex != null ? action.target !== action.listSelector ? `page.locator(${JSON.stringify(action.listSelector)}).nth(${action.targetIndex}).locator(${JSON.stringify(action.target)})` : `page.locator(${JSON.stringify(action.listSelector)}).nth(${action.targetIndex})` : `page.locator(${JSON.stringify(action.target)})`;
2331
+ const wait = ` await page.waitForTimeout(${sd[action.type] || 50});`;
2332
+ let step;
2333
+ if (action.type === "scroll") {
2334
+ step = ` await page.evaluate(() => window.scrollTo(0, ${action.scrollY || 0}));`;
2335
+ } else if (action.type === "mouseover") {
2336
+ step = ` await ${loc}.hover();
2337
+ // Pure CSS :hover \u2014 no DOM mutation to assert.
2338
+ // Fix: toggle a class in a mouseover handler, or add a page.screenshot() visual check.`;
2339
+ } else if (action.type === "input") {
2340
+ step = ` await ${loc}.fill(${JSON.stringify(action.value || "")});`;
2341
+ } else if (action.type === "change") {
2342
+ const tt = action.targetType || "";
2343
+ if (tt.indexOf(":checkbox") !== -1 || tt.indexOf(":radio") !== -1) {
2344
+ const on = action.checked !== void 0 ? action.checked : action.value === "true" || action.value === "on";
2345
+ step = ` await ${loc}.${on ? "check" : "uncheck"}();`;
2346
+ } else if (tt.indexOf("select") !== -1) {
2347
+ step = ` await ${loc}.selectOption(${JSON.stringify(action.value || "")});`;
2348
+ } else {
2349
+ step = ` await ${loc}.fill(${JSON.stringify(action.value || "")});`;
2350
+ }
2351
+ } else {
2352
+ step = ` await ${loc}.click();`;
2353
+ }
2354
+ const asserts = (recording.assertions || []).filter((a) => a.actionIdx === i).filter(
2355
+ (a, j, arr) => arr.findIndex(
2356
+ (x) => x.selector === a.selector && x.type === a.type && x.index === a.index
2357
+ ) === j
2358
+ ).map((a) => {
2359
+ const aLoc = a.listSelector != null && a.index != null ? a.selector !== a.listSelector ? `page.locator(${JSON.stringify(a.listSelector)}).nth(${a.index}).locator(${JSON.stringify(a.selector)})` : `page.locator(${JSON.stringify(a.listSelector)}).nth(${a.index})` : `page.locator(${JSON.stringify(a.selector)})`;
2360
+ if (a.type === "visible") {
2361
+ let s = ` await expect(${aLoc}).toBeVisible();`;
2362
+ if (a.text)
2363
+ s += `
2364
+ await expect(${aLoc}).toContainText(${JSON.stringify(a.text)});`;
2365
+ return s;
2366
+ }
2367
+ if (a.type === "class") {
2368
+ return ` // class on ${a.selector} changed to: "${a.className}"`;
2369
+ }
2370
+ return "";
2371
+ }).filter(Boolean).join("\n");
2372
+ return step + "\n" + wait + (asserts ? "\n" + asserts : "");
2373
+ }).join("\n");
2374
+ const hasAutoAssertions = (recording.assertions || []).length > 0;
2375
+ return `// Auto-generated by o.exportPlaywrightTest() \u2014 review and anonymize mocks before committing
2376
+ // Prerequisites: npm install @playwright/test && npx playwright install chromium
2377
+ // Run: npx playwright test recorded.spec.ts
2378
+ import { test, expect } from '@playwright/test';
2379
+
2380
+ test(${JSON.stringify(testName)}, async ({ page }) => {
2381
+ ` + (routes ? ` // Network mocks \u2014 edit/anonymize before committing
2382
+ ` + routes + "\n\n" : "") + ` // Set baseURL in playwright.config.ts: { use: { baseURL: 'https://staging.example.com' } }
2383
+ await page.goto(${JSON.stringify(baseUrl)});
2384
+
2385
+ ` + (steps ? steps + "\n\n" : "") + (!hasAutoAssertions ? ` // TODO: Add assertions before committing, e.g.:
2386
+ // await expect(page.locator('[data-qa="success-panel"]')).toBeVisible();
2387
+ // await expect(page).toHaveURL(/\\/confirmation/);
2388
+ // await expect(page.locator('[data-qa="error-banner"]')).not.toBeVisible();
2389
+ ` : ` // Auto-generated assertions above \u2014 review for correctness before committing
2390
+ `) + `});
2391
+ `;
2392
+ };
2393
+ o.playRecording = (recording, mockOverrides = {}) => {
2394
+ const allMocks = Object.assign({}, recording.mocks, mockOverrides);
2395
+ const origFetch = window.fetch;
2396
+ window.fetch = (url, opts = {}) => {
2397
+ const method = (opts.method || "GET").toUpperCase();
2398
+ const key = method + ":" + url;
2399
+ if (allMocks[key]) {
2400
+ const mock = allMocks[key];
2401
+ const body = typeof mock.response === "string" ? mock.response : JSON.stringify(mock.response);
2402
+ return Promise.resolve(new Response(body, { status: mock.status || 200 }));
2403
+ }
2404
+ return origFetch(url, opts);
2405
+ };
2406
+ const testCases = recording.actions.map((action) => [
2407
+ `${action.type} on ${action.target}`,
2408
+ () => {
2409
+ let el = null;
2410
+ if (action.target) {
2411
+ if (action.listSelector != null && action.targetIndex != null) {
2412
+ const items = o.D.querySelectorAll(action.listSelector);
2413
+ const item = items[action.targetIndex];
2414
+ if (item) {
2415
+ el = action.target !== action.listSelector ? item.querySelector(action.target) : item;
2416
+ if (!el && action.target !== action.listSelector) el = item;
2417
+ }
2418
+ } else {
2419
+ el = o.D.querySelector(action.target);
2420
+ }
2421
+ }
2422
+ if (!el && action.type !== "scroll") {
2423
+ return `element not found: ${action.target}`;
2424
+ }
2425
+ if (action.type === "scroll") {
2426
+ window.scrollTo(0, action.scrollY || 0);
2427
+ } else if (action.type === "input" || action.type === "change") {
2428
+ if (action.value !== void 0) el.value = action.value;
2429
+ if (action.checked !== void 0) el.checked = action.checked;
2430
+ el.dispatchEvent(new Event(action.type, { bubbles: true }));
2431
+ } else {
2432
+ if (action.type === "click") {
2433
+ el.click();
2434
+ } else {
2435
+ el.dispatchEvent(
2436
+ new MouseEvent(action.type, { bubbles: true, cancelable: true })
2437
+ );
2438
+ }
2439
+ }
2440
+ return true;
2441
+ }
2442
+ ]);
2443
+ const testId = o.test("Recorded playback", ...testCases, () => {
2444
+ window.fetch = origFetch;
2445
+ });
2446
+ return testId;
2447
+ };
2448
+ o.testOverlay = () => {
2449
+ const btnId = "o-test-overlay-btn";
2450
+ const panelId = "o-test-overlay-panel";
2451
+ if (o("#" + btnId).el) {
2452
+ return;
2453
+ }
2454
+ const updatePanel = () => {
2455
+ const panel = o("#" + panelId);
2456
+ if (!panel.el) return;
2457
+ const total = o.tRes.length;
2458
+ const passed = o.tRes.filter(Boolean).length;
2459
+ let html = `<b>Tests: ${passed}/${total}</b><hr style="margin:4px 0">`;
2460
+ o.tLog.forEach((log, i) => {
2461
+ const ok = o.tRes[i];
2462
+ html += `<div style="margin:2px 0;padding:2px 4px;border-radius:3px;background:${ok ? "#d4edda" : "#f8d7da"};color:${ok ? "#155724" : "#721c24"};font-size:11px;white-space:pre-wrap">${log || "Test #" + i}</div>`;
2463
+ });
2464
+ html += `<button id="o-test-export" style="margin-top:6px;padding:4px 8px;font-size:11px;cursor:pointer">Export results</button>`;
2465
+ panel.html(html);
2466
+ o("#o-test-export").on("click", () => {
2467
+ const data = JSON.stringify({ results: o.tRes, logs: o.tLog }, null, 2);
2468
+ const blob = new Blob([data], { type: "application/json" });
2469
+ const a = o.D.createElement("a");
2470
+ a.href = URL.createObjectURL(blob);
2471
+ a.download = "objs-test-results.json";
2472
+ a.click();
2473
+ });
2474
+ };
2475
+ const overlayStyle = {
2476
+ position: "fixed",
2477
+ left: "50%",
2478
+ bottom: "50px",
2479
+ transform: "translateX(-50%)",
2480
+ "z-index": "999999",
2481
+ width: "fit-content",
2482
+ "max-width": "min(90vw, 420px)",
2483
+ "font-family": "system-ui,sans-serif",
2484
+ cursor: "grab",
2485
+ "user-select": "text"
2486
+ };
2487
+ const box = o.initState({
2488
+ tag: "div",
2489
+ id: btnId,
2490
+ className: "o-test-overlay",
2491
+ style: "position:fixed;left:50%;bottom:50px;transform:translateX(-50%);z-index:999999;width:fit-content;max-width:min(90vw,420px);font-family:system-ui,sans-serif;cursor:grab;user-select:text;",
2492
+ html: `<div class="o-test-overlay-bar" style="display:flex;flex-direction:column;align-items:stretch;padding:10px 14px;background:#1e293b;color:#e2e8f0;border:1px solid #334155;border-radius:8px;font-size:14px;cursor:grab;min-width:200px;"><div style="display:flex;align-items:center;gap:12px;"><span class="o-test-overlay-summary" style="flex:1;font-size:13px;">Tests: 0/0</span><button type="button" class="o-test-overlay-toggle" style="padding:6px 10px;background:#334155;color:#e2e8f0;border:none;border-radius:6px;cursor:pointer;font-size:12px;">List</button><button type="button" class="o-test-overlay-close" style="padding:4px 8px;background:transparent;color:#94a3b8;border:none;border-radius:4px;cursor:pointer;font-size:16px;line-height:1;" title="Close">\xD7</button></div></div><div id="${panelId}" style="display:none;margin-top:4px;padding:8px;background:#fff;border:1px solid #334155;border-radius:6px;max-height:60vh;overflow-y:auto;box-shadow:0 2px 8px rgba(0,0,0,.15);font-size:11px;user-select:text;cursor:text;"></div>`
2493
+ }).appendInside("body");
2494
+ const applyOverlayStyle = () => {
2495
+ box.css(overlayStyle);
2496
+ };
2497
+ let drag = null;
2498
+ const onMove = (e) => {
2499
+ if (!drag) return;
2500
+ overlayStyle.left = drag.left + (e.clientX - drag.startX) + "px";
2501
+ overlayStyle.top = drag.top + (e.clientY - drag.startY) + "px";
2502
+ delete overlayStyle.bottom;
2503
+ overlayStyle.transform = "none";
2504
+ applyOverlayStyle();
2505
+ };
2506
+ const onUp = () => {
2507
+ if (drag) {
2508
+ overlayStyle.cursor = "grab";
2509
+ applyOverlayStyle();
2510
+ }
2511
+ drag = null;
2512
+ };
2513
+ box.on("mousedown", (e) => {
2514
+ if (e.target.closest(".o-test-overlay-close") || e.target.closest(".o-test-overlay-toggle") || e.target.closest("#" + panelId))
2515
+ return;
2516
+ const r = box.el.getBoundingClientRect();
2517
+ drag = { startX: e.clientX, startY: e.clientY, left: r.left, top: r.top };
2518
+ overlayStyle.cursor = "grabbing";
2519
+ applyOverlayStyle();
2520
+ });
2521
+ o.D.addEventListener("mousemove", onMove);
2522
+ o.D.addEventListener("mouseup", onUp);
2523
+ const refreshSummary = () => {
2524
+ const summary = o(".o-test-overlay-summary");
2525
+ if (summary.els.length)
2526
+ summary.innerText(`Tests: ${o.tRes.filter(Boolean).length}/${o.tRes.length}`);
2527
+ };
2528
+ box.first(".o-test-overlay-toggle").on("click", () => {
2529
+ const panel = o("#" + panelId);
2530
+ if (!panel.el) return;
2531
+ const isOpen = panel.el.style.display !== "none";
2532
+ panel.css({ display: isOpen ? "none" : "block" });
2533
+ if (!isOpen) updatePanel();
2534
+ });
2535
+ box.first(".o-test-overlay-close").on("click", () => {
2536
+ o.D.removeEventListener("mousemove", onMove);
2537
+ o.D.removeEventListener("mouseup", onUp);
2538
+ box.remove();
2539
+ });
2540
+ o.testOverlay.showPanel = () => {
2541
+ const panel = o("#" + panelId);
2542
+ if (!panel.el) return;
2543
+ panel.css({ display: "block" });
2544
+ updatePanel();
2545
+ refreshSummary();
2546
+ };
2547
+ if (!o._testOverlayBase) o._testOverlayBase = o.test;
2548
+ o.test = (...args) => {
2549
+ const id = o._testOverlayBase(...args);
2550
+ const origFn = o.tFns[id];
2551
+ o.tFns[id] = (n) => {
2552
+ if (typeof origFn === "function") origFn(n);
2553
+ const panel = o("#" + panelId);
2554
+ if (panel.el && panel.el.style.display !== "none") updatePanel();
2555
+ refreshSummary();
2556
+ };
2557
+ return id;
2558
+ };
2559
+ };
2560
+ o.testConfirm = (label, items = [], opts = {}) => new Promise((resolve) => {
2561
+ o(".o-tc-overlay").remove();
2562
+ const btnLabel = opts.confirm || "Continue";
2563
+ const hasCheckboxes = items.length > 0;
2564
+ const btnBg = hasCheckboxes ? "#dc2626" : "#2563eb";
2565
+ const itemIds = items.map((_, idx) => "o-tc-cb-" + idx);
2566
+ const checkboxStyle = `.o-tc-item-cb{appearance:none;-webkit-appearance:none;width:16px;height:16px;border:2px solid #ef4444;border-radius:3px;background:#fef2f2;flex-shrink:0;cursor:pointer;}.o-tc-item-cb:checked{border-color:#22c55e;background:#22c55e;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");background-size:12px 12px;background-position:center;}`;
2567
+ const itemsHtml = hasCheckboxes ? `<style>${checkboxStyle}</style><ul class="o-tc-list" style="margin:8px 0 0;padding:0;list-style:none;font-size:13px;color:#cbd5e1;">` + items.map(
2568
+ (i, idx) => `<li style="margin-bottom:4px;"><label for="${itemIds[idx]}" style="display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none;"><input type="checkbox" id="${itemIds[idx]}" class="o-tc-item-cb"> <span>${i}</span></label></li>`
2569
+ ).join("") + "</ul>" : "";
2570
+ const box = o.initState({
2571
+ tag: "div",
2572
+ className: "o-tc-overlay",
2573
+ style: "position:fixed;left:50%;bottom:50px;transform:translateX(-50%);z-index:999999;width:fit-content;max-width:min(90vw,400px);font-family:system-ui,sans-serif;cursor:grab;user-select:text;",
2574
+ html: `<div class="o-tc-bar" style="display:flex;flex-direction:column;align-items:stretch;padding:10px 14px;background:#1e293b;color:#e2e8f0;border:1px solid #334155;border-radius:8px;font-size:14px;cursor:grab;min-width:280px;"><div style="display:flex;align-items:center;gap:12px;"><span class="o-tc-label" style="flex:1;">${label}: Paused</span><button type="button" class="o-tc-ok" style="padding:6px 14px;background:${btnBg};color:#fff;border:none;border-radius:6px;font-weight:600;cursor:pointer;font-size:13px;flex-shrink:0;">${btnLabel}</button></div>` + itemsHtml + `</div>`
2575
+ }).appendInside("body");
2576
+ const okBtnStyles = {
2577
+ padding: "6px 14px",
2578
+ background: hasCheckboxes ? "#dc2626" : "#2563eb",
2579
+ color: "#fff",
2580
+ border: "none",
2581
+ "border-radius": "6px",
2582
+ "font-weight": "600",
2583
+ cursor: "pointer",
2584
+ "font-size": "13px",
2585
+ "flex-shrink": "0"
2586
+ };
2587
+ if (hasCheckboxes) {
2588
+ const okBtn = box.first(".o-tc-ok");
2589
+ const cbs = o(".o-tc-overlay .o-tc-item-cb");
2590
+ const updateBtn = () => {
2591
+ const allChecked = cbs.length > 0 && cbs.els.every((el) => el.checked);
2592
+ okBtn.css({ ...okBtnStyles, background: allChecked ? "#16a34a" : "#dc2626" });
2593
+ };
2594
+ cbs.on("change", updateBtn);
2595
+ }
2596
+ let drag = null;
2597
+ const overlayStyle = {
2598
+ position: "fixed",
2599
+ left: "50%",
2600
+ bottom: "50px",
2601
+ transform: "translateX(-50%)",
2602
+ "z-index": "999999",
2603
+ width: "fit-content",
2604
+ "max-width": "min(90vw, 400px)",
2605
+ "font-family": "system-ui,sans-serif",
2606
+ cursor: "grab",
2607
+ "user-select": "text"
2608
+ };
2609
+ const applyOverlayStyle = () => {
2610
+ box.css(overlayStyle);
2611
+ };
2612
+ const onMove = (e) => {
2613
+ if (!drag) return;
2614
+ overlayStyle.left = drag.left + (e.clientX - drag.startX) + "px";
2615
+ overlayStyle.top = drag.top + (e.clientY - drag.startY) + "px";
2616
+ delete overlayStyle.bottom;
2617
+ overlayStyle.transform = "none";
2618
+ applyOverlayStyle();
2619
+ };
2620
+ const onUp = () => {
2621
+ if (drag) {
2622
+ overlayStyle.cursor = "grab";
2623
+ applyOverlayStyle();
2624
+ }
2625
+ drag = null;
2626
+ };
2627
+ box.on("mousedown", (e) => {
2628
+ if (e.target.closest(".o-tc-ok")) return;
2629
+ const r = box.el.getBoundingClientRect();
2630
+ drag = { startX: e.clientX, startY: e.clientY, left: r.left, top: r.top };
2631
+ overlayStyle.cursor = "grabbing";
2632
+ applyOverlayStyle();
2633
+ });
2634
+ o.D.addEventListener("mousemove", onMove);
2635
+ o.D.addEventListener("mouseup", onUp);
2636
+ box.first(".o-tc-ok").on("click", () => {
2637
+ o.D.removeEventListener("mousemove", onMove);
2638
+ o.D.removeEventListener("mouseup", onUp);
2639
+ let unchecked = [];
2640
+ if (hasCheckboxes) {
2641
+ const cbsList = o(".o-tc-overlay .o-tc-item-cb");
2642
+ cbsList.els.forEach((el, idx) => {
2643
+ if (!el.checked && items[idx] !== void 0) unchecked.push(items[idx]);
2644
+ });
2645
+ }
2646
+ box.remove();
2647
+ if (unchecked.length === 0) {
2648
+ resolve({ ok: true });
2649
+ } else {
2650
+ resolve({ ok: false, errors: unchecked });
2651
+ }
2652
+ });
2653
+ });
2654
+
2655
+ export { o };
2656
+ export default o;
2657
+ if (typeof window !== "undefined") window.o = o;