objs-core 2.4.0 → 2.4.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/README.md +17 -6
- package/objs-extension/lib/objs-inject.js +1 -1
- package/objs-extension/manifest.json +1 -1
- package/objs.built.js +1 -1
- package/objs.built.min.js +58 -58
- package/objs.d.ts +1 -1
- package/objs.global.js +5308 -0
- package/objs.global.min.js +98 -0
- package/objs.js +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
(function(){/**
|
|
2
|
+
* @fileoverview Objs-core library
|
|
3
|
+
* @version 2.4.1
|
|
4
|
+
* @author Roman Torshin
|
|
5
|
+
* @license Apache-2.0
|
|
6
|
+
*/const o=query=>{let result={els:[],ie:{},delegated:{},parented:{},store:{},refs:{},_refsByIndex:[],states:[],isDebug:!1,currentState:"",savedStates:{},isRoot:!1,_parent:null},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<"u"||o.D===o.DocumentMVP,i=0,j=0;const self=result,type=obj=>typeof obj,cycleObj=(obj,func)=>{for(const item in obj)Object.hasOwn(obj,item)&&func(item,obj)},error=o.onError,typeVerify=pairs=>o.verify(pairs),returner=(f,name="")=>(...a)=>{(o.debug||result.isDebug)&&console.log(name?`${name}()`:f,a.length?"with "+a.join(", "):"without parameters");try{const res=f(a[0],a[ONE],a[TWO],a[THREE]);return res!==u?res:result}catch(err){error(err,name)}},iterator=f=>{for(i=finish;i<=start;i++)f()},toEl=el=>el?.els?el.el:(type(el)!==objectType&&(el=o.first(el).el),el),setResultVals=(clearStates=!0,els=result.els)=>{const ln=els.length;if(result.length=ln,start=ln-ONE,finish=0,result.el=ln?els[0]:u,result.last=ln?els[start]:u,clearStates&&(cycleObj(result.states,(i2,state)=>{delete result[state[i2]]}),result.states=[],result.ie={}),Array.isArray(result._refsByIndex)){const currentLen=result._refsByIndex.length;if(currentLen>ln)cycleObj(result._refsByIndex,k=>{const idx=+k;idx>=ln&&delete result._refsByIndex[idx]}),result._refsByIndex.length=ln;else if(currentLen<ln)for(let idx=currentLen;idx<ln;idx++)result._refsByIndex[idx]={}}};result.reset=o;const hydrateDataOInitIn=containerEl=>{if(ssr||!containerEl.querySelectorAll)return;const nodes=containerEl.querySelectorAll("[data-o-init]"),byId={};nodes.forEach(node=>{const id=node.getAttribute("data-o-init");id!==null&&(byId[id]||(byId[id]=[]),byId[id].push(node))}),cycleObj(byId,id=>{const inst=o.inits[id];inst&&inst.getSSR(Number(id),byId[id])})},transform=(el,state,props)=>{cycleObj(state,s=>{let value=state[s];type(value)===functionType&&(value=value(props)),s==="append"&&type(value)===objectType&&(value.els&&(value=[value]),value[0]?.els&&(valueBuff=[],cycleObj(value,i2=>{valueBuff.push(...value[i2].els)}),value=valueBuff)),value!==u&&el.getAttribute(s)!==value&&!["tag","tagName","name","sample","state","events","ssr","nodeName","revertChildren","root","ref"].includes(s)&&(["html","innerHTML"].includes(s)?(el.innerHTML=value,!ssr&&hydrateDataOInitIn(el)):s==="className"?el.setAttribute("class",value):s==="dataset"&&type(value)===objectType?cycleObj(value,data=>{el.dataset[data]=value[data]}):s==="toggleClass"?el.classList.toggle(value):s==="addClass"?type(value)===objectType?el.classList.add(...value):el.classList.add(value):s==="removeClass"?el.classList.remove(value):s==="style"&&type(value)===objectType?cycleObj(value,data=>{el.style[data]=value[data]}):(s==="append"||s==="children"||s==="childNodes")&&type(value)===objectType?cycleObj(value.length?value:[value],j2=>{s==="append"||!el.childNodes[j2]?el.appendChild(value[j2]):el.childNodes[j2]!==value[j2]&&el.childNodes[j2].replaceWith(value[j2])}):el.setAttribute(s,value))}),el.dataset.oState=state.state,o.autotag&&state.name&&(el.dataset[o.autotag]=o.camelToKebab(state.name))};result.debug=returner(()=>{result.isDebug=!0},"debug ON"),result.saveAs=returner(key=>{typeVerify([[key,[notEmptyStringType]]]),o.getSaved[key]?(o.debug||result.isDebug)&&console.warn("the key exists (not saved):"+key):o.getSaved[key]=result},"saveAs"),result.unmount=()=>((o.debug||result.isDebug)&&console.log("unmount() for initID:"+result.initID),type(result.remove)===functionType?result.remove():result.els?.length&&result.els.forEach(el=>{el?.parentNode&&el.parentNode.removeChild(el)}),o.inits[result.initID]=void 0,result={},!0),result.init=returner(states=>{typeVerify([[states,[objectType,functionType]]]);const initN=result.initID||o.inits.length||0;if(result.initID=initN,setResultVals(),o.inits[result.initID]=result,type(states)==="function"){result.render=returner(props=>{const root=D.createElement("div");setTimeout(()=>{o.reactRender(states,root,props)}),result.add(root)});return}(type(states)!==objectType||states.render===u)&&(states={render:states}),cycleObj(states,state=>{result?.render&&state==="render"||(result.states.push(state),result[state]=returner((props=[{}])=>{result.currentState=state;const data=states[state]||{tag:"div"},slice=Array.isArray(result.els)?result.els.slice(finish,start+ONE):[],els=slice.length?slice:result.els||[];type(data)===objectType&&(data.state=state,data["data-o-init"]=initN);const newEl=(n,prop={})=>{const resolved=type(data)===functionType?data(prop):data;if(type(resolved)===objectType)return D.createElement(resolved.tag||resolved.tagName||"div");const newElem=D.createElement("div");return newElem.innerHTML=resolved,newElem.children.length>ONE||!newElem.firstElementChild?(newElem.dataset.oInit=n,newElem):(newElem.firstElementChild.dataset.oInit=n,newElem.firstElementChild)},rawData=props;Array.isArray(props)||(props=[props]),props.length||(props=[props]);const creation=!els[0]&&state==="render";props=props.map((prop,i2)=>{const newProp=Object.assign({},type(prop)===objectType?prop:{},{self:result,o,i:prop.i===u?i2:prop.i,parent:result._parent,data:Array.isArray(rawData)?rawData[i2]:rawData});return creation&&(!data.ssr||ssr)&&els.push(newEl(initN,newProp)),newProp}),creation&&(result.els=els,setResultVals(!1));const initSSR=()=>{cycleObj(data.events,event=>{result.on(event,data.events[event])})};els&&(j=els.length===props.length,els.map((el,i2)=>{props[j?i2:0].i=i2+finish;const buff=type(data)===functionType?data(props[j?i2:0]):data;type(buff)===objectType&&(buff.state=state,creation&&(buff["data-o-init"]=initN,buff["data-o-init-i"]=i2,buff.events&&(result._hydrateEvents=result._hydrateEvents||[],result._hydrateEvents[i2]=buff.events)),transform(el,buff,props[j?i2:0]))}),creation&&(result._refsByIndex=[],result.refs={},result.els.forEach((el,idx)=>{if(!el.querySelectorAll)return;const refsForEl={};el.querySelectorAll("[ref]").forEach(refEl=>{const refName=refEl.getAttribute("ref"),refInstance=o(refEl);refsForEl[refName]=refInstance,idx===0&&(result.refs[refName]=refInstance)}),result._refsByIndex[idx]=refsForEl}),!ssr&&result._hydrateEvents&&(result._hydrateEvents.forEach((evts,idx)=>{evts&&(result.select(idx),cycleObj(evts,event=>{const spec=evts[event];if(type(spec)===objectType&&spec.targetRef&&type(spec.handler)===functionType){const ref=(result._refsByIndex?.[idx]??result.refs)?.[spec.targetRef];ref&&ref.on(event,spec.handler)}else type(spec)===functionType&&result.on(event,spec)}))}),result.all()))),creation&&type(data)===objectType&&data.events&&!ssr&&!data.ssr&&initSSR()}))});const renderState=states.render||states,hasStateEvents=!ssr&&type(renderState)===objectType&&renderState.events,hasHydrateEvents=!ssr&&result._hydrateEvents&&result._hydrateEvents.length;(hasStateEvents||hasHydrateEvents)&&(result.initSSRAfterGettingSSR=()=>{result._refsByIndex=[],result.refs={},result.els.forEach((el,idx)=>{if(!el.querySelectorAll)return;const refsForEl={};el.querySelectorAll("[ref]").forEach(refEl=>{const refName=refEl.getAttribute("ref"),refInstance=o(refEl);refsForEl[refName]=refInstance,idx===0&&(result.refs[refName]=refInstance),refEl.removeAttribute("ref")}),result._refsByIndex[idx]=refsForEl,idx===0&&(result.refs=refsForEl)}),hasStateEvents&&cycleObj(renderState.events,event=>{result.on(event,renderState.events[event])}),result._hydrateEvents&&(result._hydrateEvents.forEach((evts,idx)=>{evts&&(result.select(idx),cycleObj(evts,event=>{const spec=evts[event];if(type(spec)===objectType&&spec.targetRef&&type(spec.handler)===functionType){const ref=(result._refsByIndex?.[idx]??result.refs)?.[spec.targetRef];ref&&ref.on(event,spec.handler)}else type(spec)===functionType&&result.on(event,spec)}))}),result.all())})},"init"),result.connect=returner((loader,state="render",fail)=>{typeVerify([[loader,[objectType]],[state,[notEmptyStringType]],[fail,[stringType,undefinedType]]]),loader.connect(self,state,fail)},"connect"),result.getSSR=returner((initId,fromEls)=>{typeVerify([[initId,[numberType,undefinedType]]]);const effectiveId=initId!==void 0?initId:result.initID;if(ssr||type(initId)===undefinedType&&type(result.initID)===undefinedType)return;const ssrEls=fromEls&&fromEls.length?fromEls:o.D.querySelectorAll(`[data-o-init="${effectiveId}"]`);ssrEls.length&&(result.els=Array.from(ssrEls),initId!==void 0&&(result.initID=initId,o.inits[initId]=result),setResultVals(!1),type(result.initSSRAfterGettingSSR)===functionType?result.initSSRAfterGettingSSR():fromEls&&fromEls.length&&(result._refsByIndex=[],result.refs={},result.els.forEach((el,idx)=>{if(!el.querySelectorAll)return;const refsForEl={};el.querySelectorAll("[ref]").forEach(refEl=>{const refName=refEl.getAttribute("ref");refsForEl[refName]=o(refEl),idx===0&&(result.refs[refName]=refsForEl[refName]),refEl.removeAttribute("ref")}),result._refsByIndex[idx]=refsForEl,idx===0&&(result.refs=refsForEl)})))},"getSSR"),result.initState=returner((state,props)=>{typeVerify([[state,[objectType]],[props,[objectType,undefinedType]]]),result.init(state).render(props)},"initState");const parseState=(el,stateId,root)=>{const attrs=el.attributes,stateData={tagName:el.tagName.toLowerCase()};for(const attr of attrs)stateData[attr.nodeName]=attr.value;if(root){stateData.innerHTML=el.innerHTML,stateData.revertChildren=[];const initedChildren=el.querySelectorAll("[data-o-init]");for(const child of initedChildren){const initId=child.getAttribute("data-o-init");stateData.revertChildren.push(initId),o.inits[initId]?.saveState(stateId,!1)}}return stateData};return result.saveState=returner((stateId,root=!0)=>{if(typeVerify([[stateId,[notEmptyStringType,undefinedType]],[root,[booleanType]]]),!result.el)throw Error("saveState(): There are no elements to save");const targetState=stateId||"fastSavedState",stateRevert={els:[],parentNode:result.el.parentNode,root};iterator(()=>{stateRevert.els.push(parseState(result.els[i],targetState,root))}),stateRevert.ie=Object.assign({},result.ie),stateRevert.delegated=Object.assign({},result.delegated),stateRevert.store=Object.assign({},result.store),result.isRoot=result.isRoot||root,result.savedStates[targetState]=stateRevert},"saveState"),result.revertState=returner(state=>{typeVerify([[state,[notEmptyStringType,undefinedType]]]);const targetState=state||"fastSavedState";if(!result.savedStates[targetState])throw Error(`revertState(): The state "${targetState}" should have been saved by saveState()`);const stateRevert=result.savedStates[targetState];result.offAll(),result.offDelegate(),result.store=Object.assign({},stateRevert.store),stateRevert.els.forEach((elData,index)=>{if(!result.els[index]){const newEl=o.D.createElement(elData.tagName);stateRevert.parentNode&&(index?result.els[index-1].after(newEl):stateRevert.parentNode.append(newEl)),result.add(newEl)}transform(result.els[index],elData)}),result.delegated=Object.assign({},stateRevert.delegated),result.ie=Object.assign({},stateRevert.ie),result.onAll(),cycleObj(stateRevert.delegated,ev=>{stateRevert.delegated[ev].forEach(f=>{iterator(()=>{result.els[i].addEventListener(ev,f)})})}),result.currentState=targetState,stateRevert.root&&stateRevert.els.forEach(({rootElement})=>{rootElement.revertChildren.forEach(initId=>{o.inits[initId]?.revertState(targetState),o('[data-o-init="'+initId+'"]').els.forEach((el,index)=>{el.replaceWith(o.inits[initId]?.els[index])})})})},"revertState"),result.loseState=returner(stateId=>{typeVerify([[stateId,[notEmptyStringType]]]),result.savedStates[stateId]&&(delete result.savedStates[stateId],iterator(()=>{const initedChildren=result.els[i].querySelectorAll("[data-o-init]");for(const child of initedChildren){const initId=child.getAttribute("data-o-init");o.inits[initId]?.loseState(stateId)}}))},"sample"),result.sample=returner((state="render")=>(typeVerify([[state,[notEmptyStringType]]]),{[state]:parseState(result.els[finish])}),"sample"),result.select=returner(i2=>{let idx=i2;idx!=null&&type(idx)===objectType&&idx.target&&result.els.length&&(idx=result.els.findIndex(el=>el===idx.target||el.contains(idx.target)),idx<0&&(idx=0)),typeVerify([[idx,[numberType,undefinedType]]]),idx===u&&(idx=result.length-ONE),start=idx,finish=idx,result.el=result.els[idx],select=ONE,Array.isArray(result._refsByIndex)&&result._refsByIndex[idx]&&(result.refs=result._refsByIndex[idx])},"select"),result.all=returner(()=>{start=result.length-ONE,finish=0,result.el=result.els[0],select=0,Array.isArray(result._refsByIndex)&&result._refsByIndex.length&&(result.refs=result._refsByIndex[0]||{})},"all"),result.remove=returner(j2=>{if(typeVerify([[j2,[numberType,undefinedType]]]),j2===u&&select&&(j2=finish),j2!==u){const el=result.els[j2];el?.parentNode?el.parentNode.removeChild(el):el===void 0&&j2>=result.els.length&&o.onError&&o.onError("remove("+j2+"): index out of bounds","remove")}else iterator(()=>{const el=result.els[i];el?.parentNode&&el.parentNode.removeChild(el)});setResultVals(!1)},"remove"),result.skip=returner(j2=>{typeVerify([[j2,[numberType,undefinedType]]]),j2===u&&(j2=finish),result.els.splice(j2,ONE),Array.isArray(result._refsByIndex)&&result._refsByIndex.splice(j2,ONE),setResultVals()},"skip"),result.add=returner(el=>{typeVerify([[el,[stringType,objectType,numberType]]]),result.initID===u&&(type(el)==="string"&&el!==""?result.els.push(...Array.from(D.querySelectorAll(el))):type(el)===objectType?el.tagName?result.els.push(el):el.els?result.els.push(...el.els):el.length&&el[0].tagName&&result.els.push(...el):type(el)==="number"&&o.inits[el]&&(result=o.inits[el]),setResultVals(!1),result.initID!==u&&result.dataset({oInit:result.initID}))},"add"),result.appendInside=returner(el=>{typeVerify([[el,[objectType,notEmptyStringType]]]),el?.els&&(result._parent=el),iterator(()=>{toEl(el).appendChild(result.els[i])})},"appendInside"),result.appendBefore=returner(el=>{typeVerify([[el,[objectType,notEmptyStringType]]]),iterator(()=>{toEl(el).parentNode.insertBefore(result.els[i],toEl(el))})},"appendBefore"),result.appendAfter=returner(el=>{typeVerify([[el,[objectType,notEmptyStringType]]]),iterator(()=>{toEl(el).after(...result.els)})},"appendAfter"),result.find=returner((innerQuery="")=>{typeVerify([[innerQuery,stringType]]);const newEls=[];return iterator(()=>{newEls.push(...Array.from(result.els[i].querySelectorAll(":scope "+innerQuery)))}),o(newEls)},"find"),result.first=returner((innerQuery="")=>{typeVerify([[innerQuery,stringType]]);let buff=u;const newEls=[];return iterator(()=>{buff=result.els[i].querySelector(innerQuery),buff&&newEls.push(buff)}),o(newEls)},"first"),result.attr=returner((attr,val)=>{if(val!==null&&typeVerify([[attr,stringType],[val,[stringType,undefinedType]]]),val===u){const attrs=[];return iterator(()=>{attrs[i]=result.els[i].getAttribute(attr)}),select?attrs[0]:attrs}else iterator(val!==null?()=>{result.els[i].setAttribute(attr,val)}:()=>{result.els[i].removeAttribute(attr)})},"attr"),result.attrs=returner(()=>{const res=[];return iterator(()=>{const obj={};[...result.els[i].attributes].forEach(attr=>{obj[attr.nodeName]=attr.nodeValue}),res.push(obj)}),select?res[0]:res},"attrs"),result.dataset=returner(values=>{if(typeVerify([[values,[objectType,undefinedType]]]),typeof values===objectType)iterator(()=>{cycleObj(values,data=>{result.els[i].dataset[data]=values[data]})});else{const res=[];return iterator(()=>{res.push({...result.els[i].dataset})}),select?res[0]:res}},"dataset"),result.style=returner(val=>{val!==null&&typeVerify([[val,[stringType,undefinedType]]]),result.attr("style",val)},"style"),result.css=returner((styles={})=>{if(styles===null){result.style(null);return}typeVerify([[styles,objectType]]);let val="";cycleObj(styles,style=>{val+=style+":"+styles[style].replace('"',"'")+";"}),result.style(val||null)},"css"),result.cssMerge=returner((styles={})=>{if(styles===null){result.style(null);return}typeVerify([[styles,objectType]]);const normKey=k=>k.indexOf("-")!==-1?k:o.camelToKebab(k),parseStyleAttr=s=>{const out={};if(!s||typeof s!==stringType)return out;const parts=s.split(";");for(let p=0;p<parts.length;p++){const part=parts[p],idx=part.indexOf(":");if(idx===-1)continue;const key=part.slice(0,idx).trim(),val=part.slice(idx+1).trim();key&&(out[key]=val)}return out};iterator(()=>{const el=result.els[i],merged=parseStyleAttr(el.getAttribute("style"));cycleObj(styles,style=>{const k=normKey(style),v=styles[style];v===null||v===u?delete merged[k]:merged[k]=String(v).replace('"',"'")});let serialized="";cycleObj(merged,k=>{serialized+=k+":"+merged[k]+";"}),serialized?el.setAttribute("style",serialized):el.removeAttribute("style")})},"cssMerge"),result.setClass=returner(cl=>{typeVerify([[cl,stringType]]),iterator(()=>{result.els[i].setAttribute("class",cl)})},"setClass"),result.addClass=returner((...cls)=>{iterator(()=>{result.els[i].classList.add(...cls)})},"addClass"),result.removeClass=returner((...cls)=>{iterator(()=>{result.els[i].classList.remove(...cls)})},"removeClass"),result.toggleClass=returner((cl,check)=>{typeVerify([[cl,notEmptyStringType],[check,[booleanType,undefinedType]]]),iterator(()=>{result.els[i].classList.toggle(cl,check)})},"toggleClass"),result.haveClass=cl=>{typeVerify([[cl,notEmptyStringType]]);let res=!0;return iterator(()=>{result.els[i].classList.contains(cl)||(res=!1)}),(result.isDebug||o.debug)&&console.log("haveClass() with",cl),res},result.innerHTML=returner(html=>{if(typeVerify([[html,[stringType,undefinedType]]]),html!==u)iterator(()=>{result.els[i].innerHTML=html});else{let res="";return iterator(()=>{res+=ssr&&result.els[i].innerHTML.length===0?o.D.parseElement(result.els[i],!1):result.els[i].innerHTML}),res}},"innerHTML"),result.innerText=returner(text=>{typeVerify([[text,[stringType]]]),iterator(()=>{result.els[i].innerText=text})},"innerText"),result.textContent=returner(text=>{typeVerify([[text,[stringType]]]),iterator(()=>{result.els[i].textContent=text})},"textContent"),result.html=returner(value=>{if(typeVerify([[value,[stringType,undefinedType]]]),value!==void 0)result.innerHTML(value);else{let html="";return iterator(()=>{html+=ssr?result.els[i].outerHTML():result.els[i].outerHTML}),html}},"html"),result.toString=function(){return result.html()},result[Symbol.toPrimitive]=function(hint){return hint==="string"||hint==="default"?result.html():hint==="number"?result.els?.length??0:result.html()},result.val=returner(value=>{if(value===void 0)return result.el?.value;iterator(()=>{result.els[i].value=value})},"val"),result.forEach=returner(f=>{typeVerify([[f,[functionType]]]),iterator(()=>{f({self:result,i,o,el:result.els[i]})})},"forEach"),result.prepareFor=returner((reactArg,ReactComponent)=>{typeVerify([[reactArg,[objectType,functionType,undefinedType]],[ReactComponent,[functionType,undefinedType]]]);const isFullReact=reactArg&&type(reactArg)===objectType&&reactArg.createElement;if(!isFullReact&&type(reactArg)!==functionType)throw Error("prepareFor(): pass React (full object) or React.createElement as first argument");const createElement=isFullReact?reactArg.createElement:reactArg,useEffect=isFullReact?reactArg.useEffect:void 0;return p=>{if(p.ref===u)throw Error("No ref property to convert Objs to React");const props=Object.assign({},p),reactElement=createElement("div",{ref:p.ref});return delete props.ref,useEffect(()=>{cycleObj(props,key=>{if(key.substring(0,1)==="on"){const e=o.camelToKebab(key).split("-")[1];result.on(e,props[key]),delete props[key]}}),result.render(props),result.appendInside(reactElement.ref.current)},[]),reactElement}},"prepareFor"),result.on=returner((a,b,c,d)=>{typeVerify([[a,[notEmptyStringType]],[b,[functionType]],[c,[objectType,undefinedType]],[d,[booleanType,undefinedType]]]),a.split(", ").forEach(ev=>{iterator(()=>{result.els[i].addEventListener(ev,b,c,d)}),result.ie[ev]||(result.ie[ev]=[]),result.ie[ev].push([b,c,d])})},"on"),result.off=returner((a,b,c)=>{typeVerify([[a,[notEmptyStringType]],[b,[functionType]],[c,[objectType,undefinedType]]]),a.split(", ").forEach(ev=>{iterator(()=>{result.els[i].removeEventListener(ev,b,c)}),result.ie[ev]&&(result.ie[ev]=result.ie[ev].filter(f=>f[0]!==b))})},"off"),result.onDelegate=returner((e,selector,f)=>{typeVerify([[e,[notEmptyStringType]],[selector,[notEmptyStringType]],[f,[functionType]]]),e.split(", ").forEach(ev=>{const delegateCheck=event=>{const delegate=event.target.closest(selector);delegate&&(event.delegate=delegate,event.objs=result,f(event))};iterator(()=>{result.els[i].addEventListener(ev,delegateCheck)}),result.delegated[ev]||(result.delegated[ev]=[]),result.delegated[ev].push(delegateCheck)})},"onDelegate"),result.offDelegate=returner(e=>{typeVerify([[e,[notEmptyStringType]]]),cycleObj(result.delegated,ev=>{if(!e||e===ev)for(;result.delegated[ev].length;){const f=result.delegated[ev].pop();iterator(()=>{result.els[i].removeEventListener(ev,f)})}}),result.delegated={}},"offDelegate"),result.onParent=returner((e,selector,f)=>{typeVerify([[e,[notEmptyStringType]],[selector,[notEmptyStringType,objectType]],[f,[functionType]]]);const parent=type(selector)===objectType?selector:o.D.querySelector(selector);e.split(", ").forEach(ev=>{const parentCheck=event=>{event.objs=result,f(event)};parent.addEventListener(ev,parentCheck),result.parented[ev]||(result.parented[ev]=[]),result.parented[ev].push(parentCheck)})},"onParent"),result.offParent=returner((e,query2)=>{typeVerify([[e,[notEmptyStringType]],[query2,[notEmptyStringType,objectType]]]);const parent=type(query2)===objectType?query2:o.D.querySelector(query2);cycleObj(result.parented,ev=>{(!e||e===ev)&&(result.parented[ev].forEach(f=>{parent.removeEventListener(ev,f)}),delete result.parented[ev])})},"offParent"),result.onAll=returner((type2,off)=>{typeVerify([[type2,[notEmptyStringType,undefinedType]],[off,[booleanType,undefinedType]]]),cycleObj(result.ie,(ev,events)=>{(!type2||type2===ev)&&events[ev].forEach(data=>{iterator(()=>{off?result.els[i].removeEventListener(ev,data[0]):result.els[i].addEventListener(ev,data[0],data[ONE],data[TWO])})})})},"onAll"),result.offAll=returner(type2=>{typeVerify([[type2,[notEmptyStringType]]]),result.onAll(type2,ONE)},"offAll"),query&&result.add(query),result.take=innerQuery=>{if(typeVerify([[innerQuery,[stringType,objectType,numberType]]]),result.add(innerQuery),result.el){const initID=result.el.dataset.oInit;if(initID!==u&&o.inits[initID])return result.length===ONE?(j=result.els[0],Object.assign(result,o.inits[initID]),result.els=[j]):result=o.inits[initID],setResultVals(!1,result.els),result}},result};o.first=query=>(o.verify([[query,["notEmptyString"]]]),o.debug&&console.log(query," -> ","o.first()"),o(o.D.querySelector(query)).select(0)),o.inits=[],o.getSaved={},o.errors=[],o.showErrors=!1,o.logErrors=()=>{o.errors.length?o.errors.forEach(e=>console.error(e)):console.log("No errors")},o.onError=(e,name)=>{o.showErrors?console.error(e,name):(o.errors.push(e),name&&o.errors.push(name))},o.reactRender=()=>new Error("React render function is not defined"),o.autotag=void 0,o.reactQA=name=>({["data-"+(o.autotag||"qa")]:name.replace(/([A-Z])/g,(_,l)=>"-"+l.toLowerCase()).replace(/^-/,"")}),o.specialTypes={notEmptyString:(val,type)=>type==="string"&&val.length,array:val=>Array.isArray(val),promise:val=>val instanceof Promise||!!(val&&typeof val.then=="function")},o.verify=(pairs,safe=!1)=>{for(const pair of pairs){const type=typeof pair[0];let expectedTypes=Array.isArray(pair[1])?pair[1]:[pair[1]],isValid=!1;if(expectedTypes.includes(type))return!0;expectedTypes=expectedTypes.filter(t=>!!o.specialTypes[t]);for(const expectedType of expectedTypes)if(isValid=o.specialTypes[expectedType](pair[0],type),isValid)return!0}return safe?!1:new Error("Type verification failed")},o.safeVerify=pairs=>o.verify(pairs,!0),o.init=(states,reactRender)=>o().init(states,reactRender),o.initState=(state,props)=>o().init(state).render(props),o.take=query=>o().take(query),o.getStates=()=>o.inits.reduce((acc,result)=>(acc.push(result?.states),acc),[]),o.getStores=()=>o.inits.reduce((acc,result)=>(acc.push(result?.store),acc),[]),o.getListeners=()=>o.inits.reduce((acc,result)=>(acc.push(result?.ie),acc),[]),o.createStore=defaults=>{const store=Object.assign({},defaults);return store._defaults=Object.assign({},defaults),store._listeners=[],store.subscribe=function(component,stateName){return this._listeners.push(data=>component[stateName]?.(data)),this},store.notify=function(){this._listeners.forEach(fn=>fn(this))},store.reset=function(){const skip={_listeners:1,subscribe:1,notify:1,_defaults:1,reset:1};for(const key of Object.keys(this._defaults))skip[key]||(this[key]=this._defaults[key])},store},o.U=void 0,o.W=2,o.H=100,o.F=!1,o.C=(a,b)=>Object.hasOwn(a,b),o.kebabToCamel=str=>str.replace(/-./g,m=>m.toUpperCase()[1]),o.camelToKebab=str=>str.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),o.route=(path,task)=>{o.verify([[path,["notEmptyString","function","boolean"]],[task,["function","object"]]]);const result=typeof path=="function"?path(window.location.pathname):path;return result===!0||window.location.pathname===result?task?typeof task=="function"?(task(),!0):task:o:typeof task=="function"?!1:{}},o.router=(routes={})=>{o.verify([[routes,["object"]]]);for(const route in routes)if(o.C(routes,route)&&window.location.pathname===route)return typeof routes[route]=="function"?(routes[route](),!0):routes[route];return!1},o.DocumentMVP={addEventListener:()=>{},parseElement:(elem,outer=!0)=>{o.verify([[elem,["object","string"]],[outer,["boolean"]]]);const attrToStr=(attrs,prefix="")=>{let attrStr="";for(const attr in attrs)o.C(attrs,attr)&&(attrStr+=` ${prefix}${o.camelToKebab(attr)}="${typeof attrs[attr]!="object"?attrs[attr]:Object.entries(attrs[attr]).map(e=>`${e[0]}: ${e[1]};`).join(" ")}"`);return attrStr};if(typeof elem=="string")return elem;if(outer){const selfClosing=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"],tagName=elem.tagName.toLowerCase(),dataOInit=elem.attributes["data-o-init"],dataOInitAttr=dataOInit!==void 0?` data-o-init="${dataOInit}"`:"";return`<${tagName}${elem.className?` class="${elem.className}"`:""}${attrToStr(elem.attributes)}${dataOInitAttr}${attrToStr(elem.dataset,"data-")}${selfClosing.includes(tagName)?"/":""}>${selfClosing.includes(tagName)?"":elem.innerHTML.length?elem.innerHTML:elem.children.map(el=>o.D.parseElement(el)).join("")}${selfClosing.includes(tagName)?"":`</${tagName}>`}`}return elem.innerHTML.length?elem.innerHTML:elem.children.map(el=>o.D.parseElement(el)).join("")},createElement:tag=>{o.verify([[tag,["notEmptyString"]]]);const elem={tagName:tag.toUpperCase(),attributes:{},innerHTML:"",children:[],dataset:{},className:"",classArray:[],style:{},addEventListener:()=>{},removeEventListener:()=>{}};return elem.classList={add:(...cl)=>{o.verify([[cl,["array"]]]),elem.classArray.push(cl),elem.className=elem.classArray.join(" ")},has:cl=>(o.verify([[cl,["notEmptyString"]]]),elem.classArray.includes(cl)),remove:cl=>{o.verify([[cl,["notEmptyString"]]]),elem.classArray=elem.classArray.filter(listed=>cl!==listed),elem.className=elem.classArray.join(" ")}},elem.classList.toggle=cl=>{o.verify([[cl,["notEmptyString"]]]),elem.classList.has(cl)?elem.classList.remove(cl):elem.classList.add(cl)},elem.setAttribute=(attr,val)=>{o.verify([[attr,["notEmptyString"]],[val,["string","number","boolean","undefined"]]]),elem.attributes[attr]=val},elem.getAttribute=attr=>(o.verify([[attr,["notEmptyString"]]]),elem.attributes[attr]),elem.removeAttribute=attr=>{o.verify([[attr,["notEmptyString"]]]),delete elem.attributes[attr]},elem.appendChild=el=>{o.verify([[el,["object"]]]),elem.children.push(el),elem.firstElementChild=elem.children[0]},elem.outerHTML=()=>o.D.parseElement(elem),elem}},o.D=typeof document<"u"&&typeof process>"u"?document:o.DocumentMVP,o.setCookie=(title,value="",params={})=>{if(o.verify([[title,["notEmptyString"]],[value,["string","number","boolean"]],[params,["object"]]]),o.D.cookie===void 0){console.log("Cookies are not supported on server side");return}let str=encodeURIComponent(title)+"="+encodeURIComponent(value);params={path:"/",...params},params.expires instanceof Date?params.expires=params.expires.toUTCString():typeof params.expires=="number"&&(params.expires=new Date(params.expires).toUTCString());for(const key in params){str+="; "+key;const r=params[key];r!==!0&&(str+="="+r)}o.D.cookie=str},o.getCookie=title=>{if(o.verify([[title,["notEmptyString"]]]),o.D.cookie===void 0){console.log("Cookies are not supported on server side");return}const val=o.D.cookie.match(RegExp("(?:^|; )"+title.replace(/([.$?*|{}()[\]\\/+^])/g,"\\$1")+"=([^;]*)"));return val?decodeURIComponent(val[1]):void 0},o.deleteCookie=title=>{o.verify([[title,["notEmptyString"]]]),o.setCookie(title,"",{"max-age":0})},o.clearCookies=()=>{if(o.D.cookie===void 0){console.log("Cookies are not supported on server side");return}const ca=o.D.cookie.split(";");for(;ca.length;){let c=ca.pop();for(;c.charAt(0)===" ";)c=c.substring(1);const key=c.split("=")[0];o.deleteCookie(key)}},o.clearLocalStorage=all=>{if(o.verify([[all,["boolean","undefined"]]]),!(typeof localStorage>"u"))if(all)localStorage.clear();else for(let i=localStorage.length-1;i>=0;i--){const key=localStorage.key(i);key.indexOf("oInc-")===-1&&key.indexOf("oTest-")===-1&&localStorage.removeItem(key)}},o.clearSessionStorage=onlyTests=>{if(o.verify([[onlyTests,["boolean","undefined"]]]),!(typeof sessionStorage>"u"))if(!onlyTests)sessionStorage.clear();else for(let i=sessionStorage.length-1;i>=0;i--){const key=sessionStorage.key(i);key&&key.indexOf("oTest-")===0&&sessionStorage.removeItem(key)}},o.clearTestsStorage=()=>{o.clearSessionStorage(1)},o.clearAfterTests=()=>{o.clearCookies(),o.clearLocalStorage(!1),o.clearTestsStorage()},o.ajax=(url,props={})=>{o.verify([[url,["notEmptyString"]],[props,["object"]]]);const row=new URLSearchParams;if(props.data&&typeof props.data=="object"){for(const param in props.data)o.C(props.data,param)&&(typeof props.data[param]=="object"?row.set(param,encodeURIComponent(JSON.stringify(props.data[param]))):row.set(param,props.data[param]));props.method.toLowerCase()==="get"?url+="?"+row.toString():props.body||(props.body=row),delete props.data}return props.headers||(props.headers={"X-Requested-With":"XMLHttpRequest"}),fetch(url,props)},o.get=(url,props={})=>(o.verify([[url,["notEmptyString"]],[props,["object"]]]),o.ajax(url,{...props,method:"GET"})),o.post=(url,props={})=>(o.verify([[url,["notEmptyString"]],[props,["object"]]]),o.ajax(url,{...props,method:"POST"})),o.newLoader=promise=>{o.verify([[promise,["promise","undefined"]]]);let listeners=[],data=null,finished=!1,error=!1;const reload=p=>{finished=!1,error=!1,data=null,setTimeout(()=>{p.then(response=>{finished=!0,!response.ok&&typeof response.ok<"u"?(error=!0,listeners.forEach(([listener,_state,fail])=>{fail&&listener[fail](response)})):typeof response.json=="function"?response.json().then(jsonData=>{data=jsonData,listeners.forEach(([listener,state])=>{listener[state](data)})}):(data=response,listeners.forEach(([listener,state])=>{listener[state](data)}))}).catch(err=>{error=!0,listeners.forEach(([listener,_state,fail])=>{fail&&listener[fail](err)})})},33)};return promise&&reload(promise),{reload,isObjsLoader:!0,listeners,isFinished:()=>finished,getStore:()=>data,connect:(listener,state="render",fail)=>{o.verify([[listener,["object"]],[state,["notEmptyString"]],[fail,["string","undefined"]]]),finished?error?fail&&listener[fail]():typeof listener[state]=="function"&&listener[state](data):listeners.push([listener,state,fail])},disconnect:listener=>{o.verify([[listener,["object"]]]),listeners=listeners.filter(([l])=>l!==listener)}}},o.getParams=key=>{o.verify([[key,["string","undefined"]]]);const params={},paramsRaw=new URLSearchParams(window.location.search).entries();for(const entry of paramsRaw)params[entry[0]]=entry[1];return key?params[key]:params},o.incCache=!0,o.incCacheExp=1e3*60*60*24,o.incTimeout=6e3,o.incSource="",o.incForce=o.F,o.incAsync=!0,o.incCors=o.F,o.incSeparator="?",o.incFns={},o.incSet=[0],o.incReady=[0],o.incN=0,o.incGetHash=path=>path.split(o.incSeparator)[1]||"",o.incCheck=(set=0,fnId,loaded=0)=>(o.verify([[set,["number"]],[fnId,["number","undefined"]],[loaded,["number"]]]),!loaded&&set&&fnId===o.U&&o.incReady[set]?o.incSet[set]===1:o.incReady[set]===o.U||o.incReady[set][fnId]===o.U?o.F:(o.incReady[set][fnId].loaded=loaded,o.incFns[o.incReady[set][fnId].name]=loaded,o.incReady[set][0]+=loaded,set&&o.incReady[set].length===o.incReady[set][0]&&(typeof o.incSet[set]=="function"&&o.incSet[set](set),o.incSet[set]=1),o.incSet[set]===1)),o.incCacheClear=(all=o.F)=>{o.verify([[all,["boolean"]]]);for(const name in o.incFns)o.C(o.incFns,name)&&(localStorage.removeItem("oInc-"+name),localStorage.removeItem("oInc-"+name+"-expires"));return all&&(o.incReady.forEach((val,i)=>{i&&val.forEach((_a,j)=>{j&&o("#oInc-"+i+"-"+j).remove()})}),o.incN=0,o.incFns={},o.incSet=[0],o.incReady=[0]),!0},o.inc=(sources,callBack,callBad)=>{if(o.verify([[sources,["object","undefined"]],[callBack,["function","undefined"]],[callBad,["function","undefined"]]]),typeof localStorage>"u")return;let sourcesN=0,sourcesReady=0,hash="",preload=!1;const f="function",no=-1;if(typeof sources!="object"||!sources)return o.incSet[0];o.incSet[0]++;const setN=o.incSet[0];o.incSet[setN]=callBack||0,o.incReady[setN]=[];const fnsStatus=o.incReady[setN];fnsStatus[0]=1;const fnId={};for(const name in sources)if(o.C(sources,name)){if(name==="preload"){preload=!0;continue}hash=o.incGetHash(sources[name]),sourcesN++,o.incN++;const tag=sources[name].indexOf(".css")>no?"style":"script";if(sources[name]=(o.incSource?o.incSource+"/":"")+sources[name],Number.isNaN(Number(name))&&o.C(o.incFns,name)&&o.incFns[name]&&!o.incForce){fnsStatus[sourcesN]={name,loaded:1},sourcesReady++;continue}if(fnsStatus[sourcesN]={name,loaded:0},Number.isNaN(Number(name))&&(o.incFns[name]=0),Number.isNaN(Number(name))&&o.incCache&&(sources[name].substring(0,4)!=="http"||!o.incCors)&&window.location.protocol!=="file:"&&(sources[name].indexOf(".css")>no||sources[name].indexOf(".js")>no)){const ls=localStorage,script=ls.getItem("oInc-"+name),cacheSavedTill=ls.getItem("oInc-"+name+"-expires"),cacheHash=ls.getItem("oInc-"+name+"-hash");script&&cacheSavedTill&&Date.now()<cacheSavedTill&&cacheHash===hash?(preload||o.initState({tag,id:"oInc-"+setN+"-"+sourcesN,innerHTML:script,"data-o-inc":setN}).appendInside("head"),fnsStatus[sourcesN].loaded=1,o.incFns[name]=1,sourcesReady++):(fnId[name]=sourcesN,o.get(sources[name],{mode:o.incCors?"cors":"same-origin"}).then(response=>{if(response.status!==200){o.onError&&o.onError({message:o.incSource+sources[name]+" was not loaded"});return}response.text().then(script2=>{ls.setItem("oInc-"+name,script2),ls.setItem("oInc-"+name+"-expires",Date.now()+o.incCacheExp),ls.setItem("oInc-"+name+"-hash",hash),preload||o.initState({tag,id:"oInc-"+setN+"-"+fnId[name],innerHTML:script2,"data-o-inc":setN}).appendInside("head"),o.incCheck(setN,fnId[name],1)})}))}else{const state={tag,id:"oInc-"+setN+"-"+sourcesN,"data-o-inc":setN,async:o.incAsync,onload:"o.incCheck("+setN+","+sourcesN+",1)"};sources[name].indexOf(".css")>no?(state.tag="link",state.rel="stylesheet",state.href=sources[name]):(sources[name].indexOf(".js")>no||(state.tag="img",state.style="display:none;"),state.src=sources[name]),o.initState(state).appendInside(state.style?"body":"head")}}return fnsStatus[0]+=sourcesReady,sourcesN!==0&&(sourcesReady===sourcesN?typeof callBack===f&&callBack(setN):setTimeout(set=>{o.incReady[set]&&o.incReady[set].length<o.incReady[set][0]&&(o.incSet[set]=0,typeof callBad===f&&callBad(setN))},o.incTimeout,setN)),o.incSet[0]},o.connectRedux=(store,selector,component,state="render")=>{o.verify([[store,["object"]],[selector,["function"]],[component,["object"]],[state,["notEmptyString"]]]);const update=()=>{if(typeof component[state]=="function"){const val=selector(store.getState());component[state](val!==null&&typeof val=="object"?val:{value:val})}};return update(),store.subscribe(update)},o.connectMobX=(mobx,observable,accessor,component,state="render")=>(o.verify([[mobx,["object"]],[observable,["object"]],[accessor,["function"]],[component,["object"]],[state,["notEmptyString"]]]),mobx.autorun(()=>{if(typeof component[state]=="function"){const val=accessor(observable);component[state](val!==null&&typeof val=="object"?val:{value:val})}})),o.ObjsContext=null,o.withReactContext=(React,Context,selector,component,state="render")=>function(){const value=React.useContext(Context);return React.useEffect(()=>{if(typeof component[state]=="function"){const val=selector(value);component[state](val!==null&&typeof val=="object"?val:{value:val})}},[value]),null},o.debug=!1,o.sleep=ms=>new Promise(r=>setTimeout(r,ms)),o.tLog=[],o.tRes=[],o.tStatus=[],o.tFns=[],o.tShowOk=o.F,o.tStyled=o.F,o.tTime=2e3,o.tests=[],o.tExpectedSteps={},o.tFinalized={},o.tAutolog=o.F,o.tBeforeEach=void 0,o.tAfterEach=void 0,o.addTest=(title,...tests)=>{o.verify([[title,["notEmptyString"]],[tests,["array"]]]);let hooks={};tests.length&&typeof tests[tests.length-1]=="object"&&!Array.isArray(tests[tests.length-1])&&(hooks=tests.pop());const testId=o.tests.length;return o.tests[testId]={title,tests,hooks},{run:()=>{typeof hooks.before=="function"&&(o.tBeforeEach=hooks.before),typeof hooks.after=="function"&&(o.tAfterEach=hooks.after),o.runTest(testId)},autorun:()=>{typeof hooks.before=="function"&&(o.tBeforeEach=hooks.before),typeof hooks.after=="function"&&(o.tAfterEach=hooks.after),o.runTest(testId,!0)},testId}},o.updateLogs=()=>{for(let i=0;i<o.tests.length;i++)o.tLog[i]=o.tLog[testN]=sessionStorage.getItem(`oTest-Log-${testN}`)||"";return o.tLog},o.runTest=(testId=0,autoRun,savePrev)=>{if(o.verify([[testId,["number"]],[autoRun,["boolean","undefined"]],[savePrev,["boolean","undefined"]]]),!o.tests[testId])return;savePrev||(sessionStorage?.removeItem(`oTest-Log-${testId}`),sessionStorage?.removeItem(`oTest-Res-${testId}`),sessionStorage?.removeItem(`oTest-Status-${testId}`)),sessionStorage?.setItem("oTest-Run",testId),autoRun?sessionStorage?.setItem("oTest-Autorun",autoRun):sessionStorage?.removeItem("oTest-Autorun");const testSession=o.tests[testId];let lastTest=testSession.tests.pop();typeof lastTest!="function"&&(testSession.tests.push(lastTest),lastTest=()=>{}),testSession.tests.push(testN2=>{lastTest(testN2),sessionStorage.setItem("dddd",1),sessionStorage?.removeItem("oTest-Run"),autoRun&&o.runTest(testId+1,autoRun)}),o.test(testSession.title,...testSession.tests)},o.tPre='<div style="font-family:monospace;text-align:left;">',o.tOk='<span style="background:#cfc;padding: 0 15px;">OK</span> ',o.tXx='<div style="background:#fcc;padding:3px;">',o.tDc="</div>",o.test=(title="",...tests)=>{o.verify([[title,["notEmptyString"]],[tests,["array"]]]);const testSession=sessionStorage?.getItem("oTest-Run"),testN2=testSession||o.tLog.length;let waits=0,preOk="\u251C OK: ",preXx="\u251C \u2718 ",posOk=`
|
|
7
|
+
`,posXx=`
|
|
8
|
+
`,row="",num=tests.length,done=0;const log=(line="",error=!1,log2=!1)=>{o.tAutolog&&(error?console.error(line):(o.tShowOk||log2)&&console.log(line))};let opts={};if(typeof tests[num-1]=="function"&&(o.tFns[testN2]=tests[num-1],num--),num>0&&typeof tests[num-1]=="object"&&!Array.isArray(tests[num-1])&&(tests[num-1].sync!==void 0||tests[num-1].confirmOnFailure!==void 0)&&(opts=tests[num-1],num--),testSession){o.tLog[testN2]=sessionStorage.getItem(`oTest-Log-${testN2}`)||"",o.tRes[testN2]=sessionStorage.getItem(`oTest-Res-${testN2}`)||!1,o.tStatus[testN2]=JSON.parse(sessionStorage.getItem(`oTest-Status-${testN2}`)||"[]");for(let i=0;i<o.tStatus[testN2].length;i++){const s=o.tStatus[testN2][i];(s===!0||s===!1)&&done++}}o.tStyled&&(preOk=o.tPre+o.tOk,preXx=o.tPre+o.tXx,posOk=o.tDc,posXx=posOk+posOk),(!testSession||o.tLog[testN2].length===0)&&(log("\u2552 "+title+" #"+testN2,!1,!0),o.tStyled?o.tLog[testN2]="<div><b>"+title+" #"+testN2+"</b></div>":o.tLog[testN2]="\u2552 "+title+" #"+testN2+`
|
|
9
|
+
`,o.tRes[testN2]=o.F,o.tStatus[testN2]=[]),o.tExpectedSteps[testN2]=num,o.tFinalized[testN2]=!1;const showConfirmOnFailureOverlay=(stepIdx,msg)=>new Promise(resolve=>{const box=o.overlay({innerHTML:`<div style="display:flex;flex-direction:column;gap:8px;"><div style="cursor:grab;">Step ${stepIdx+1} failed: ${msg||"error"}. Continue testing?</div><div style="display:flex;gap:8px;"><button class="o-cf-continue" style="padding:6px 12px;background:#2563eb;color:#fff;border:none;border-radius:6px;cursor:pointer;">Continue</button><button class="o-cf-stop" style="padding:6px 12px;background:#dc2626;color:#fff;border:none;border-radius:6px;cursor:pointer;">Stop</button></div></div>`,timeout:opts.confirmOnFailureTimeout||void 0,onClose:r=>resolve(r||{continue:!1}),excludeDragSelector:".o-cf-continue, .o-cf-stop"});box.first(".o-cf-continue").on("click",()=>{box._overlayCleanup(),resolve({continue:!0})}),box.first(".o-cf-stop").on("click",()=>{box._overlayCleanup(),resolve({continue:!1})})}),finalize=()=>{if(o.tFinalized[testN2])return;if(waits>0){row="\u251C ",row+="DONE "+done+"/"+num+", waiting: "+waits,log(row,!0),o.tStyled?o.tLog[testN2]+=o.tPre+'<div style="color:orange;"><b>DONE '+done+"/"+num+", waiting: "+waits+"</b>"+o.tDc+o.tDc:o.tLog[testN2]+=row+`
|
|
10
|
+
`;return}o.tFinalized[testN2]=!0;const anyFailed=o.tStatus[testN2].some(s=>s===!1);o.tRes[testN2]=!anyFailed&&done===num,row="\u2558 ",row+="DONE "+done+"/"+num,log(row,done!==num),log(),o.tStyled?o.tLog[testN2]+=o.tPre+'<div style="color:'+(done!==num?"red":"green")+';"><b>DONE '+done+"/"+num+"</b>"+o.tDc+o.tDc:o.tLog[testN2]+=row+`
|
|
11
|
+
`,testSession&&(sessionStorage.setItem(`oTest-Log-${testN2}`,o.tLog[testN2]),sessionStorage.setItem(`oTest-Res-${testN2}`,o.tRes[testN2]),sessionStorage.setItem(`oTest-Status-${testN2}`,JSON.stringify(o.tStatus[testN2]))),typeof o.tFns[testN2]=="function"&&o.tFns[testN2](testN2)};if(opts.sync||opts.confirmOnFailure)return(async()=>{for(let i=o.tStatus[testN2].length;i<num;i++){const testInfo={n:testN2,i,title:tests[i][0],tShowOk:o.tShowOk,tStyled:o.tStyled};let res=tests[i][1];if(typeof res>"u"){o.tStyled?o.tLog[testN2]+="<div>"+testInfo.title+"</div>":o.tLog[testN2]+=testInfo.title+`
|
|
12
|
+
`,log("\u251C "+testInfo.title,!1,!0),o.tStatus[testN2][i]=!0,done++;continue}if(typeof o.tBeforeEach=="function"&&o.tBeforeEach(testInfo),typeof res=="function")try{res=res(testInfo)}catch(error){res=error.message,o.onError&&o.onError(error)}if(typeof o.tAfterEach=="function"&&o.tAfterEach(testInfo,res),res&&typeof res.then=="function"){try{const value=await res,ok=value===!0||value==null||value&&typeof value=="object"&&value.ok===!0,msg=value&&value.errors&&value.errors.length?value.errors.join("; "):typeof value=="string"?value:"";if(o.testUpdate(testInfo,ok,ok?"":msg?": "+msg:""),done++,!ok&&opts.confirmOnFailure&&!(await showConfirmOnFailureOverlay(i,msg)).continue)break}catch(err){if(o.testUpdate(testInfo,!1,err.message||"Promise rejected"),opts.confirmOnFailure&&!(await showConfirmOnFailureOverlay(i,err.message||"Promise rejected")).continue)break}continue}if(typeof o.tStatus[testN2][i]>"u")o.tStatus[testN2][i]=typeof res=="string"?o.F:res;else{sessionStorage.setItem(`oTest-Status-${testN2}`,JSON.stringify(o.tStatus[testN2]));return}if(res===!0)done++,o.tShowOk&&(o.tLog[testN2]+=preOk+tests[i][0]+posOk,log("\u251C OK: "+tests[i][0]));else if(res!==o.U){if(o.tLog[testN2]+=preXx+tests[i][0]+(res!==o.F?": "+res:"")+posXx,log("\u251C \u2718 "+tests[i][0]+(res!==o.F?": "+res:""),!0),opts.confirmOnFailure&&!(await showConfirmOnFailureOverlay(i,typeof res=="string"?res:"")).continue)break}else{waits++,setTimeout(info=>{info.title+=" (timeout)",o.testUpdate(info)},o.tTime,testInfo);return}}finalize()})(),testN2;for(let i=o.tStatus[testN2].length;i<num;i++){const testInfo={n:testN2,i,title:tests[i][0],tShowOk:o.tShowOk,tStyled:o.tStyled};let res=tests[i][1];if(typeof res>"u"){o.tStyled?o.tLog[testN2]+="<div>"+testInfo.title+"</div>":o.tLog[testN2]+=testInfo.title+`
|
|
13
|
+
`,log("\u251C "+testInfo.title,!1,!0),o.tStatus[testN2][i]=!0,done++;continue}if(typeof o.tBeforeEach=="function"&&o.tBeforeEach(testInfo),typeof res=="function")try{res=res(testInfo)}catch(error){res=error.message,o.onError&&o.onError(error)}if(typeof o.tAfterEach=="function"&&o.tAfterEach(testInfo,res),res&&typeof res.then=="function"){waits++;const timeoutId=setTimeout(()=>{testInfo.title+=" (timeout)",o.testUpdate(testInfo)},o.tTime);res.then(value=>{clearTimeout(timeoutId);const ok=value===!0||value&&value.ok===!0,msg=value&&value.errors&&value.errors.length?value.errors.join("; "):typeof value=="string"?value:"";o.testUpdate(testInfo,ok,ok?"":msg?": "+msg:"")}).catch(err=>{clearTimeout(timeoutId),o.testUpdate(testInfo,!1,err.message||"Promise rejected")});continue}if(typeof o.tStatus[testN2][i]>"u")o.tStatus[testN2][i]=typeof res=="string"?o.F:res;else{sessionStorage.setItem(`oTest-Status-${testN2}`,JSON.stringify(o.tStatus[testN2]));return}res===!0?(done++,o.tShowOk&&(o.tLog[testN2]+=preOk+tests[i][0]+posOk,log("\u251C OK: "+tests[i][0]))):res!==o.U?(o.tLog[testN2]+=preXx+tests[i][0]+(res!==o.F?": "+res:"")+posXx,log("\u251C \u2718 "+tests[i][0]+(res!==o.F?": "+res:""),!0)):(waits++,setTimeout(info=>{info.title+=" (timeout)",o.testUpdate(info)},o.tTime,testInfo))}return finalize(),testN2},o.testUpdate=(info,res=o.F,suff="")=>{o.verify([[info,["object"]],[res,["boolean","string"]],[suff,["string"]]]);let row="";const testN2=info.n,log=(line="",error=!1)=>{o.tAutolog&&(error?console.error(line):console.log(line))};if(o.tStatus[testN2][info.i]===o.U||o.tStatus[testN2][info.i]===null){o.tStatus[testN2][info.i]=res===!0,res===!0?info.tShowOk&&(row="\u251C OK: "+info.title+suff,log(row),info.tStyled?o.tLog[testN2]+=o.tPre+o.tOk+info.title+suff+o.tDc:o.tLog[testN2]+=row+`
|
|
14
|
+
`):(o.tRes[testN2]=o.F,row="\u251C \u2718 "+info.title+(res?": "+res:"")+suff,log(row,!0),info.tStyled?o.tLog[testN2]+=o.tPre+o.tXx+info.title+suff+(res?": "+res:"")+o.tDc+o.tDc:o.tLog[testN2]+=row+`
|
|
15
|
+
`);let fails=0,n=0;for(const s of o.tStatus[testN2]){if(s===o.U||s===null)return;s||fails++,n++}const expectedSteps=o.tests[testN2]?.tests?.length??o.tExpectedSteps[testN2]??Number.MAX_SAFE_INTEGER;if(n<expectedSteps){sessionStorage?.getItem("oTest-Run")===testN2&&(sessionStorage.setItem(`oTest-Log-${testN2}`,o.tLog[testN2]),sessionStorage.setItem(`oTest-Res-${testN2}`,o.tRes[testN2]),sessionStorage.setItem(`oTest-Status-${testN2}`,JSON.stringify(o.tStatus[testN2])));return}if(o.tFinalized[testN2])return;o.tFinalized[testN2]=!0,sessionStorage?.getItem("oTest-Run")===testN2&&(sessionStorage.setItem(`oTest-Log-${testN2}`,o.tLog[testN2]),sessionStorage.setItem(`oTest-Res-${testN2}`,o.tRes[testN2]),sessionStorage.setItem(`oTest-Status-${testN2}`,JSON.stringify(o.tStatus[testN2]))),o.tRes[testN2]=!fails,row=fails?"FAILED "+fails+"/"+n:"DONE "+n+"/"+n,log("\u2558 "+row,!!fails),log(),info.tStyled?o.tLog[testN2]+=o.tPre+'<b style="color:'+(fails?"red":"green")+';">'+row+"</b>"+o.tDc:o.tLog[testN2]+="\u2558 "+row+`
|
|
16
|
+
`,typeof o.tFns[testN2]=="function"&&o.tFns[testN2](testN2)}},sessionStorage?.getItem("oTest-Run")&&window?.addEventListener("load",()=>{o.runTest(sessionStorage?.getItem("oTest-Run"),sessionStorage?.getItem("oTest-Autorun")||o.F,!0)},!1),o.measure=el=>{if(!el)return{};const rect=el.getBoundingClientRect(),style=window.getComputedStyle(el);return{width:rect.width,height:rect.height,top:rect.top,left:rect.left,visible:style.display!=="none"&&style.visibility!=="hidden"&&rect.width>0,opacity:style.opacity,zIndex:style.zIndex}},o.assertVisible=el=>!!o.measure(el).visible,o.assertSize=(el,expected={})=>{if(!el)return"element is null";const m=o.measure(el);if(expected.w!==void 0&&Math.round(m.width)!==expected.w)return`width: expected ${expected.w}, got ${Math.round(m.width)}`;if(expected.h!==void 0&&Math.round(m.height)!==expected.h)return`height: expected ${expected.h}, got ${Math.round(m.height)}`;const getPx=prop=>{const v=window.getComputedStyle(el).getPropertyValue(prop);return v?parseFloat(v):0};return expected.padding!==void 0&&getPx("padding-top")!==expected.padding?`padding: expected ${expected.padding}, got ${getPx("padding-top")}`:expected.paddingTop!==void 0&&getPx("padding-top")!==expected.paddingTop?`paddingTop: expected ${expected.paddingTop}, got ${getPx("padding-top")}`:expected.paddingRight!==void 0&&getPx("padding-right")!==expected.paddingRight?`paddingRight: expected ${expected.paddingRight}, got ${getPx("padding-right")}`:expected.paddingBottom!==void 0&&getPx("padding-bottom")!==expected.paddingBottom?`paddingBottom: expected ${expected.paddingBottom}, got ${getPx("padding-bottom")}`:expected.paddingLeft!==void 0&&getPx("padding-left")!==expected.paddingLeft?`paddingLeft: expected ${expected.paddingLeft}, got ${getPx("padding-left")}`:expected.margin!==void 0&&getPx("margin-top")!==expected.margin?`margin: expected ${expected.margin}, got ${getPx("margin-top")}`:expected.marginTop!==void 0&&getPx("margin-top")!==expected.marginTop?`marginTop: expected ${expected.marginTop}, got ${getPx("margin-top")}`:expected.marginRight!==void 0&&getPx("margin-right")!==expected.marginRight?`marginRight: expected ${expected.marginRight}, got ${getPx("margin-right")}`:expected.marginBottom!==void 0&&getPx("margin-bottom")!==expected.marginBottom?`marginBottom: expected ${expected.marginBottom}, got ${getPx("margin-bottom")}`:expected.marginLeft!==void 0&&getPx("margin-left")!==expected.marginLeft?`marginLeft: expected ${expected.marginLeft}, got ${getPx("margin-left")}`:!0},o.recorder={active:!1,actions:[],mocks:{},initialData:{},assertions:[],observeRoot:null,strictCapture:null,_originalFetch:null,_listeners:[],_observer:null},o.recordingAssertionDebug=!1,o.startRecording=(observe,events,timeouts)=>{if(o.recorder.active)return;let observeSel,eventsOpt,timeoutsOpt,strictCapture=null;if(observe!=null&&typeof observe=="object"&&!Array.isArray(observe)&&(o.C(observe,"observe")||o.C(observe,"events")||o.C(observe,"timeouts")||o.C(observe,"strictCaptureAssertions")||o.C(observe,"strictCaptureNetwork")||o.C(observe,"strictCaptureWebSocket"))){const bag=observe;observeSel=bag.observe!=null?String(bag.observe):void 0,eventsOpt=bag.events,timeoutsOpt=bag.timeouts,(o.C(bag,"strictCaptureAssertions")||o.C(bag,"strictCaptureNetwork")||o.C(bag,"strictCaptureWebSocket"))&&(strictCapture={assertions:!!bag.strictCaptureAssertions,network:!!bag.strictCaptureNetwork,websocket:!!bag.strictCaptureWebSocket})}else observeSel=typeof observe=="string"?observe:void 0,eventsOpt=events,timeoutsOpt=timeouts;const defaultEvents=["click","mouseover","scroll","input","change","submit","keydown","focus","blur"],defaultStepDelays={click:100,mouseover:50,scroll:30,input:50,change:50,submit:100,keydown:50,focus:50,blur:50},listenEvents=eventsOpt||defaultEvents,stepDelays=Object.assign({},defaultStepDelays,timeoutsOpt||{}),captureDebounce={scroll:30,mouseover:50,keydown:50,focus:50,blur:50},rec=o.recorder;rec.active=!0,rec.actions=[],rec.mocks={},rec.stepDelays=stepDelays,rec.initialData={url:window.location.href,timestamp:Date.now()},rec.strictCapture=strictCapture,rec.observeRoot=observeSel||null,rec.assertions=[],rec.removedElements=[],o.inits.forEach((inst,idx)=>{inst?.store&&(rec.initialData["init_"+idx]=JSON.parse(JSON.stringify(inst.store)))}),rec._originalFetch=window.fetch,window.fetch=async(url,opts={})=>{const method=(opts.method||"GET").toUpperCase();let reqBody;try{reqBody=opts.body?JSON.parse(opts.body):void 0}catch{reqBody=opts.body}const response=await rec._originalFetch(url,opts),clone=response.clone();let respBody;try{respBody=await clone.json()}catch{respBody=await clone.text().catch(()=>null)}const key=method+":"+url;return rec.mocks[key]={url,method,request:reqBody,response:respBody,status:response.status},response},rec._originalXHROpen=XMLHttpRequest.prototype.open,rec._originalXHRSend=XMLHttpRequest.prototype.send,XMLHttpRequest.prototype.open=function(method,url){return this._oMethod=(method||"GET").toUpperCase(),this._oUrl=url,rec._originalXHROpen.apply(this,arguments)},XMLHttpRequest.prototype.send=function(body){const capture=()=>{if(this.readyState!==4)return;let reqBody;try{reqBody=body?JSON.parse(body):void 0}catch{reqBody=body}let respBody;try{const text=this.responseText;respBody=text?JSON.parse(text):null}catch{respBody=this.responseText??null}const key=(this._oMethod||"GET")+":"+(this._oUrl||"");rec.mocks[key]={url:this._oUrl,method:this._oMethod,request:reqBody,response:respBody,status:this.status}};return this.addEventListener("readystatechange",capture),rec._originalXHRSend.apply(this,arguments)},rec.websocketEvents=[],rec._originalWebSocket=window.WebSocket,window.WebSocket=function(url,protocols){const ws=new rec._originalWebSocket(url,protocols),id=rec.websocketEvents.length;rec.websocketEvents.push({url:typeof url=="string"?url:String(url),protocol:Array.isArray(protocols)?protocols[0]:protocols,open:!0,messages:[]}),ws.addEventListener("message",e=>{const data=typeof e.data=="string"?e.data:String(e.data);rec.websocketEvents[id].messages.push({dir:"in",data})}),ws.addEventListener("close",()=>{rec.websocketEvents[id].open=!1});const origSend=ws.send.bind(ws);return ws.send=function(data){const d=typeof data=="string"?data:String(data);return rec.websocketEvents[id].messages.push({dir:"out",data:d}),origSend(data)},ws};const unstableDataAttrs={"data-o-init":1,"data-o-init-i":1,"data-o-state":1},qualify=(sel,fromNode)=>{if(o.D.querySelectorAll(sel).length<=1)return sel;let node=fromNode;for(;node&&node!==o.D.body;){let ancestorSel=node.id?"#"+node.id:null;if(!ancestorSel&&node.attributes){const autotagAttr=o.autotag?"data-"+o.autotag:null;if(autotagAttr){const val=node.getAttribute(autotagAttr);val!=null&&(ancestorSel=`[${autotagAttr}="${val}"]`)}if(!ancestorSel){for(const attr of node.attributes)if(attr.name.startsWith("data-")&&!unstableDataAttrs[attr.name]){ancestorSel=`[${attr.name}="${attr.value}"]`;break}}}if(ancestorSel){const q=ancestorSel+" "+sel;if(o.D.querySelectorAll(q).length===1)return q}node=node.parentElement}return sel},buildSelector=node=>{if(!node||node.nodeType!==1)return"";let sel="";if(node.dataset){const qaKey=o.autotag&&node.dataset[o.autotag];qaKey&&(sel=qualify(`[data-${o.autotag}="${qaKey}"]`,node.parentElement))}if(!sel&&node.tagName){const base=node.id?"#"+node.id:node.className?node.tagName.toLowerCase()+"."+[...node.classList].join("."):node.tagName.toLowerCase();sel=node.id?base:qualify(base,node.parentElement)}return sel},observeTarget=observeSel&&o.D.querySelector(observeSel)||o.D.body;rec._observer=new MutationObserver(mutations=>{const actionIdx=rec.actions.length-1;if(actionIdx<0)return;const lastAction=rec.actions[actionIdx];o.recordingAssertionDebug&&typeof console<"u"&&console.log&&console.log("[recording] MutationObserver batch:",{actionIdx,lastAction:lastAction?{type:lastAction.type,target:lastAction.target}:null,mutationTypes:mutations.map(x=>x.type),addedCount:mutations.reduce((n,x)=>n+(x.addedNodes?.length||0),0),removedCount:mutations.reduce((n,x)=>n+(x.removedNodes?.length||0),0)}),mutations.forEach(m=>{const addAssertionIndex=(sel,node)=>{let listSelector,index;if(sel&&observeTarget){const matches=observeTarget.querySelectorAll(sel);if(matches.length>1){const idxAmong=[...matches].indexOf(node);if(idxAmong!==-1)listSelector=sel,index=idxAmong;else{let n=node;for(;n&&n!==observeTarget&&n.nodeType===1;){const qaAttr=o.autotag&&n.dataset&&n.dataset[o.autotag];if(qaAttr){const itemSel=`[data-${o.autotag}="${qaAttr}"]`,itemMatches=observeTarget.querySelectorAll(itemSel);if(itemMatches.length>1){const idx=[...itemMatches].indexOf(n);if(idx!==-1){listSelector=itemSel,index=idx;break}}}n=n.parentElement}}}}return{listSelector,index}};if(m.type==="childList"&&(m.addedNodes.forEach(node=>{if(node.nodeType!==1||!node.offsetParent)return;const sel=buildSelector(node);if(!sel||rec.assertions.some(a2=>a2.actionIdx===actionIdx&&a2.selector===sel&&a2.type==="visible"))return;const text=(node.textContent?.trim()||"").slice(0,80)||void 0,{listSelector:aListSel,index:aIdx}=addAssertionIndex(sel,node),a={actionIdx,type:"visible",selector:sel,text};aListSel!=null&&(a.listSelector=aListSel),aIdx!=null&&(a.index=aIdx),rec.assertions.push(a),o.recordingAssertionDebug&&typeof console<"u"&&console.log&&console.log("[recording] +visible assertion:",{actionIdx,lastAction:lastAction?.type+" "+lastAction?.target,selector:sel,text:(text||"").slice(0,40),index:aIdx,listSelector:aListSel})}),m.removedNodes.forEach(node=>{if(node.nodeType!==1)return;const sel=buildSelector(node);if(!sel)return;const text=(node.textContent?.trim()||"").slice(0,80)||void 0,parent=m.target;let index;node.previousSibling?index=Array.from(parent.children).indexOf(node.previousSibling)+1:node.nextSibling?index=Array.from(parent.children).indexOf(node.nextSibling):index=0;let listSelector;if(o.autotag&&node.dataset?.[o.autotag]){const qaVal=node.dataset[o.autotag];listSelector=`[data-${o.autotag}="${qaVal}"]`}const entry={actionIdx,type:"removed",selector:sel,text};listSelector&&(entry.listSelector=listSelector),entry.index=index,rec.removedElements.push(entry),o.recordingAssertionDebug&&typeof console<"u"&&console.log&&console.log("[recording] +removed element:",{actionIdx,lastAction:lastAction?.type+" "+lastAction?.target,selector:sel,text:(text||"").slice(0,40),index,listSelector})})),m.type==="attributes"){const attr=m.attributeName;if(!attr)return;const sel=buildSelector(m.target);if(!sel)return;const type={class:"class",style:"style",hidden:"hidden",disabled:"disabled","aria-expanded":"aria-expanded","aria-checked":"aria-checked"}[attr];if(!type||rec.assertions.some(a2=>a2.actionIdx===actionIdx&&a2.selector===sel&&a2.type===type))return;const{listSelector:aListSel,index:aIdx}=addAssertionIndex(sel,m.target),el=m.target;let value;type==="class"?value=el.className:type==="style"?value=el.style?.cssText||el.getAttribute("style")||"":type==="hidden"?value=el.hidden:type==="disabled"?value=el.disabled===!0:type==="aria-expanded"?value=el.getAttribute("aria-expanded"):type==="aria-checked"&&(value=el.getAttribute("aria-checked"));const a={actionIdx,type,selector:sel};type==="class"?a.className=value:type==="style"?a.style=value:type==="hidden"?a.hidden=value:type==="disabled"?a.disabled=value:type==="aria-expanded"?a.ariaExpanded=value:type==="aria-checked"&&(a.ariaChecked=value),aListSel!=null&&(a.listSelector=aListSel),aIdx!=null&&(a.index=aIdx),rec.assertions.push(a),o.recordingAssertionDebug&&typeof console<"u"&&console.log&&console.log("[recording] +attr assertion:",{actionIdx,lastAction:lastAction?.type+" "+lastAction?.target,selector:sel,type,value,index:aIdx,listSelector:aListSel})}})}),rec._observer.observe(observeTarget,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["class","style","hidden","disabled","aria-expanded","aria-checked"]});const timers={};listenEvents.forEach(ev=>{const handler=e=>{const target=e.target;if(observeSel&&observeTarget&&target?.nodeType===1&&!observeTarget.contains(target))return;let selector="";if(target?.dataset){const qaKey=o.autotag&&target.dataset[o.autotag];qaKey&&(selector=qualify(`[data-${o.autotag}="${qaKey}"]`,target.parentElement))}if(!selector&&target?.tagName){const base=target.id?"#"+target.id:target.className?target.tagName.toLowerCase()+"."+[...target.classList].join("."):target.tagName.toLowerCase();selector=target.id?base:qualify(base,target.parentElement)}let listSelector,targetIndex;if(selector&&observeTarget){const matches=observeTarget.querySelectorAll(selector);if(matches.length>1){const idxAmongMatches=[...matches].indexOf(target);if(idxAmongMatches!==-1)listSelector=selector,targetIndex=idxAmongMatches;else{let node=target;for(;node&&node!==observeTarget&&node.nodeType===1;){const qaAttr=o.autotag&&node.dataset&&node.dataset[o.autotag];if(qaAttr){const itemSel=`[data-${o.autotag}="${qaAttr}"]`,itemMatches=observeTarget.querySelectorAll(itemSel);if(itemMatches.length>1){const idx=[...itemMatches].indexOf(node);if(idx!==-1){listSelector=itemSel,targetIndex=idx;break}}}node=node.parentElement}}}}const targetType=target?.tagName?target.tagName.toLowerCase()+(target.type?":"+target.type:""):void 0,scrollY=ev==="scroll"?window.scrollY:void 0,value=ev==="input"||ev==="change"?target?.value:void 0,checked=ev==="change"&&(target?.type==="checkbox"||target?.type==="radio")?target?.checked:void 0,key=ev==="keydown"?target?.key:void 0,code=ev==="keydown"?target?.code:void 0,delay=ev==="click"||ev==="change"||ev==="submit"?0:stepDelays[ev]!==void 0?stepDelays[ev]:captureDebounce[ev]??0,pushAction=()=>{if((ev==="blur"||ev==="focus")&&selector){const lastIdx=rec.actions.length-1,lastAction=lastIdx>=0?rec.actions[lastIdx]:null;if(lastAction){if(lastAction.target===selector&&lastAction.listSelector==null==(listSelector==null)&&lastAction.targetIndex==null==(targetIndex==null)&&(lastAction.targetIndex==null||lastAction.targetIndex===targetIndex))return;for(const r of rec.removedElements)if(r.actionIdx===lastIdx&&(r.selector===selector||selector.startsWith(r.selector+" ")||selector.startsWith(r.selector+">")))return}}const action={type:ev,target:selector,time:Date.now()};targetType&&(action.targetType=targetType),scrollY!==void 0&&(action.scrollY=scrollY),value!==void 0&&(action.value=value),checked!==void 0&&(action.checked=checked),key!==void 0&&(action.key=key),code!==void 0&&(action.code=code),listSelector!=null&&(action.listSelector=listSelector),targetIndex!=null&&(action.targetIndex=targetIndex),rec.actions.push(action)};delay===0?pushAction():(clearTimeout(timers[ev]),timers[ev]=setTimeout(pushAction,delay))};o.D.addEventListener(ev,handler,!0),rec._listeners.push({ev,handler})})},o.stopRecording=()=>{const rec=o.recorder;rec.active=!1,rec._originalFetch&&(window.fetch=rec._originalFetch,rec._originalFetch=null),rec._originalXHROpen&&(XMLHttpRequest.prototype.open=rec._originalXHROpen,XMLHttpRequest.prototype.send=rec._originalXHRSend,rec._originalXHROpen=null,rec._originalXHRSend=null),rec._originalWebSocket&&(window.WebSocket=rec._originalWebSocket,rec._originalWebSocket=null),rec._listeners.forEach(({ev,handler})=>{o.D.removeEventListener(ev,handler,!0)}),rec._listeners=[],rec._observer&&(rec._observer.disconnect(),rec._observer=null);const out={actions:[...rec.actions],mocks:{...rec.mocks},initialData:{...rec.initialData},stepDelays:{...rec.stepDelays},assertions:[...rec.assertions||[]],removedElements:[...rec.removedElements||[]],observeRoot:rec.observeRoot||null,websocketEvents:[...rec.websocketEvents||[]]};return rec.strictCapture&&(out.strictCapture={...rec.strictCapture}),out},o.clearRecording=id=>{if(id!==void 0)sessionStorage?.removeItem("oTest-Recording-"+id);else for(let i=sessionStorage?.length-1;i>=0;i--){const key=sessionStorage?.key(i);key&&key.indexOf("oTest-Recording-")===0&&sessionStorage?.removeItem(key)}},o.runRecordingAssertions=(recording,root,actionIdx,opts)=>{const strictAssertions=!!(opts&&opts.strictAssertions),strictRemoved=opts&&opts.strictRemoved!==void 0?!!opts.strictRemoved:strictAssertions,preFiltered=opts&&opts.assertions,assertions=preFiltered??(recording.assertions||[]).filter(a=>actionIdx==null||a.actionIdx===actionIdx);o.recordingAssertionDebug&&typeof console<"u"&&console.log&&console.log("[runRecordingAssertions] run:",{actionIdx,scope:actionIdx==null?"teardown (all)":"per-action",assertionsCount:assertions.length,assertions:assertions.map(a=>({actionIdx:a.actionIdx,type:a.type,selector:a.selector,index:a.index,text:(a.text||"").slice(0,40)}))});const seen=new Set,deduped=assertions.filter(a=>{const key=`${a.selector}|${a.type}|${a.actionIdx}|${a.index??""}`;return seen.has(key)?!1:(seen.add(key),!0)}),r=(()=>{if(root!=null)return typeof root=="string"?o.D.querySelector(root)||o.D.body:root;const sel=recording.observeRoot;return sel&&o.D.querySelector(sel)||o.D.body})(),norm=s=>(s||"").trim().replace(/\s+/g," "),styleNorm=s=>norm(String(s||"").replace(/\s*:\s*/g,": ").replace(/\s*;\s*/g,"; ")),getText=el=>el?norm(el.textContent||""):"",removedElements=opts?.removedElements||[],isRemoved=a=>{if(!removedElements.length||actionIdx==null)return!1;const expText=norm(a.text||"");for(const r2 of removedElements)if(!(r2.actionIdx>actionIdx)&&norm(r2.text||"")===expText&&r2.selector===a.selector&&!(a.listSelector!=null&&r2.listSelector!==a.listSelector)&&!(a.index!=null&&r2.index!==a.index))return!0;return!1};let passed=0;const failures=[];for(const a of deduped){if(isRemoved(a)){if(!strictRemoved){passed+=1,o.recordingAssertionDebug&&typeof console<"u"&&console.log&&console.log("[runRecordingAssertions] skip (explicit removed):",{actionIdx:a.actionIdx,selector:a.selector,text:(a.text||"").slice(0,40)});continue}let ghost=null;const expText=norm(a.text||"");if(a.listSelector!=null&&a.index!=null){const items=r.querySelectorAll(a.listSelector);let item=items[a.index];if(!item&&a.index>0&&(item=items[a.index-1]),item&&(ghost=a.selector!==a.listSelector&&item.querySelector(a.selector)||item),!ghost&&expText&&a.type==="visible")for(let j=0;j<items.length;j++){const it=items[j],cand=a.selector!==a.listSelector&&it.querySelector(a.selector)||it;if(cand&&getText(cand).indexOf(expText)!==-1){ghost=cand;break}}}else{const matches=r.querySelectorAll(a.selector);ghost=matches.length>0?matches[0]:o.D.querySelector(a.selector)}if(ghost&&a.type==="visible"){const vis=ghost.nodeType===1&&(ghost.offsetParent!==null||ghost.getBoundingClientRect&&ghost.getBoundingClientRect().width>0),gtext=getText(ghost);if(vis&&(!expText||gtext.indexOf(expText)!==-1||expText.indexOf(gtext)!==-1)){failures.push({selector:a.selector,message:"expected absent (recorded removed) but matching content still visible"});continue}}else if(ghost&&a.type!=="visible"){failures.push({selector:a.selector,message:"expected absent (recorded removed) but element still present"});continue}passed+=1;continue}let el=null,indexOutOfBounds=!1,listItemsLength=-1;if(a.listSelector!=null&&a.index!=null){const items=r.querySelectorAll(a.listSelector);listItemsLength=items.length;const expectedText=norm(a.text||""),tryItem=idx=>{const it=items[idx];return it?(a.selector!==a.listSelector?it.querySelector(a.selector):it)||(a.selector!==a.listSelector?it:null):null};let item;if(strictAssertions)item=items[a.index],item&&(el=tryItem(a.index),!el&&a.selector!==a.listSelector&&(el=item));else if(item=items[a.index],!item&&a.index>0&&(item=items[a.index-1]),item&&(el=tryItem(a.index)||(a.index>0?tryItem(a.index-1):null),!el&&a.selector!==a.listSelector&&(el=item),a.type==="visible"&&expectedText&&el)){const actualText=getText(el);if(actualText.indexOf(expectedText)===-1&&expectedText.indexOf(actualText)===-1)for(let j=0;j<items.length;j++){const candEl=tryItem(j);if(candEl&&getText(candEl).indexOf(expectedText)!==-1){el=candEl,item=items[j];break}}}item||(indexOutOfBounds=!0)}else{const matches=r.querySelectorAll(a.selector);el=matches.length>0?matches[0]:o.D.querySelector(a.selector)}if(a.type==="visible"){const visible=el&&el.nodeType===1&&(el.offsetParent!==null||el.getBoundingClientRect&&el.getBoundingClientRect().width>0),expectedText=norm(a.text||""),actualText=getText(el),textOk=strictAssertions?!expectedText||actualText===expectedText:!expectedText||actualText.indexOf(expectedText)!==-1||expectedText.length>0&&expectedText.indexOf(actualText)!==-1;if(visible&&textOk)passed+=1;else{const listCount=listItemsLength>=0?listItemsLength:r.querySelectorAll(a.listSelector||a.selector).length,message=indexOutOfBounds?`index out of bounds (list has ${listCount} items, assertion expected index ${a.index})`:el?visible?textOk?"fail":"text mismatch":"not visible":"element not found";failures.push({selector:a.selector,message}),typeof console<"u"&&console.warn&&console.warn("[runRecordingAssertions] visible failed:",{actionIdx:a.actionIdx,selector:a.selector,listSelector:a.listSelector,index:a.index,expectedText:a.text||"(any)",actualText:actualText.slice(0,80),message})}}else if(a.type==="class"){const tokens=(a.className||"").trim().split(/\s+/).filter(Boolean),hasClass=el&&(tokens.length===0||tokens.every(c=>el.classList?.contains(c))),classOrderOk=!strictAssertions||!a.className||norm((el?.className||"").trim())===norm((a.className||"").trim());if(hasClass&&classOrderOk)passed+=1;else{const msg=indexOutOfBounds?`index out of bounds (list has ${r.querySelectorAll(a.listSelector).length} items, expected index ${a.index})`:el?hasClass&&!classOrderOk?`expected exact className "${a.className}" (strict)`:`expected class "${a.className}"`:"element not found";failures.push({selector:a.selector,message:msg}),typeof console<"u"&&console.warn&&console.warn("[runRecordingAssertions] failed:",{type:a.type,selector:a.selector,actionIdx:a.actionIdx,listSelector:a.listSelector,index:a.index,itemsInRoot:a.listSelector?r.querySelectorAll(a.listSelector).length:"-",message:msg})}}else if(a.type==="style"){const expected=(a.style||"").trim(),actual=(el?.style?.cssText||el?.getAttribute?.("style")||"").trim();if(el&&(!expected||(strictAssertions?styleNorm(actual)===styleNorm(expected):actual.indexOf(expected)!==-1||expected===actual)))passed+=1;else{const msg=el?`expected style "${expected.slice(0,60)}..."`:"element not found";failures.push({selector:a.selector,message:msg})}}else if(a.type==="hidden")if(el&&el.hidden===a.hidden)passed+=1;else{const msg=el?`expected hidden=${a.hidden}`:"element not found";failures.push({selector:a.selector,message:msg})}else if(a.type==="disabled")if(el&&el.disabled===a.disabled)passed+=1;else{const msg=el?`expected disabled=${a.disabled}`:"element not found";failures.push({selector:a.selector,message:msg})}else if(a.type==="aria-expanded"){const actual=el?.getAttribute?.("aria-expanded");if(el&&(a.ariaExpanded==null||String(actual)===String(a.ariaExpanded)))passed+=1;else{const msg=el?`expected aria-expanded="${a.ariaExpanded}"`:"element not found";failures.push({selector:a.selector,message:msg})}}else if(a.type==="aria-checked"){const actual=el?.getAttribute?.("aria-checked");if(el&&(a.ariaChecked==null||String(actual)===String(a.ariaChecked)))passed+=1;else{const msg=el?`expected aria-checked="${a.ariaChecked}"`:"element not found";failures.push({selector:a.selector,message:msg})}}}return{passed,total:deduped.length,failures}},o.exportTest=(recording,options={})=>{const delay=options.delay!==void 0?options.delay:16,extensionExport=options.extensionExport===!0,recordingData={actions:recording.actions,assertions:recording.assertions||[],observeRoot:recording.observeRoot||null},rootVar=recording.observeRoot?`(o.D.querySelector('${recording.observeRoot.replace(/'/g,"\\'")}') || o.D.body)`:"o.D.body",getEl=a=>{if(a.listSelector!=null&&a.targetIndex!=null){const listSel=JSON.stringify(a.listSelector),useItem=a.target===a.listSelector,targetSel=useItem?listSel:JSON.stringify(a.target);return` const items = o.D.querySelectorAll(${listSel});
|
|
17
|
+
const item = items[${a.targetIndex}];
|
|
18
|
+
let el = null;
|
|
19
|
+
if (item) { el = ${useItem?"item":`item.querySelector(${targetSel}) || item`}; }`}return` const el = o.D.querySelector(${JSON.stringify(a.target)});`},endSuffix=delay>0?`
|
|
20
|
+
await o.sleep(${delay});
|
|
21
|
+
return true;
|
|
22
|
+
`:` return true;
|
|
23
|
+
`,stepFn=delay>0?"async () =>":"() =>",steps=[];for(let i=0;i<recording.actions.length;i++){const a=recording.actions[i];let body;a.type==="scroll"?body=` window.scrollTo(0, ${a.scrollY||0});${endSuffix}`:a.type==="input"||a.type==="change"?body=(a.value!==void 0?` el.value = ${JSON.stringify(a.value)};
|
|
24
|
+
`:"")+(a.checked!==void 0?` el.checked = ${a.checked};
|
|
25
|
+
`:"")+` el.dispatchEvent(new Event('${a.type}', {bubbles:true}));${endSuffix}`:a.type==="submit"?body=` (el.requestSubmit && el.requestSubmit()) || el.submit();${endSuffix}`:a.type==="keydown"?body=` el.dispatchEvent(new KeyboardEvent('keydown', {key:${JSON.stringify(a.key||"")}, code:${JSON.stringify(a.code||"")}, bubbles:true, cancelable:true}));${endSuffix}`:a.type==="focus"?body=` el.focus();${endSuffix}`:a.type==="blur"?body=` el.blur();${endSuffix}`:body=a.type==="click"?` el.click();${endSuffix}`:` el.dispatchEvent(new MouseEvent('${a.type}', {bubbles:true,cancelable:true}));${endSuffix}`;const skipIfMissing=a.type==="blur"||a.type==="focus";steps.push(` ['${a.type} on ${a.target}', ${stepFn} {
|
|
26
|
+
`+getEl(a)+`
|
|
27
|
+
if (!el && '${a.type}' !== 'scroll') { if (${skipIfMissing}) return true; return 'element not found: ${a.target.replace(/'/g,"\\'")}'; }
|
|
28
|
+
`+body+" }]"),(recording.assertions||[]).filter(x=>x.actionIdx===i).length>0&&steps.push(` ['assert after ${a.type}', () => {
|
|
29
|
+
const r = o.runRecordingAssertions(recordingData, ${rootVar}, ${i});
|
|
30
|
+
return r.passed === r.total ? true : r.failures.map(f => f.selector + ': ' + f.message).join('; ');
|
|
31
|
+
}]`)}const header=`// Auto-generated by o.exportTest() \u2014 review and anonymize mocks before committing
|
|
32
|
+
const recordingMocks = ${Object.keys(recording.mocks||{}).length?JSON.stringify(recording.mocks,null,2):"{}"};
|
|
33
|
+
const recordingData = { actions: ${JSON.stringify(recording.actions)}, assertions: ${JSON.stringify(recording.assertions||[])}, observeRoot: ${JSON.stringify(recording.observeRoot||null)} };
|
|
34
|
+
|
|
35
|
+
`,manualLine=" // Add manual checks: ['Manual: label', () => o.testConfirm('label', ['item1'])],";return extensionExport?header+`const __objsExtensionTestRun = o.test('Recorded test',
|
|
36
|
+
${steps.join(`,
|
|
37
|
+
`)},
|
|
38
|
+
${manualLine}
|
|
39
|
+
{ sync: true }, () => {
|
|
40
|
+
// teardown
|
|
41
|
+
});
|
|
42
|
+
`:header+`o.addTest('Recorded test', [
|
|
43
|
+
${steps.join(`,
|
|
44
|
+
`)}
|
|
45
|
+
${manualLine}
|
|
46
|
+
], () => {
|
|
47
|
+
// teardown
|
|
48
|
+
});
|
|
49
|
+
`},o.exportPlaywrightTest=(recording,options={})=>{const testName=options.testName||"Recorded session",rawUrl=recording.initialData?.url??"/";let path="/";try{path=new URL(rawUrl).pathname||"/"}catch{path=rawUrl}const baseUrl=options.baseUrl||path,routes=Object.values(recording.mocks).map(mock=>{let urlPath=mock.url;try{urlPath=new URL(mock.url).pathname||urlPath}catch{}urlPath.startsWith("/")||(urlPath="/"+urlPath);const respBody=JSON.stringify(mock.response),reqBody=JSON.stringify(mock.request),method=(mock.method||"GET").toUpperCase();let verify=` if (route.request().method() !== ${JSON.stringify(method)}) { await route.continue(); return; }
|
|
50
|
+
`;return mock.request!=null&&(method==="POST"||method==="PUT"||method==="PATCH")&&(verify+=` const postData = route.request().postData();
|
|
51
|
+
const body = (() => { try { return JSON.parse(postData || '{}'); } catch { return {}; } })();
|
|
52
|
+
expect(body).toEqual(${reqBody});
|
|
53
|
+
`),` await page.route('**${urlPath}', async route => {
|
|
54
|
+
`+verify+` await route.fulfill({ status: ${mock.status||200}, contentType: 'application/json',
|
|
55
|
+
body: JSON.stringify(${respBody}) });
|
|
56
|
+
});`}).join(`
|
|
57
|
+
`),sd=Object.assign({click:100,mouseover:50,scroll:30,input:50,change:50,submit:100,keydown:50,focus:50,blur:50},recording.stepDelays||{}),steps=recording.actions.map((action,i)=>{const loc=action.listSelector!=null&&action.targetIndex!=null?action.target!==action.listSelector?`page.locator(${JSON.stringify(action.listSelector)}).nth(${action.targetIndex}).locator(${JSON.stringify(action.target)})`:`page.locator(${JSON.stringify(action.listSelector)}).nth(${action.targetIndex})`:`page.locator(${JSON.stringify(action.target)})`,wait=` await page.waitForTimeout(${sd[action.type]||50});`;let step;if(action.type==="scroll")step=` await page.evaluate(() => window.scrollTo(0, ${action.scrollY||0}));`;else if(action.type==="mouseover")step=` await ${loc}.hover();
|
|
58
|
+
// Pure CSS :hover \u2014 no DOM mutation to assert.
|
|
59
|
+
// Fix: toggle a class in a mouseover handler, or add a page.screenshot() visual check.`;else if(action.type==="input")step=` await ${loc}.fill(${JSON.stringify(action.value||"")});`;else if(action.type==="change"){const tt=action.targetType||"";if(tt.indexOf(":checkbox")!==-1||tt.indexOf(":radio")!==-1){const on=action.checked!==void 0?action.checked:action.value==="true"||action.value==="on";step=` await ${loc}.${on?"check":"uncheck"}();`}else tt.indexOf("select")!==-1?step=` await ${loc}.selectOption(${JSON.stringify(action.value||"")});`:step=` await ${loc}.fill(${JSON.stringify(action.value||"")});`}else if(action.type==="submit")step=` await ${loc}.evaluate((el) => el.requestSubmit?.() || el.submit());`;else if(action.type==="keydown"){const key=action.key||"";step=key==="Enter"?` await ${loc}.press("Enter");`:key?` await ${loc}.press(${JSON.stringify(key)});`:` await ${loc}.press(${JSON.stringify(action.code||"")});`}else action.type==="focus"?step=` if (await ${loc}.count() > 0) await ${loc}.focus();`:action.type==="blur"?step=` if (await ${loc}.count() > 0) await ${loc}.blur();`:step=` await ${loc}.click();`;const asserts=(recording.assertions||[]).filter(a=>a.actionIdx===i).filter((a,j,arr)=>arr.findIndex(x=>x.selector===a.selector&&x.type===a.type&&x.index===a.index)===j).map(a=>{const aLoc=a.listSelector!=null&&a.index!=null?a.selector!==a.listSelector?`page.locator(${JSON.stringify(a.listSelector)}).nth(${a.index}).locator(${JSON.stringify(a.selector)})`:`page.locator(${JSON.stringify(a.listSelector)}).nth(${a.index})`:`page.locator(${JSON.stringify(a.selector)})`;if(a.type==="visible"){let s=` await expect(${aLoc}).toBeVisible();`;return a.text&&(s+=`
|
|
60
|
+
await expect(${aLoc}).toContainText(${JSON.stringify(a.text)});`),s}if(a.type==="class"){const classes=(a.className||"").trim().split(/\s+/).filter(Boolean);return classes.length>0?classes.map(c=>` await expect(${aLoc}).toHaveClass(${JSON.stringify(c)});`).join(`
|
|
61
|
+
`):` // class on ${a.selector} (no specific classes asserted)`}if(a.type==="style"){const style=(a.style||"").trim();if(style){const m=style.match(/(\w+)\s*:\s*([^;]+)/);return m?` await expect(${aLoc}).toHaveCSS(${JSON.stringify(m[1])}, ${JSON.stringify(m[2].trim())});`:` await expect(${aLoc}).toHaveAttribute("style", ${JSON.stringify(style)});`}return""}return a.type==="hidden"?a.hidden?` await expect(${aLoc}).toBeHidden();`:` await expect(${aLoc}).toBeVisible();`:a.type==="disabled"?a.disabled?` await expect(${aLoc}).toBeDisabled();`:` await expect(${aLoc}).toBeEnabled();`:a.type==="aria-expanded"&&a.ariaExpanded!=null?` await expect(${aLoc}).toHaveAttribute("aria-expanded", ${JSON.stringify(String(a.ariaExpanded))});`:a.type==="aria-checked"&&a.ariaChecked!=null?` await expect(${aLoc}).toHaveAttribute("aria-checked", ${JSON.stringify(String(a.ariaChecked))});`:""}).filter(Boolean).join(`
|
|
62
|
+
`);return step+`
|
|
63
|
+
`+wait+(asserts?`
|
|
64
|
+
`+asserts:"")}).join(`
|
|
65
|
+
`),hasAutoAssertions=(recording.assertions||[]).length>0,wsEvents=recording.websocketEvents||[],hasWsEvents=wsEvents.length>0&&wsEvents.some(c=>c.messages?.length>0),wsSetup=hasWsEvents?` const wsCollected = [];
|
|
66
|
+
page.on('websocket', ws => {
|
|
67
|
+
ws.on('framereceived', ev => wsCollected.push({ dir: 'in', payload: typeof ev.payload === 'string' ? ev.payload : String(ev.payload) }));
|
|
68
|
+
ws.on('framesent', ev => wsCollected.push({ dir: 'out', payload: typeof ev.payload === 'string' ? ev.payload : String(ev.payload) }));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
`:"",wsAssertions=hasWsEvents?wsEvents.flatMap(conn=>(conn.messages||[]).map(msg=>({dir:msg.dir,data:msg.data}))).map(msg=>` expect(wsCollected).toContainEqual({ dir: ${JSON.stringify(msg.dir)}, payload: ${JSON.stringify(msg.data)} });`).join(`
|
|
72
|
+
`)+`
|
|
73
|
+
|
|
74
|
+
`:"";return`// Auto-generated by o.exportPlaywrightTest() \u2014 review and anonymize mocks before committing
|
|
75
|
+
// Prerequisites: npm install @playwright/test && npx playwright install chromium
|
|
76
|
+
// Run: npx playwright test recorded.spec.ts
|
|
77
|
+
import { test, expect } from '@playwright/test';
|
|
78
|
+
|
|
79
|
+
test(${JSON.stringify(testName)}, async ({ page }) => {
|
|
80
|
+
`+(routes?` // Network mocks \u2014 edit/anonymize before committing
|
|
81
|
+
`+routes+`
|
|
82
|
+
|
|
83
|
+
`:"")+wsSetup+` // Set baseURL in playwright.config.ts: { use: { baseURL: 'https://staging.example.com' } }
|
|
84
|
+
await page.goto(${JSON.stringify(baseUrl)});
|
|
85
|
+
|
|
86
|
+
`+(steps?steps+`
|
|
87
|
+
|
|
88
|
+
`:"")+(wsAssertions?` // WebSocket verifications
|
|
89
|
+
`+wsAssertions:"")+(!hasAutoAssertions&&!hasWsEvents?` // TODO: Add assertions before committing, e.g.:
|
|
90
|
+
// await expect(page.locator('[data-qa="success-panel"]')).toBeVisible();
|
|
91
|
+
// await expect(page).toHaveURL(/\\/confirmation/);
|
|
92
|
+
// await expect(page.locator('[data-qa="error-banner"]')).not.toBeVisible();
|
|
93
|
+
`:hasAutoAssertions||hasWsEvents?` // Auto-generated assertions above \u2014 review for correctness before committing
|
|
94
|
+
`:"")+`});
|
|
95
|
+
`},o.playRecording=(recording,opts={})=>{const isOptions=opts&&typeof opts=="object"&&(opts.runAssertions!==void 0||opts.root!==void 0||opts.manualChecks!==void 0||opts.actionDelay!==void 0||opts.skipWebSocketMock!==void 0||opts.skipNetworkMocks!==void 0||opts.recordingAssertionDebug!==void 0||opts.strictPlay!==void 0||opts.strictAssertions!==void 0||opts.strictNetwork!==void 0||opts.strictWebSocket!==void 0||opts.strictRemoved!==void 0||opts.onComplete!==void 0),mockOverrides=isOptions?opts.mockOverrides||{}:opts,runAssertions=isOptions&&opts.runAssertions,rootOpt=isOptions?opts.root:void 0,manualChecks=isOptions&&opts.manualChecks||[],actionDelay=isOptions&&opts.actionDelay!==void 0?opts.actionDelay:16,skipWebSocketMock=isOptions&&opts.skipWebSocketMock,skipNetworkMocks=isOptions&&opts.skipNetworkMocks;isOptions&&opts.recordingAssertionDebug!==void 0&&(o.recordingAssertionDebug=!!opts.recordingAssertionDebug);const sc=recording.strictCapture||{},strictPlay=isOptions&&opts.strictPlay===!0,strictAssertions=isOptions&&opts.strictAssertions!==void 0?!!opts.strictAssertions:strictPlay?!0:!!sc.assertions,strictNetwork=isOptions&&opts.strictNetwork!==void 0?!!opts.strictNetwork:strictPlay?!0:!!sc.network,strictWebSocket=isOptions&&opts.strictWebSocket!==void 0?!!opts.strictWebSocket:strictPlay?!0:!!sc.websocket,strictRemoved=isOptions&&opts.strictRemoved!==void 0?!!opts.strictRemoved:strictAssertions,parseBodyLikeRecorder=body=>{if(!(body==null||body==="")){if(typeof body=="string")try{return JSON.parse(body)}catch{return body}return body}},mockRequestMatchesLive=(recordedReq,liveBody)=>{const live=parseBodyLikeRecorder(liveBody);return recordedReq===live||recordedReq==null&&live==null?!0:recordedReq==null||live==null?!1:typeof recordedReq=="object"&&typeof live=="object"?JSON.stringify(recordedReq)===JSON.stringify(live):String(recordedReq)===String(live)},normWsData=s=>String(s||"").trim().replace(/\s+/g," "),allMocks=Object.assign({},recording.mocks,mockOverrides),origFetch=window.fetch,origXHROpen=XMLHttpRequest.prototype.open,origXHRSend=XMLHttpRequest.prototype.send;skipNetworkMocks||(window.fetch=(url,fetchOpts={})=>{const key=(fetchOpts.method||"GET").toUpperCase()+":"+url;if(allMocks[key]){const mock=allMocks[key];if(strictNetwork&&o.C(mock,"request")&&!mockRequestMatchesLive(mock.request,fetchOpts.body))return Promise.reject(new Error("[Objs playRecording] strictNetwork: request body does not match recording for "+key));const body=typeof mock.response=="string"?mock.response:JSON.stringify(mock.response);return Promise.resolve(new Response(body,{status:mock.status||200}))}return origFetch(url,fetchOpts)},XMLHttpRequest.prototype.open=function(method,url){return this._oMethod=(method||"GET").toUpperCase(),this._oUrl=url,origXHROpen.apply(this,arguments)},XMLHttpRequest.prototype.send=function(body){const xhr=this,key=(xhr._oMethod||"GET")+":"+(xhr._oUrl||""),mock=allMocks[key];if(mock){if(strictNetwork&&o.C(mock,"request")&&!mockRequestMatchesLive(mock.request,body)){setTimeout(()=>{xhr.readyState=4,xhr.status=0,xhr.statusText="Objs strictNetwork mismatch",xhr.dispatchEvent(new Event("readystatechange")),xhr.dispatchEvent(new Event("error"))},0);return}const respBody=typeof mock.response=="string"?mock.response:JSON.stringify(mock.response);setTimeout(()=>{xhr.readyState=4,xhr.status=mock.status||200,xhr.statusText="OK",xhr.responseText=respBody,xhr.response=respBody,xhr.dispatchEvent(new Event("readystatechange")),xhr.dispatchEvent(new Event("load"))},0);return}return origXHRSend.apply(this,arguments)});let origWebSocket=null;const wsEvents=recording.websocketEvents||[];if(!skipWebSocketMock&&wsEvents.length>0&&wsEvents.some(e=>e.messages&&e.messages.length>0)&&typeof window.WebSocket=="function"){origWebSocket=window.WebSocket;let wsConsumeIdx=0;const normalizeWsUrl=u=>{const s=typeof u=="string"?u:String(u);try{return new URL(s,window.location.href).href}catch{return s}},takeNextRecorded=urlStr=>{const norm=normalizeWsUrl(urlStr);for(let i=wsConsumeIdx;i<wsEvents.length;i++)if(normalizeWsUrl(wsEvents[i].url)===norm)return wsConsumeIdx=i+1,wsEvents[i];for(let i=wsConsumeIdx;i<wsEvents.length;i++)if(String(wsEvents[i].url)===String(urlStr))return wsConsumeIdx=i+1,wsEvents[i];return null},C=origWebSocket;class O_MockWebSocket extends EventTarget{constructor(url,protocols,recorded){super();const urlStr=typeof url=="string"?url:String(url);this.url=urlStr,this.readyState=C.CONNECTING;const p=protocols;this.protocol=Array.isArray(p)?p[0]||"":p?String(p):"",this.extensions="",this.binaryType="blob",this._messages=(recorded.messages||[]).slice(),this._pos=0;const self=this;setTimeout(()=>{self.readyState!==C.CLOSED&&(self.readyState=C.OPEN,self._dispatchOpen(),self._drainInbound())},0)}_dispatchOpen(){const ev=new Event("open");this.dispatchEvent(ev),typeof this.onopen=="function"&&this.onopen(ev)}_dispatchMessage(data){const ev=new MessageEvent("message",{data});this.dispatchEvent(ev),typeof this.onmessage=="function"&&this.onmessage(ev)}_drainInbound(){for(;this._pos<this._messages.length&&this._messages[this._pos].dir==="in";){const m=this._messages[this._pos++];this._dispatchMessage(m.data)}}send(data){if(this.readyState!==C.OPEN)throw typeof DOMException<"u"?new DOMException("Still in CONNECTING state.","InvalidStateError"):new Error("InvalidStateError");if(this._pos>=this._messages.length){if(strictWebSocket)throw new Error("[Objs playRecording] strictWebSocket: unexpected send() after recorded frames exhausted");this._drainInbound();return}const next=this._messages[this._pos];if(next.dir==="out"){if(strictWebSocket){const got=typeof data=="string"?data:String(data),exp=String(next.data!=null?next.data:"");if(normWsData(got)!==normWsData(exp))throw new Error("[Objs playRecording] strictWebSocket: outbound frame mismatch")}this._pos++}this._drainInbound()}close(code,reason){if(this.readyState===C.CLOSING||this.readyState===C.CLOSED)return;this.readyState=C.CLOSING;const self=this;setTimeout(()=>{self.readyState=C.CLOSED;const ev=typeof CloseEvent<"u"?new CloseEvent("close",{code:code!==void 0?code:1e3,reason:reason!==void 0?String(reason):"",wasClean:!0}):new Event("close");self.dispatchEvent(ev),typeof self.onclose=="function"&&self.onclose(ev)},0)}}const MockWebSocketCtor=function(url,protocols){const urlStr=typeof url=="string"?url:String(url),rec=takeNextRecorded(urlStr);return!rec||!rec.messages||rec.messages.length===0?new origWebSocket(url,protocols):new O_MockWebSocket(url,protocols,rec)};MockWebSocketCtor.CONNECTING=C.CONNECTING,MockWebSocketCtor.OPEN=C.OPEN,MockWebSocketCtor.CLOSING=C.CLOSING,MockWebSocketCtor.CLOSED=C.CLOSED,window.WebSocket=MockWebSocketCtor}const resolveRoot=()=>{if(rootOpt!=null)return typeof rootOpt=="string"?o.D.querySelector(rootOpt)||o.D.body:rootOpt;const sel=recording.observeRoot;return sel&&o.D.querySelector(sel)||o.D.body},rootEl=runAssertions?resolveRoot():null,actionScope=rootOpt!=null?resolveRoot():o.D,actions=recording.actions,assertions=recording.assertions||[],assertionsByAction={};for(const a of assertions){const k=a.actionIdx;assertionsByAction[k]||(assertionsByAction[k]=[]),assertionsByAction[k].push(a)}if(o.recordingAssertionDebug&&runAssertions&&typeof console<"u"&&console.log){const summary=actions.map((act,i)=>({i,action:act.type+" "+(act.target||""),assertions:(assertionsByAction[i]||[]).length,assertionDetails:(assertionsByAction[i]||[]).map(x=>({type:x.type,index:x.index,text:(x.text||"").slice(0,30)}))}));console.log("[playRecording] assertions by action:",summary)}const manualByAction={};for(const mc of manualChecks){const k=mc.afterAction;manualByAction[k]||(manualByAction[k]=[]),manualByAction[k].push(mc)}const testCases=[];let assertionAccum={passed:0,total:0,failures:[]};for(let i=0;i<actions.length;i++){const action=actions[i];testCases.push([`${action.type} on ${action.target}`,async()=>{let el=null;const scope=actionScope;if(action.target)if(action.listSelector!=null&&action.targetIndex!=null){const item=scope.querySelectorAll(action.listSelector)[action.targetIndex];item&&(el=action.target!==action.listSelector?item.querySelector(action.target):item,!el&&action.target!==action.listSelector&&(el=item))}else el=scope.querySelector(action.target);return!el&&action.type!=="scroll"?action.type==="blur"||action.type==="focus"?!0:`element not found: ${action.target}`:(action.type==="scroll"?window.scrollTo(0,action.scrollY||0):action.type==="input"||action.type==="change"?(action.value!==void 0&&(el.value=action.value),action.checked!==void 0&&(el.checked=action.checked),el.dispatchEvent(new Event(action.type,{bubbles:!0}))):action.type==="submit"?typeof el.requestSubmit=="function"?el.requestSubmit():el.submit():action.type==="keydown"?el.dispatchEvent(new KeyboardEvent("keydown",{key:action.key||"",code:action.code||"",bubbles:!0,cancelable:!0})):action.type==="focus"?el.focus():action.type==="blur"?el.blur():action.type==="click"?el.click():el.dispatchEvent(new MouseEvent(action.type,{bubbles:!0,cancelable:!0})),actionDelay>0&&await o.sleep(actionDelay),!0)}]);const asserted=assertionsByAction[i];runAssertions&&asserted&&asserted.length>0&&testCases.push([`assert after ${action.type}`,()=>new Promise(resolve=>{const run=()=>{const r=o.runRecordingAssertions(recording,rootEl,i,{assertions:asserted,removedElements:recording.removedElements,strictAssertions,strictRemoved});assertionAccum.passed+=r.passed,assertionAccum.total+=r.total,assertionAccum.failures.push(...r.failures),resolve(r.passed===r.total?!0:r.failures.map(f=>f.selector+": "+f.message).join("; "))};requestAnimationFrame(()=>requestAnimationFrame(run))})]);for(const mc of manualByAction[i]||[])testCases.push([`Manual: ${mc.label}`,()=>typeof o.testConfirm=="function"?o.testConfirm(mc.label,mc.items||[]):{ok:!0}])}for(const mc of manualByAction.end||[])testCases.push([`Manual: ${mc.label}`,()=>typeof o.testConfirm=="function"?o.testConfirm(mc.label,mc.items||[]):{ok:!0}]);const onComplete=isOptions&&opts.onComplete,testId=o.test("Recorded playback",...testCases,{sync:!0},testId2=>{window.fetch=origFetch,XMLHttpRequest.prototype.open=origXHROpen,XMLHttpRequest.prototype.send=origXHRSend,origWebSocket&&(window.WebSocket=origWebSocket);const assertionResult=runAssertions&&assertions.length>0?assertionAccum:void 0;if(assertionResult?.failures?.length>0){o.tRes[testId2]=!1;const failLines=assertionResult.failures.map(f=>`${f.selector}: ${f.message}`).join("; "),suffix=o.tStyled?o.tPre+o.tXx+"Assertions failed: "+failLines+o.tDc:`
|
|
96
|
+
\u2718 Assertions failed: `+failLines;o.tLog[testId2]=(o.tLog[testId2]||"")+suffix}typeof onComplete=="function"&&onComplete(assertionResult)});return runAssertions?{testId}:testId},o.testOverlay=()=>{const btnId="o-test-overlay-btn",panelId="o-test-overlay-panel";if(o("#"+btnId).el)return;const scrollId="o-test-overlay-scroll",exportBtnId="o-test-export-objs",copyBtnId="o-test-copy-txt",btnBarStyle="padding:6px 10px;background:#334155;color:#e2e8f0;border:none;border-radius:6px;cursor:pointer;font-size:12px;",buildListPlainText=()=>o.tLog.map((log,i)=>(log!=null&&log!==""?String(log):"Test #"+i)+(o.tRes[i]?" \u2713":" \u2717")).join(`
|
|
97
|
+
|
|
98
|
+
`),updatePanel=()=>{const scroll=o("#"+scrollId);if(!scroll.el)return;let html="";o.tLog.forEach((log,i)=>{const ok=o.tRes[i];html+=`<div style="margin:2px 0;padding:2px 4px;border-radius:3px;background:${ok?"#14532d":"#450a0a"};color:${ok?"#86efac":"#fca5a5"};font-size:11px;white-space:pre-wrap">${log||"Test #"+i}</div>`}),scroll.html(html)},innerHTML=`<div class="o-test-overlay-root" style="display:flex;flex-direction:column;gap:4px;max-height:min(88vh,560px);overflow:hidden;"><div style="display:flex;align-items:center;gap:12px;flex-shrink:0;"><span class="o-test-overlay-summary" style="flex:1;font-size:13px;cursor:grab;">Tests: 0/0</span><button type="button" class="o-test-overlay-toggle" style="${btnBarStyle}">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 id="${panelId}" style="display:none;flex-direction:column;margin-top:4px;max-height:min(52vh,420px);background:#0a0f1e;border:1px solid #1e293b;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.35);overflow:hidden;"><div id="${scrollId}" style="box-sizing:border-box;height:min(48vh,380px);overflow-y:scroll;padding:8px;font-size:11px;user-select:text;cursor:text;"></div><div id="o-test-overlay-footer" style="display:flex;flex-wrap:wrap;gap:8px;padding:8px;border-top:1px solid #1e293b;background:#0f172a;flex-shrink:0;"><button type="button" id="${exportBtnId}" class="o-test-overlay-export-btn" style="${btnBarStyle}">Export (objs)</button><button type="button" id="${copyBtnId}" class="o-test-overlay-export-btn" style="${btnBarStyle}">Copy (txt)</button></div></div></div>`,box=o.overlay({innerHTML,removeExisting:!1,className:"o-test-overlay",id:btnId,excludeDragSelector:".o-test-overlay-close, .o-test-overlay-toggle, #"+panelId+", #"+scrollId+", #o-test-overlay-footer, .o-test-overlay-export-btn"});o("#"+exportBtnId).on("click",()=>{const data=JSON.stringify({results:o.tRes,logs:o.tLog},null,2),blob=new Blob([data],{type:"application/json"}),a=o.D.createElement("a");a.href=URL.createObjectURL(blob),a.download="objs-test-results.json",a.click()}),o("#"+copyBtnId).on("click",()=>{const text=buildListPlainText(),write=()=>{const ta=o.D.createElement("textarea");ta.value=text,ta.setAttribute("readonly",""),ta.style.cssText="position:fixed;left:-9999px;top:0",o.D.body.appendChild(ta),ta.select(),o.D.execCommand("copy"),ta.remove()};typeof navigator<"u"&&navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(text).catch(write):write()});const refreshSummary=()=>{const summary=o(".o-test-overlay-summary");summary.els.length&&summary.innerText(`Tests: ${o.tRes.filter(Boolean).length}/${o.tRes.length}`)};box.first(".o-test-overlay-toggle").on("click",()=>{const panel=o("#"+panelId);if(!panel.el)return;panel.el.style.display!=="none"?panel.el.style.display="none":(panel.el.style.display="flex",updatePanel())}),box.first(".o-test-overlay-close").on("click",()=>{box._overlayCleanup()}),o.testOverlay.showPanel=()=>{const panel=o("#"+panelId);panel.el&&(panel.el.style.display="flex",updatePanel(),refreshSummary())},o._testOverlayBase||(o._testOverlayBase=o.test),o.test=(...args)=>{const id=o._testOverlayBase(...args),origFn=o.tFns[id];return o.tFns[id]=n=>{typeof origFn=="function"&&origFn(n);const panel=o("#"+panelId);panel.el&&panel.el.style.display!=="none"&&updatePanel(),refreshSummary()},id}},o.overlay=(opts={})=>{const{innerHTML,onClose,timeout,excludeDragSelector,removeExisting=!0,className="o-overlay-common",id}=opts;if(removeExisting)o("."+className).remove();else if(id&&o("#"+id).el)return o("#"+id);const overlayStyle={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","user-select":"text"},countdownId="o-overlay-countdown",barHtml='<div class="o-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;min-width:200px;max-height:90vh;overflow-y:auto;">'+innerHTML+(timeout?`<div id="${countdownId}" style="margin-top:6px;font-size:11px;color:#94a3b8;"></div>`:"")+"</div>",box=o.initState({tag:"div",className,id:id||void 0,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;user-select:text;",html:barHtml}).appendInside("body"),applyStyle=()=>box.css(overlayStyle);let drag=null;const onMove=e=>{drag&&(overlayStyle.left=drag.left+(e.clientX-drag.startX)+"px",overlayStyle.top=drag.top+(e.clientY-drag.startY)+"px",delete overlayStyle.bottom,overlayStyle.transform="none",applyStyle())},onUp=()=>{drag&&(delete overlayStyle.cursor,applyStyle()),drag=null};box.on("mousedown",e=>{if(excludeDragSelector&&e.target.closest(excludeDragSelector))return;const r=box.el.getBoundingClientRect();drag={startX:e.clientX,startY:e.clientY,left:r.left,top:r.top},overlayStyle.cursor="grabbing",applyStyle()}),o.D.addEventListener("mousemove",onMove),o.D.addEventListener("mouseup",onUp);let timerId;const cleanup=()=>{o.D.removeEventListener("mousemove",onMove),o.D.removeEventListener("mouseup",onUp),timerId&&clearInterval(timerId),box.remove()};if(timeout&&timeout>0){let remaining=Math.ceil(timeout/1e3);const cd=o("#"+countdownId);cd.el&&(cd.el.textContent=remaining?`Continue in ${remaining}s`:""),timerId=setInterval(()=>{remaining-=1,cd.el&&(cd.el.textContent=remaining>0?`Continue in ${remaining}s`:""),remaining<=0&&(clearInterval(timerId),timerId=null,cleanup(),typeof onClose=="function"&&onClose({ok:!1,errors:["timeout"]}))},1e3)}return box._overlayCleanup=cleanup,box._overlayOnClose=onClose,box},o.testConfirm=(label,items=[],opts={})=>new Promise(resolve=>{const btnLabel=opts.confirm||"Continue",hasCheckboxes=items.length>0,btnBg=hasCheckboxes?"#dc2626":"#2563eb",itemIds=items.map((_,idx)=>"o-tc-cb-"+idx),itemsHtml=hasCheckboxes?`<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;cursor:grab;">`+items.map((i,idx)=>`<li style="margin-bottom:4px;"><label for="${itemIds[idx]}" style="display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none;"><input type="checkbox" id="${itemIds[idx]}" class="o-tc-item-cb"> <span>${i}</span></label></li>`).join("")+"</ul>":"",innerHTML=`<div style="display:flex;align-items:center;gap:12px;"><span class="o-tc-label" style="flex:1;cursor:grab;">${label}: Paused</span><button type="button" class="o-tc-ok" style="padding:6px 14px;background:${btnBg};color:#fff;border:none;border-radius:6px;font-weight:600;cursor:pointer;font-size:13px;flex-shrink:0;">${btnLabel}</button></div>`+itemsHtml,box=o.overlay({innerHTML,timeout:opts.timeout,excludeDragSelector:".o-tc-ok",onClose:r=>resolve(r||{ok:!0})}),okBtnStyles={padding:"6px 14px",background:hasCheckboxes?"#dc2626":"#2563eb",color:"#fff",border:"none","border-radius":"6px","font-weight":"600",cursor:"pointer","font-size":"13px","flex-shrink":"0"};if(hasCheckboxes){const okBtn=box.first(".o-tc-ok"),cbs=o(".o-overlay-common .o-tc-item-cb"),updateBtn=()=>{const allChecked=cbs.length>0&&cbs.els.every(el=>el.checked);okBtn.css({...okBtnStyles,background:allChecked?"#16a34a":"#dc2626"})};cbs.on("change",updateBtn)}box.first(".o-tc-ok").on("click",()=>{let unchecked=[];if(hasCheckboxes){const cbsList=o(".o-overlay-common .o-tc-item-cb");cbsList.els.length&&cbsList.els.forEach((el,idx)=>{!el.checked&&items[idx]!==void 0&&unchecked.push(items[idx])})}box._overlayCleanup(),resolve(unchecked.length===0?{ok:!0}:{ok:!1,errors:unchecked})})}),typeof window<"u"&&(window.o=o)})();
|
package/objs.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objs-core",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lightweight (~19 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/",
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
"objs.js",
|
|
25
25
|
"objs.built.js",
|
|
26
26
|
"objs.built.min.js",
|
|
27
|
+
"objs.global.js",
|
|
28
|
+
"objs.global.min.js",
|
|
27
29
|
"objs.d.ts",
|
|
28
30
|
"objs-extension",
|
|
29
31
|
"SKILL.md",
|