objs-core 2.0.0 → 2.0.2
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/EXAMPLES.md +80 -73
- package/README.md +6 -6
- package/objs.built.js +10 -8
- package/objs.built.min.js +15 -15
- package/objs.js +14 -11
- package/package.json +1 -1
package/EXAMPLES.md
CHANGED
|
@@ -125,14 +125,14 @@ function Counter() {
|
|
|
125
125
|
|
|
126
126
|
**Objs**
|
|
127
127
|
```js
|
|
128
|
-
// State lives in the element
|
|
128
|
+
// State lives in the element; events in state — no separate .on() needed
|
|
129
|
+
let counterRef;
|
|
129
130
|
const counter = o.init({
|
|
130
131
|
name: 'Counter',
|
|
131
|
-
render: { tag: 'button', html: '0' },
|
|
132
|
+
render: { tag: 'button', html: '0', events: { click: () => counterRef.inc() } },
|
|
132
133
|
inc: ({ self }) => { self.html(+self.el.textContent + 1); },
|
|
133
134
|
}).render().appendInside('#app');
|
|
134
|
-
|
|
135
|
-
counter.on('click', () => counter.inc());
|
|
135
|
+
counterRef = counter;
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
> In React/Vue/Solid, the framework schedules a re-render when state changes.
|
|
@@ -315,33 +315,12 @@ function ProductList() {
|
|
|
315
315
|
|
|
316
316
|
**Objs**
|
|
317
317
|
```js
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
self.store.items = {};
|
|
325
|
-
products.forEach(p => {
|
|
326
|
-
const item = o.initState({ tag: 'li', html: p.name });
|
|
327
|
-
item.appendInside(self.el);
|
|
328
|
-
self.store.items[p.id] = item;
|
|
329
|
-
});
|
|
330
|
-
},
|
|
331
|
-
// Update one item — O(1), only that text node changes
|
|
332
|
-
updateName: ({ self }, { id, name }) => { self.store.items[id]?.html(name); },
|
|
333
|
-
// Remove one item — only that node is removed
|
|
334
|
-
remove: ({ self }, id) => { self.store.items[id]?.unmount(); delete self.store.items[id]; },
|
|
335
|
-
// Add one item — no list re-render
|
|
336
|
-
addItem: ({ self }, p) => {
|
|
337
|
-
const item = o.initState({ tag: 'li', html: p.name });
|
|
338
|
-
item.appendInside(self.el);
|
|
339
|
-
self.store.items[p.id] = item;
|
|
340
|
-
},
|
|
341
|
-
};
|
|
342
|
-
const list = o.init(listStates).render().appendInside('#app');
|
|
343
|
-
list.load(products);
|
|
344
|
-
list.updateName({ id: 42, name: 'New name' }); // one write
|
|
318
|
+
// One init, render(array) — one element per item (native)
|
|
319
|
+
const listStates = { name: 'ProductList', render: { tag: 'li', html: (p) => p.name } };
|
|
320
|
+
const ul = o.init({ render: { tag: 'ul' } }).render().appendInside('#app');
|
|
321
|
+
const list = o.init(listStates).render(products);
|
|
322
|
+
list.appendInside(ul.el);
|
|
323
|
+
// Re-render: list.render(products). Append/remove: list.select(i).el.remove(), then list.render(newProducts), etc.
|
|
345
324
|
```
|
|
346
325
|
|
|
347
326
|
---
|
|
@@ -405,18 +384,15 @@ function Form() {
|
|
|
405
384
|
```js
|
|
406
385
|
// The company field is a full atom — show/hide are state methods on it
|
|
407
386
|
const companyField = o.init(FieldStates).render({ name: 'company', label: 'Company name' });
|
|
408
|
-
companyField.hide();
|
|
387
|
+
companyField.hide();
|
|
409
388
|
|
|
389
|
+
// events in state — change bubbles from the input to the label root
|
|
410
390
|
const bizBox = o.initState({
|
|
411
391
|
tag: 'label', class: 'field',
|
|
412
392
|
html: '<input type="checkbox" class="biz-check"> Business account',
|
|
393
|
+
events: { change: (e) => { e.target.checked ? companyField.show() : companyField.hide(); } },
|
|
413
394
|
});
|
|
414
|
-
|
|
415
|
-
bizBox.first('.biz-check').on('change', e => {
|
|
416
|
-
e.target.checked ? companyField.show() : companyField.hide();
|
|
417
|
-
});
|
|
418
|
-
// DOM node is never removed — toggled with display:none/''
|
|
419
|
-
// To fully unmount and remount use .unmount() / .appendInside()
|
|
395
|
+
// DOM node is never removed — toggled with display:none/''. To unmount/remount use .unmount() / .appendInside()
|
|
420
396
|
```
|
|
421
397
|
|
|
422
398
|
---
|
|
@@ -549,19 +525,16 @@ function ProductList() {
|
|
|
549
525
|
const listStates = {
|
|
550
526
|
name: 'ProductList',
|
|
551
527
|
render: { tag: 'div', html: '<p class="loading">Loading…</p>' },
|
|
552
|
-
// Called by the loader when data arrives — success state
|
|
553
528
|
load: ({ self }, products) => {
|
|
554
529
|
self.el.innerHTML = '';
|
|
555
|
-
|
|
530
|
+
o.init({ render: { tag: 'p', html: (p) => p.name } }).render(products).appendInside(self.el);
|
|
556
531
|
},
|
|
557
|
-
|
|
558
|
-
loadFailed: ({ self }) => { self.first('.loading').html('Failed to load. Retry?'); },
|
|
532
|
+
loadFailed: ({ self }) => { self.el.innerHTML = '<p class="loading">Failed to load. Retry?</p>'; },
|
|
559
533
|
};
|
|
560
534
|
|
|
561
535
|
const list = o.init(listStates).render().appendInside('#app');
|
|
562
536
|
const loader = o.newLoader(o.get('/api/products'));
|
|
563
537
|
list.connect(loader, 'load', 'loadFailed');
|
|
564
|
-
// loader fires the request; .connect wires success → load, failure → loadFailed
|
|
565
538
|
```
|
|
566
539
|
|
|
567
540
|
---
|
|
@@ -576,14 +549,14 @@ list.connect(loader, 'load', 'loadFailed');
|
|
|
576
549
|
| Update a value | `setV(newVal)` → re-render | `v.value = newVal` → patch | `setV(newVal)` → fine patch | `comp.setState(newVal)` → direct write |
|
|
577
550
|
| Read in template | `{v}` | `{{ v }}` | `{v()}` | Not needed — state methods write directly |
|
|
578
551
|
| Props | function parameter | `defineProps()` | function parameter | `render(props)` argument |
|
|
579
|
-
| Events | `onClick={handler}` | `@click="handler"` | `onClick={handler}` | `.on('click', handler)` |
|
|
552
|
+
| Events | `onClick={handler}` | `@click="handler"` | `onClick={handler}` | `events: { click }` in state or `.on('click', handler)` |
|
|
580
553
|
| Child ref | `useRef()` | `ref="name"` | `let el` / `ref` | `self.store.child = childInstance` |
|
|
581
554
|
| Lifecycle: mount | `useEffect(fn, [])` | `onMounted(fn)` | `onMount(fn)` | `init` state method called after render |
|
|
582
555
|
| Lifecycle: unmount | `useEffect` return fn | `onBeforeUnmount(fn)` | `onCleanup(fn)` | `comp.unmount()` |
|
|
583
556
|
| Shared state | Context / Zustand | Pinia | Signals / store | Plain observer or `o.connectRedux` |
|
|
584
557
|
| Fetch on mount | `useEffect` + `useState` | `onMounted` + `ref` | `createResource` | `o.newLoader` + `.connect()` |
|
|
585
558
|
| Conditional render | `{flag && <El/>}` | `v-if="flag"` | `<Show when={flag}>` | `comp.show()` / `comp.hide()` |
|
|
586
|
-
| List render | `arr.map(x => <El key>)` | `v-for="x in arr"` | `<For each={arr}>` | `arr
|
|
559
|
+
| List render | `arr.map(x => <El key>)` | `v-for="x in arr"` | `<For each={arr}>` | `render(arr)` — one element per item; or forEach in a state method |
|
|
587
560
|
| CSS class toggle | `className={flag?'a':'b'}` | `:class="{a: flag}"` | `classList={{a: flag}}` | `comp.toggleClass('a', flag)` |
|
|
588
561
|
|
|
589
562
|
---
|
|
@@ -718,6 +691,7 @@ grid.reconcile(cards);
|
|
|
718
691
|
Add a `ref="name"` attribute to any element in an HTML-string render. After `init`, every such element is available on the component as `component.refs.name` — an ObjsInstance wrapper, not a raw DOM node.
|
|
719
692
|
|
|
720
693
|
```js
|
|
694
|
+
let cardRef;
|
|
721
695
|
const cardStates = {
|
|
722
696
|
name: 'ProductCard',
|
|
723
697
|
render: ({ title, price }) => ({
|
|
@@ -726,8 +700,8 @@ const cardStates = {
|
|
|
726
700
|
html: `<h3 ref="title">${title}</h3>
|
|
727
701
|
<p ref="price">$${price}</p>
|
|
728
702
|
<button ref="addBtn">Add to cart</button>`,
|
|
703
|
+
events: { click: (e) => { if (e.target.closest('button')) cardRef?.setAdded(); } },
|
|
729
704
|
}),
|
|
730
|
-
// Destructure refs for clean, selector-free access
|
|
731
705
|
setAdded: ({ self }) => {
|
|
732
706
|
const { addBtn } = self.refs;
|
|
733
707
|
addBtn.html('✓ Added').attr('disabled', '');
|
|
@@ -738,9 +712,8 @@ const cardStates = {
|
|
|
738
712
|
};
|
|
739
713
|
|
|
740
714
|
const card = o.init(cardStates).render({ title: 'Widget', price: 9.99 }).appendInside('#app');
|
|
741
|
-
|
|
742
|
-
// card.refs.title
|
|
743
|
-
card.refs.addBtn.on('click', () => card.setAdded());
|
|
715
|
+
cardRef = card;
|
|
716
|
+
// card.refs.addBtn, card.refs.title — refs for selector-free updates
|
|
744
717
|
```
|
|
745
718
|
|
|
746
719
|
> In React, `useRef` accesses a single DOM node. Objs `refs` auto-collects all named children at init time — no `useRef` call per element, no `ref={myRef}` on every JSX tag.
|
|
@@ -826,9 +799,41 @@ const FieldStates = {
|
|
|
826
799
|
|
|
827
800
|
## 3. Nesting & composition
|
|
828
801
|
|
|
829
|
-
|
|
802
|
+
Four patterns for building composite components. Choose based on how the parent and children relate.
|
|
803
|
+
|
|
804
|
+
### Pattern A — refs (`ref` attribute in innerHTML)
|
|
805
|
+
|
|
806
|
+
Best for: a single component whose render output has named regions you need to update or wire (buttons, labels, blocks). No child components — just one root element with marked descendants.
|
|
807
|
+
|
|
808
|
+
Add `ref="name"` to elements in the `html` string. After init, `self.refs.name` is an ObjsInstance for that element — use it in state methods for selector-free updates and events.
|
|
809
|
+
|
|
810
|
+
```js
|
|
811
|
+
let cardRef;
|
|
812
|
+
const cardStates = {
|
|
813
|
+
name: 'ProductCard',
|
|
814
|
+
render: ({ title, price }) => ({
|
|
815
|
+
tag: 'article',
|
|
816
|
+
className: 'card',
|
|
817
|
+
html: `<h3 ref="title">${title}</h3>
|
|
818
|
+
<p ref="price">$${price}</p>
|
|
819
|
+
<button ref="addBtn">Add to cart</button>`,
|
|
820
|
+
events: { click: (e) => { if (e.target.closest('button')) cardRef?.setAdded(); } },
|
|
821
|
+
}),
|
|
822
|
+
setAdded: ({ self }) => {
|
|
823
|
+
self.refs.addBtn.html('✓ Added').attr('disabled', '');
|
|
824
|
+
},
|
|
825
|
+
updatePrice: ({ self }, newPrice) => {
|
|
826
|
+
self.refs.price.html('$' + newPrice);
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
const card = o.init(cardStates).render({ title: 'Widget', price: 9.99 }).appendInside('#app');
|
|
831
|
+
cardRef = card;
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
**When to update:** call `self.refs.name.html(...)`, `.attr()`, `.on()`, etc. No selectors, no child components to mount.
|
|
830
835
|
|
|
831
|
-
### Pattern
|
|
836
|
+
### Pattern B — Slot pattern
|
|
832
837
|
|
|
833
838
|
Best for: containers with named regions (cards, dialogs, panels, toolbars).
|
|
834
839
|
|
|
@@ -885,7 +890,7 @@ title.setLabel('New Product Name'); // only touches card__header's <button>
|
|
|
885
890
|
price.setCount(39); // only touches card__body's <span>
|
|
886
891
|
```
|
|
887
892
|
|
|
888
|
-
### Pattern
|
|
893
|
+
### Pattern C — append in render
|
|
889
894
|
|
|
890
895
|
Best for: molecules assembled from known atoms at creation time. Children are immutable after render.
|
|
891
896
|
|
|
@@ -927,9 +932,11 @@ const searchBar = createSearchBar().appendInside('.toolbar');
|
|
|
927
932
|
document.addEventListener('search', (e) => console.log('query:', e.detail));
|
|
928
933
|
```
|
|
929
934
|
|
|
930
|
-
### Pattern
|
|
935
|
+
### Pattern D — factory function with lazy child creation
|
|
936
|
+
|
|
937
|
+
Best for: dynamic lists with **complex per-item components** (e.g. cards with slots), O(1) per-item updates, or when children change at runtime.
|
|
931
938
|
|
|
932
|
-
|
|
939
|
+
For **simple lists** (one element per item, e.g. `<li>` text), use native `render(array)` and `.appendInside(ul.el)` — see Pattern 5.
|
|
933
940
|
|
|
934
941
|
Children are created inside a state method (not in render). The parent stores all child references and exposes methods to add, update, or remove individual items.
|
|
935
942
|
|
|
@@ -1221,7 +1228,8 @@ const productCardStates = {
|
|
|
1221
1228
|
},
|
|
1222
1229
|
};
|
|
1223
1230
|
|
|
1224
|
-
// ── Product list (
|
|
1231
|
+
// ── Product list (complex items: each row is a card with button; store refs for O(1) updates)
|
|
1232
|
+
// For a simple list (e.g. just names), use render(products) and appendInside(ul.el) — see Pattern 5.
|
|
1225
1233
|
const productListStates = {
|
|
1226
1234
|
name: 'ProductList',
|
|
1227
1235
|
render: { tag: 'div', class: 'card-list' },
|
|
@@ -1230,11 +1238,7 @@ const productListStates = {
|
|
|
1230
1238
|
products.forEach((product) => {
|
|
1231
1239
|
const card = o.init(productCardStates).render(product);
|
|
1232
1240
|
card.appendInside(self.el);
|
|
1233
|
-
|
|
1234
|
-
card.first('.card__btn').on('click', () => {
|
|
1235
|
-
cartAdd(product);
|
|
1236
|
-
card.setAdded(); // only this card's button changes
|
|
1237
|
-
});
|
|
1241
|
+
card.first('.card__btn').on('click', () => { cartAdd(product); card.setAdded(); });
|
|
1238
1242
|
self.store.cards[product.id] = card;
|
|
1239
1243
|
});
|
|
1240
1244
|
},
|
|
@@ -1251,6 +1255,7 @@ Promo dialog triggered by `?promo=CODE` URL parameter. sessionStorage prevents r
|
|
|
1251
1255
|
```js
|
|
1252
1256
|
const PROMO_KEY = 'oTest-promo-shown';
|
|
1253
1257
|
|
|
1258
|
+
let dialogRef;
|
|
1254
1259
|
const dialogStates = {
|
|
1255
1260
|
name: 'PromoDialog',
|
|
1256
1261
|
render: {
|
|
@@ -1261,6 +1266,9 @@ const dialogStates = {
|
|
|
1261
1266
|
<p class="dialog__body"></p>
|
|
1262
1267
|
<a class="dialog__cta" href="#">Get offer</a>
|
|
1263
1268
|
</div>`,
|
|
1269
|
+
events: {
|
|
1270
|
+
click: (e) => { if (e.target.closest('.dialog__close') || e.target === dialogRef?.el) dialogRef?.close(); },
|
|
1271
|
+
},
|
|
1264
1272
|
},
|
|
1265
1273
|
open: ({ self }, { title, body, cta, ctaUrl }) => {
|
|
1266
1274
|
self.first('.dialog__title').html(title);
|
|
@@ -1273,8 +1281,7 @@ const dialogStates = {
|
|
|
1273
1281
|
};
|
|
1274
1282
|
|
|
1275
1283
|
const dialog = o.init(dialogStates).render().appendInside('body');
|
|
1276
|
-
|
|
1277
|
-
dialog.on('click', (e) => { if (e.target === dialog.el) dialog.close(); });
|
|
1284
|
+
dialogRef = dialog;
|
|
1278
1285
|
|
|
1279
1286
|
const promoCode = o.getParams('promo');
|
|
1280
1287
|
if (promoCode && !sessionStorage.getItem(PROMO_KEY)) {
|
|
@@ -1299,6 +1306,7 @@ const applyFilters = (filters, productsLoader) => {
|
|
|
1299
1306
|
productsLoader.reload(o.get('/api/products?' + params.toString()));
|
|
1300
1307
|
};
|
|
1301
1308
|
|
|
1309
|
+
let drawerRef;
|
|
1302
1310
|
const drawerStates = {
|
|
1303
1311
|
name: 'FilterDrawer',
|
|
1304
1312
|
render: {
|
|
@@ -1310,6 +1318,15 @@ const drawerStates = {
|
|
|
1310
1318
|
<input class="drawer__max" type="number" placeholder="Max $">
|
|
1311
1319
|
<button class="drawer__apply">Apply</button>
|
|
1312
1320
|
<button class="drawer__reset">Reset</button>`,
|
|
1321
|
+
events: {
|
|
1322
|
+
click: (e) => {
|
|
1323
|
+
const d = drawerRef;
|
|
1324
|
+
if (!d) return;
|
|
1325
|
+
if (e.target.closest('.drawer__close')) d.close();
|
|
1326
|
+
else if (e.target.closest('.drawer__apply')) { applyFilters(d.getValues(), productsLoader); d.close(); }
|
|
1327
|
+
else if (e.target.closest('.drawer__reset')) { applyFilters({ category: '', minPrice: '', maxPrice: '' }, productsLoader); d.restore({ category: '', minPrice: '', maxPrice: '' }); }
|
|
1328
|
+
},
|
|
1329
|
+
},
|
|
1313
1330
|
},
|
|
1314
1331
|
open: ({ self }) => { self.css({ transform: 'translateX(0)' }); },
|
|
1315
1332
|
close: ({ self }) => { self.css({ transform: 'translateX(-100%)' }); },
|
|
@@ -1326,19 +1343,9 @@ const drawerStates = {
|
|
|
1326
1343
|
};
|
|
1327
1344
|
|
|
1328
1345
|
const drawer = o.init(drawerStates).render().appendInside('body');
|
|
1346
|
+
drawerRef = drawer;
|
|
1329
1347
|
drawer.restore(getFilters());
|
|
1330
1348
|
|
|
1331
|
-
drawer.first('.drawer__close').on('click', () => drawer.close());
|
|
1332
|
-
drawer.first('.drawer__apply').on('click', () => {
|
|
1333
|
-
applyFilters(drawer.getValues(), productsLoader);
|
|
1334
|
-
drawer.close();
|
|
1335
|
-
});
|
|
1336
|
-
drawer.first('.drawer__reset').on('click', () => {
|
|
1337
|
-
const empty = { category: '', minPrice: '', maxPrice: '' };
|
|
1338
|
-
applyFilters(empty, productsLoader);
|
|
1339
|
-
drawer.restore(empty);
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
1349
|
o.first('#open-filters').on('click', () => drawer.open());
|
|
1343
1350
|
```
|
|
1344
1351
|
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Objs
|
|
2
|
-
> Fast and simple library to speed up developing by AI context friendly architecture, auto-tests recording, cache control and other. Develop new features without rewriting anything. Works standalone or alongside React. Examples and full documentation: [Full Documentation](https://en.fous.name/objs)
|
|
2
|
+
> Fast and simple library to speed up developing by AI context friendly architecture, auto-tests recording, cache control and other. Develop new features without rewriting anything. Works standalone or alongside React. Examples and full documentation: [Full Documentation](https://en.fous.name/objs/documentation)
|
|
3
3
|
|
|
4
4
|
**AI-friendly** — one file, `SKILL.md` primer (~6,000 tokens). An LLM generates correct Objs code from a description without JSX, virtual DOM, or React lifecycle knowledge.
|
|
5
5
|
|
|
6
6
|
**React-developer-friendly** — familiar `className`, `ref`/`refs`, `o.createStore`. Add one script tag to an existing React app and get Playwright test generation without touching any components.
|
|
7
7
|
|
|
8
|
-
**Live examples** — real patterns in [`examples/`](examples/
|
|
8
|
+
**Live examples** — real patterns in [`examples/`](https://foggysq.github.io/objs/examples/), narrative walkthroughs in [`EXAMPLES.md`](EXAMPLES.md).
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
- **Works standalone or with React** — Add one script tag to an existing React app; no architecture change. Familiar `className`, `ref`/`refs`, `o.createStore`. Built-in SSR (Node) with `DocumentMVP` and in-browser hydration.
|
|
22
22
|
- **AI-friendly** — One file, ~6,000-token `SKILL.md` primer. No JSX, virtual DOM, or React lifecycle; fewer tokens than typical React context for runnable output. No stale closures, dependency arrays, or re-render cascades. Same code runs in Node (SSR) so tools can verify output without a browser — **verify generated code without user review**: run `o.init(states).render()` in Node, serialize or assert structure before returning to the user.
|
|
23
23
|
|
|
24
|
-
→ [Full comparison and live demo](examples/ai-workflow/index.html)
|
|
24
|
+
→ [Full comparison and live demo](https://foggysq.github.io/objs/examples/ai-workflow/index.html)
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
@@ -174,12 +174,12 @@ timer.stop();
|
|
|
174
174
|
`o.errors` – an array of all hidden errors, can be logged by `o.logErrors()` for debug
|
|
175
175
|
`o.onError` – a function than will be called with an error as an argument
|
|
176
176
|
|
|
177
|
-
> This and some more complex live examples are in the [full documentation](https://fous.name/objs). There are lots of useful methods and settings.
|
|
177
|
+
> This and some more complex live examples are in the [full documentation](https://fous.name/objs/documentation). There are lots of useful methods and settings.
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
### Tests - unit tests, e2e, recording etc.
|
|
181
181
|
|
|
182
|
-
Testing is a first-class part of Objs: use `o.test()` and `o.addTest()` for sync and async unit tests, including tests with page reload and autorun. Record user sessions with `o.startRecording()` / `o.stopRecording()`, then export to Objs-style tests (`o.exportTest()`) or Playwright (`.spec.ts`) with `o.exportPlaywrightTest()` for e2e in CI. Replay with `o.playRecording()` (all builds); call `o.testOverlay()` to show a results panel so assessors can see if all auto tests passed and which manual checks failed. Use `o.testConfirm()` for manual checks (e.g. hover effects). Dev-only: `o.assertSize()` / `o.assertVisible()` for layout assertions. See the [recording example](examples/recording/index.html) and the full documentation for details.
|
|
182
|
+
Testing is a first-class part of Objs: use `o.test()` and `o.addTest()` for sync and async unit tests, including tests with page reload and autorun. Record user sessions with `o.startRecording()` / `o.stopRecording()`, then export to Objs-style tests (`o.exportTest()`) or Playwright (`.spec.ts`) with `o.exportPlaywrightTest()` for e2e in CI. Replay with `o.playRecording()` (all builds); call `o.testOverlay()` to show a results panel so assessors can see if all auto tests passed and which manual checks failed. Use `o.testConfirm()` for manual checks (e.g. hover effects). Dev-only: `o.assertSize()` / `o.assertVisible()` for layout assertions. See the [recording example](https://foggysq.github.io/objs/examples/recording/index.html) and the full documentation for details.
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
|
|
@@ -435,7 +435,7 @@ Use for design system or UI verification tests (e.g. button height 24px, contain
|
|
|
435
435
|
|
|
436
436
|
`o.testOverlay()` – Renders a fixed overlay button (🧪 Tests). Click to see pass/fail results for all test runs (auto steps and manual checks). For assessors: after replay, open the overlay to see if all auto tests passed and which manual checks failed. Available in all builds.
|
|
437
437
|
|
|
438
|
-
`o.testConfirm(label, items?, opts?)` – Shows a draggable overlay titled "Label: Paused" with an optional checklist; returns `Promise<{ ok: boolean, errors?: string[] }>`. Use after replay for manual checks (e.g. hover effects). Available in all builds. See the [recording example](examples/recording/index.html) for a live demo.
|
|
438
|
+
`o.testConfirm(label, items?, opts?)` – Shows a draggable overlay titled "Label: Paused" with an optional checklist; returns `Promise<{ ok: boolean, errors?: string[] }>`. Use after replay for manual checks (e.g. hover effects). Available in all builds. See the [recording example](https://foggysq.github.io/objs/examples/recording/index.html) for a live demo.
|
|
439
439
|
|
|
440
440
|
### SSR and Node
|
|
441
441
|
In Node, **o.D** is **o.DocumentMVP** (no real DOM); **o.init().render()** builds a virtual tree and you can serialize with the same code path that produces HTML for SSR. See full docs for getSSR and hydration.
|
package/objs.built.js
CHANGED
|
@@ -20,6 +20,7 @@ const o = (query) => {
|
|
|
20
20
|
isRoot: false,
|
|
21
21
|
_parent: null
|
|
22
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;
|
|
23
24
|
const type = (obj) => typeof obj;
|
|
24
25
|
const cycleObj = (obj, func) => {
|
|
25
26
|
for (const item in obj) if (Object.hasOwn(obj, item)) func(item, obj);
|
|
@@ -190,7 +191,8 @@ const o = (query) => {
|
|
|
190
191
|
result[state] = returner((props = [{}]) => {
|
|
191
192
|
result.currentState = state;
|
|
192
193
|
const data = states[state] || { tag: "div" };
|
|
193
|
-
const
|
|
194
|
+
const slice = Array.isArray(result.els) ? result.els.slice(finish, start + ONE) : [];
|
|
195
|
+
const els = slice.length ? slice : result.els || [];
|
|
194
196
|
if (type(data) === objectType) {
|
|
195
197
|
data.state = state;
|
|
196
198
|
data["data-o-init"] = initN;
|
|
@@ -290,7 +292,7 @@ const o = (query) => {
|
|
|
290
292
|
[state, [notEmptyStringType]],
|
|
291
293
|
[fail, [stringType, undefinedType]]
|
|
292
294
|
]);
|
|
293
|
-
loader.connect(
|
|
295
|
+
loader.connect(self, state, fail);
|
|
294
296
|
}, "connect");
|
|
295
297
|
result.getSSR = returner((initId) => {
|
|
296
298
|
typeVerify([[initId, [numberType, undefinedType]]]);
|
|
@@ -964,6 +966,7 @@ o.createStore = (defaults) => {
|
|
|
964
966
|
};
|
|
965
967
|
return store;
|
|
966
968
|
};
|
|
969
|
+
o.U = void 0;
|
|
967
970
|
o.W = 2;
|
|
968
971
|
o.H = 100;
|
|
969
972
|
o.F = false;
|
|
@@ -1316,7 +1319,7 @@ o.newLoader = (promise) => {
|
|
|
1316
1319
|
if (finished) {
|
|
1317
1320
|
if (error) {
|
|
1318
1321
|
fail ? listener[fail]() : "";
|
|
1319
|
-
} else {
|
|
1322
|
+
} else if (typeof listener[state] === "function") {
|
|
1320
1323
|
listener[state](data);
|
|
1321
1324
|
}
|
|
1322
1325
|
} else {
|
|
@@ -1699,9 +1702,8 @@ o.test = (title = "", ...tests) => {
|
|
|
1699
1702
|
sessionStorage.getItem(`oTest-Status-${testN2}`) || "[]"
|
|
1700
1703
|
);
|
|
1701
1704
|
for (let i = 0; i < o.tStatus[testN2].length; i++) {
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
}
|
|
1705
|
+
const s = o.tStatus[testN2][i];
|
|
1706
|
+
if (s === true || s === false) done++;
|
|
1705
1707
|
}
|
|
1706
1708
|
}
|
|
1707
1709
|
if (o.tStyled) {
|
|
@@ -1839,7 +1841,7 @@ o.testUpdate = (info, res = o.F, suff = "") => {
|
|
|
1839
1841
|
}
|
|
1840
1842
|
}
|
|
1841
1843
|
};
|
|
1842
|
-
if (o.tStatus[testN2][info.i] === o.U) {
|
|
1844
|
+
if (o.tStatus[testN2][info.i] === o.U || o.tStatus[testN2][info.i] === null) {
|
|
1843
1845
|
o.tStatus[testN2][info.i] = res === true;
|
|
1844
1846
|
if (res === true) {
|
|
1845
1847
|
if (info.tShowOk) {
|
|
@@ -1863,7 +1865,7 @@ o.testUpdate = (info, res = o.F, suff = "") => {
|
|
|
1863
1865
|
}
|
|
1864
1866
|
let fails = 0, n = 0;
|
|
1865
1867
|
for (const s of o.tStatus[testN2]) {
|
|
1866
|
-
if (s === o.U) {
|
|
1868
|
+
if (s === o.U || s === null) {
|
|
1867
1869
|
return;
|
|
1868
1870
|
}
|
|
1869
1871
|
if (!s) {
|
package/objs.built.min.js
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
* @version 2.0
|
|
4
4
|
* @author Roman Torshin
|
|
5
5
|
* @license Apache-2.0
|
|
6
|
-
*/const X=!0,t=n=>{let e={els:[],ie:{},delegated:{},parented:{},store:{},refs:{},states:[],isDebug:!1,currentState:"",savedStates:{},isRoot:!1,_parent:null},o=1,r=2,c=3,w="boolean",a="object",b="function",S="string",N="number",l="notEmptyString",E="undefined",m="dangerouslySetInnerHTML",k,f=t.D,v=-1,u=0,h=0,x=typeof process<"u"||t.D===t.DocumentMVP,y=0,D=0;const C=s=>typeof s,R=(s,i)=>{for(const g in s)Object.hasOwn(s,g)&&i(g,s)},j=t.onError,L=s=>t.verify(s),T=(s,i="")=>(...g)=>{(t.debug||e.isDebug)&&console.log(i?`${i}()`:s,g.length?"with "+g.join(", "):"without parameters");try{const p=s(g[0],g[o],g[r],g[c]);return p!==k?p:e}catch(p){j(p,i)}},O=s=>{for(y=u;y<=v;y++)s()},A=s=>s?.els?s.el:(C(s)!==a&&(s=t.first(s).el),s),_=(s=!0,i=e.els)=>{const g=i.length;e.length=g,v=g-o,u=0,e.el=g?i[0]:k,e.last=g?i[v]:k,s&&(R(e.states,(p,d)=>{delete e[d[p]]}),e.states=[],e.ie={})};e.reset=t;const H=(s,i,g)=>{R(i,p=>{let d=i[p];C(d)===b&&(d=d(g)),p==="append"&&C(d)===a&&(d.els&&(d=[d]),d[0]?.els&&(valueBuff=[],R(d,$=>{valueBuff.push(...d[$].els)}),d=valueBuff)),d!==k&&s.getAttribute(p)!==d&&!["tag","tagName","name","sample","state","events","ssr","nodeName","revertChildren","root","ref"].includes(p)&&(["html","innerHTML"].includes(p)?s.innerHTML=d:p==="className"?s.setAttribute("class",d):p==="dataset"&&C(d)===a?R(d,$=>{s.dataset[$]=d[$]}):p==="toggleClass"?s.classList.toggle(d):p==="addClass"?C(d)===a?s.classList.add(...d):s.classList.add(d):p==="removeClass"?s.classList.remove(d):p==="style"&&C(d)===a?R(d,$=>{s.style[$]=d[$]}):(p==="append"||p==="children"||p==="childNodes")&&C(d)===a?R(d.length?d:[d],$=>{p==="append"||!s.childNodes[$]?s.appendChild(d[$]):s.childNodes[$]!==d[$]&&s.childNodes[$].replaceWith(d[$])}):s.setAttribute(p,d))}),s.dataset.oState=i.state,t.autotag&&i.name&&(s.dataset[t.autotag]=t.camelToKebab(i.name))};e.debug=T(()=>{e.isDebug=!0},"debug ON"),e.saveAs=T(s=>{L([[s,[l]]]),t.getSaved[s]?(t.debug||e.isDebug)&&console.warn("the key exists (not saved):"+s):t.getSaved[s]=e},"saveAs"),e.unmount=()=>((t.debug||e.isDebug)&&console.log("unmount() for initID:"+e.initID),C(e.remove)===b?e.remove():e.els?.length&&e.els.forEach(s=>{s?.parentNode&&s.parentNode.removeChild(s)}),t.inits[e.initID]=void 0,e={},!0),e.init=T(s=>{L([[s,[a,b]]]);const i=e.initID||t.inits.length||0;if(e.initID=i,_(),t.inits[e.initID]=e,C(s)==="function"){e.render=T(p=>{const d=f.createElement("div");setTimeout(()=>{t.reactRender(s,d,p)}),e.add(d)});return}(C(s)!==a||s.render===k)&&(s={render:s}),R(s,p=>{e?.render&&p==="render"||(e.states.push(p),e[p]=T((d=[{}])=>{e.currentState=p;const $=s[p]||{tag:"div"},I=e.els.slice(u,v+o);C($)===a&&($.state=p,$["data-o-init"]=i);const U=(M,B={})=>{if(C($)===a)return f.createElement($.tag||$.tagName||"div");{const F=f.createElement("div");return F.innerHTML=C($)===b?$(B):$,F.children.length>o||!F.firstElementChild?(F.dataset.oInit=M,F):(F.firstElementChild.dataset.oInit=M,F.firstElementChild)}},P=d;d.length||(d=[d]);const q=!I[0]&&p==="render";d=d.map((M,B)=>{const F=Object.assign({},C(M)===a?M:{},{self:e,o:t,i:M.i===k?B:M.i,parent:e._parent,data:Array.isArray(P)?P[B]:P});return q&&(!$.ssr||x)&&I.push(U(i,F)),F}),q&&(e.els=I,_(!1));const z=()=>{R($.events,M=>{e.on(M,$.events[M])})};I&&(D=I.length===d.length,I.map((M,B)=>{d[D?B:0].i=B+u;const F=C($)===b?$(d[D?B:0]):$;C(F)===a&&(F.state=p,q&&(F["data-o-init"]=i,F["data-o-init-i"]=B),H(M,F,d[D?B:0]))}),q&&(e.refs={},e.els.forEach(M=>{M.querySelectorAll&&M.querySelectorAll("[ref]").forEach(B=>{e.refs[B.getAttribute("ref")]=t(B),B.removeAttribute("ref")})}))),q&&C($)===a&&$.events&&!x&&!$.ssr&&z()}))});const g=s.render||s;!x&&C(g)===a&&g.events&&g.ssr&&(e.initSSRAfterGettingSSR=()=>{e.refs={},e.els.forEach(p=>{p.querySelectorAll&&p.querySelectorAll("[ref]").forEach(d=>{e.refs[d.getAttribute("ref")]=t(d),d.removeAttribute("ref")})}),R(g.events,p=>{e.on(p,g.events[p])})})},"init"),e.connect=T((s,i="render",g)=>{L([[s,[a]],[i,[l]],[g,[S,E]]]),s.connect(e,i,g)},"connect"),e.getSSR=T(s=>{L([[s,[N,E]]]);const i=s!==void 0?s:e.initID;if(x||C(s)===E&&C(e.initID)===E)return;const g=t.D.querySelectorAll(`[data-o-init="${i}"]`);g.length&&!e.els.length&&(e.els=Array.from(g),e.initID=s,t.inits[s]=e,_(!1),C(e.initSSRAfterGettingSSR)===b&&(e.initSSRAfterGettingSSR(),delete e.initSSRAfterGettingSSR))},"getSSR"),e.initState=T((s,i)=>{L([[s,[a]],[i,[a,E]]]),e.init(s).render(i)},"initState");const J=(s,i,g)=>{const p=s.attributes,d={tagName:s.tagName.toLowerCase()};for(const $ of p)d[$.nodeName]=$.value;if(g){d.innerHTML=s.innerHTML,d.revertChildren=[];const $=s.querySelectorAll("[data-o-init]");for(const I of $){const U=I.getAttribute("data-o-init");d.revertChildren.push(U),t.inits[U]?.saveState(i,!1)}}return d};return e.saveState=T((s,i=!0)=>{if(L([[s,[l,E]],[i,[w]]]),!e.el)throw Error("saveState(): There are no elements to save");const g=s||"fastSavedState",p={els:[],parentNode:e.el.parentNode,root:i};O(()=>{p.els.push(J(e.els[y],g,i))}),p.ie=Object.assign({},e.ie),p.delegated=Object.assign({},e.delegated),p.store=Object.assign({},e.store),e.isRoot=e.isRoot||i,e.savedStates[g]=p},"saveState"),e.revertState=T(s=>{L([[s,[l,E]]]);const i=s||"fastSavedState";if(!e.savedStates[i])throw Error(`revertState(): The state "${i}" should have been saved by saveState()`);const g=e.savedStates[i];e.offAll(),e.offDelegate(),e.store=Object.assign({},g.store),g.els.forEach((p,d)=>{if(!e.els[d]){const $=t.D.createElement(p.tagName);g.parentNode&&(d?e.els[d-1].after($):g.parentNode.append($)),e.add($)}H(e.els[d],p)}),e.delegated=Object.assign({},g.delegated),e.ie=Object.assign({},g.ie),e.onAll(),R(g.delegated,p=>{g.delegated[p].forEach(d=>{O(()=>{e.els[y].addEventListener(p,d)})})}),e.currentState=i,g.root&&g.els.forEach(({rootElement:p})=>{p.revertChildren.forEach(d=>{t.inits[d]?.revertState(i),t('[data-o-init="'+d+'"]').els.forEach(($,I)=>{$.replaceWith(t.inits[d]?.els[I])})})})},"revertState"),e.loseState=T(s=>{L([[s,[l]]]),e.savedStates[s]&&(delete e.savedStates[s],O(()=>{const i=e.els[y].querySelectorAll("[data-o-init]");for(const g of i){const p=g.getAttribute("data-o-init");t.inits[p]?.loseState(s)}}))},"sample"),e.sample=T((s="render")=>(L([[s,[l]]]),{[s]:J(e.els[u])}),"sample"),e.select=T(s=>{L([[s,[N,E]]]),s===k&&(s=e.length-o),v=s,u=s,e.el=e.els[s],h=o},"select"),e.all=T(()=>{v=e.length-o,u=0,e.el=e.els[0],h=0},"all"),e.remove=T(s=>{if(L([[s,[N,E]]]),s===k&&h&&(s=u),s!==k){const i=e.els[s];i?.parentNode?i.parentNode.removeChild(i):i===void 0&&s>=e.els.length&&t.onError&&t.onError("remove("+s+"): index out of bounds","remove")}else O(()=>{const i=e.els[y];i?.parentNode&&i.parentNode.removeChild(i)});_(!1)},"remove"),e.skip=T(s=>{L([[s,[N,E]]]),s===k&&(s=u),e.els.splice(y,o),_()},"skip"),e.add=T(s=>{L([[s,[S,a,N]]]),e.initID===k&&(C(s)==="string"&&s!==""?e.els.push(...Array.from(f.querySelectorAll(s))):C(s)===a?s.tagName?e.els.push(s):s.els?e.els.push(...s.els):s.length&&s[0].tagName&&e.els.push(...s):C(s)==="number"&&t.inits[s]&&(e=t.inits[s]),_(!1),e.initID!==k&&e.dataset({oInit:e.initID}))},"add"),e.appendInside=T(s=>{L([[s,[a,l]]]),s?.els&&(e._parent=s),O(()=>{A(s).appendChild(e.els[y])})},"appendInside"),e.appendBefore=T(s=>{L([[s,[a,l]]]),O(()=>{A(s).parentNode.insertBefore(e.els[y],A(s))})},"appendBefore"),e.appendAfter=T(s=>{L([[s,[a,l]]]),O(()=>{A(s).after(...e.els)})},"appendAfter"),e.find=T((s="")=>{L([[s,S]]);const i=[];return O(()=>{i.push(...Array.from(e.els[y].querySelectorAll(":scope "+s)))}),t(i)},"find"),e.first=T((s="")=>{L([[s,S]]);let i=k;const g=[];return O(()=>{i=e.els[y].querySelector(s),i&&g.push(i)}),t(g)},"first"),e.attr=T((s,i)=>{if(i!==null&&L([[s,S],[i,[S,E]]]),i===k){const g=[];return O(()=>{g[y]=e.els[y].getAttribute(s)}),h?g[0]:g}else O(i!==null?()=>{e.els[y].setAttribute(s,i)}:()=>{e.els[y].removeAttribute(s)})},"attr"),e.attrs=T(()=>{const s=[];return O(()=>{const i={};[...e.els[y].attributes].forEach(g=>{i[g.nodeName]=g.nodeValue}),s.push(i)}),h?s[0]:s},"attrs"),e.dataset=T(s=>{if(L([[s,[a,E]]]),typeof s===a)O(()=>{R(s,i=>{e.els[y].dataset[i]=s[i]})});else{const i=[];return O(()=>{i.push({...e.els[y].dataset})}),h?i[0]:i}},"dataset"),e.style=T(s=>{s!==null&&L([[s,[S,E]]]),e.attr("style",s)},"style"),e.css=T((s={})=>{if(s===null){e.style(null);return}L([[s,a]]);let i="";R(s,g=>{i+=g+":"+s[g].replace('"',"'")+";"}),e.style(i||null)},"css"),e.setClass=T(s=>{L([[s,S]]),O(()=>{e.els[y].setAttribute("class",s)})},"setClass"),e.addClass=T((...s)=>{O(()=>{e.els[y].classList.add(...s)})},"addClass"),e.removeClass=T((...s)=>{O(()=>{e.els[y].classList.remove(...s)})},"removeClass"),e.toggleClass=T((s,i)=>{L([[s,l],[i,[w,E]]]),O(()=>{e.els[y].classList.toggle(s,i)})},"toggleClass"),e.haveClass=s=>{L([[s,l]]);let i=!0;return O(()=>{e.els[y].classList.contains(s)||(i=!1)}),(e.isDebug||t.debug)&&console.log("haveClass() with",s),i},e.innerHTML=T(s=>{if(L([[s,[S,E]]]),s!==k)O(()=>{e.els[y].innerHTML=s});else{let i="";return O(()=>{i+=x&&e.els[y].innerHTML.length===0?t.D.parseElement(e.els[y],!1):e.els[y].innerHTML}),i}},"innerHTML"),e.innerText=T(s=>{L([[s,[S]]]),O(()=>{e.els[y].innerText=s})},"innerText"),e.textContent=T(s=>{L([[s,[S]]]),O(()=>{e.els[y].textContent=s})},"textContent"),e.html=T(s=>{if(L([[s,[S,E]]]),s!==void 0)e.innerHTML(s);else{let i="";return O(()=>{i+=x?e.els[y].outerHTML():e.els[y].outerHTML}),i}},"html"),e.val=T(s=>{if(s===void 0)return e.el?.value;O(()=>{e.els[y].value=s})},"val"),e.forEach=T(s=>{L([[s,[b]]]),O(()=>{s({self:e,i:y,o:t,el:e.els[y]})})},"forEach"),e.prepareFor=T((s,i)=>{L([[s,[a,b,E]],[i,[b,E]]]);const g=s&&C(s)===a&&s.createElement;if(!g&&C(s)!==b)throw Error("prepareFor(): pass React (full object) or React.createElement as first argument");const p=g?s.createElement:s,d=g?s.useEffect:void 0;return $=>{if($.ref===k)throw Error("No ref property to convert Objs to React");const I=Object.assign({},$),U=p("div",{ref:$.ref});return delete I.ref,d(()=>{R(I,P=>{if(P.substring(0,1)==="on"){const q=t.camelToKebab(P).split("-")[1];e.on(q,I[P]),delete I[P]}}),e.render(I),e.appendInside(U.ref.current)},[]),U}},"prepareFor"),e.on=T((s,i,g,p)=>{L([[s,[l]],[i,[b]],[g,[a,E]],[p,[w,E]]]),s.split(", ").forEach(d=>{O(()=>{e.els[y].addEventListener(d,i,g,p)}),e.ie[d]||(e.ie[d]=[]),e.ie[d].push([i,g,p])})},"on"),e.off=T((s,i,g)=>{L([[s,[l]],[i,[b]],[g,[a,E]]]),s.split(", ").forEach(p=>{O(()=>{e.els[y].removeEventListener(p,i,g)}),e.ie[p]&&(e.ie[p]=e.ie[p].filter(d=>d[0]!==i))})},"off"),e.onDelegate=T((s,i,g)=>{L([[s,[l]],[i,[l]],[g,[b]]]),s.split(", ").forEach(p=>{const d=$=>{const I=$.target.closest(i);I&&($.delegate=I,$.objs=e,g($))};O(()=>{e.els[y].addEventListener(p,d)}),e.delegated[p]||(e.delegated[p]=[]),e.delegated[p].push(d)})},"onDelegate"),e.offDelegate=T(s=>{L([[s,[l]]]),R(e.delegated,i=>{if(!s||s===i)for(;e.delegated[i].length;){const g=e.delegated[i].pop();O(()=>{e.els[y].removeEventListener(i,g)})}}),e.delegated={}},"offDelegate"),e.onParent=T((s,i,g)=>{L([[s,[l]],[i,[l,a]],[g,[b]]]);const p=C(i)===a?i:t.D.querySelector(i);s.split(", ").forEach(d=>{const $=I=>{I.objs=e,g(I)};p.addEventListener(d,$),e.parented[d]||(e.parented[d]=[]),e.parented[d].push($)})},"onParent"),e.offParent=T((s,i)=>{L([[s,[l]],[i,[l,a]]]);const g=C(i)===a?i:t.D.querySelector(i);R(e.parented,p=>{(!s||s===p)&&(e.parented[p].forEach(d=>{g.removeEventListener(p,d)}),delete e.parented[p])})},"offParent"),e.onAll=T((s,i)=>{L([[s,[l,E]],[i,[w,E]]]),R(e.ie,(g,p)=>{(!s||s===g)&&p[g].forEach(d=>{O(()=>{i?e.els[y].removeEventListener(g,d[0]):e.els[y].addEventListener(g,d[0],d[o],d[r])})})})},"onAll"),e.offAll=T(s=>{L([[s,[l]]]),e.onAll(s,o)},"offAll"),n&&e.add(n),e.take=s=>{if(L([[s,[S,a,N]]]),e.add(s),e.el){const i=e.el.dataset.oInit;if(i!==k&&t.inits[i])return e.length===o?(D=e.els[0],Object.assign(e,t.inits[i]),e.els=[D]):e=t.inits[i],_(!1,e.els),e}},e};t.first=n=>(t.verify([[n,["notEmptyString"]]]),t.debug&&console.log(n," -> ","o.first()"),t(t.D.querySelector(n)).select(0)),t.inits=[],t.getSaved={},t.errors=[],t.showErrors=!1,t.logErrors=()=>{t.errors.length?t.errors.forEach(n=>console.error(n)):console.log("No errors")},t.onError=(n,e)=>{t.showErrors?console.error(n,e):(t.errors.push(n),e&&t.errors.push(e))},t.reactRender=()=>new Error("React render function is not defined"),t.autotag=void 0,t.reactQA=n=>({["data-"+(t.autotag||"qa")]:n.replace(/([A-Z])/g,(e,o)=>"-"+o.toLowerCase()).replace(/^-/,"")}),t.specialTypes={notEmptyString:(n,e)=>e==="string"&&n.length,array:n=>Array.isArray(n),promise:n=>n instanceof Promise||!!(n&&typeof n.then=="function")},t.verify=(n,e=!1)=>{for(const o of n){const r=typeof o[0];let c=Array.isArray(o[1])?o[1]:[o[1]],w=!1;if(c.includes(r))return!0;c=c.filter(a=>!!t.specialTypes[a]);for(const a of c)if(w=t.specialTypes[a](o[0],r),w)return!0}return e?!1:new Error("Type verification failed")},t.safeVerify=n=>t.verify(n,!0),t.init=(n,e)=>t().init(n,e),t.initState=(n,e)=>t().init(n).render(e),t.take=n=>t().take(n),t.getStates=()=>t.inits.reduce((n,e)=>(n.push(e?.states),n),[]),t.getStores=()=>t.inits.reduce((n,e)=>(n.push(e?.store),n),[]),t.getListeners=()=>t.inits.reduce((n,e)=>(n.push(e?.ie),n),[]),t.createStore=n=>{const e=Object.assign({},n);return e._defaults=Object.assign({},n),e._listeners=[],e.subscribe=function(o,r){return this._listeners.push(c=>o[r]?.(c)),this},e.notify=function(){this._listeners.forEach(o=>o(this))},e.reset=function(){const o={_listeners:1,subscribe:1,notify:1,_defaults:1,reset:1};for(const r of Object.keys(this._defaults))o[r]||(this[r]=this._defaults[r])},e},t.W=2,t.H=100,t.F=!1,t.C=(n,e)=>Object.hasOwn(n,e),t.kebabToCamel=n=>n.replace(/-./g,e=>e.toUpperCase()[1]),t.camelToKebab=n=>n.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),t.route=(n,e)=>{t.verify([[n,["notEmptyString","function","boolean"]],[e,["function","object"]]]);const o=typeof n=="function"?n(window.location.pathname):n;return o===!0||window.location.pathname===o?e?typeof e=="function"?(e(),!0):e:t:typeof e=="function"?!1:{}},t.router=(n={})=>{t.verify([[n,["object"]]]);for(const e in n)if(t.C(n,e)&&window.location.pathname===e)return typeof n[e]=="function"?(n[e](),!0):n[e];return!1},t.DocumentMVP={addEventListener:()=>{},parseElement:(n,e=!0)=>{t.verify([[n,["object","string"]],[e,["boolean"]]]);const o=(r,c="")=>{let w="";for(const a in r)t.C(r,a)&&(w+=` ${c}${t.camelToKebab(a)}="${typeof r[a]!="object"?r[a]:Object.entries(r[a]).map(b=>`${b[0]}: ${b[1]};`).join(" ")}"`);return w};if(typeof n=="string")return n;if(e){const r=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"],c=n.tagName.toLowerCase(),w=n.attributes["data-o-init"],a=w!==void 0?` data-o-init="${w}"`:"";return`<${c}${n.className?` class="${n.className}"`:""}${o(n.attributes)}${a}${o(n.dataset,"data-")}${r.includes(c)?"/":""}>${r.includes(c)?"":n.innerHTML.length?n.innerHTML:n.children.map(b=>t.D.parseElement(b)).join("")}${r.includes(c)?"":`</${c}>`}`}return n.innerHTML.length?n.innerHTML:n.children.map(r=>t.D.parseElement(r)).join("")},createElement:n=>{t.verify([[n,["notEmptyString"]]]);const e={tagName:n.toUpperCase(),attributes:{},innerHTML:"",children:[],dataset:{},className:"",classArray:[],style:{},addEventListener:()=>{},removeEventListener:()=>{}};return e.classList={add:(...o)=>{t.verify([[o,["array"]]]),e.classArray.push(o),e.className=e.classArray.join(" ")},has:o=>(t.verify([[o,["notEmptyString"]]]),e.classArray.includes(o)),remove:o=>{t.verify([[o,["notEmptyString"]]]),e.classArray=e.classArray.filter(r=>o!==r),e.className=e.classArray.join(" ")}},e.classList.toggle=o=>{t.verify([[o,["notEmptyString"]]]),e.classList.has(o)?e.classList.remove(o):e.classList.add(o)},e.setAttribute=(o,r)=>{t.verify([[o,["notEmptyString"]],[r,["string","number","boolean","undefined"]]]),e.attributes[o]=r},e.getAttribute=o=>(t.verify([[o,["notEmptyString"]]]),e.attributes[o]),e.removeAttribute=o=>{t.verify([[o,["notEmptyString"]]]),delete e.attributes[o]},e.appendChild=o=>{t.verify([[o,["object"]]]),e.children.push(o),e.firstElementChild=e.children[0]},e.outerHTML=()=>t.D.parseElement(e),e}},t.D=typeof document<"u"&&typeof process>"u"?document:t.DocumentMVP,t.setCookie=(n,e="",o={})=>{if(t.verify([[n,["notEmptyString"]],[e,["string","number","boolean"]],[o,["object"]]]),t.D.cookie===void 0){console.log("Cookies are not supported on server side");return}let r=encodeURIComponent(n)+"="+encodeURIComponent(e);o={path:"/",...o},o.expires instanceof Date?o.expires=o.expires.toUTCString():typeof o.expires=="number"&&(o.expires=new Date(o.expires).toUTCString());for(const c in o){r+="; "+c;const w=o[c];w!==!0&&(r+="="+w)}t.D.cookie=r},t.getCookie=n=>{if(t.verify([[n,["notEmptyString"]]]),t.D.cookie===void 0){console.log("Cookies are not supported on server side");return}const e=t.D.cookie.match(RegExp("(?:^|; )"+n.replace(/([.$?*|{}()[\]\\/+^])/g,"\\$1")+"=([^;]*)"));return e?decodeURIComponent(e[1]):void 0},t.deleteCookie=n=>{t.verify([[n,["notEmptyString"]]]),t.setCookie(n,"",{"max-age":0})},t.clearCookies=()=>{if(t.D.cookie===void 0){console.log("Cookies are not supported on server side");return}const n=t.D.cookie.split(";");for(;n.length;){let e=n.pop();for(;e.charAt(0)===" ";)e=e.substring(1);const o=e.split("=")[0];t.deleteCookie(o)}},t.clearLocalStorage=n=>{if(t.verify([[n,["boolean","undefined"]]]),!(typeof localStorage>"u"))if(n)localStorage.clear();else for(let e=localStorage.length-1;e>=0;e--){const o=localStorage.key(e);o.indexOf("oInc-")===-1&&o.indexOf("oTest-")===-1&&localStorage.removeItem(o)}},t.clearSessionStorage=n=>{if(t.verify([[n,["boolean","undefined"]]]),!(typeof sessionStorage>"u"))if(!n)sessionStorage.clear();else for(let e=sessionStorage.length-1;e>=0;e--){const o=sessionStorage.key(e);o&&o.indexOf("oTest-")===0&&sessionStorage.removeItem(o)}},t.clearTestsStorage=()=>{t.clearSessionStorage(1)},t.clearAfterTests=()=>{t.clearCookies(),t.clearLocalStorage(!1),t.clearTestsStorage()},t.ajax=(n,e={})=>{t.verify([[n,["notEmptyString"]],[e,["object"]]]);const o=new URLSearchParams;if(e.data&&typeof e.data=="object"){for(const r in e.data)t.C(e.data,r)&&(typeof e.data[r]=="object"?o.set(r,encodeURIComponent(JSON.stringify(e.data[r]))):o.set(r,e.data[r]));e.method.toLowerCase()==="get"?n+="?"+o.toString():e.body||(e.body=o),delete e.data}return e.headers||(e.headers={"X-Requested-With":"XMLHttpRequest"}),fetch(n,e)},t.get=(n,e={})=>(t.verify([[n,["notEmptyString"]],[e,["object"]]]),t.ajax(n,{...e,method:"GET"})),t.post=(n,e={})=>(t.verify([[n,["notEmptyString"]],[e,["object"]]]),t.ajax(n,{...e,method:"POST"})),t.newLoader=n=>{t.verify([[n,["promise","undefined"]]]);let e=[],o=null,r=!1,c=!1;const w=a=>{r=!1,c=!1,o=null,setTimeout(()=>{a.then(b=>{r=!0,!b.ok&&typeof b.ok<"u"?(c=!0,e.forEach(([S,N,l])=>{l&&S[l](b)})):typeof b.json=="function"?b.json().then(S=>{o=S,e.forEach(([N,l])=>{N[l](o)})}):(o=b,e.forEach(([S,N])=>{S[N](o)}))}).catch(b=>{c=!0,e.forEach(([S,N,l])=>{l&&S[l](b)})})},33)};return n&&w(n),{reload:w,isObjsLoader:!0,listeners:e,isFinished:()=>r,getStore:()=>o,connect:(a,b="render",S)=>{t.verify([[a,["object"]],[b,["notEmptyString"]],[S,["string","undefined"]]]),r?c?S&&a[S]():a[b](o):e.push([a,b,S])},disconnect:a=>{t.verify([[a,["object"]]]),e=e.filter(([b])=>b!==a)}}},t.getParams=n=>{t.verify([[n,["string","undefined"]]]);const e={},o=new URLSearchParams(window.location.search).entries();for(const r of o)e[r[0]]=r[1];return n?e[n]:e},t.incCache=!0,t.incCacheExp=1e3*60*60*24,t.incTimeout=6e3,t.incSource="",t.incForce=t.F,t.incAsync=!0,t.incCors=t.F,t.incSeparator="?",t.incFns={},t.incSet=[0],t.incReady=[0],t.incN=0,t.incGetHash=n=>n.split(t.incSeparator)[1]||"",t.incCheck=(n=0,e,o=0)=>(t.verify([[n,["number"]],[e,["number","undefined"]],[o,["number"]]]),!o&&n&&e===t.U&&t.incReady[n]?t.incSet[n]===1:t.incReady[n]===t.U||t.incReady[n][e]===t.U?t.F:(t.incReady[n][e].loaded=o,t.incFns[t.incReady[n][e].name]=o,t.incReady[n][0]+=o,n&&t.incReady[n].length===t.incReady[n][0]&&(typeof t.incSet[n]=="function"&&t.incSet[n](n),t.incSet[n]=1),t.incSet[n]===1)),t.incCacheClear=(n=t.F)=>{t.verify([[n,["boolean"]]]);for(const e in t.incFns)t.C(t.incFns,e)&&(localStorage.removeItem("oInc-"+e),localStorage.removeItem("oInc-"+e+"-expires"));return n&&(t.incReady.forEach((e,o)=>{o&&e.forEach((r,c)=>{c&&t("#oInc-"+o+"-"+c).remove()})}),t.incN=0,t.incFns={},t.incSet=[0],t.incReady=[0]),!0},t.inc=(n,e,o)=>{if(t.verify([[n,["object","undefined"]],[e,["function","undefined"]],[o,["function","undefined"]]]),typeof localStorage>"u")return;let r=0,c=0,w="",a=!1;const b="function",S=-1;if(typeof n!="object"||!n)return t.incSet[0];t.incSet[0]++;const N=t.incSet[0];t.incSet[N]=e||0,t.incReady[N]=[];const l=t.incReady[N];l[0]=1;const E={};for(const m in n)if(t.C(n,m)){if(m==="preload"){a=!0;continue}w=t.incGetHash(n[m]),r++,t.incN++;const k=n[m].indexOf(".css")>S?"style":"script";if(n[m]=(t.incSource?t.incSource+"/":"")+n[m],Number.isNaN(Number(m))&&t.C(t.incFns,m)&&t.incFns[m]&&!t.incForce){l[r]={name:m,loaded:1},c++;continue}if(l[r]={name:m,loaded:0},Number.isNaN(Number(m))&&(t.incFns[m]=0),Number.isNaN(Number(m))&&t.incCache&&(n[m].substring(0,4)!=="http"||!t.incCors)&&window.location.protocol!=="file:"&&(n[m].indexOf(".css")>S||n[m].indexOf(".js")>S)){const f=localStorage,v=f.getItem("oInc-"+m),u=f.getItem("oInc-"+m+"-expires"),h=f.getItem("oInc-"+m+"-hash");v&&u&&Date.now()<u&&h===w?(a||t.initState({tag:k,id:"oInc-"+N+"-"+r,innerHTML:v,"data-o-inc":N}).appendInside("head"),l[r].loaded=1,t.incFns[m]=1,c++):(E[m]=r,t.get(n[m],{mode:t.incCors?"cors":"same-origin"}).then(x=>{if(x.status!==200){t.onError&&t.onError({message:t.incSource+n[m]+" was not loaded"});return}x.text().then(y=>{f.setItem("oInc-"+m,y),f.setItem("oInc-"+m+"-expires",Date.now()+t.incCacheExp),f.setItem("oInc-"+m+"-hash",w),a||t.initState({tag:k,id:"oInc-"+N+"-"+E[m],innerHTML:y,"data-o-inc":N}).appendInside("head"),t.incCheck(N,E[m],1)})}))}else{const f={tag:k,id:"oInc-"+N+"-"+r,"data-o-inc":N,async:t.incAsync,onload:"o.incCheck("+N+","+r+",1)"};n[m].indexOf(".css")>S?(f.tag="link",f.rel="stylesheet",f.href=n[m]):(n[m].indexOf(".js")>S||(f.tag="img",f.style="display:none;"),f.src=n[m]),t.initState(f).appendInside(f.style?"body":"head")}}return l[0]+=c,r!==0&&(c===r?typeof e===b&&e(N):setTimeout(m=>{t.incReady[m]&&t.incReady[m].length<t.incReady[m][0]&&(t.incSet[m]=0,typeof o===b&&o(N))},t.incTimeout,N)),t.incSet[0]},t.connectRedux=(n,e,o,r="render")=>{t.verify([[n,["object"]],[e,["function"]],[o,["object"]],[r,["notEmptyString"]]]);const c=()=>{if(typeof o[r]=="function"){const w=e(n.getState());o[r](w!==null&&typeof w=="object"?w:{value:w})}};return c(),n.subscribe(c)},t.connectMobX=(n,e,o,r,c="render")=>(t.verify([[n,["object"]],[e,["object"]],[o,["function"]],[r,["object"]],[c,["notEmptyString"]]]),n.autorun(()=>{if(typeof r[c]=="function"){const w=o(e);r[c](w!==null&&typeof w=="object"?w:{value:w})}})),t.ObjsContext=null,t.withReactContext=(n,e,o,r,c="render")=>function(){const a=n.useContext(e);return n.useEffect(()=>{if(typeof r[c]=="function"){const b=o(a);r[c](b!==null&&typeof b=="object"?b:{value:b})}},[a]),null},t.debug=!1,t.tLog=[],t.tRes=[],t.tStatus=[],t.tFns=[],t.tShowOk=t.F,t.tStyled=t.F,t.tTime=2e3,t.tests=[],t.tAutolog=t.F,t.tBeforeEach=void 0,t.tAfterEach=void 0,t.addTest=(n,...e)=>{t.verify([[n,["notEmptyString"]],[e,["array"]]]);let o={};e.length&&typeof e[e.length-1]=="object"&&!Array.isArray(e[e.length-1])&&(o=e.pop());const r=t.tests.length;return t.tests[r]={title:n,tests:e,hooks:o},{run:()=>{typeof o.before=="function"&&(t.tBeforeEach=o.before),typeof o.after=="function"&&(t.tAfterEach=o.after),t.runTest(r)},autorun:()=>{typeof o.before=="function"&&(t.tBeforeEach=o.before),typeof o.after=="function"&&(t.tAfterEach=o.after),t.runTest(r,!0)},testId:r}},t.updateLogs=()=>{for(let n=0;n<t.tests.length;n++)t.tLog[n]=t.tLog[testN]=sessionStorage.getItem(`oTest-Log-${testN}`)||"";return t.tLog},t.runTest=(n=0,e,o)=>{if(t.verify([[n,["number"]],[e,["boolean","undefined"]],[o,["boolean","undefined"]]]),!t.tests[n])return;o||(sessionStorage?.removeItem(`oTest-Log-${n}`),sessionStorage?.removeItem(`oTest-Res-${n}`),sessionStorage?.removeItem(`oTest-Status-${n}`)),sessionStorage?.setItem("oTest-Run",n),e?sessionStorage?.setItem("oTest-Autorun",e):sessionStorage?.removeItem("oTest-Autorun");const r=t.tests[n];let c=r.tests.pop();typeof c!="function"&&(r.tests.push(c),c=()=>{}),r.tests.push(w=>{c(w),sessionStorage.setItem("dddd",1),sessionStorage?.removeItem("oTest-Run"),e&&t.runTest(n+1,e)}),t.test(r.title,...r.tests)},t.tPre='<div style="font-family:monospace;text-align:left;">',t.tOk='<span style="background:#cfc;padding: 0 15px;">OK</span> ',t.tXx='<div style="background:#fcc;padding:3px;">',t.tDc="</div>",t.test=(n="",...e)=>{t.verify([[n,["notEmptyString"]],[e,["array"]]]);const o=sessionStorage?.getItem("oTest-Run"),r=o||t.tLog.length;let c=0,w="\u251C OK: ",a="\u251C \u2718 ",b=`
|
|
6
|
+
*/const Y=!0,t=n=>{let e={els:[],ie:{},delegated:{},parented:{},store:{},refs:{},states:[],isDebug:!1,currentState:"",savedStates:{},isRoot:!1,_parent:null},o=1,r=2,c=3,w="boolean",a="object",h="function",S="string",C="number",l="notEmptyString",k="undefined",m="dangerouslySetInnerHTML",E,f=t.D,v=-1,u=0,y=0,x=typeof process<"u"||t.D===t.DocumentMVP,b=0,D=0;const I=e,O=s=>typeof s,R=(s,i)=>{for(const g in s)Object.hasOwn(s,g)&&i(g,s)},U=t.onError,L=s=>t.verify(s),$=(s,i="")=>(...g)=>{(t.debug||e.isDebug)&&console.log(i?`${i}()`:s,g.length?"with "+g.join(", "):"without parameters");try{const p=s(g[0],g[o],g[r],g[c]);return p!==E?p:e}catch(p){U(p,i)}},N=s=>{for(b=u;b<=v;b++)s()},M=s=>s?.els?s.el:(O(s)!==a&&(s=t.first(s).el),s),P=(s=!0,i=e.els)=>{const g=i.length;e.length=g,v=g-o,u=0,e.el=g?i[0]:E,e.last=g?i[v]:E,s&&(R(e.states,(p,d)=>{delete e[d[p]]}),e.states=[],e.ie={})};e.reset=t;const H=(s,i,g)=>{R(i,p=>{let d=i[p];O(d)===h&&(d=d(g)),p==="append"&&O(d)===a&&(d.els&&(d=[d]),d[0]?.els&&(valueBuff=[],R(d,T=>{valueBuff.push(...d[T].els)}),d=valueBuff)),d!==E&&s.getAttribute(p)!==d&&!["tag","tagName","name","sample","state","events","ssr","nodeName","revertChildren","root","ref"].includes(p)&&(["html","innerHTML"].includes(p)?s.innerHTML=d:p==="className"?s.setAttribute("class",d):p==="dataset"&&O(d)===a?R(d,T=>{s.dataset[T]=d[T]}):p==="toggleClass"?s.classList.toggle(d):p==="addClass"?O(d)===a?s.classList.add(...d):s.classList.add(d):p==="removeClass"?s.classList.remove(d):p==="style"&&O(d)===a?R(d,T=>{s.style[T]=d[T]}):(p==="append"||p==="children"||p==="childNodes")&&O(d)===a?R(d.length?d:[d],T=>{p==="append"||!s.childNodes[T]?s.appendChild(d[T]):s.childNodes[T]!==d[T]&&s.childNodes[T].replaceWith(d[T])}):s.setAttribute(p,d))}),s.dataset.oState=i.state,t.autotag&&i.name&&(s.dataset[t.autotag]=t.camelToKebab(i.name))};e.debug=$(()=>{e.isDebug=!0},"debug ON"),e.saveAs=$(s=>{L([[s,[l]]]),t.getSaved[s]?(t.debug||e.isDebug)&&console.warn("the key exists (not saved):"+s):t.getSaved[s]=e},"saveAs"),e.unmount=()=>((t.debug||e.isDebug)&&console.log("unmount() for initID:"+e.initID),O(e.remove)===h?e.remove():e.els?.length&&e.els.forEach(s=>{s?.parentNode&&s.parentNode.removeChild(s)}),t.inits[e.initID]=void 0,e={},!0),e.init=$(s=>{L([[s,[a,h]]]);const i=e.initID||t.inits.length||0;if(e.initID=i,P(),t.inits[e.initID]=e,O(s)==="function"){e.render=$(p=>{const d=f.createElement("div");setTimeout(()=>{t.reactRender(s,d,p)}),e.add(d)});return}(O(s)!==a||s.render===E)&&(s={render:s}),R(s,p=>{e?.render&&p==="render"||(e.states.push(p),e[p]=$((d=[{}])=>{e.currentState=p;const T=s[p]||{tag:"div"},A=Array.isArray(e.els)?e.els.slice(u,v+o):[],B=A.length?A:e.els||[];O(T)===a&&(T.state=p,T["data-o-init"]=i);const q=(_,F={})=>{if(O(T)===a)return f.createElement(T.tag||T.tagName||"div");{const j=f.createElement("div");return j.innerHTML=O(T)===h?T(F):T,j.children.length>o||!j.firstElementChild?(j.dataset.oInit=_,j):(j.firstElementChild.dataset.oInit=_,j.firstElementChild)}},z=d;d.length||(d=[d]);const X=!B[0]&&p==="render";d=d.map((_,F)=>{const j=Object.assign({},O(_)===a?_:{},{self:e,o:t,i:_.i===E?F:_.i,parent:e._parent,data:Array.isArray(z)?z[F]:z});return X&&(!T.ssr||x)&&B.push(q(i,j)),j}),X&&(e.els=B,P(!1));const V=()=>{R(T.events,_=>{e.on(_,T.events[_])})};B&&(D=B.length===d.length,B.map((_,F)=>{d[D?F:0].i=F+u;const j=O(T)===h?T(d[D?F:0]):T;O(j)===a&&(j.state=p,X&&(j["data-o-init"]=i,j["data-o-init-i"]=F),H(_,j,d[D?F:0]))}),X&&(e.refs={},e.els.forEach(_=>{_.querySelectorAll&&_.querySelectorAll("[ref]").forEach(F=>{e.refs[F.getAttribute("ref")]=t(F),F.removeAttribute("ref")})}))),X&&O(T)===a&&T.events&&!x&&!T.ssr&&V()}))});const g=s.render||s;!x&&O(g)===a&&g.events&&g.ssr&&(e.initSSRAfterGettingSSR=()=>{e.refs={},e.els.forEach(p=>{p.querySelectorAll&&p.querySelectorAll("[ref]").forEach(d=>{e.refs[d.getAttribute("ref")]=t(d),d.removeAttribute("ref")})}),R(g.events,p=>{e.on(p,g.events[p])})})},"init"),e.connect=$((s,i="render",g)=>{L([[s,[a]],[i,[l]],[g,[S,k]]]),s.connect(I,i,g)},"connect"),e.getSSR=$(s=>{L([[s,[C,k]]]);const i=s!==void 0?s:e.initID;if(x||O(s)===k&&O(e.initID)===k)return;const g=t.D.querySelectorAll(`[data-o-init="${i}"]`);g.length&&!e.els.length&&(e.els=Array.from(g),e.initID=s,t.inits[s]=e,P(!1),O(e.initSSRAfterGettingSSR)===h&&(e.initSSRAfterGettingSSR(),delete e.initSSRAfterGettingSSR))},"getSSR"),e.initState=$((s,i)=>{L([[s,[a]],[i,[a,k]]]),e.init(s).render(i)},"initState");const J=(s,i,g)=>{const p=s.attributes,d={tagName:s.tagName.toLowerCase()};for(const T of p)d[T.nodeName]=T.value;if(g){d.innerHTML=s.innerHTML,d.revertChildren=[];const T=s.querySelectorAll("[data-o-init]");for(const A of T){const B=A.getAttribute("data-o-init");d.revertChildren.push(B),t.inits[B]?.saveState(i,!1)}}return d};return e.saveState=$((s,i=!0)=>{if(L([[s,[l,k]],[i,[w]]]),!e.el)throw Error("saveState(): There are no elements to save");const g=s||"fastSavedState",p={els:[],parentNode:e.el.parentNode,root:i};N(()=>{p.els.push(J(e.els[b],g,i))}),p.ie=Object.assign({},e.ie),p.delegated=Object.assign({},e.delegated),p.store=Object.assign({},e.store),e.isRoot=e.isRoot||i,e.savedStates[g]=p},"saveState"),e.revertState=$(s=>{L([[s,[l,k]]]);const i=s||"fastSavedState";if(!e.savedStates[i])throw Error(`revertState(): The state "${i}" should have been saved by saveState()`);const g=e.savedStates[i];e.offAll(),e.offDelegate(),e.store=Object.assign({},g.store),g.els.forEach((p,d)=>{if(!e.els[d]){const T=t.D.createElement(p.tagName);g.parentNode&&(d?e.els[d-1].after(T):g.parentNode.append(T)),e.add(T)}H(e.els[d],p)}),e.delegated=Object.assign({},g.delegated),e.ie=Object.assign({},g.ie),e.onAll(),R(g.delegated,p=>{g.delegated[p].forEach(d=>{N(()=>{e.els[b].addEventListener(p,d)})})}),e.currentState=i,g.root&&g.els.forEach(({rootElement:p})=>{p.revertChildren.forEach(d=>{t.inits[d]?.revertState(i),t('[data-o-init="'+d+'"]').els.forEach((T,A)=>{T.replaceWith(t.inits[d]?.els[A])})})})},"revertState"),e.loseState=$(s=>{L([[s,[l]]]),e.savedStates[s]&&(delete e.savedStates[s],N(()=>{const i=e.els[b].querySelectorAll("[data-o-init]");for(const g of i){const p=g.getAttribute("data-o-init");t.inits[p]?.loseState(s)}}))},"sample"),e.sample=$((s="render")=>(L([[s,[l]]]),{[s]:J(e.els[u])}),"sample"),e.select=$(s=>{L([[s,[C,k]]]),s===E&&(s=e.length-o),v=s,u=s,e.el=e.els[s],y=o},"select"),e.all=$(()=>{v=e.length-o,u=0,e.el=e.els[0],y=0},"all"),e.remove=$(s=>{if(L([[s,[C,k]]]),s===E&&y&&(s=u),s!==E){const i=e.els[s];i?.parentNode?i.parentNode.removeChild(i):i===void 0&&s>=e.els.length&&t.onError&&t.onError("remove("+s+"): index out of bounds","remove")}else N(()=>{const i=e.els[b];i?.parentNode&&i.parentNode.removeChild(i)});P(!1)},"remove"),e.skip=$(s=>{L([[s,[C,k]]]),s===E&&(s=u),e.els.splice(b,o),P()},"skip"),e.add=$(s=>{L([[s,[S,a,C]]]),e.initID===E&&(O(s)==="string"&&s!==""?e.els.push(...Array.from(f.querySelectorAll(s))):O(s)===a?s.tagName?e.els.push(s):s.els?e.els.push(...s.els):s.length&&s[0].tagName&&e.els.push(...s):O(s)==="number"&&t.inits[s]&&(e=t.inits[s]),P(!1),e.initID!==E&&e.dataset({oInit:e.initID}))},"add"),e.appendInside=$(s=>{L([[s,[a,l]]]),s?.els&&(e._parent=s),N(()=>{M(s).appendChild(e.els[b])})},"appendInside"),e.appendBefore=$(s=>{L([[s,[a,l]]]),N(()=>{M(s).parentNode.insertBefore(e.els[b],M(s))})},"appendBefore"),e.appendAfter=$(s=>{L([[s,[a,l]]]),N(()=>{M(s).after(...e.els)})},"appendAfter"),e.find=$((s="")=>{L([[s,S]]);const i=[];return N(()=>{i.push(...Array.from(e.els[b].querySelectorAll(":scope "+s)))}),t(i)},"find"),e.first=$((s="")=>{L([[s,S]]);let i=E;const g=[];return N(()=>{i=e.els[b].querySelector(s),i&&g.push(i)}),t(g)},"first"),e.attr=$((s,i)=>{if(i!==null&&L([[s,S],[i,[S,k]]]),i===E){const g=[];return N(()=>{g[b]=e.els[b].getAttribute(s)}),y?g[0]:g}else N(i!==null?()=>{e.els[b].setAttribute(s,i)}:()=>{e.els[b].removeAttribute(s)})},"attr"),e.attrs=$(()=>{const s=[];return N(()=>{const i={};[...e.els[b].attributes].forEach(g=>{i[g.nodeName]=g.nodeValue}),s.push(i)}),y?s[0]:s},"attrs"),e.dataset=$(s=>{if(L([[s,[a,k]]]),typeof s===a)N(()=>{R(s,i=>{e.els[b].dataset[i]=s[i]})});else{const i=[];return N(()=>{i.push({...e.els[b].dataset})}),y?i[0]:i}},"dataset"),e.style=$(s=>{s!==null&&L([[s,[S,k]]]),e.attr("style",s)},"style"),e.css=$((s={})=>{if(s===null){e.style(null);return}L([[s,a]]);let i="";R(s,g=>{i+=g+":"+s[g].replace('"',"'")+";"}),e.style(i||null)},"css"),e.setClass=$(s=>{L([[s,S]]),N(()=>{e.els[b].setAttribute("class",s)})},"setClass"),e.addClass=$((...s)=>{N(()=>{e.els[b].classList.add(...s)})},"addClass"),e.removeClass=$((...s)=>{N(()=>{e.els[b].classList.remove(...s)})},"removeClass"),e.toggleClass=$((s,i)=>{L([[s,l],[i,[w,k]]]),N(()=>{e.els[b].classList.toggle(s,i)})},"toggleClass"),e.haveClass=s=>{L([[s,l]]);let i=!0;return N(()=>{e.els[b].classList.contains(s)||(i=!1)}),(e.isDebug||t.debug)&&console.log("haveClass() with",s),i},e.innerHTML=$(s=>{if(L([[s,[S,k]]]),s!==E)N(()=>{e.els[b].innerHTML=s});else{let i="";return N(()=>{i+=x&&e.els[b].innerHTML.length===0?t.D.parseElement(e.els[b],!1):e.els[b].innerHTML}),i}},"innerHTML"),e.innerText=$(s=>{L([[s,[S]]]),N(()=>{e.els[b].innerText=s})},"innerText"),e.textContent=$(s=>{L([[s,[S]]]),N(()=>{e.els[b].textContent=s})},"textContent"),e.html=$(s=>{if(L([[s,[S,k]]]),s!==void 0)e.innerHTML(s);else{let i="";return N(()=>{i+=x?e.els[b].outerHTML():e.els[b].outerHTML}),i}},"html"),e.val=$(s=>{if(s===void 0)return e.el?.value;N(()=>{e.els[b].value=s})},"val"),e.forEach=$(s=>{L([[s,[h]]]),N(()=>{s({self:e,i:b,o:t,el:e.els[b]})})},"forEach"),e.prepareFor=$((s,i)=>{L([[s,[a,h,k]],[i,[h,k]]]);const g=s&&O(s)===a&&s.createElement;if(!g&&O(s)!==h)throw Error("prepareFor(): pass React (full object) or React.createElement as first argument");const p=g?s.createElement:s,d=g?s.useEffect:void 0;return T=>{if(T.ref===E)throw Error("No ref property to convert Objs to React");const A=Object.assign({},T),B=p("div",{ref:T.ref});return delete A.ref,d(()=>{R(A,q=>{if(q.substring(0,1)==="on"){const z=t.camelToKebab(q).split("-")[1];e.on(z,A[q]),delete A[q]}}),e.render(A),e.appendInside(B.ref.current)},[]),B}},"prepareFor"),e.on=$((s,i,g,p)=>{L([[s,[l]],[i,[h]],[g,[a,k]],[p,[w,k]]]),s.split(", ").forEach(d=>{N(()=>{e.els[b].addEventListener(d,i,g,p)}),e.ie[d]||(e.ie[d]=[]),e.ie[d].push([i,g,p])})},"on"),e.off=$((s,i,g)=>{L([[s,[l]],[i,[h]],[g,[a,k]]]),s.split(", ").forEach(p=>{N(()=>{e.els[b].removeEventListener(p,i,g)}),e.ie[p]&&(e.ie[p]=e.ie[p].filter(d=>d[0]!==i))})},"off"),e.onDelegate=$((s,i,g)=>{L([[s,[l]],[i,[l]],[g,[h]]]),s.split(", ").forEach(p=>{const d=T=>{const A=T.target.closest(i);A&&(T.delegate=A,T.objs=e,g(T))};N(()=>{e.els[b].addEventListener(p,d)}),e.delegated[p]||(e.delegated[p]=[]),e.delegated[p].push(d)})},"onDelegate"),e.offDelegate=$(s=>{L([[s,[l]]]),R(e.delegated,i=>{if(!s||s===i)for(;e.delegated[i].length;){const g=e.delegated[i].pop();N(()=>{e.els[b].removeEventListener(i,g)})}}),e.delegated={}},"offDelegate"),e.onParent=$((s,i,g)=>{L([[s,[l]],[i,[l,a]],[g,[h]]]);const p=O(i)===a?i:t.D.querySelector(i);s.split(", ").forEach(d=>{const T=A=>{A.objs=e,g(A)};p.addEventListener(d,T),e.parented[d]||(e.parented[d]=[]),e.parented[d].push(T)})},"onParent"),e.offParent=$((s,i)=>{L([[s,[l]],[i,[l,a]]]);const g=O(i)===a?i:t.D.querySelector(i);R(e.parented,p=>{(!s||s===p)&&(e.parented[p].forEach(d=>{g.removeEventListener(p,d)}),delete e.parented[p])})},"offParent"),e.onAll=$((s,i)=>{L([[s,[l,k]],[i,[w,k]]]),R(e.ie,(g,p)=>{(!s||s===g)&&p[g].forEach(d=>{N(()=>{i?e.els[b].removeEventListener(g,d[0]):e.els[b].addEventListener(g,d[0],d[o],d[r])})})})},"onAll"),e.offAll=$(s=>{L([[s,[l]]]),e.onAll(s,o)},"offAll"),n&&e.add(n),e.take=s=>{if(L([[s,[S,a,C]]]),e.add(s),e.el){const i=e.el.dataset.oInit;if(i!==E&&t.inits[i])return e.length===o?(D=e.els[0],Object.assign(e,t.inits[i]),e.els=[D]):e=t.inits[i],P(!1,e.els),e}},e};t.first=n=>(t.verify([[n,["notEmptyString"]]]),t.debug&&console.log(n," -> ","o.first()"),t(t.D.querySelector(n)).select(0)),t.inits=[],t.getSaved={},t.errors=[],t.showErrors=!1,t.logErrors=()=>{t.errors.length?t.errors.forEach(n=>console.error(n)):console.log("No errors")},t.onError=(n,e)=>{t.showErrors?console.error(n,e):(t.errors.push(n),e&&t.errors.push(e))},t.reactRender=()=>new Error("React render function is not defined"),t.autotag=void 0,t.reactQA=n=>({["data-"+(t.autotag||"qa")]:n.replace(/([A-Z])/g,(e,o)=>"-"+o.toLowerCase()).replace(/^-/,"")}),t.specialTypes={notEmptyString:(n,e)=>e==="string"&&n.length,array:n=>Array.isArray(n),promise:n=>n instanceof Promise||!!(n&&typeof n.then=="function")},t.verify=(n,e=!1)=>{for(const o of n){const r=typeof o[0];let c=Array.isArray(o[1])?o[1]:[o[1]],w=!1;if(c.includes(r))return!0;c=c.filter(a=>!!t.specialTypes[a]);for(const a of c)if(w=t.specialTypes[a](o[0],r),w)return!0}return e?!1:new Error("Type verification failed")},t.safeVerify=n=>t.verify(n,!0),t.init=(n,e)=>t().init(n,e),t.initState=(n,e)=>t().init(n).render(e),t.take=n=>t().take(n),t.getStates=()=>t.inits.reduce((n,e)=>(n.push(e?.states),n),[]),t.getStores=()=>t.inits.reduce((n,e)=>(n.push(e?.store),n),[]),t.getListeners=()=>t.inits.reduce((n,e)=>(n.push(e?.ie),n),[]),t.createStore=n=>{const e=Object.assign({},n);return e._defaults=Object.assign({},n),e._listeners=[],e.subscribe=function(o,r){return this._listeners.push(c=>o[r]?.(c)),this},e.notify=function(){this._listeners.forEach(o=>o(this))},e.reset=function(){const o={_listeners:1,subscribe:1,notify:1,_defaults:1,reset:1};for(const r of Object.keys(this._defaults))o[r]||(this[r]=this._defaults[r])},e},t.U=void 0,t.W=2,t.H=100,t.F=!1,t.C=(n,e)=>Object.hasOwn(n,e),t.kebabToCamel=n=>n.replace(/-./g,e=>e.toUpperCase()[1]),t.camelToKebab=n=>n.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),t.route=(n,e)=>{t.verify([[n,["notEmptyString","function","boolean"]],[e,["function","object"]]]);const o=typeof n=="function"?n(window.location.pathname):n;return o===!0||window.location.pathname===o?e?typeof e=="function"?(e(),!0):e:t:typeof e=="function"?!1:{}},t.router=(n={})=>{t.verify([[n,["object"]]]);for(const e in n)if(t.C(n,e)&&window.location.pathname===e)return typeof n[e]=="function"?(n[e](),!0):n[e];return!1},t.DocumentMVP={addEventListener:()=>{},parseElement:(n,e=!0)=>{t.verify([[n,["object","string"]],[e,["boolean"]]]);const o=(r,c="")=>{let w="";for(const a in r)t.C(r,a)&&(w+=` ${c}${t.camelToKebab(a)}="${typeof r[a]!="object"?r[a]:Object.entries(r[a]).map(h=>`${h[0]}: ${h[1]};`).join(" ")}"`);return w};if(typeof n=="string")return n;if(e){const r=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"],c=n.tagName.toLowerCase(),w=n.attributes["data-o-init"],a=w!==void 0?` data-o-init="${w}"`:"";return`<${c}${n.className?` class="${n.className}"`:""}${o(n.attributes)}${a}${o(n.dataset,"data-")}${r.includes(c)?"/":""}>${r.includes(c)?"":n.innerHTML.length?n.innerHTML:n.children.map(h=>t.D.parseElement(h)).join("")}${r.includes(c)?"":`</${c}>`}`}return n.innerHTML.length?n.innerHTML:n.children.map(r=>t.D.parseElement(r)).join("")},createElement:n=>{t.verify([[n,["notEmptyString"]]]);const e={tagName:n.toUpperCase(),attributes:{},innerHTML:"",children:[],dataset:{},className:"",classArray:[],style:{},addEventListener:()=>{},removeEventListener:()=>{}};return e.classList={add:(...o)=>{t.verify([[o,["array"]]]),e.classArray.push(o),e.className=e.classArray.join(" ")},has:o=>(t.verify([[o,["notEmptyString"]]]),e.classArray.includes(o)),remove:o=>{t.verify([[o,["notEmptyString"]]]),e.classArray=e.classArray.filter(r=>o!==r),e.className=e.classArray.join(" ")}},e.classList.toggle=o=>{t.verify([[o,["notEmptyString"]]]),e.classList.has(o)?e.classList.remove(o):e.classList.add(o)},e.setAttribute=(o,r)=>{t.verify([[o,["notEmptyString"]],[r,["string","number","boolean","undefined"]]]),e.attributes[o]=r},e.getAttribute=o=>(t.verify([[o,["notEmptyString"]]]),e.attributes[o]),e.removeAttribute=o=>{t.verify([[o,["notEmptyString"]]]),delete e.attributes[o]},e.appendChild=o=>{t.verify([[o,["object"]]]),e.children.push(o),e.firstElementChild=e.children[0]},e.outerHTML=()=>t.D.parseElement(e),e}},t.D=typeof document<"u"&&typeof process>"u"?document:t.DocumentMVP,t.setCookie=(n,e="",o={})=>{if(t.verify([[n,["notEmptyString"]],[e,["string","number","boolean"]],[o,["object"]]]),t.D.cookie===void 0){console.log("Cookies are not supported on server side");return}let r=encodeURIComponent(n)+"="+encodeURIComponent(e);o={path:"/",...o},o.expires instanceof Date?o.expires=o.expires.toUTCString():typeof o.expires=="number"&&(o.expires=new Date(o.expires).toUTCString());for(const c in o){r+="; "+c;const w=o[c];w!==!0&&(r+="="+w)}t.D.cookie=r},t.getCookie=n=>{if(t.verify([[n,["notEmptyString"]]]),t.D.cookie===void 0){console.log("Cookies are not supported on server side");return}const e=t.D.cookie.match(RegExp("(?:^|; )"+n.replace(/([.$?*|{}()[\]\\/+^])/g,"\\$1")+"=([^;]*)"));return e?decodeURIComponent(e[1]):void 0},t.deleteCookie=n=>{t.verify([[n,["notEmptyString"]]]),t.setCookie(n,"",{"max-age":0})},t.clearCookies=()=>{if(t.D.cookie===void 0){console.log("Cookies are not supported on server side");return}const n=t.D.cookie.split(";");for(;n.length;){let e=n.pop();for(;e.charAt(0)===" ";)e=e.substring(1);const o=e.split("=")[0];t.deleteCookie(o)}},t.clearLocalStorage=n=>{if(t.verify([[n,["boolean","undefined"]]]),!(typeof localStorage>"u"))if(n)localStorage.clear();else for(let e=localStorage.length-1;e>=0;e--){const o=localStorage.key(e);o.indexOf("oInc-")===-1&&o.indexOf("oTest-")===-1&&localStorage.removeItem(o)}},t.clearSessionStorage=n=>{if(t.verify([[n,["boolean","undefined"]]]),!(typeof sessionStorage>"u"))if(!n)sessionStorage.clear();else for(let e=sessionStorage.length-1;e>=0;e--){const o=sessionStorage.key(e);o&&o.indexOf("oTest-")===0&&sessionStorage.removeItem(o)}},t.clearTestsStorage=()=>{t.clearSessionStorage(1)},t.clearAfterTests=()=>{t.clearCookies(),t.clearLocalStorage(!1),t.clearTestsStorage()},t.ajax=(n,e={})=>{t.verify([[n,["notEmptyString"]],[e,["object"]]]);const o=new URLSearchParams;if(e.data&&typeof e.data=="object"){for(const r in e.data)t.C(e.data,r)&&(typeof e.data[r]=="object"?o.set(r,encodeURIComponent(JSON.stringify(e.data[r]))):o.set(r,e.data[r]));e.method.toLowerCase()==="get"?n+="?"+o.toString():e.body||(e.body=o),delete e.data}return e.headers||(e.headers={"X-Requested-With":"XMLHttpRequest"}),fetch(n,e)},t.get=(n,e={})=>(t.verify([[n,["notEmptyString"]],[e,["object"]]]),t.ajax(n,{...e,method:"GET"})),t.post=(n,e={})=>(t.verify([[n,["notEmptyString"]],[e,["object"]]]),t.ajax(n,{...e,method:"POST"})),t.newLoader=n=>{t.verify([[n,["promise","undefined"]]]);let e=[],o=null,r=!1,c=!1;const w=a=>{r=!1,c=!1,o=null,setTimeout(()=>{a.then(h=>{r=!0,!h.ok&&typeof h.ok<"u"?(c=!0,e.forEach(([S,C,l])=>{l&&S[l](h)})):typeof h.json=="function"?h.json().then(S=>{o=S,e.forEach(([C,l])=>{C[l](o)})}):(o=h,e.forEach(([S,C])=>{S[C](o)}))}).catch(h=>{c=!0,e.forEach(([S,C,l])=>{l&&S[l](h)})})},33)};return n&&w(n),{reload:w,isObjsLoader:!0,listeners:e,isFinished:()=>r,getStore:()=>o,connect:(a,h="render",S)=>{t.verify([[a,["object"]],[h,["notEmptyString"]],[S,["string","undefined"]]]),r?c?S&&a[S]():typeof a[h]=="function"&&a[h](o):e.push([a,h,S])},disconnect:a=>{t.verify([[a,["object"]]]),e=e.filter(([h])=>h!==a)}}},t.getParams=n=>{t.verify([[n,["string","undefined"]]]);const e={},o=new URLSearchParams(window.location.search).entries();for(const r of o)e[r[0]]=r[1];return n?e[n]:e},t.incCache=!0,t.incCacheExp=1e3*60*60*24,t.incTimeout=6e3,t.incSource="",t.incForce=t.F,t.incAsync=!0,t.incCors=t.F,t.incSeparator="?",t.incFns={},t.incSet=[0],t.incReady=[0],t.incN=0,t.incGetHash=n=>n.split(t.incSeparator)[1]||"",t.incCheck=(n=0,e,o=0)=>(t.verify([[n,["number"]],[e,["number","undefined"]],[o,["number"]]]),!o&&n&&e===t.U&&t.incReady[n]?t.incSet[n]===1:t.incReady[n]===t.U||t.incReady[n][e]===t.U?t.F:(t.incReady[n][e].loaded=o,t.incFns[t.incReady[n][e].name]=o,t.incReady[n][0]+=o,n&&t.incReady[n].length===t.incReady[n][0]&&(typeof t.incSet[n]=="function"&&t.incSet[n](n),t.incSet[n]=1),t.incSet[n]===1)),t.incCacheClear=(n=t.F)=>{t.verify([[n,["boolean"]]]);for(const e in t.incFns)t.C(t.incFns,e)&&(localStorage.removeItem("oInc-"+e),localStorage.removeItem("oInc-"+e+"-expires"));return n&&(t.incReady.forEach((e,o)=>{o&&e.forEach((r,c)=>{c&&t("#oInc-"+o+"-"+c).remove()})}),t.incN=0,t.incFns={},t.incSet=[0],t.incReady=[0]),!0},t.inc=(n,e,o)=>{if(t.verify([[n,["object","undefined"]],[e,["function","undefined"]],[o,["function","undefined"]]]),typeof localStorage>"u")return;let r=0,c=0,w="",a=!1;const h="function",S=-1;if(typeof n!="object"||!n)return t.incSet[0];t.incSet[0]++;const C=t.incSet[0];t.incSet[C]=e||0,t.incReady[C]=[];const l=t.incReady[C];l[0]=1;const k={};for(const m in n)if(t.C(n,m)){if(m==="preload"){a=!0;continue}w=t.incGetHash(n[m]),r++,t.incN++;const E=n[m].indexOf(".css")>S?"style":"script";if(n[m]=(t.incSource?t.incSource+"/":"")+n[m],Number.isNaN(Number(m))&&t.C(t.incFns,m)&&t.incFns[m]&&!t.incForce){l[r]={name:m,loaded:1},c++;continue}if(l[r]={name:m,loaded:0},Number.isNaN(Number(m))&&(t.incFns[m]=0),Number.isNaN(Number(m))&&t.incCache&&(n[m].substring(0,4)!=="http"||!t.incCors)&&window.location.protocol!=="file:"&&(n[m].indexOf(".css")>S||n[m].indexOf(".js")>S)){const f=localStorage,v=f.getItem("oInc-"+m),u=f.getItem("oInc-"+m+"-expires"),y=f.getItem("oInc-"+m+"-hash");v&&u&&Date.now()<u&&y===w?(a||t.initState({tag:E,id:"oInc-"+C+"-"+r,innerHTML:v,"data-o-inc":C}).appendInside("head"),l[r].loaded=1,t.incFns[m]=1,c++):(k[m]=r,t.get(n[m],{mode:t.incCors?"cors":"same-origin"}).then(x=>{if(x.status!==200){t.onError&&t.onError({message:t.incSource+n[m]+" was not loaded"});return}x.text().then(b=>{f.setItem("oInc-"+m,b),f.setItem("oInc-"+m+"-expires",Date.now()+t.incCacheExp),f.setItem("oInc-"+m+"-hash",w),a||t.initState({tag:E,id:"oInc-"+C+"-"+k[m],innerHTML:b,"data-o-inc":C}).appendInside("head"),t.incCheck(C,k[m],1)})}))}else{const f={tag:E,id:"oInc-"+C+"-"+r,"data-o-inc":C,async:t.incAsync,onload:"o.incCheck("+C+","+r+",1)"};n[m].indexOf(".css")>S?(f.tag="link",f.rel="stylesheet",f.href=n[m]):(n[m].indexOf(".js")>S||(f.tag="img",f.style="display:none;"),f.src=n[m]),t.initState(f).appendInside(f.style?"body":"head")}}return l[0]+=c,r!==0&&(c===r?typeof e===h&&e(C):setTimeout(m=>{t.incReady[m]&&t.incReady[m].length<t.incReady[m][0]&&(t.incSet[m]=0,typeof o===h&&o(C))},t.incTimeout,C)),t.incSet[0]},t.connectRedux=(n,e,o,r="render")=>{t.verify([[n,["object"]],[e,["function"]],[o,["object"]],[r,["notEmptyString"]]]);const c=()=>{if(typeof o[r]=="function"){const w=e(n.getState());o[r](w!==null&&typeof w=="object"?w:{value:w})}};return c(),n.subscribe(c)},t.connectMobX=(n,e,o,r,c="render")=>(t.verify([[n,["object"]],[e,["object"]],[o,["function"]],[r,["object"]],[c,["notEmptyString"]]]),n.autorun(()=>{if(typeof r[c]=="function"){const w=o(e);r[c](w!==null&&typeof w=="object"?w:{value:w})}})),t.ObjsContext=null,t.withReactContext=(n,e,o,r,c="render")=>function(){const a=n.useContext(e);return n.useEffect(()=>{if(typeof r[c]=="function"){const h=o(a);r[c](h!==null&&typeof h=="object"?h:{value:h})}},[a]),null},t.debug=!1,t.tLog=[],t.tRes=[],t.tStatus=[],t.tFns=[],t.tShowOk=t.F,t.tStyled=t.F,t.tTime=2e3,t.tests=[],t.tAutolog=t.F,t.tBeforeEach=void 0,t.tAfterEach=void 0,t.addTest=(n,...e)=>{t.verify([[n,["notEmptyString"]],[e,["array"]]]);let o={};e.length&&typeof e[e.length-1]=="object"&&!Array.isArray(e[e.length-1])&&(o=e.pop());const r=t.tests.length;return t.tests[r]={title:n,tests:e,hooks:o},{run:()=>{typeof o.before=="function"&&(t.tBeforeEach=o.before),typeof o.after=="function"&&(t.tAfterEach=o.after),t.runTest(r)},autorun:()=>{typeof o.before=="function"&&(t.tBeforeEach=o.before),typeof o.after=="function"&&(t.tAfterEach=o.after),t.runTest(r,!0)},testId:r}},t.updateLogs=()=>{for(let n=0;n<t.tests.length;n++)t.tLog[n]=t.tLog[testN]=sessionStorage.getItem(`oTest-Log-${testN}`)||"";return t.tLog},t.runTest=(n=0,e,o)=>{if(t.verify([[n,["number"]],[e,["boolean","undefined"]],[o,["boolean","undefined"]]]),!t.tests[n])return;o||(sessionStorage?.removeItem(`oTest-Log-${n}`),sessionStorage?.removeItem(`oTest-Res-${n}`),sessionStorage?.removeItem(`oTest-Status-${n}`)),sessionStorage?.setItem("oTest-Run",n),e?sessionStorage?.setItem("oTest-Autorun",e):sessionStorage?.removeItem("oTest-Autorun");const r=t.tests[n];let c=r.tests.pop();typeof c!="function"&&(r.tests.push(c),c=()=>{}),r.tests.push(w=>{c(w),sessionStorage.setItem("dddd",1),sessionStorage?.removeItem("oTest-Run"),e&&t.runTest(n+1,e)}),t.test(r.title,...r.tests)},t.tPre='<div style="font-family:monospace;text-align:left;">',t.tOk='<span style="background:#cfc;padding: 0 15px;">OK</span> ',t.tXx='<div style="background:#fcc;padding:3px;">',t.tDc="</div>",t.test=(n="",...e)=>{t.verify([[n,["notEmptyString"]],[e,["array"]]]);const o=sessionStorage?.getItem("oTest-Run"),r=o||t.tLog.length;let c=0,w="\u251C OK: ",a="\u251C \u2718 ",h=`
|
|
7
7
|
`,S=`
|
|
8
|
-
`,
|
|
9
|
-
`,t.tRes[r]=t.F,t.tStatus[r]=[]);for(let
|
|
10
|
-
`,m("\u251C "+f.title,!1,!0),t.tStatus[r][
|
|
11
|
-
`,o&&(sessionStorage.setItem(`oTest-Log-${r}`,t.tLog[r]),sessionStorage.setItem(`oTest-Res-${r}`,t.tRes[r]),sessionStorage.setItem(`oTest-Status-${r}`,JSON.stringify(t.tStatus[r]))),!c&&typeof t.tFns[r]=="function"&&t.tFns[r](r),r},t.testUpdate=(n,e=t.F,o="")=>{t.verify([[n,["object"]],[e,["boolean","string"]],[o,["string"]]]);let r="";const c=n.n,w=(a="",
|
|
8
|
+
`,C="",l=e.length,k=0;const m=(E="",f=!1,v=!1)=>{t.tAutolog&&(f?console.error(E):(t.tShowOk||v)&&console.log(E))};if(typeof e[l-1]=="function"&&(t.tFns[r]=e[l-1],l--),o){t.tLog[r]=sessionStorage.getItem(`oTest-Log-${r}`)||"",t.tRes[r]=sessionStorage.getItem(`oTest-Res-${r}`)||!1,t.tStatus[r]=JSON.parse(sessionStorage.getItem(`oTest-Status-${r}`)||"[]");for(let E=0;E<t.tStatus[r].length;E++){const f=t.tStatus[r][E];(f===!0||f===!1)&&k++}}t.tStyled&&(w=t.tPre+t.tOk,a=t.tPre+t.tXx,h=t.tDc,S=h+h),(!o||t.tLog[r].length===0)&&(m("\u2552 "+n+" #"+r,!1,!0),t.tStyled?t.tLog[r]="<div><b>"+n+" #"+r+"</b></div>":t.tLog[r]="\u2552 "+n+" #"+r+`
|
|
9
|
+
`,t.tRes[r]=t.F,t.tStatus[r]=[]);for(let E=t.tStatus[r].length;E<l;E++){const f={n:r,i:E,title:e[E][0],tShowOk:t.tShowOk,tStyled:t.tStyled};let v=e[E][1];if(typeof v>"u"){t.tStyled?t.tLog[r]+="<div>"+f.title+"</div>":t.tLog[r]+=f.title+`
|
|
10
|
+
`,m("\u251C "+f.title,!1,!0),t.tStatus[r][E]=!0,k++;continue}if(typeof t.tBeforeEach=="function"&&t.tBeforeEach(f),typeof v=="function")try{v=v(f)}catch(u){v=u.message,t.onError&&t.onError(u)}if(typeof t.tAfterEach=="function"&&t.tAfterEach(f,v),v&&typeof v.then=="function"){c++;const u=setTimeout(()=>{f.title+=" (timeout)",t.testUpdate(f)},t.tTime);v.then(y=>{clearTimeout(u);const x=y===!0||y&&y.ok===!0,b=y&&y.errors&&y.errors.length?y.errors.join("; "):typeof y=="string"?y:"";t.testUpdate(f,x,x?"":b?": "+b:"")}).catch(y=>{clearTimeout(u),t.testUpdate(f,!1,y.message||"Promise rejected")});continue}if(typeof t.tStatus[r][E]>"u")t.tStatus[r][E]=typeof v=="string"?t.F:v;else{sessionStorage.setItem(`oTest-Status-${r}`,JSON.stringify(t.tStatus[r]));return}v===!0?(k++,t.tShowOk&&(t.tLog[r]+=w+e[E][0]+h,m("\u251C OK: "+e[E][0]))):v!==t.U?(t.tLog[r]+=a+e[E][0]+(v!==t.F?": "+v:"")+S,m("\u251C \u2718 "+e[E][0]+(v!==t.F?": "+v:""),!0)):(c++,setTimeout(u=>{u.title+=" (timeout)",t.testUpdate(u)},t.tTime,f))}return t.tRes[r]=k===l,C=c?"\u251C ":"\u2558 ",C+="DONE "+k+"/"+l+(c?", waiting: "+c:""),m(C,k+c!==l),c||m(),t.tStyled?t.tLog[r]+=t.tPre+'<div style="color:'+(k+c!==l?"red":"green")+';"><b>DONE '+k+"/"+l+(c?", waiting: "+c:"")+"</b>"+t.tDc+t.tDc:t.tLog[r]+=C+`
|
|
11
|
+
`,o&&(sessionStorage.setItem(`oTest-Log-${r}`,t.tLog[r]),sessionStorage.setItem(`oTest-Res-${r}`,t.tRes[r]),sessionStorage.setItem(`oTest-Status-${r}`,JSON.stringify(t.tStatus[r]))),!c&&typeof t.tFns[r]=="function"&&t.tFns[r](r),r},t.testUpdate=(n,e=t.F,o="")=>{t.verify([[n,["object"]],[e,["boolean","string"]],[o,["string"]]]);let r="";const c=n.n,w=(a="",h=!1)=>{t.tAutolog&&(h?console.error(a):console.log(a))};if(t.tStatus[c][n.i]===t.U||t.tStatus[c][n.i]===null){t.tStatus[c][n.i]=e===!0,e===!0?n.tShowOk&&(r="\u251C OK: "+n.title+o,w(r),n.tStyled?t.tLog[c]+=t.tPre+t.tOk+n.title+o+t.tDc:t.tLog[c]+=r+`
|
|
12
12
|
`):(t.tRes[c]=t.F,r="\u251C \u2718 "+n.title+(e?": "+e:"")+o,w(r,!0),n.tStyled?t.tLog[c]+=t.tPre+t.tXx+n.title+o+(e?": "+e:"")+t.tDc+t.tDc:t.tLog[c]+=r+`
|
|
13
|
-
`);let a=0,
|
|
14
|
-
`,typeof t.tFns[c]=="function"&&t.tFns[c](c)}},sessionStorage?.getItem("oTest-Run")&&window?.addEventListener("load",()=>{t.runTest(sessionStorage?.getItem("oTest-Run"),sessionStorage?.getItem("oTest-Autorun")||t.F,!0)},!1),t.measure=n=>{if(!n)return{};const e=n.getBoundingClientRect(),o=window.getComputedStyle(n);return{width:e.width,height:e.height,top:e.top,left:e.left,visible:o.display!=="none"&&o.visibility!=="hidden"&&e.width>0,opacity:o.opacity,zIndex:o.zIndex}},t.assertVisible=n=>!!t.measure(n).visible,t.assertSize=(n,e={})=>{if(!n)return"element is null";const o=t.measure(n);if(e.w!==void 0&&Math.round(o.width)!==e.w)return`width: expected ${e.w}, got ${Math.round(o.width)}`;if(e.h!==void 0&&Math.round(o.height)!==e.h)return`height: expected ${e.h}, got ${Math.round(o.height)}`;const r=c=>{const w=window.getComputedStyle(n).getPropertyValue(c);return w?parseFloat(w):0};return e.padding!==void 0&&r("padding-top")!==e.padding?`padding: expected ${e.padding}, got ${r("padding-top")}`:e.paddingTop!==void 0&&r("padding-top")!==e.paddingTop?`paddingTop: expected ${e.paddingTop}, got ${r("padding-top")}`:e.paddingRight!==void 0&&r("padding-right")!==e.paddingRight?`paddingRight: expected ${e.paddingRight}, got ${r("padding-right")}`:e.paddingBottom!==void 0&&r("padding-bottom")!==e.paddingBottom?`paddingBottom: expected ${e.paddingBottom}, got ${r("padding-bottom")}`:e.paddingLeft!==void 0&&r("padding-left")!==e.paddingLeft?`paddingLeft: expected ${e.paddingLeft}, got ${r("padding-left")}`:e.margin!==void 0&&r("margin-top")!==e.margin?`margin: expected ${e.margin}, got ${r("margin-top")}`:e.marginTop!==void 0&&r("margin-top")!==e.marginTop?`marginTop: expected ${e.marginTop}, got ${r("margin-top")}`:e.marginRight!==void 0&&r("margin-right")!==e.marginRight?`marginRight: expected ${e.marginRight}, got ${r("margin-right")}`:e.marginBottom!==void 0&&r("margin-bottom")!==e.marginBottom?`marginBottom: expected ${e.marginBottom}, got ${r("margin-bottom")}`:e.marginLeft!==void 0&&r("margin-left")!==e.marginLeft?`marginLeft: expected ${e.marginLeft}, got ${r("margin-left")}`:!0},t.recorder={active:!1,actions:[],mocks:{},initialData:{},assertions:[],observeRoot:null,_originalFetch:null,_listeners:[],_observer:null},t.startRecording=(n,e,o)=>{if(t.recorder.active)return;const r=["click","mouseover","scroll","input","change"],c={click:100,mouseover:50,scroll:30,input:50,change:50},w=e||r,a=Object.assign({},c,o||{}),
|
|
13
|
+
`);let a=0,h=0;for(const S of t.tStatus[c]){if(S===t.U||S===null)return;S||a++,h++}if(sessionStorage?.getItem("oTest-Run")===c&&(sessionStorage.setItem(`oTest-Log-${c}`,t.tLog[c]),sessionStorage.setItem(`oTest-Res-${c}`,t.tRes[c]),sessionStorage.setItem(`oTest-Status-${c}`,JSON.stringify(t.tStatus[c])),h<t.tests[c].tests.length))return;t.tRes[c]=!a,r=a?"FAILED "+a+"/"+h:"DONE "+h+"/"+h,w("\u2558 "+r,!!a),w(),n.tStyled?t.tLog[c]+=t.tPre+'<b style="color:'+(a?"red":"green")+';">'+r+"</b>"+t.tDc:t.tLog[c]+="\u2558 "+r+`
|
|
14
|
+
`,typeof t.tFns[c]=="function"&&t.tFns[c](c)}},sessionStorage?.getItem("oTest-Run")&&window?.addEventListener("load",()=>{t.runTest(sessionStorage?.getItem("oTest-Run"),sessionStorage?.getItem("oTest-Autorun")||t.F,!0)},!1),t.measure=n=>{if(!n)return{};const e=n.getBoundingClientRect(),o=window.getComputedStyle(n);return{width:e.width,height:e.height,top:e.top,left:e.left,visible:o.display!=="none"&&o.visibility!=="hidden"&&e.width>0,opacity:o.opacity,zIndex:o.zIndex}},t.assertVisible=n=>!!t.measure(n).visible,t.assertSize=(n,e={})=>{if(!n)return"element is null";const o=t.measure(n);if(e.w!==void 0&&Math.round(o.width)!==e.w)return`width: expected ${e.w}, got ${Math.round(o.width)}`;if(e.h!==void 0&&Math.round(o.height)!==e.h)return`height: expected ${e.h}, got ${Math.round(o.height)}`;const r=c=>{const w=window.getComputedStyle(n).getPropertyValue(c);return w?parseFloat(w):0};return e.padding!==void 0&&r("padding-top")!==e.padding?`padding: expected ${e.padding}, got ${r("padding-top")}`:e.paddingTop!==void 0&&r("padding-top")!==e.paddingTop?`paddingTop: expected ${e.paddingTop}, got ${r("padding-top")}`:e.paddingRight!==void 0&&r("padding-right")!==e.paddingRight?`paddingRight: expected ${e.paddingRight}, got ${r("padding-right")}`:e.paddingBottom!==void 0&&r("padding-bottom")!==e.paddingBottom?`paddingBottom: expected ${e.paddingBottom}, got ${r("padding-bottom")}`:e.paddingLeft!==void 0&&r("padding-left")!==e.paddingLeft?`paddingLeft: expected ${e.paddingLeft}, got ${r("padding-left")}`:e.margin!==void 0&&r("margin-top")!==e.margin?`margin: expected ${e.margin}, got ${r("margin-top")}`:e.marginTop!==void 0&&r("margin-top")!==e.marginTop?`marginTop: expected ${e.marginTop}, got ${r("margin-top")}`:e.marginRight!==void 0&&r("margin-right")!==e.marginRight?`marginRight: expected ${e.marginRight}, got ${r("margin-right")}`:e.marginBottom!==void 0&&r("margin-bottom")!==e.marginBottom?`marginBottom: expected ${e.marginBottom}, got ${r("margin-bottom")}`:e.marginLeft!==void 0&&r("margin-left")!==e.marginLeft?`marginLeft: expected ${e.marginLeft}, got ${r("margin-left")}`:!0},t.recorder={active:!1,actions:[],mocks:{},initialData:{},assertions:[],observeRoot:null,_originalFetch:null,_listeners:[],_observer:null},t.startRecording=(n,e,o)=>{if(t.recorder.active)return;const r=["click","mouseover","scroll","input","change"],c={click:100,mouseover:50,scroll:30,input:50,change:50},w=e||r,a=Object.assign({},c,o||{}),h={scroll:30,mouseover:50},S=t.recorder;S.active=!0,S.actions=[],S.mocks={},S.stepDelays=a,S.initialData={url:window.location.href,timestamp:Date.now()},S.observeRoot=n||null,S.assertions=[],t.inits.forEach((f,v)=>{f?.store&&(S.initialData["init_"+v]=JSON.parse(JSON.stringify(f.store)))}),S._originalFetch=window.fetch,window.fetch=async(f,v={})=>{const u=(v.method||"GET").toUpperCase();let y;try{y=v.body?JSON.parse(v.body):void 0}catch{y=v.body}const x=await S._originalFetch(f,v),b=x.clone();let D;try{D=await b.json()}catch{D=await b.text().catch(()=>null)}const I=u+":"+f;return S.mocks[I]={url:f,method:u,request:y,response:D,status:x.status},x};const C={"data-o-init":1,"data-o-init-i":1,"data-o-state":1},l=(f,v)=>{if(t.D.querySelectorAll(f).length<=1)return f;let u=v;for(;u&&u!==t.D.body;){let y=u.id?"#"+u.id:null;if(!y&&u.attributes){const x=t.autotag?"data-"+t.autotag:null;if(x){const b=u.getAttribute(x);b!=null&&(y=`[${x}="${b}"]`)}if(!y){for(const b of u.attributes)if(b.name.startsWith("data-")&&!C[b.name]){y=`[${b.name}="${b.value}"]`;break}}}if(y){const x=y+" "+f;if(t.D.querySelectorAll(x).length===1)return x}u=u.parentElement}return f},k=f=>{if(!f||f.nodeType!==1)return"";let v="";if(f.dataset){const u=t.autotag&&f.dataset[t.autotag];u&&(v=l(`[data-${t.autotag}="${u}"]`,f.parentElement))}if(!v&&f.tagName){const u=f.id?"#"+f.id:f.className?f.tagName.toLowerCase()+"."+[...f.classList].join("."):f.tagName.toLowerCase();v=f.id?u:l(u,f.parentElement)}return v},m=n&&t.D.querySelector(n)||t.D.body;S._observer=new MutationObserver(f=>{const v=S.actions.length-1;v<0||f.forEach(u=>{const y=(x,b)=>{let D,I;if(x&&m&&m.querySelectorAll(x).length>1){let R=b;for(;R&&R!==m&&R.nodeType===1;){const U=t.autotag&&R.dataset&&R.dataset[t.autotag];if(U){const L=`[data-${t.autotag}="${U}"]`,$=m.querySelectorAll(L);if($.length>1){const N=[...$].indexOf(R);if(N!==-1){D=L,I=N;break}}}R=R.parentElement}}return{listSelector:D,index:I}};if(u.type==="childList"&&u.addedNodes.forEach(x=>{if(x.nodeType!==1||!x.offsetParent)return;const b=k(x);if(!b||S.assertions.some(L=>L.actionIdx===v&&L.selector===b&&L.type==="visible"))return;const I=((x.querySelector?.(".task-text")||x).textContent?.trim()||x.textContent?.trim()||"").slice(0,80)||void 0,{listSelector:O,index:R}=y(b,x),U={actionIdx:v,type:"visible",selector:b,text:I};O!=null&&(U.listSelector=O),R!=null&&(U.index=R),S.assertions.push(U)}),u.type==="attributes"){const x=k(u.target);if(!x||S.assertions.some(O=>O.actionIdx===v&&O.selector===x&&O.type==="class"))return;const{listSelector:b,index:D}=y(x,u.target),I={actionIdx:v,type:"class",selector:x,className:u.target.className};b!=null&&(I.listSelector=b),D!=null&&(I.index=D),S.assertions.push(I)}})}),S._observer.observe(m,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["class","style","hidden","disabled","aria-expanded","aria-checked"]});const E={};w.forEach(f=>{const v=u=>{const y=u.target;if(n&&m&&y?.nodeType===1&&!m.contains(y))return;let x="";if(y?.dataset){const N=t.autotag&&y.dataset[t.autotag];N&&(x=l(`[data-${t.autotag}="${N}"]`,y.parentElement))}if(!x&&y?.tagName){const N=y.id?"#"+y.id:y.className?y.tagName.toLowerCase()+"."+[...y.classList].join("."):y.tagName.toLowerCase();x=y.id?N:l(N,y.parentElement)}let b,D;if(x&&m&&m.querySelectorAll(x).length>1){let M=y;for(;M&&M!==m&&M.nodeType===1;){const P=t.autotag&&M.dataset&&M.dataset[t.autotag];if(P){const H=`[data-${t.autotag}="${P}"]`,J=m.querySelectorAll(H);if(J.length>1){const s=[...J].indexOf(M);if(s!==-1){b=H,D=s;break}}}M=M.parentElement}}const I=y?.tagName?y.tagName.toLowerCase()+(y.type?":"+y.type:""):void 0,O=f==="scroll"?window.scrollY:void 0,R=f==="input"||f==="change"?y?.value:void 0,U=f==="change"&&(y?.type==="checkbox"||y?.type==="radio")?y?.checked:void 0,L=a[f]!==void 0?a[f]:h[f]??0,$=()=>{const N={type:f,target:x,time:Date.now()};I&&(N.targetType=I),O!==void 0&&(N.scrollY=O),R!==void 0&&(N.value=R),U!==void 0&&(N.checked=U),b!=null&&(N.listSelector=b),D!=null&&(N.targetIndex=D),S.actions.push(N)};L===0?$():(clearTimeout(E[f]),E[f]=setTimeout($,L))};t.D.addEventListener(f,v,!0),S._listeners.push({ev:f,handler:v})})},t.stopRecording=()=>{const n=t.recorder;return n.active=!1,n._originalFetch&&(window.fetch=n._originalFetch,n._originalFetch=null),n._listeners.forEach(({ev:e,handler:o})=>{t.D.removeEventListener(e,o,!0)}),n._listeners=[],n._observer&&(n._observer.disconnect(),n._observer=null),{actions:[...n.actions],mocks:{...n.mocks},initialData:{...n.initialData},stepDelays:{...n.stepDelays},assertions:[...n.assertions||[]],observeRoot:n.observeRoot||null}},t.clearRecording=n=>{if(n!==void 0)sessionStorage?.removeItem("oTest-Recording-"+n);else for(let e=sessionStorage?.length-1;e>=0;e--){const o=sessionStorage?.key(e);o&&o.indexOf("oTest-Recording-")===0&&sessionStorage?.removeItem(o)}},t.exportTest=n=>{const e=n.actions.map(r=>{let c;return r.type==="scroll"?c=` window.scrollTo(0, ${r.scrollY||0}); return true;
|
|
15
15
|
`:r.type==="input"||r.type==="change"?c=(r.value!==void 0?` el.value = ${JSON.stringify(r.value)};
|
|
16
16
|
`:"")+(r.checked!==void 0?` el.checked = ${r.checked};
|
|
17
17
|
`:"")+` el.dispatchEvent(new Event('${r.type}', {bubbles:true})); return true;
|
|
@@ -29,18 +29,18 @@ ${e}
|
|
|
29
29
|
], () => {
|
|
30
30
|
// teardown
|
|
31
31
|
});
|
|
32
|
-
`},t.exportPlaywrightTest=(n,e={})=>{const o=e.testName||"Recorded session",r=n.initialData?.url??"/";let c="/";try{c=new URL(r).pathname||"/"}catch{c=r}const w=e.baseUrl||c,a=Object.values(n.mocks).map(l=>{const
|
|
32
|
+
`},t.exportPlaywrightTest=(n,e={})=>{const o=e.testName||"Recorded session",r=n.initialData?.url??"/";let c="/";try{c=new URL(r).pathname||"/"}catch{c=r}const w=e.baseUrl||c,a=Object.values(n.mocks).map(l=>{const k=l.url.startsWith("/")?l.url:"/"+l.url,m=JSON.stringify(l.response);return` await page.route('**${k}', async route => {
|
|
33
33
|
await route.fulfill({ status: ${l.status||200}, contentType: 'application/json',
|
|
34
34
|
body: JSON.stringify(${m}) });
|
|
35
35
|
});`}).join(`
|
|
36
|
-
`),
|
|
36
|
+
`),h=Object.assign({click:100,mouseover:50,scroll:30,input:50,change:50},n.stepDelays||{}),S=n.actions.map((l,k)=>{const m=l.listSelector!=null&&l.targetIndex!=null?l.target!==l.listSelector?`page.locator(${JSON.stringify(l.listSelector)}).nth(${l.targetIndex}).locator(${JSON.stringify(l.target)})`:`page.locator(${JSON.stringify(l.listSelector)}).nth(${l.targetIndex})`:`page.locator(${JSON.stringify(l.target)})`,E=` await page.waitForTimeout(${h[l.type]||50});`;let f;if(l.type==="scroll")f=` await page.evaluate(() => window.scrollTo(0, ${l.scrollY||0}));`;else if(l.type==="mouseover")f=` await ${m}.hover();
|
|
37
37
|
// Pure CSS :hover \u2014 no DOM mutation to assert.
|
|
38
|
-
// Fix: toggle a class in a mouseover handler, or add a page.screenshot() visual check.`;else if(l.type==="input")f=` await ${m}.fill(${JSON.stringify(l.value||"")});`;else if(l.type==="change"){const u=l.targetType||"";if(u.indexOf(":checkbox")!==-1||u.indexOf(":radio")!==-1){const
|
|
39
|
-
await expect(${
|
|
38
|
+
// Fix: toggle a class in a mouseover handler, or add a page.screenshot() visual check.`;else if(l.type==="input")f=` await ${m}.fill(${JSON.stringify(l.value||"")});`;else if(l.type==="change"){const u=l.targetType||"";if(u.indexOf(":checkbox")!==-1||u.indexOf(":radio")!==-1){const y=l.checked!==void 0?l.checked:l.value==="true"||l.value==="on";f=` await ${m}.${y?"check":"uncheck"}();`}else u.indexOf("select")!==-1?f=` await ${m}.selectOption(${JSON.stringify(l.value||"")});`:f=` await ${m}.fill(${JSON.stringify(l.value||"")});`}else f=` await ${m}.click();`;const v=(n.assertions||[]).filter(u=>u.actionIdx===k).filter((u,y,x)=>x.findIndex(b=>b.selector===u.selector&&b.type===u.type&&b.index===u.index)===y).map(u=>{const y=u.listSelector!=null&&u.index!=null?u.selector!==u.listSelector?`page.locator(${JSON.stringify(u.listSelector)}).nth(${u.index}).locator(${JSON.stringify(u.selector)})`:`page.locator(${JSON.stringify(u.listSelector)}).nth(${u.index})`:`page.locator(${JSON.stringify(u.selector)})`;if(u.type==="visible"){let x=` await expect(${y}).toBeVisible();`;return u.text&&(x+=`
|
|
39
|
+
await expect(${y}).toContainText(${JSON.stringify(u.text)});`),x}return u.type==="class"?` // class on ${u.selector} changed to: "${u.className}"`:""}).filter(Boolean).join(`
|
|
40
40
|
`);return f+`
|
|
41
|
-
`+
|
|
41
|
+
`+E+(v?`
|
|
42
42
|
`+v:"")}).join(`
|
|
43
|
-
`),
|
|
43
|
+
`),C=(n.assertions||[]).length>0;return`// Auto-generated by o.exportPlaywrightTest() \u2014 review and anonymize mocks before committing
|
|
44
44
|
// Prerequisites: npm install @playwright/test && npx playwright install chromium
|
|
45
45
|
// Run: npx playwright test recorded.spec.ts
|
|
46
46
|
import { test, expect } from '@playwright/test';
|
|
@@ -54,13 +54,13 @@ test(${JSON.stringify(o)}, async ({ page }) => {
|
|
|
54
54
|
|
|
55
55
|
`+(S?S+`
|
|
56
56
|
|
|
57
|
-
`:"")+(
|
|
57
|
+
`:"")+(C?` // Auto-generated assertions above \u2014 review for correctness before committing
|
|
58
58
|
`:` // TODO: Add assertions before committing, e.g.:
|
|
59
59
|
// await expect(page.locator('[data-qa="success-panel"]')).toBeVisible();
|
|
60
60
|
// await expect(page).toHaveURL(/\\/confirmation/);
|
|
61
61
|
// await expect(page.locator('[data-qa="error-banner"]')).not.toBeVisible();
|
|
62
62
|
`)+`});
|
|
63
|
-
`},t.playRecording=(n,e={})=>{const o=Object.assign({},n.mocks,e),r=window.fetch;window.fetch=(a,
|
|
63
|
+
`},t.playRecording=(n,e={})=>{const o=Object.assign({},n.mocks,e),r=window.fetch;window.fetch=(a,h={})=>{const C=(h.method||"GET").toUpperCase()+":"+a;if(o[C]){const l=o[C],k=typeof l.response=="string"?l.response:JSON.stringify(l.response);return Promise.resolve(new Response(k,{status:l.status||200}))}return r(a,h)};const c=n.actions.map(a=>[`${a.type} on ${a.target}`,()=>{let h=null;if(a.target)if(a.listSelector!=null&&a.targetIndex!=null){const C=t.D.querySelectorAll(a.listSelector)[a.targetIndex];C&&(h=a.target!==a.listSelector?C.querySelector(a.target):C,!h&&a.target!==a.listSelector&&(h=C))}else h=t.D.querySelector(a.target);return!h&&a.type!=="scroll"?`element not found: ${a.target}`:(a.type==="scroll"?window.scrollTo(0,a.scrollY||0):a.type==="input"||a.type==="change"?(a.value!==void 0&&(h.value=a.value),a.checked!==void 0&&(h.checked=a.checked),h.dispatchEvent(new Event(a.type,{bubbles:!0}))):a.type==="click"?h.click():h.dispatchEvent(new MouseEvent(a.type,{bubbles:!0,cancelable:!0})),!0)}]);return t.test("Recorded playback",...c,()=>{window.fetch=r})},t.testOverlay=()=>{const n="o-test-overlay-btn",e="o-test-overlay-panel";if(t("#"+n).el)return;const o=()=>{const l=t("#"+e);if(!l.el)return;const k=t.tRes.length;let E=`<b>Tests: ${t.tRes.filter(Boolean).length}/${k}</b><hr style="margin:4px 0">`;t.tLog.forEach((f,v)=>{const u=t.tRes[v];E+=`<div style="margin:2px 0;padding:2px 4px;border-radius:3px;background:${u?"#d4edda":"#f8d7da"};color:${u?"#155724":"#721c24"};font-size:11px;white-space:pre-wrap">${f||"Test #"+v}</div>`}),E+='<button id="o-test-export" style="margin-top:6px;padding:4px 8px;font-size:11px;cursor:pointer">Export results</button>',l.html(E),t("#o-test-export").on("click",()=>{const f=JSON.stringify({results:t.tRes,logs:t.tLog},null,2),v=new Blob([f],{type:"application/json"}),u=t.D.createElement("a");u.href=URL.createObjectURL(v),u.download="objs-test-results.json",u.click()})},r={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"},c=t.initState({tag:"div",id:n,className:"o-test-overlay",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;",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="${e}" 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>`}).appendInside("body"),w=()=>{c.css(r)};let a=null;const h=l=>{a&&(r.left=a.left+(l.clientX-a.startX)+"px",r.top=a.top+(l.clientY-a.startY)+"px",delete r.bottom,r.transform="none",w())},S=()=>{a&&(r.cursor="grab",w()),a=null};c.on("mousedown",l=>{if(l.target.closest(".o-test-overlay-close")||l.target.closest(".o-test-overlay-toggle")||l.target.closest("#"+e))return;const k=c.el.getBoundingClientRect();a={startX:l.clientX,startY:l.clientY,left:k.left,top:k.top},r.cursor="grabbing",w()}),t.D.addEventListener("mousemove",h),t.D.addEventListener("mouseup",S);const C=()=>{const l=t(".o-test-overlay-summary");l.els.length&&l.innerText(`Tests: ${t.tRes.filter(Boolean).length}/${t.tRes.length}`)};c.first(".o-test-overlay-toggle").on("click",()=>{const l=t("#"+e);if(!l.el)return;const k=l.el.style.display!=="none";l.css({display:k?"none":"block"}),k||o()}),c.first(".o-test-overlay-close").on("click",()=>{t.D.removeEventListener("mousemove",h),t.D.removeEventListener("mouseup",S),c.remove()}),t.testOverlay.showPanel=()=>{const l=t("#"+e);l.el&&(l.css({display:"block"}),o(),C())},t._testOverlayBase||(t._testOverlayBase=t.test),t.test=(...l)=>{const k=t._testOverlayBase(...l),m=t.tFns[k];return t.tFns[k]=E=>{typeof m=="function"&&m(E);const f=t("#"+e);f.el&&f.el.style.display!=="none"&&o(),C()},k}},t.testConfirm=(n,e=[],o={})=>new Promise(r=>{t(".o-tc-overlay").remove();const c=o.confirm||"Continue",w=e.length>0,a=w?"#dc2626":"#2563eb",h=e.map((y,x)=>"o-tc-cb-"+x),C=w?`<style>.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;}</style><ul class="o-tc-list" style="margin:8px 0 0;padding:0;list-style:none;font-size:13px;color:#cbd5e1;">`+e.map((y,x)=>`<li style="margin-bottom:4px;"><label for="${h[x]}" style="display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none;"><input type="checkbox" id="${h[x]}" class="o-tc-item-cb"> <span>${y}</span></label></li>`).join("")+"</ul>":"",l=t.initState({tag:"div",className:"o-tc-overlay",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;",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;">${n}: Paused</span><button type="button" class="o-tc-ok" style="padding:6px 14px;background:${a};color:#fff;border:none;border-radius:6px;font-weight:600;cursor:pointer;font-size:13px;flex-shrink:0;">${c}</button></div>`+C+"</div>"}).appendInside("body"),k={padding:"6px 14px",background:w?"#dc2626":"#2563eb",color:"#fff",border:"none","border-radius":"6px","font-weight":"600",cursor:"pointer","font-size":"13px","flex-shrink":"0"};if(w){const y=l.first(".o-tc-ok"),x=t(".o-tc-overlay .o-tc-item-cb"),b=()=>{const D=x.length>0&&x.els.every(I=>I.checked);y.css({...k,background:D?"#16a34a":"#dc2626"})};x.on("change",b)}let m=null;const E={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"},f=()=>{l.css(E)},v=y=>{m&&(E.left=m.left+(y.clientX-m.startX)+"px",E.top=m.top+(y.clientY-m.startY)+"px",delete E.bottom,E.transform="none",f())},u=()=>{m&&(E.cursor="grab",f()),m=null};l.on("mousedown",y=>{if(y.target.closest(".o-tc-ok"))return;const x=l.el.getBoundingClientRect();m={startX:y.clientX,startY:y.clientY,left:x.left,top:x.top},E.cursor="grabbing",f()}),t.D.addEventListener("mousemove",v),t.D.addEventListener("mouseup",u),l.first(".o-tc-ok").on("click",()=>{t.D.removeEventListener("mousemove",v),t.D.removeEventListener("mouseup",u);let y=[];w&&t(".o-tc-overlay .o-tc-item-cb").els.forEach((b,D)=>{!b.checked&&e[D]!==void 0&&y.push(e[D])}),l.remove(),y.length===0?r({ok:!0}):r({ok:!1,errors:y})})});
|
|
64
64
|
|
|
65
65
|
export { o };
|
|
66
66
|
export default o;
|
package/objs.js
CHANGED
|
@@ -48,6 +48,7 @@ const o = (query) => {
|
|
|
48
48
|
ssr = typeof process !== "undefined" || o.D === o.DocumentMVP,
|
|
49
49
|
i = 0,
|
|
50
50
|
j = 0;
|
|
51
|
+
const self = result; // capture so connect() always passes this instance to loader
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
54
|
* Shortcut for typeof operator
|
|
@@ -313,7 +314,10 @@ const o = (query) => {
|
|
|
313
314
|
result[state] = returner((props = [{}]) => {
|
|
314
315
|
result.currentState = state;
|
|
315
316
|
const data = states[state] || { tag: "div" };
|
|
316
|
-
const
|
|
317
|
+
const slice = Array.isArray(result.els)
|
|
318
|
+
? result.els.slice(finish, start + ONE)
|
|
319
|
+
: [];
|
|
320
|
+
const els = slice.length ? slice : (result.els || []);
|
|
317
321
|
|
|
318
322
|
if (type(data) === objectType) {
|
|
319
323
|
data.state = state;
|
|
@@ -439,8 +443,7 @@ const o = (query) => {
|
|
|
439
443
|
[state, [notEmptyStringType]],
|
|
440
444
|
[fail, [stringType, undefinedType]],
|
|
441
445
|
]);
|
|
442
|
-
|
|
443
|
-
loader.connect(result, state, fail);
|
|
446
|
+
loader.connect(self, state, fail);
|
|
444
447
|
}, "connect");
|
|
445
448
|
|
|
446
449
|
/**
|
|
@@ -1491,7 +1494,8 @@ o.createStore = (defaults) => {
|
|
|
1491
1494
|
return store;
|
|
1492
1495
|
};
|
|
1493
1496
|
|
|
1494
|
-
// Short values
|
|
1497
|
+
// Short values (o.U = async "waiting" sentinel for tests)
|
|
1498
|
+
o.U = undefined;
|
|
1495
1499
|
o.W = 2;
|
|
1496
1500
|
o.H = 100;
|
|
1497
1501
|
o.F = false;
|
|
@@ -1974,7 +1978,7 @@ o.newLoader = (promise) => {
|
|
|
1974
1978
|
if (finished) {
|
|
1975
1979
|
if (error) {
|
|
1976
1980
|
fail ? listener[fail]() : "";
|
|
1977
|
-
} else {
|
|
1981
|
+
} else if (typeof listener[state] === "function") {
|
|
1978
1982
|
listener[state](data);
|
|
1979
1983
|
}
|
|
1980
1984
|
} else {
|
|
@@ -2550,9 +2554,8 @@ o.test = (title = "", ...tests) => {
|
|
|
2550
2554
|
sessionStorage.getItem(`oTest-Status-${testN}`) || "[]",
|
|
2551
2555
|
);
|
|
2552
2556
|
for (let i = 0; i < o.tStatus[testN].length; i++) {
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
}
|
|
2557
|
+
const s = o.tStatus[testN][i];
|
|
2558
|
+
if (s === true || s === false) done++;
|
|
2556
2559
|
}
|
|
2557
2560
|
}
|
|
2558
2561
|
|
|
@@ -2737,7 +2740,7 @@ o.testUpdate = (info, res = o.F, suff = "") => {
|
|
|
2737
2740
|
}
|
|
2738
2741
|
};
|
|
2739
2742
|
|
|
2740
|
-
if (o.tStatus[testN][info.i] === o.U) {
|
|
2743
|
+
if (o.tStatus[testN][info.i] === o.U || o.tStatus[testN][info.i] === null) {
|
|
2741
2744
|
o.tStatus[testN][info.i] = res === true;
|
|
2742
2745
|
if (res === true) {
|
|
2743
2746
|
if (info.tShowOk) {
|
|
@@ -2765,8 +2768,8 @@ o.testUpdate = (info, res = o.F, suff = "") => {
|
|
|
2765
2768
|
n = 0;
|
|
2766
2769
|
|
|
2767
2770
|
for (const s of o.tStatus[testN]) {
|
|
2768
|
-
if (s === o.U) {
|
|
2769
|
-
// if waiting tests there
|
|
2771
|
+
if (s === o.U || s === null) {
|
|
2772
|
+
// if waiting tests there (null = restored from JSON)
|
|
2770
2773
|
return;
|
|
2771
2774
|
}
|
|
2772
2775
|
if (!s) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objs-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lightweight (~6 kB) library for fast, AI-friendly front-end development: samples and state control, built-in store (createStore), routing, caching, and recording → Playwright tests. No build step; split design into samples and give them data and actions. Works standalone or alongside React; SSR and hydrate from server-rendered DOM. v2.0: exportPlaywrightTest(), refs, TypeScript definitions, recording in all builds.",
|
|
6
6
|
"homepage": "https://en.fous.name/objs/",
|