objs-core 1.1.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXAMPLES.md +1637 -0
- package/README.md +346 -75
- package/SKILL.md +500 -0
- package/objs.built.js +2657 -0
- package/objs.built.min.js +67 -0
- package/objs.d.ts +455 -0
- package/objs.js +3805 -0
- package/package.json +70 -37
- package/objs.1.1.js +0 -1205
- package/objs.1.1.js.zip +0 -0
- package/objs.1.1.min.js +0 -2
- package/objs.1.1.min.js.zip +0 -0
- package/objs.npm.1.1.js +0 -16
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Objs-core library
|
|
3
|
+
* @version 2.0
|
|
4
|
+
* @author Roman Torshin
|
|
5
|
+
* @license Apache-2.0
|
|
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
|
+
`,S=`
|
|
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
|
+
`):(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,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
|
+
`:r.type==="input"||r.type==="change"?c=(r.value!==void 0?` el.value = ${JSON.stringify(r.value)};
|
|
16
|
+
`:"")+(r.checked!==void 0?` el.checked = ${r.checked};
|
|
17
|
+
`:"")+` el.dispatchEvent(new Event('${r.type}', {bubbles:true})); return true;
|
|
18
|
+
`:c=r.type==="click"?` el.click(); return true;
|
|
19
|
+
`:` el.dispatchEvent(new MouseEvent('${r.type}', {bubbles:true,cancelable:true})); return true;
|
|
20
|
+
`,` ['${r.type} on ${r.target}', () => {
|
|
21
|
+
const el = document.querySelector('${r.target}');
|
|
22
|
+
if (!el) return 'element not found';
|
|
23
|
+
`+c+" }],"}).join(`
|
|
24
|
+
`);return`// Auto-generated by o.exportTest() \u2014 review and anonymize mocks before committing
|
|
25
|
+
const recordingMocks = ${Object.keys(n.mocks).length?JSON.stringify(n.mocks,null,2):"{}"};
|
|
26
|
+
|
|
27
|
+
o.addTest('Recorded test', [
|
|
28
|
+
${e}
|
|
29
|
+
], () => {
|
|
30
|
+
// teardown
|
|
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 k=l.url.startsWith("/")?l.url:"/"+l.url,m=JSON.stringify(l.response);return` await page.route('**${k}', async route => {
|
|
33
|
+
await route.fulfill({ status: ${l.status||200}, contentType: 'application/json',
|
|
34
|
+
body: JSON.stringify(${m}) });
|
|
35
|
+
});`}).join(`
|
|
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
|
+
// 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 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
|
+
`);return f+`
|
|
41
|
+
`+E+(v?`
|
|
42
|
+
`+v:"")}).join(`
|
|
43
|
+
`),C=(n.assertions||[]).length>0;return`// Auto-generated by o.exportPlaywrightTest() \u2014 review and anonymize mocks before committing
|
|
44
|
+
// Prerequisites: npm install @playwright/test && npx playwright install chromium
|
|
45
|
+
// Run: npx playwright test recorded.spec.ts
|
|
46
|
+
import { test, expect } from '@playwright/test';
|
|
47
|
+
|
|
48
|
+
test(${JSON.stringify(o)}, async ({ page }) => {
|
|
49
|
+
`+(a?` // Network mocks \u2014 edit/anonymize before committing
|
|
50
|
+
`+a+`
|
|
51
|
+
|
|
52
|
+
`:"")+` // Set baseURL in playwright.config.ts: { use: { baseURL: 'https://staging.example.com' } }
|
|
53
|
+
await page.goto(${JSON.stringify(w)});
|
|
54
|
+
|
|
55
|
+
`+(S?S+`
|
|
56
|
+
|
|
57
|
+
`:"")+(C?` // Auto-generated assertions above \u2014 review for correctness before committing
|
|
58
|
+
`:` // TODO: Add assertions before committing, e.g.:
|
|
59
|
+
// await expect(page.locator('[data-qa="success-panel"]')).toBeVisible();
|
|
60
|
+
// await expect(page).toHaveURL(/\\/confirmation/);
|
|
61
|
+
// await expect(page.locator('[data-qa="error-banner"]')).not.toBeVisible();
|
|
62
|
+
`)+`});
|
|
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
|
+
|
|
65
|
+
export { o };
|
|
66
|
+
export default o;
|
|
67
|
+
if (typeof window !== "undefined") window.o = o;
|
package/objs.d.ts
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Objs-core v2.0 TypeScript definitions
|
|
3
|
+
* @license Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ─── Core instance types ──────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
/** State definition object for o.init(). `render` is the reserved creation state. */
|
|
9
|
+
export interface StateObject {
|
|
10
|
+
tag?: string;
|
|
11
|
+
tagName?: string;
|
|
12
|
+
/** Component name — used for o.autotag data-* attribute (camelCase → kebab-case) */
|
|
13
|
+
name?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
/** React-style alias for `class` */
|
|
16
|
+
className?: string;
|
|
17
|
+
id?: string;
|
|
18
|
+
html?: string;
|
|
19
|
+
innerHTML?: string;
|
|
20
|
+
style?: string | Record<string, string>;
|
|
21
|
+
dataset?: Record<string, string>;
|
|
22
|
+
events?: Record<string, EventListener>;
|
|
23
|
+
[attr: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Parameter object passed to every state function.
|
|
27
|
+
*
|
|
28
|
+
* State functions receive ONE merged object: { ...passedProps, self, o, i, parent, data }.
|
|
29
|
+
* - Properties from the calling argument are spread into the context.
|
|
30
|
+
* - `data` holds the raw first argument (useful for primitives: `comp.setState(5)` → `data = 5`).
|
|
31
|
+
* - `parent` is the ObjsInstance that this component was `appendInside()` into (if any).
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Passing an object — destructure its keys:
|
|
35
|
+
* sync: ({ self, values, errors }) => { ... } // comp.sync(store)
|
|
36
|
+
* // Passing a primitive — use `data`:
|
|
37
|
+
* setCount: ({ self, data }) => { self.html(data); } // comp.setCount(5)
|
|
38
|
+
*/
|
|
39
|
+
export interface StateParams {
|
|
40
|
+
self: ObjsInstance;
|
|
41
|
+
o: typeof o;
|
|
42
|
+
i: number;
|
|
43
|
+
/** The ObjsInstance this component was appendInside() into, or null */
|
|
44
|
+
parent: ObjsInstance | null;
|
|
45
|
+
/** The raw first argument passed when the state method was called */
|
|
46
|
+
data: unknown;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** State function signature */
|
|
51
|
+
export type StateFunction = (params: StateParams) => void | unknown;
|
|
52
|
+
|
|
53
|
+
/** States map passed to o.init() */
|
|
54
|
+
export type StatesMap = {
|
|
55
|
+
render?: StateObject | StateFunction | string;
|
|
56
|
+
[stateName: string]: StateObject | StateFunction | string | undefined;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Test case tuple: [title, testFn | true | false | string] */
|
|
60
|
+
export type TestCase = [string, ((info: TestInfo) => boolean | string | undefined) | boolean | string | undefined];
|
|
61
|
+
|
|
62
|
+
export interface TestInfo {
|
|
63
|
+
n: number;
|
|
64
|
+
i: number;
|
|
65
|
+
title: string;
|
|
66
|
+
tShowOk: boolean;
|
|
67
|
+
tStyled: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Object returned by o.addTest() */
|
|
71
|
+
export interface TestHandle {
|
|
72
|
+
testId: number;
|
|
73
|
+
run(): void;
|
|
74
|
+
autorun(): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Object returned by o.newLoader() */
|
|
78
|
+
export interface ObjsLoader<T = unknown> {
|
|
79
|
+
reload(promise: Promise<T>): void;
|
|
80
|
+
isObjsLoader: true;
|
|
81
|
+
listeners: Array<[ObjsInstance, string, string?]>;
|
|
82
|
+
isFinished(): boolean;
|
|
83
|
+
getStore(): T | null;
|
|
84
|
+
connect(listener: ObjsInstance, state?: string, fail?: string): void;
|
|
85
|
+
disconnect(listener: ObjsInstance): void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Element measurement result from o.measure() */
|
|
89
|
+
export interface MeasureResult {
|
|
90
|
+
width: number;
|
|
91
|
+
height: number;
|
|
92
|
+
top: number;
|
|
93
|
+
left: number;
|
|
94
|
+
visible: boolean;
|
|
95
|
+
opacity: string;
|
|
96
|
+
zIndex: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Recorded action entry */
|
|
100
|
+
export interface RecordedAction {
|
|
101
|
+
type: string;
|
|
102
|
+
target: string;
|
|
103
|
+
time: number;
|
|
104
|
+
scrollY?: number;
|
|
105
|
+
value?: string;
|
|
106
|
+
/** When target matches multiple elements, selector for the repeated list item container */
|
|
107
|
+
listSelector?: string;
|
|
108
|
+
/** Index within listSelector matches for replay by index */
|
|
109
|
+
targetIndex?: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type Assertion = {
|
|
113
|
+
actionIdx: number;
|
|
114
|
+
type: 'visible' | 'class';
|
|
115
|
+
selector: string;
|
|
116
|
+
text?: string;
|
|
117
|
+
className?: string;
|
|
118
|
+
/** When selector matches multiple, selector for the list item container */
|
|
119
|
+
listSelector?: string;
|
|
120
|
+
/** Index within listSelector matches */
|
|
121
|
+
index?: number;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** Full recording object */
|
|
125
|
+
export interface Recording {
|
|
126
|
+
actions: RecordedAction[];
|
|
127
|
+
mocks: Record<string, { url: string; method: string; request: unknown; response: unknown; status: number }>;
|
|
128
|
+
initialData: Record<string, unknown>;
|
|
129
|
+
stepDelays?: Record<string, number>;
|
|
130
|
+
assertions: Assertion[];
|
|
131
|
+
observeRoot: string | null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Reactive store returned by o.createStore().
|
|
136
|
+
* Components subscribe via store.subscribe(component, stateName).
|
|
137
|
+
* When store.notify() is called, each subscribed component's state method
|
|
138
|
+
* receives the full store object, whose properties are merged into the
|
|
139
|
+
* state context: { ...storeProps, self, o, i, parent, data }.
|
|
140
|
+
*/
|
|
141
|
+
export interface ObjsStore<T extends object = Record<string, unknown>> {
|
|
142
|
+
_listeners: Array<(store: this) => void>;
|
|
143
|
+
/** Subscribe an Objs component state to this store.
|
|
144
|
+
* On every notify(), component[stateName](store) is called. */
|
|
145
|
+
subscribe(component: ObjsInstance, stateName: string): this;
|
|
146
|
+
/** Notify all subscribers — passes `this` store as the argument. */
|
|
147
|
+
notify(): void;
|
|
148
|
+
/** Restore store data from initial defaults; does not replace subscribe, notify, _listeners. */
|
|
149
|
+
reset(): void;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ─── ObjsInstance (chainable result of o() / o.init()) ────────────────────
|
|
153
|
+
|
|
154
|
+
export interface ObjsInstance {
|
|
155
|
+
// Properties
|
|
156
|
+
els: Element[];
|
|
157
|
+
el: Element | undefined;
|
|
158
|
+
last: Element | undefined;
|
|
159
|
+
length: number;
|
|
160
|
+
initID: number | undefined;
|
|
161
|
+
ie: Record<string, Array<[EventListener, unknown?, unknown?]>>;
|
|
162
|
+
delegated: Record<string, EventListener[]>;
|
|
163
|
+
store: Record<string, unknown>;
|
|
164
|
+
states: string[];
|
|
165
|
+
isDebug: boolean;
|
|
166
|
+
currentState: string;
|
|
167
|
+
savedStates: Record<string, unknown>;
|
|
168
|
+
isRoot: boolean;
|
|
169
|
+
/** The ObjsInstance this component was appendInside() into, or null */
|
|
170
|
+
_parent: ObjsInstance | null;
|
|
171
|
+
/**
|
|
172
|
+
* Named sub-elements collected from `ref="name"` attributes in the rendered HTML.
|
|
173
|
+
* Populated automatically on first render; `ref` attributes are removed from the DOM.
|
|
174
|
+
* @example
|
|
175
|
+
* // In render: html: `<div ref="body"></div>`
|
|
176
|
+
* // After render: component.refs.body → ObjsInstance wrapping that div
|
|
177
|
+
*/
|
|
178
|
+
refs: Record<string, ObjsInstance>;
|
|
179
|
+
|
|
180
|
+
// Selection
|
|
181
|
+
reset(query: QueryArg): ObjsInstance;
|
|
182
|
+
select(i?: number): ObjsInstance;
|
|
183
|
+
all(): ObjsInstance;
|
|
184
|
+
find(query: string): ObjsInstance;
|
|
185
|
+
first(query?: string): ObjsInstance;
|
|
186
|
+
add(el: QueryArg): ObjsInstance;
|
|
187
|
+
skip(i?: number): ObjsInstance;
|
|
188
|
+
remove(i?: number): ObjsInstance;
|
|
189
|
+
|
|
190
|
+
// State
|
|
191
|
+
init(states: StatesMap | StateFunction | string): ObjsInstance;
|
|
192
|
+
initState(state: StateObject, props?: Record<string, unknown>): ObjsInstance;
|
|
193
|
+
sample(state?: string): StatesMap;
|
|
194
|
+
saveState(stateId?: string, root?: boolean): ObjsInstance;
|
|
195
|
+
revertState(stateId?: string): ObjsInstance;
|
|
196
|
+
loseState(stateId: string): ObjsInstance;
|
|
197
|
+
|
|
198
|
+
// DOM manipulation
|
|
199
|
+
html(html?: string): string | ObjsInstance;
|
|
200
|
+
innerHTML(html?: string): string[] | ObjsInstance;
|
|
201
|
+
innerText(text: string): ObjsInstance;
|
|
202
|
+
textContent(content: string): ObjsInstance;
|
|
203
|
+
val(value?: string): string | ObjsInstance;
|
|
204
|
+
attr(attr: string, value?: string | null): string | string[] | ObjsInstance;
|
|
205
|
+
attrs(): Record<string, string> | Array<Record<string, string>>;
|
|
206
|
+
dataset(values?: Record<string, string>): Record<string, string> | Array<Record<string, string>> | ObjsInstance;
|
|
207
|
+
style(value?: string | null): ObjsInstance;
|
|
208
|
+
css(styles: Record<string, string> | null): ObjsInstance;
|
|
209
|
+
setClass(value: string): ObjsInstance;
|
|
210
|
+
addClass(...cls: string[]): ObjsInstance;
|
|
211
|
+
removeClass(...cls: string[]): ObjsInstance;
|
|
212
|
+
toggleClass(cls: string, rule?: boolean): ObjsInstance;
|
|
213
|
+
haveClass(cls: string): boolean;
|
|
214
|
+
|
|
215
|
+
// Events
|
|
216
|
+
on(events: string, handler: EventListener, options?: EventListenerOptions): ObjsInstance;
|
|
217
|
+
off(events: string, handler: EventListener, options?: EventListenerOptions): ObjsInstance;
|
|
218
|
+
offAll(event?: string): ObjsInstance;
|
|
219
|
+
onAll(event?: string): ObjsInstance;
|
|
220
|
+
onDelegate(events: string, selector: string, handler: EventListener): ObjsInstance;
|
|
221
|
+
offDelegate(event?: string): ObjsInstance;
|
|
222
|
+
onParent(events: string, selectorOrEl: string | Element, handler: EventListener): ObjsInstance;
|
|
223
|
+
offParent(events: string, query: string): ObjsInstance;
|
|
224
|
+
|
|
225
|
+
// DOM insertion
|
|
226
|
+
appendInside(el: QueryArg): ObjsInstance;
|
|
227
|
+
appendBefore(el: QueryArg): ObjsInstance;
|
|
228
|
+
appendAfter(el: QueryArg): ObjsInstance;
|
|
229
|
+
|
|
230
|
+
// Misc
|
|
231
|
+
forEach(fn: (ctx: { o: typeof o; self: ObjsInstance; i: number; el: Element }) => void): ObjsInstance;
|
|
232
|
+
connect(loader: ObjsLoader, state?: string, fail?: string): ObjsInstance;
|
|
233
|
+
getSSR(initId?: number): ObjsInstance;
|
|
234
|
+
saveAs(key: string): ObjsInstance;
|
|
235
|
+
unmount(): boolean;
|
|
236
|
+
take(query: QueryArg): ObjsInstance;
|
|
237
|
+
|
|
238
|
+
// React
|
|
239
|
+
prepareFor(createElement: Function, component?: Function): unknown;
|
|
240
|
+
|
|
241
|
+
// Debug (behind __DEV__ in build)
|
|
242
|
+
debug?: () => ObjsInstance;
|
|
243
|
+
|
|
244
|
+
// Dynamic state methods added by init()
|
|
245
|
+
render?: (props?: Record<string, unknown> | Array<Record<string, unknown>>) => ObjsInstance;
|
|
246
|
+
[stateName: string]: unknown;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
type QueryArg = string | Element | Element[] | ObjsInstance | number;
|
|
250
|
+
|
|
251
|
+
// ─── o() function and static API ─────────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
declare function o(query?: QueryArg): ObjsInstance;
|
|
254
|
+
|
|
255
|
+
declare namespace o {
|
|
256
|
+
// Core
|
|
257
|
+
const inits: ObjsInstance[];
|
|
258
|
+
const getSaved: Record<string, ObjsInstance>;
|
|
259
|
+
const errors: Error[];
|
|
260
|
+
let debug: boolean;
|
|
261
|
+
let showErrors: boolean;
|
|
262
|
+
function logErrors(): void;
|
|
263
|
+
function onError(error: Error, name?: string): void;
|
|
264
|
+
function reactRender(...args: unknown[]): unknown;
|
|
265
|
+
|
|
266
|
+
// Autotag
|
|
267
|
+
let autotag: string | undefined;
|
|
268
|
+
|
|
269
|
+
function reactQA(componentName: string): Record<string, string>;
|
|
270
|
+
|
|
271
|
+
// Element selection
|
|
272
|
+
function first(query: string): ObjsInstance;
|
|
273
|
+
function take(query: QueryArg): ObjsInstance;
|
|
274
|
+
|
|
275
|
+
// State
|
|
276
|
+
function init(states: StatesMap | StateFunction | string): ObjsInstance;
|
|
277
|
+
function initState(state: StateObject, props?: Record<string, unknown>): ObjsInstance;
|
|
278
|
+
function getStates(): (string[] | undefined)[];
|
|
279
|
+
function getStores(): (Record<string, unknown> | undefined)[];
|
|
280
|
+
function getListeners(): (Record<string, unknown> | undefined)[];
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Create a reactive store — plain object with built-in subscribe / notify.
|
|
284
|
+
* Components subscribed via store.subscribe(comp, 'state') receive the store
|
|
285
|
+
* as their merged state context: { ...storeProps, self, o, i, parent, data }.
|
|
286
|
+
* @example
|
|
287
|
+
* const store = o.createStore({ count: 0, inc() { this.count++; this.notify(); } });
|
|
288
|
+
* store.subscribe(badge, 'sync'); // badge.sync(store) on every notify
|
|
289
|
+
*/
|
|
290
|
+
function createStore<T extends object>(defaults: T): T & ObjsStore<T>;
|
|
291
|
+
|
|
292
|
+
// AJAX
|
|
293
|
+
function ajax(url: string, props?: Record<string, unknown>): Promise<Response>;
|
|
294
|
+
function get(url: string, props?: Record<string, unknown>): Promise<Response>;
|
|
295
|
+
function post(url: string, props?: Record<string, unknown>): Promise<Response>;
|
|
296
|
+
function getParams(key?: string): Record<string, string> | string;
|
|
297
|
+
|
|
298
|
+
// Loader
|
|
299
|
+
function newLoader<T = unknown>(promise?: Promise<T>): ObjsLoader<T>;
|
|
300
|
+
|
|
301
|
+
// Include
|
|
302
|
+
let incCache: boolean;
|
|
303
|
+
let incCacheExp: number;
|
|
304
|
+
let incTimeout: number;
|
|
305
|
+
let incSource: string;
|
|
306
|
+
let incForce: boolean;
|
|
307
|
+
let incAsync: boolean;
|
|
308
|
+
let incCors: boolean;
|
|
309
|
+
let incFns: Record<string, number>;
|
|
310
|
+
function inc(sources: Record<string, string>, callBack?: (setId: number) => void, callBad?: (setId: number) => void): number;
|
|
311
|
+
function incCheck(set?: number, fnId?: number, loaded?: number): boolean;
|
|
312
|
+
function incCacheClear(all?: boolean): boolean;
|
|
313
|
+
|
|
314
|
+
// Routing
|
|
315
|
+
function route(path: string | boolean | ((path: string) => boolean), task?: Function | object): boolean | object;
|
|
316
|
+
function router(routes: Record<string, Function | object>): boolean;
|
|
317
|
+
|
|
318
|
+
// Cookies
|
|
319
|
+
function setCookie(title: string, value?: string | number | boolean, params?: Record<string, unknown>): void;
|
|
320
|
+
function getCookie(title: string): string | undefined;
|
|
321
|
+
function deleteCookie(title: string): void;
|
|
322
|
+
function clearCookies(): void;
|
|
323
|
+
/** Clear cookies, localStorage, and test-related sessionStorage. Call after Cookies/LS/SS tests. */
|
|
324
|
+
function clearAfterTests(): void;
|
|
325
|
+
|
|
326
|
+
// Storage
|
|
327
|
+
function clearLocalStorage(all?: boolean): void;
|
|
328
|
+
function clearSessionStorage(onlyTests?: boolean): void;
|
|
329
|
+
function clearTestsStorage(): void;
|
|
330
|
+
|
|
331
|
+
// Utilities
|
|
332
|
+
function camelToKebab(str: string): string;
|
|
333
|
+
function kebabToCamel(str: string): string;
|
|
334
|
+
function verify(pairs: Array<[unknown, string | string[]]>, safe?: boolean): boolean | Error;
|
|
335
|
+
function safeVerify(pairs: Array<[unknown, string | string[]]>): boolean;
|
|
336
|
+
const C: (obj: object, key: string) => boolean;
|
|
337
|
+
const F: false;
|
|
338
|
+
const U: undefined;
|
|
339
|
+
|
|
340
|
+
// Store adapters (always present, prod + dev)
|
|
341
|
+
function connectRedux<S = unknown>(
|
|
342
|
+
store: { getState(): S; subscribe(listener: () => void): () => void },
|
|
343
|
+
selector: (state: S) => unknown,
|
|
344
|
+
component: ObjsInstance,
|
|
345
|
+
state?: string
|
|
346
|
+
): () => void;
|
|
347
|
+
|
|
348
|
+
function connectMobX<T = unknown>(
|
|
349
|
+
mobx: { autorun(fn: () => void): () => void },
|
|
350
|
+
observable: T,
|
|
351
|
+
accessor: (obs: T) => unknown,
|
|
352
|
+
component: ObjsInstance,
|
|
353
|
+
state?: string
|
|
354
|
+
): () => void;
|
|
355
|
+
|
|
356
|
+
let ObjsContext: unknown;
|
|
357
|
+
|
|
358
|
+
function withReactContext<C = unknown>(
|
|
359
|
+
React: { useContext(ctx: unknown): C; useEffect(fn: () => void, deps: unknown[]): void },
|
|
360
|
+
Context: unknown,
|
|
361
|
+
selector: (value: C) => unknown,
|
|
362
|
+
component: ObjsInstance,
|
|
363
|
+
state?: string
|
|
364
|
+
): () => null;
|
|
365
|
+
|
|
366
|
+
// SSR
|
|
367
|
+
const DocumentMVP: {
|
|
368
|
+
createElement(tag: string): unknown;
|
|
369
|
+
addEventListener(): void;
|
|
370
|
+
parseElement(elem: unknown, outer?: boolean): string;
|
|
371
|
+
};
|
|
372
|
+
let D: Document | typeof DocumentMVP;
|
|
373
|
+
|
|
374
|
+
const recorder: {
|
|
375
|
+
active: boolean;
|
|
376
|
+
actions: RecordedAction[];
|
|
377
|
+
mocks: Recording['mocks'];
|
|
378
|
+
initialData: Record<string, unknown>;
|
|
379
|
+
assertions: Assertion[];
|
|
380
|
+
observeRoot: string | null;
|
|
381
|
+
_observer: MutationObserver | null;
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* Start recording user interactions.
|
|
385
|
+
* Available in all builds — QA testers can record on staging/production.
|
|
386
|
+
* @param observe CSS selector to scope the MutationObserver (reduces assertion noise)
|
|
387
|
+
* @param events Events to record (default: click, mouseover, scroll, input, change)
|
|
388
|
+
* @param timeouts Debounce delays per event type in ms
|
|
389
|
+
*/
|
|
390
|
+
function startRecording(observe?: string, events?: string[], timeouts?: Record<string, number>): void;
|
|
391
|
+
function stopRecording(): Recording;
|
|
392
|
+
function clearRecording(id?: number): void;
|
|
393
|
+
function exportTest(recording: Recording): string;
|
|
394
|
+
function exportPlaywrightTest(
|
|
395
|
+
recording: Recording,
|
|
396
|
+
options?: { testName?: string; baseUrl?: string }
|
|
397
|
+
): string;
|
|
398
|
+
|
|
399
|
+
// Test framework
|
|
400
|
+
let tLog: string[];
|
|
401
|
+
let tRes: boolean[];
|
|
402
|
+
let tStatus: Array<Array<boolean | undefined>>;
|
|
403
|
+
let tFns: Array<((testN: number) => void) | undefined>;
|
|
404
|
+
let tShowOk: boolean;
|
|
405
|
+
let tStyled: boolean;
|
|
406
|
+
let tTime: number;
|
|
407
|
+
let tests: Array<{ title: string; tests: TestCase[]; hooks: { before?: Function; after?: Function } }>;
|
|
408
|
+
let tAutolog: boolean;
|
|
409
|
+
let tBeforeEach: ((info: TestInfo) => void) | undefined;
|
|
410
|
+
let tAfterEach: ((info: TestInfo, result: unknown) => void) | undefined;
|
|
411
|
+
|
|
412
|
+
function test(title: string, ...cases: Array<TestCase | (() => void)>): number;
|
|
413
|
+
function addTest(title: string, ...cases: Array<TestCase | { before?: Function; after?: Function }>): TestHandle;
|
|
414
|
+
function runTest(testId?: number, autoRun?: boolean, savePrev?: boolean): void;
|
|
415
|
+
function testUpdate(info: TestInfo, result?: boolean | string, suffix?: string): void;
|
|
416
|
+
function updateLogs(): string[];
|
|
417
|
+
|
|
418
|
+
// Measurements
|
|
419
|
+
function measure(el: Element): MeasureResult;
|
|
420
|
+
function assertVisible(el: Element): boolean;
|
|
421
|
+
/** Expected size/padding/margin for design system or UI verification. All values in px. */
|
|
422
|
+
function assertSize(
|
|
423
|
+
el: Element,
|
|
424
|
+
expected?: {
|
|
425
|
+
w?: number;
|
|
426
|
+
h?: number;
|
|
427
|
+
padding?: number;
|
|
428
|
+
paddingTop?: number;
|
|
429
|
+
paddingRight?: number;
|
|
430
|
+
paddingBottom?: number;
|
|
431
|
+
paddingLeft?: number;
|
|
432
|
+
margin?: number;
|
|
433
|
+
marginTop?: number;
|
|
434
|
+
marginRight?: number;
|
|
435
|
+
marginBottom?: number;
|
|
436
|
+
marginLeft?: number;
|
|
437
|
+
}
|
|
438
|
+
): boolean | string;
|
|
439
|
+
|
|
440
|
+
// Dev-only replay + overlay (depend on o.test framework)
|
|
441
|
+
function playRecording(recording: Recording, mockOverrides?: Recording['mocks']): number;
|
|
442
|
+
function testOverlay(): void;
|
|
443
|
+
/**
|
|
444
|
+
* Pause an Objs browser test; shows a draggable bar with "Test title: Paused", optional checklist (labels + checkboxes), and Continue.
|
|
445
|
+
* Dev-only. Returns ok: true if all items checked; ok: false with errors = list of unchecked item texts.
|
|
446
|
+
*/
|
|
447
|
+
function testConfirm(
|
|
448
|
+
label: string,
|
|
449
|
+
items?: string[],
|
|
450
|
+
opts?: { confirm?: string }
|
|
451
|
+
): Promise<{ ok: boolean; errors?: string[] }>;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export { o };
|
|
455
|
+
export default o;
|