dolphin-client 1.1.2 → 1.1.3
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/.vscode/dolphin-tags.json +8 -0
- package/dist/dolphin-client.js +102 -9
- package/dist/dolphin-client.min.js +19 -19
- package/dist/index.cjs +102 -9
- package/dist/index.js +102 -9
- package/dist/testing.d.ts +5 -5
- package/fulltutorial.md +55 -0
- package/package.json +1 -1
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"name": "dolphin-toast",
|
|
42
42
|
"description": "Dolphin CLI Component"
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
"name": "dolphin-store",
|
|
46
|
+
"description": "Declarative UI Store Initialization Component (Seeds state via attributes or JSON text content)"
|
|
47
|
+
},
|
|
44
48
|
{
|
|
45
49
|
"name": "dolphin-modal",
|
|
46
50
|
"description": "Dolphin CLI Component"
|
|
@@ -464,6 +468,10 @@
|
|
|
464
468
|
"name": "dolphin-toast",
|
|
465
469
|
"description": "Dolphin CLI Component (dolphin-toast.html)"
|
|
466
470
|
},
|
|
471
|
+
{
|
|
472
|
+
"name": "dolphin-store",
|
|
473
|
+
"description": "Declarative UI Store Component"
|
|
474
|
+
},
|
|
467
475
|
{
|
|
468
476
|
"name": "dolphin-modal",
|
|
469
477
|
"description": "Dolphin CLI Component (dolphin-modal.html)"
|
package/dist/dolphin-client.js
CHANGED
|
@@ -1381,6 +1381,73 @@ var DolphinModule = (() => {
|
|
|
1381
1381
|
};
|
|
1382
1382
|
clientProto._scanStoreBinds = function() {
|
|
1383
1383
|
if (typeof document === "undefined") return;
|
|
1384
|
+
const storeElements = document.querySelectorAll("dolphin-store");
|
|
1385
|
+
storeElements.forEach((el) => {
|
|
1386
|
+
if (typeof el.getAttribute !== "function") return;
|
|
1387
|
+
const storeName = el.getAttribute("name") || el.getAttribute("data-store");
|
|
1388
|
+
if (!storeName) return;
|
|
1389
|
+
const hasChildren = el.children && el.children.length > 0;
|
|
1390
|
+
if (hasChildren) {
|
|
1391
|
+
if (typeof el.setAttribute === "function") {
|
|
1392
|
+
el.setAttribute("data-rt-bind", `store/${storeName}`);
|
|
1393
|
+
el.setAttribute("data-rt-type", "context");
|
|
1394
|
+
}
|
|
1395
|
+
} else {
|
|
1396
|
+
if (el.style) {
|
|
1397
|
+
el.style.display = "none";
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
if (!hasChildren) {
|
|
1401
|
+
const content = el.textContent ? el.textContent.trim() : "";
|
|
1402
|
+
if (content && content.startsWith("{")) {
|
|
1403
|
+
try {
|
|
1404
|
+
const parsed = JSON.parse(content);
|
|
1405
|
+
if (parsed && typeof parsed === "object") {
|
|
1406
|
+
Object.keys(parsed).forEach((key) => {
|
|
1407
|
+
this.setStoreState(storeName, key, parsed[key]);
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
} catch (err) {
|
|
1411
|
+
console.error(`[Dolphin Store Init Error] Failed to parse JSON inside <dolphin-store name="${storeName}">:`, err);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
const templateSelector = el.getAttribute("template");
|
|
1416
|
+
if (el.attributes) {
|
|
1417
|
+
const excludeAttrs = ["name", "data-store", "style", "data-rt-bind", "data-rt-type", "template"];
|
|
1418
|
+
Array.from(el.attributes).forEach((attr) => {
|
|
1419
|
+
if (!excludeAttrs.includes(attr.name)) {
|
|
1420
|
+
let val = attr.value;
|
|
1421
|
+
if (val === "true") val = true;
|
|
1422
|
+
else if (val === "false") val = false;
|
|
1423
|
+
else if (val === "null") val = null;
|
|
1424
|
+
else if (!isNaN(Number(val)) && val.trim() !== "") val = Number(val);
|
|
1425
|
+
this.setStoreState(storeName, attr.name, val);
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
if (templateSelector && !hasChildren && el.parentNode && typeof document !== "undefined") {
|
|
1430
|
+
const markerId = `_ds_${storeName}_${templateSelector.replace(/[^a-z0-9]/gi, "_")}`;
|
|
1431
|
+
let wrapper = document.querySelector(`[data-ds-wired="${markerId}"]`);
|
|
1432
|
+
if (!wrapper) {
|
|
1433
|
+
wrapper = document.createElement("div");
|
|
1434
|
+
wrapper.setAttribute("data-rt-bind", `store/${storeName}`);
|
|
1435
|
+
wrapper.setAttribute("data-rt-template", templateSelector);
|
|
1436
|
+
wrapper.setAttribute("data-ds-wired", markerId);
|
|
1437
|
+
el.parentNode.insertBefore(wrapper, el.nextSibling);
|
|
1438
|
+
}
|
|
1439
|
+
if (typeof this._updateDOM === "function") {
|
|
1440
|
+
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1441
|
+
const currentStore = this.uiStores.get(storeName) || {};
|
|
1442
|
+
this._updateDOM(`store/${storeName}`, currentStore);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
if (hasChildren && typeof this._updateDOM === "function") {
|
|
1446
|
+
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1447
|
+
const currentStore = this.uiStores.get(storeName) || {};
|
|
1448
|
+
this._updateDOM(`store/${storeName}`, currentStore);
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1384
1451
|
const writeEls = document.querySelectorAll("[data-store-write]");
|
|
1385
1452
|
writeEls.forEach((el) => {
|
|
1386
1453
|
const writeBind = el.getAttribute("data-store-write");
|
|
@@ -2786,6 +2853,32 @@ var DolphinModule = (() => {
|
|
|
2786
2853
|
}
|
|
2787
2854
|
|
|
2788
2855
|
// src/testing.ts
|
|
2856
|
+
function createMockFn() {
|
|
2857
|
+
if (typeof jest !== "undefined" && typeof jest.fn === "function") {
|
|
2858
|
+
return jest.fn();
|
|
2859
|
+
}
|
|
2860
|
+
const fn = (...args) => {
|
|
2861
|
+
fn.mock.calls.push(args);
|
|
2862
|
+
if (fn._implementation) {
|
|
2863
|
+
return fn._implementation(...args);
|
|
2864
|
+
}
|
|
2865
|
+
return fn._returnValue;
|
|
2866
|
+
};
|
|
2867
|
+
fn.mock = {
|
|
2868
|
+
calls: []
|
|
2869
|
+
};
|
|
2870
|
+
fn._returnValue = void 0;
|
|
2871
|
+
fn._implementation = null;
|
|
2872
|
+
fn.mockReturnValue = (val) => {
|
|
2873
|
+
fn._returnValue = val;
|
|
2874
|
+
return fn;
|
|
2875
|
+
};
|
|
2876
|
+
fn.mockImplementation = (impl) => {
|
|
2877
|
+
fn._implementation = impl;
|
|
2878
|
+
return fn;
|
|
2879
|
+
};
|
|
2880
|
+
return fn;
|
|
2881
|
+
}
|
|
2789
2882
|
var DolphinTestUtils = class {
|
|
2790
2883
|
static render(html) {
|
|
2791
2884
|
if (typeof document === "undefined") {
|
|
@@ -2812,11 +2905,11 @@ var DolphinModule = (() => {
|
|
|
2812
2905
|
send: (data) => {
|
|
2813
2906
|
sentMessages.push(data);
|
|
2814
2907
|
},
|
|
2815
|
-
close:
|
|
2816
|
-
onopen:
|
|
2817
|
-
onmessage:
|
|
2818
|
-
onclose:
|
|
2819
|
-
onerror:
|
|
2908
|
+
close: createMockFn(),
|
|
2909
|
+
onopen: createMockFn(),
|
|
2910
|
+
onmessage: createMockFn(),
|
|
2911
|
+
onclose: createMockFn(),
|
|
2912
|
+
onerror: createMockFn(),
|
|
2820
2913
|
sentMessages
|
|
2821
2914
|
};
|
|
2822
2915
|
global.WebSocket = class {
|
|
@@ -2851,8 +2944,8 @@ var DolphinModule = (() => {
|
|
|
2851
2944
|
static simulateClick(el) {
|
|
2852
2945
|
const clickEvt = {
|
|
2853
2946
|
target: el,
|
|
2854
|
-
preventDefault:
|
|
2855
|
-
stopPropagation:
|
|
2947
|
+
preventDefault: createMockFn(),
|
|
2948
|
+
stopPropagation: createMockFn()
|
|
2856
2949
|
};
|
|
2857
2950
|
const clickListeners = global.document._listeners?.["click"] || [];
|
|
2858
2951
|
clickListeners.forEach((listener) => listener(clickEvt));
|
|
@@ -2861,8 +2954,8 @@ var DolphinModule = (() => {
|
|
|
2861
2954
|
el.value = value;
|
|
2862
2955
|
const changeEvt = {
|
|
2863
2956
|
target: el,
|
|
2864
|
-
preventDefault:
|
|
2865
|
-
stopPropagation:
|
|
2957
|
+
preventDefault: createMockFn(),
|
|
2958
|
+
stopPropagation: createMockFn()
|
|
2866
2959
|
};
|
|
2867
2960
|
const changeListeners = global.document._listeners?.["change"] || [];
|
|
2868
2961
|
changeListeners.forEach((listener) => listener(changeEvt));
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
var DolphinModule=(()=>{var q=Object.defineProperty;var et=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var st=Object.prototype.hasOwnProperty;var it=(_,t)=>{for(var e in t)q(_,e,{get:t[e],enumerable:!0})},rt=(_,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of nt(t))!st.call(_,i)&&i!==e&&q(_,i,{get:()=>t[i],enumerable:!(n=et(t,i))||n.enumerable});return _};var ot=_=>rt(q({},"__esModule",{value:!0}),_);var ct={};it(ct,{DolphinClient:()=>x});var I=class{client;constructor(t){return this.client=t,this._createProxy([])}_createProxy(t){let e=t.join("/"),n=r=>this.request("GET",e,null,r);n.get=(r,a)=>typeof r=="string"?this.request("GET",r,null,a):this.request("GET",e,null,r),n.post=(r,a,h)=>typeof r=="string"?this.request("POST",r,a,h):this.request("POST",e,r,a),n.put=(r,a,h)=>typeof r=="string"?this.request("PUT",r,a,h):this.request("PUT",e,r,a),n.patch=(r,a,h)=>typeof r=="string"?this.request("PATCH",r,a,h):this.request("PATCH",e,r,a),n.del=(r,a)=>typeof r=="string"?this.request("DELETE",r,null,a):this.request("DELETE",e,null,r),n.request=(r,a,h,T)=>{let E=a?e?`${e}/${a.startsWith("/")?a.slice(1):a}`:a:e;return this.request(r,E,h,T)},n.requestDirect=(r,a,h,T)=>this.requestDirect(r,a,h,T),n._findCSRFToken=()=>this._findCSRFToken(),n._resolveBaseUrl=r=>this._resolveBaseUrl(r),n._normalizeValidationErrors=r=>this._normalizeValidationErrors(r);let i=["get","post","put","patch","del","request","requestDirect","_findCSRFToken","_resolveBaseUrl","_normalizeValidationErrors"];return new Proxy(n,{get:(r,a)=>typeof a=="string"&&!i.includes(a)?this._createProxy([...t,a]):r[a]})}_findCSRFToken(){if(typeof document>"u")return null;let t=["csrf-token","_csrf","xsrf-token","csrf_token"];for(let r of t){let a=document.querySelector(`meta[name="${r}"], meta[content][name$="${r}"]`);if(a){let h=a.getAttribute("content");if(h)return h}}let e=["_csrfToken","_token","_csrf","csrf_token"];for(let r of e){let a=document.querySelector(`input[type="hidden"][name="${r}"]`);if(a&&a.value)return a.value}let n=["csrfToken","XSRF-TOKEN","_csrf","csrf_token"];for(let r of n){let a=document.cookie.match(new RegExp("(^|;\\s*)"+r+"=([^;]*)"));if(a)return decodeURIComponent(a[2])}let i=typeof window<"u"&&window.wpApiSettings?.nonce;return i||null}_resolveBaseUrl(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;let e=this.client.httpUrl;if(typeof document<"u"){let i=document.querySelector("base[href]");if(i){let r=i.getAttribute("href")||"";if(r&&r!=="/"){let a=r.endsWith("/")?r.slice(0,-1):r;e=`${this.client.httpUrl}${a.startsWith("/")?a:"/"+a}`}}else{let r=document.querySelector('meta[name="base-path"]');if(r){let a=r.getAttribute("content")||"";if(a&&a!=="/"){let h=a.endsWith("/")?a.slice(0,-1):a;e=`${this.client.httpUrl}${h.startsWith("/")?h:"/"+h}`}}}}let n=t.startsWith("/")?t:"/"+t;return`${e}${n}`}_normalizeValidationErrors(t){let e={};if(!t||typeof t!="object")return e;let n=t.errors||t.validationErrors||t;if(Array.isArray(n)){for(let i of n)if(i&&typeof i=="object"){let r=i.path||i.param||i.field||i.property,a=i.msg||i.message||i.error;r&&a&&(e[r]=Array.isArray(a)?a[0]:a)}return e}if(typeof n=="object"&&n!==null)for(let i in n){let r=n[i];if(r)if(Array.isArray(r))r.length>0&&(e[i]=String(r[0]));else if(typeof r=="object"){let a=Object.keys(r);if(a.length>0){let h=a[0];e[i]=String(r[h])}}else e[i]=String(r)}return e}async request(t,e,n=null,i={}){if(this.client.offline){let r=this.client.offline.isOnline,a=`${t.toUpperCase()}:${e}`;if(t.toUpperCase()==="GET")if(r)try{let h=await this.requestDirect(t,e,n,i);return await this.client.offline.setCache(a,h),h}catch(h){let T=await this.client.offline.getCache(a);if(T!=null)return T;throw h}else{let h=await this.client.offline.getCache(a);if(h!=null)return h;throw{status:503,data:{error:"Offline, no cache available"}}}else return r?this.requestDirect(t,e,n,i):(await this.client.offline.queueMutation(t,e,n),this.client._dispatch("offline:queued",{method:t,path:e,body:n}),{success:!0,offline:!0,message:"Mutation queued offline"})}return this.requestDirect(t,e,n,i)}async requestDirect(t,e,n=null,i={}){let r=i._isRetry===!0,a=t.toUpperCase(),h=n,E={...!["GET","HEAD"].includes(a)?{"Content-Type":"application/json"}:{},...i.headers||{}};["PUT","PATCH","DELETE"].includes(a)&&(this.client.options.methodSpoofing||i.methodSpoofing)&&(E["X-HTTP-Method-Override"]=a,h instanceof FormData?h.append("_method",a):h&&typeof h=="object"?h={...h,_method:a}:h||(h={_method:a}),a="POST");let D=this._resolveBaseUrl(e);this.client.options.debug&&console.log("%c\u{1F680} [Dolphin API Request]:","color: #3b82f6; font-weight: bold;",t.toUpperCase(),e,h||"");let k=new AbortController,M=setTimeout(()=>k.abort(),this.client.options.timeout);if(this.client.accessToken&&(E.Authorization=`Bearer ${this.client.accessToken}`),["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let d=this._findCSRFToken();d&&(E["X-CSRF-Token"]=d,E["X-XSRF-TOKEN"]=d,E["X-CSRFToken"]=d,E["X-WP-Nonce"]=d,h&&typeof h=="object"&&!h._csrfToken&&!h._token&&!h._csrf&&(h={...h,_csrfToken:d,_token:d,_csrf:d}))}let $={...i};delete $._isRetry,delete $.methodSpoofing;try{let d=await fetch(D,{method:a,headers:E,signal:k.signal,...h?{body:JSON.stringify(h)}:{},...$});if(clearTimeout(M),d.status===401&&!r&&this.client.options.autoRefreshToken&&await this.client.auth._silentRefresh())return this.request(t,e,n,{...i,_isRetry:!0});let l=(d.headers.get("content-type")||"").includes("application/json")?await d.json():await d.text();if(!d.ok)throw{status:d.status,data:l};if(this.client.options.debug&&console.log("%c\u2705 [Dolphin API Success]:","color: #10b981; font-weight: bold;",t.toUpperCase(),e,l),l&&typeof l=="object"&&l.accessToken&&(this.client.setToken(l.accessToken),l.user&&(this.client.auth.user=l.user)),this.client.options.autoBroadcast&&["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let s=e.startsWith("/")?e.substring(1):e;this.client.publish(s,{method:t.toUpperCase(),payload:n,result:l})}return l}catch(d){if(clearTimeout(M),this.client.options.debug&&console.error("%c\u274C [Dolphin API Error]:","color: #ef4444; font-weight: bold;",t.toUpperCase(),e,d),d&&typeof d=="object"&&d.data){let f=this._normalizeValidationErrors(d.data);if(Object.keys(f).length>0)for(let l in f)this.client.publish(`errors/${l}`,f[l])}throw d.name==="AbortError"?{status:408,data:{error:"Request timed out"}}:d}}};var R=class{client;user;_refreshing;constructor(t){this.client=t,this.user=null,this._refreshing=!1}async login(t,e){let n=await this.client.api.post("/api/auth/login",{email:t,password:e});return n.accessToken&&(this.client.setToken(n.accessToken),this.user=n.user||null),n}async register(t){return this.client.api.post("/api/auth/register",t)}async me(){let t=await this.client.api.get("/api/auth/me");return t.success&&(this.user=t.data),t}async logout(){try{await this.client.api.post("/api/auth/logout")}catch{}this.client.setToken(null),this.user=null}async refresh(){return this._silentRefresh()}async _silentRefresh(){if(this._refreshing)return!1;this._refreshing=!0;try{let t=await this.client.api.post("/api/auth/refresh",null,{_isRetry:!0});return t.accessToken?(this.client.setToken(t.accessToken),!0):!1}catch{return this.client.setToken(null),!1}finally{this._refreshing=!1}}async verify2FA(t,e){let n={code:t,email:e||this.user?.email},i=await this.client.api.post("/api/auth/2fa/verify",n);return i.accessToken&&(this.client.setToken(i.accessToken),i.user&&(this.user=i.user)),i}async enable2FA(){return this.client.api.post("/api/auth/2fa/enable")}async disable2FA(t){return this.client.api.post("/api/auth/2fa/disable",{code:t})}async forgotPassword(t){return this.client.api.post("/api/auth/forgot-password",{email:t})}async resetPassword(t,e){return this.client.api.post("/api/auth/reset-password",{token:t,newPassword:e})}};var N=class{client;data;listeners;subscribed;_unsubscribers;constructor(t){return this.client=t,this.data=new Map,this.listeners=new Set,this.subscribed=new Set,this._unsubscribers=new Map,new Proxy(this,{get:(e,n)=>{if(n in e)return e[n];if(typeof n=="string")return this._getCollection(n)}})}_getCollection(t){if(!this.data.has(t)){let e={_rawItems:[],items:[],loading:!0,error:null,success:!1,_filter:null,_sort:null,where:n=>(e._filter=n,this._applyTransform(e),e),orderBy:(n,i="asc")=>(e._sort={key:n,direction:i},this._applyTransform(e),e),reset:()=>(e._filter=null,e._sort=null,this._applyTransform(e),e)};this.data.set(t,e),this._fetchAndSync(t)}return this.data.get(t)}async _fetchAndSync(t){let e=this.data.get(t);try{let n=await this.client.api.get(`/${t.toLowerCase()}`);if(e._rawItems=Array.isArray(n)?n:n.data||[],e.loading=!1,e.success=!0,e.error=null,this._applyTransform(e),!this.subscribed.has(t)){let i=()=>this.client.unsubscribe(`db:sync/${t.toLowerCase()}`,r),r=a=>{this._handleRemoteUpdate(t,a)};this.client.subscribe(`db:sync/${t.toLowerCase()}`,r),this._unsubscribers.set(t,i),this.subscribed.add(t)}}catch(n){e.loading=!1,e.success=!1,e.error=n.data?.error||n.message||"Fetch failed",this._notify()}}_applyTransform(t){let e=[...t._rawItems];if(t._filter&&(e=e.filter(t._filter)),t._sort){let{key:n,direction:i}=t._sort;e.sort((r,a)=>r[n]===a[n]?0:(r[n]>a[n]?1:-1)*(i==="asc"?1:-1))}t.items=e,this._notify()}_handleRemoteUpdate(t,e){let n=this.data.get(t);if(!n)return;let{type:i,data:r}=e,a=n._rawItems;i==="create"?a=[...a,r]:i==="update"?a=a.map(h=>h.id===r.id||h._id===r._id?{...h,...r}:h):i==="delete"&&(a=a.filter(h=>!(r.id!=null&&h.id===r.id||r._id!=null&&h._id===r._id))),n._rawItems=a,this._applyTransform(n)}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}getSnapshot(t){return this.data.get(t)||{items:[],loading:!1,error:null,success:!1}}_notify(){this.listeners.forEach(t=>t())}destroy(){this._unsubscribers.forEach(t=>{try{t()}catch{}}),this._unsubscribers.clear(),this.subscribed.clear(),this.listeners.clear(),this.data.clear()}};var x=class{host;httpUrl;deviceId;options;socket;storage;accessToken;api;auth;store;handlers;signalHandlers;fileHandlers;_offlineQueue;reconnectAttempts;_reconnectTimer;_attachedListeners;constructor(t="",e="",n={}){!t&&typeof window<"u"&&(t=window.location.host);let i="http:";t.startsWith("https://")?i="https:":t.startsWith("http://")?i="http:":typeof window<"u"&&(i=window.location.protocol),this.host=(t||"localhost").replace(/\/$/,"").replace(/^https?:\/\//,""),this.httpUrl=`${i}//${this.host}`,this.deviceId=e||(typeof crypto<"u"&&typeof crypto.randomUUID=="function"?"web_"+crypto.randomUUID().replace(/-/g,"").slice(0,8):"web_"+Math.random().toString(36).slice(2,10)),this.options={timeout:15e3,chunkSize:65536,maxReconnect:5,autoRefreshToken:!0,debug:!1,methodSpoofing:!1,routerViewport:"main, #viewport, body",routerTransitions:!0,...n},this.socket=null,this.storage=typeof localStorage<"u"?localStorage:{getItem:()=>null,setItem:()=>{},removeItem:()=>{}},this.accessToken=this.storage.getItem("dolphin_token"),this.api=new I(this),this.auth=new R(this),this.store=new N(this),this.handlers=new Map,this.signalHandlers=new Set,this.fileHandlers=new Set,this._offlineQueue=[],this.reconnectAttempts=0,this._reconnectTimer=null,this._attachedListeners=[],typeof window<"u"&&typeof this._initDOMBinding=="function"&&this._initDOMBinding(),typeof this._initOffline=="function"&&this._initOffline(),typeof this._initA11y=="function"&&this._initA11y(),typeof this._initI18n=="function"&&this._initI18n(),typeof this._initDragDrop=="function"&&this._initDragDrop(),typeof this._initCollab=="function"&&this._initCollab()}setToken(t){this.accessToken=t,t?this.storage.setItem("dolphin_token",t):this.storage.removeItem("dolphin_token")}async connect(){return this.socket&&(this.socket.readyState===WebSocket.OPEN||this.socket.readyState===WebSocket.CONNECTING)?Promise.resolve():new Promise((t,e)=>{let i=`${this.httpUrl.startsWith("https")?"wss:":"ws:"}//${this.host}/realtime?deviceId=${this.deviceId}`;console.log(`[Dolphin] Connecting to ${i}...`),this.socket=new WebSocket(i),this.socket.onopen=()=>{console.log(`[Dolphin] Connected as "${this.deviceId}" \u{1F42C}`),this.reconnectAttempts=0,this._flushOfflineQueue(),t()},this.socket.onmessage=r=>this._handleMessage(r.data),this.socket.onclose=()=>{console.warn("[Dolphin] Connection closed"),this._maybeReconnect()},this.socket.onerror=r=>{console.error("[Dolphin] WebSocket error:",r),e(r)}})}disconnect(){this._reconnectTimer!==null&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this.socket&&(this.socket.onclose=null,this.socket.close(),this.socket=null),typeof this._collabCleanup=="function"&&this._collabCleanup(),this.cleanupDomListeners()}_handleMessage(t){try{let e=JSON.parse(t);this.options.debug&&console.log("%c\u{1F4E5} [Dolphin WS Incoming]:","color: #eab308; font-weight: bold;",e),e.type&&e.from&&(e.to===this.deviceId||e.to==="all")&&(e.msgId&&e.type!=="ACK"&&this._sendAck(e.from,e.msgId),this.signalHandlers.forEach(n=>n(e))),e.type==="FILE_AVAILABLE"&&this.fileHandlers.forEach(n=>n(e)),e.type==="FILE_CHUNK"&&(this.saveFileProgress(e.fileId,e.chunkIndex),this._dispatch("file:chunk",e),this._dispatch(`file:chunk/${e.fileId}`,e)),e.type==="FILE_UPLOAD_ACK"&&this._dispatch(`file:upload:ack/${e.fileId}`,e),e.type==="PULL_RESPONSE"&&(this._dispatch("pull:response",e.payload,e.topic),this._dispatch(`pull:response/${e.topic}`,e.payload,e.topic)),e.topic&&e.payload!==void 0&&this.handlers.forEach((n,i)=>{this._matchTopic(i,e.topic)&&n.forEach(r=>r(e.payload,e.topic))})}catch{this._dispatch("raw",t)}}_dispatch(t,e,n){let i=this.handlers.get(t);i&&i.forEach(r=>r(e,n||t))}_sendRaw(t){this.options.debug&&console.log("%c\u{1F4E4} [Dolphin WS Outgoing]:","color: #8b5cf6; font-weight: bold;",t);let e=typeof t=="string"?t:JSON.stringify(t);this.socket&&this.socket.readyState===WebSocket.OPEN?this.socket.send(e):this._offlineQueue.length<100&&this._offlineQueue.push(e)}_flushOfflineQueue(){for(;this._offlineQueue.length>0;){let t=this._offlineQueue.shift();this.socket&&this.socket.readyState===WebSocket.OPEN&&this.socket.send(t)}}_sendAck(t,e){this._sendRaw({type:"ACK",from:this.deviceId,to:t,data:{ackId:e},timestamp:Date.now()})}_matchTopic(t,e){if(t===e||t==="#")return!0;let n=t.split("/"),i=e.split("/");if(n.length!==i.length&&!t.includes("#"))return!1;for(let r=0;r<n.length;r++){if(n[r]==="#")return!0;if(n[r]!=="+"&&n[r]!==i[r])return!1}return n.length===i.length}_maybeReconnect(){if(this.reconnectAttempts<this.options.maxReconnect){this.reconnectAttempts++;let t=Math.pow(2,this.reconnectAttempts)*1e3;console.log(`[Dolphin] Reconnecting in ${t/1e3}s (attempt ${this.reconnectAttempts})...`),this._reconnectTimer=setTimeout(()=>{this._reconnectTimer=null,this.connect().catch(()=>{})},t)}else console.error("[Dolphin] Max reconnect attempts reached.")}subscribe(t,e){this.handlers.has(t)||(this.handlers.set(t,new Set),this._sendRaw({type:"sub",topic:t})),this.handlers.get(t).add(e)}unsubscribe(t,e){if(this.handlers.has(t)){let n=this.handlers.get(t);n.delete(e),n.size===0&&(this.handlers.delete(t),this._sendRaw({type:"unsub",topic:t}))}}publish(t,e){this._sendRaw({topic:t,payload:e})}pubPush(t,e){this._sendRaw({type:"pub",topic:t,payload:e})}subPull(t,e=10){this._sendRaw({type:"PULL_REQUEST",topic:t,count:e})}async pubFile(t,e,n="",i){let r;e instanceof Blob?r=await e.arrayBuffer():e instanceof ArrayBuffer?r=e:r=e.buffer||e;let a=new Uint8Array(r),h=this.options.chunkSize,T=Math.ceil(a.length/h);this._sendRaw({type:"FILE_UPLOAD_START",fileId:t,name:n,size:a.length,totalChunks:T,chunkSize:h});for(let E=0;E<T;E++){let D=a.slice(E*h,(E+1)*h),k=this._uint8ToBase64(D);this._sendRaw({type:"FILE_UPLOAD_CHUNK",fileId:t,chunkIndex:E,totalChunks:T,data:k}),i&&i(Math.round((E+1)/T*100)),E%10===0&&await new Promise(M=>setTimeout(M,0))}this._sendRaw({type:"FILE_UPLOAD_DONE",fileId:t})}_uint8ToBase64(t){let e="";for(let n=0;n<t.length;n++)e+=String.fromCharCode(t[n]);return typeof btoa<"u"?btoa(e):Buffer.from(e,"binary").toString("base64")}subFile(t,e=0){this._sendRaw({type:"FILE_REQUEST",fileId:t,startChunk:e})}resumeFile(t){let e=parseInt(this.storage.getItem(`dolphin_file_${t}`)||"-1");this.subFile(t,e+1)}saveFileProgress(t,e){this.storage.setItem(`dolphin_file_${t}`,e.toString())}onSignal(t){this.signalHandlers.add(t)}offSignal(t){this.signalHandlers.delete(t)}onFileAvailable(t){this.fileHandlers.add(t)}offFileAvailable(t){this.fileHandlers.delete(t)}addDomListener(t,e,n){t&&(t.addEventListener(e,n),this._attachedListeners=this._attachedListeners||[],this._attachedListeners.push({target:t,event:e,cb:n}))}cleanupDomListeners(){this._attachedListeners&&(this._attachedListeners.forEach(({target:t,event:e,cb:n})=>{try{t.removeEventListener(e,n)}catch{}}),this._attachedListeners=[])}};function W(_){let t=new Map;function e(d){return d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function n(d,f){if(!(!f||typeof f!="object"))try{let l=new Proxy(f,{has(o,c){return!0},get(o,c){if(typeof c=="string"){if(c in o)return o[c];if(typeof globalThis<"u"&&c in globalThis)return globalThis[c];if(typeof window<"u"&&c in window)return window[c]}}});return new Function("ctx",`with(ctx) { return (${d}); }`)(l)}catch{return f[d]}}function i(d,f){let l=[],s="",o=!1,c=!1,p=!1,u=0;for(let b=0;b<d.length;b++){let g=d[b];g==="'"&&!c&&!p?o=!o:g==='"'&&!o&&!p?c=!c:g==="`"&&!o&&!c?p=!p:g==="("||g==="["||g==="{"?!o&&!c&&!p&&u++:(g===")"||g==="]"||g==="}")&&!o&&!c&&!p&&u--,g===f&&!o&&!c&&!p&&u===0?(l.push(s),s=""):s+=g}return l.push(s),l}function r(d){let f=!1,l=!1,s=!1,o=0;for(let c=0;c<d.length;c++){let p=d[c];if(p==="'"&&!l&&!s?f=!f:p==='"'&&!f&&!s?l=!l:p==="`"&&!f&&!l?s=!s:p==="("||p==="["||p==="{"?!f&&!l&&!s&&o++:(p===")"||p==="]"||p==="}")&&!f&&!l&&!s&&o--,p===":"&&!f&&!l&&!s&&o===0)return[d.slice(0,c),d.slice(c+1)]}return null}function a(d){let f=d.getAttribute("data-rt-template");if(!f)return null;if(typeof document<"u"&&!f.includes("<"))try{let l=document.querySelector(f);if(l)return l.innerHTML}catch{}return f}function h(d,f){if(!d.includes("{#if")&&!d.includes("{#each")){let l=d;for(let s in f){let o=s.replace(/[.*+?^$${}()|[\]\\]/g,"\\$&");l=l.replace(new RegExp("\\{\\{"+o+"\\}}","g"),f[s]!==void 0&&f[s]!==null?f[s]:"")}return l=l.replace(/\{\{([\s\S]*?)\}\}/g,(s,o)=>{let c=o.trim();if(!c)return"";if(/^[a-zA-Z_$][a-zA-Z0-9_$]*(?:\??\.[a-zA-Z_$][a-zA-Z0-9_$]*)+$/.test(c)){let p=c.split(/\??\./),u=f;for(let b of p){if(u==null){u=void 0;break}u=u[b]}return u??""}return s}),l}try{let l=y=>y.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"),s=`let out = "";
|
|
2
|
-
`,o=0,c=/(\{\{([\s\S]*?)\}\}|\{#if\s+([\s\S]*?)\}|\{:else\s+if\s+([\s\S]*?)\}|\{:else\}|\{\/if\}|\{#each\s+([\s\S]*?)\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*,\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\}|\{\/each\}|\{([^{}]+?)\})/g,p=[],
|
|
3
|
-
`);let
|
|
4
|
-
`}else if(
|
|
5
|
-
`}else if(
|
|
6
|
-
`}else if(
|
|
7
|
-
`;else if(
|
|
8
|
-
`;else if(
|
|
9
|
-
`,v&&(
|
|
10
|
-
`),
|
|
11
|
-
`}else if(
|
|
12
|
-
`),
|
|
1
|
+
var DolphinModule=(()=>{var U=Object.defineProperty;var nt=Object.getOwnPropertyDescriptor;var it=Object.getOwnPropertyNames;var st=Object.prototype.hasOwnProperty;var rt=(w,t)=>{for(var e in t)U(w,e,{get:t[e],enumerable:!0})},ot=(w,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of it(t))!st.call(w,s)&&s!==e&&U(w,s,{get:()=>t[s],enumerable:!(i=nt(t,s))||i.enumerable});return w};var at=w=>ot(U({},"__esModule",{value:!0}),w);var lt={};rt(lt,{DolphinClient:()=>$});var N=class{client;constructor(t){return this.client=t,this._createProxy([])}_createProxy(t){let e=t.join("/"),i=r=>this.request("GET",e,null,r);i.get=(r,a)=>typeof r=="string"?this.request("GET",r,null,a):this.request("GET",e,null,r),i.post=(r,a,y)=>typeof r=="string"?this.request("POST",r,a,y):this.request("POST",e,r,a),i.put=(r,a,y)=>typeof r=="string"?this.request("PUT",r,a,y):this.request("PUT",e,r,a),i.patch=(r,a,y)=>typeof r=="string"?this.request("PATCH",r,a,y):this.request("PATCH",e,r,a),i.del=(r,a)=>typeof r=="string"?this.request("DELETE",r,null,a):this.request("DELETE",e,null,r),i.request=(r,a,y,T)=>{let E=a?e?`${e}/${a.startsWith("/")?a.slice(1):a}`:a:e;return this.request(r,E,y,T)},i.requestDirect=(r,a,y,T)=>this.requestDirect(r,a,y,T),i._findCSRFToken=()=>this._findCSRFToken(),i._resolveBaseUrl=r=>this._resolveBaseUrl(r),i._normalizeValidationErrors=r=>this._normalizeValidationErrors(r);let s=["get","post","put","patch","del","request","requestDirect","_findCSRFToken","_resolveBaseUrl","_normalizeValidationErrors"];return new Proxy(i,{get:(r,a)=>typeof a=="string"&&!s.includes(a)?this._createProxy([...t,a]):r[a]})}_findCSRFToken(){if(typeof document>"u")return null;let t=["csrf-token","_csrf","xsrf-token","csrf_token"];for(let r of t){let a=document.querySelector(`meta[name="${r}"], meta[content][name$="${r}"]`);if(a){let y=a.getAttribute("content");if(y)return y}}let e=["_csrfToken","_token","_csrf","csrf_token"];for(let r of e){let a=document.querySelector(`input[type="hidden"][name="${r}"]`);if(a&&a.value)return a.value}let i=["csrfToken","XSRF-TOKEN","_csrf","csrf_token"];for(let r of i){let a=document.cookie.match(new RegExp("(^|;\\s*)"+r+"=([^;]*)"));if(a)return decodeURIComponent(a[2])}let s=typeof window<"u"&&window.wpApiSettings?.nonce;return s||null}_resolveBaseUrl(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;let e=this.client.httpUrl;if(typeof document<"u"){let s=document.querySelector("base[href]");if(s){let r=s.getAttribute("href")||"";if(r&&r!=="/"){let a=r.endsWith("/")?r.slice(0,-1):r;e=`${this.client.httpUrl}${a.startsWith("/")?a:"/"+a}`}}else{let r=document.querySelector('meta[name="base-path"]');if(r){let a=r.getAttribute("content")||"";if(a&&a!=="/"){let y=a.endsWith("/")?a.slice(0,-1):a;e=`${this.client.httpUrl}${y.startsWith("/")?y:"/"+y}`}}}}let i=t.startsWith("/")?t:"/"+t;return`${e}${i}`}_normalizeValidationErrors(t){let e={};if(!t||typeof t!="object")return e;let i=t.errors||t.validationErrors||t;if(Array.isArray(i)){for(let s of i)if(s&&typeof s=="object"){let r=s.path||s.param||s.field||s.property,a=s.msg||s.message||s.error;r&&a&&(e[r]=Array.isArray(a)?a[0]:a)}return e}if(typeof i=="object"&&i!==null)for(let s in i){let r=i[s];if(r)if(Array.isArray(r))r.length>0&&(e[s]=String(r[0]));else if(typeof r=="object"){let a=Object.keys(r);if(a.length>0){let y=a[0];e[s]=String(r[y])}}else e[s]=String(r)}return e}async request(t,e,i=null,s={}){if(this.client.offline){let r=this.client.offline.isOnline,a=`${t.toUpperCase()}:${e}`;if(t.toUpperCase()==="GET")if(r)try{let y=await this.requestDirect(t,e,i,s);return await this.client.offline.setCache(a,y),y}catch(y){let T=await this.client.offline.getCache(a);if(T!=null)return T;throw y}else{let y=await this.client.offline.getCache(a);if(y!=null)return y;throw{status:503,data:{error:"Offline, no cache available"}}}else return r?this.requestDirect(t,e,i,s):(await this.client.offline.queueMutation(t,e,i),this.client._dispatch("offline:queued",{method:t,path:e,body:i}),{success:!0,offline:!0,message:"Mutation queued offline"})}return this.requestDirect(t,e,i,s)}async requestDirect(t,e,i=null,s={}){let r=s._isRetry===!0,a=t.toUpperCase(),y=i,E={...!["GET","HEAD"].includes(a)?{"Content-Type":"application/json"}:{},...s.headers||{}};["PUT","PATCH","DELETE"].includes(a)&&(this.client.options.methodSpoofing||s.methodSpoofing)&&(E["X-HTTP-Method-Override"]=a,y instanceof FormData?y.append("_method",a):y&&typeof y=="object"?y={...y,_method:a}:y||(y={_method:a}),a="POST");let D=this._resolveBaseUrl(e);this.client.options.debug&&console.log("%c\u{1F680} [Dolphin API Request]:","color: #3b82f6; font-weight: bold;",t.toUpperCase(),e,y||"");let k=new AbortController,M=setTimeout(()=>k.abort(),this.client.options.timeout);if(this.client.accessToken&&(E.Authorization=`Bearer ${this.client.accessToken}`),["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let h=this._findCSRFToken();h&&(E["X-CSRF-Token"]=h,E["X-XSRF-TOKEN"]=h,E["X-CSRFToken"]=h,E["X-WP-Nonce"]=h,y&&typeof y=="object"&&!y._csrfToken&&!y._token&&!y._csrf&&(y={...y,_csrfToken:h,_token:h,_csrf:h}))}let x={...s};delete x._isRetry,delete x.methodSpoofing;try{let h=await fetch(D,{method:a,headers:E,signal:k.signal,...y?{body:JSON.stringify(y)}:{},...x});if(clearTimeout(M),h.status===401&&!r&&this.client.options.autoRefreshToken&&await this.client.auth._silentRefresh())return this.request(t,e,i,{...s,_isRetry:!0});let f=(h.headers.get("content-type")||"").includes("application/json")?await h.json():await h.text();if(!h.ok)throw{status:h.status,data:f};if(this.client.options.debug&&console.log("%c\u2705 [Dolphin API Success]:","color: #10b981; font-weight: bold;",t.toUpperCase(),e,f),f&&typeof f=="object"&&f.accessToken&&(this.client.setToken(f.accessToken),f.user&&(this.client.auth.user=f.user)),this.client.options.autoBroadcast&&["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let n=e.startsWith("/")?e.substring(1):e;this.client.publish(n,{method:t.toUpperCase(),payload:i,result:f})}return f}catch(h){if(clearTimeout(M),this.client.options.debug&&console.error("%c\u274C [Dolphin API Error]:","color: #ef4444; font-weight: bold;",t.toUpperCase(),e,h),h&&typeof h=="object"&&h.data){let d=this._normalizeValidationErrors(h.data);if(Object.keys(d).length>0)for(let f in d)this.client.publish(`errors/${f}`,d[f])}throw h.name==="AbortError"?{status:408,data:{error:"Request timed out"}}:h}}};var R=class{client;user;_refreshing;constructor(t){this.client=t,this.user=null,this._refreshing=!1}async login(t,e){let i=await this.client.api.post("/api/auth/login",{email:t,password:e});return i.accessToken&&(this.client.setToken(i.accessToken),this.user=i.user||null),i}async register(t){return this.client.api.post("/api/auth/register",t)}async me(){let t=await this.client.api.get("/api/auth/me");return t.success&&(this.user=t.data),t}async logout(){try{await this.client.api.post("/api/auth/logout")}catch{}this.client.setToken(null),this.user=null}async refresh(){return this._silentRefresh()}async _silentRefresh(){if(this._refreshing)return!1;this._refreshing=!0;try{let t=await this.client.api.post("/api/auth/refresh",null,{_isRetry:!0});return t.accessToken?(this.client.setToken(t.accessToken),!0):!1}catch{return this.client.setToken(null),!1}finally{this._refreshing=!1}}async verify2FA(t,e){let i={code:t,email:e||this.user?.email},s=await this.client.api.post("/api/auth/2fa/verify",i);return s.accessToken&&(this.client.setToken(s.accessToken),s.user&&(this.user=s.user)),s}async enable2FA(){return this.client.api.post("/api/auth/2fa/enable")}async disable2FA(t){return this.client.api.post("/api/auth/2fa/disable",{code:t})}async forgotPassword(t){return this.client.api.post("/api/auth/forgot-password",{email:t})}async resetPassword(t,e){return this.client.api.post("/api/auth/reset-password",{token:t,newPassword:e})}};var O=class{client;data;listeners;subscribed;_unsubscribers;constructor(t){return this.client=t,this.data=new Map,this.listeners=new Set,this.subscribed=new Set,this._unsubscribers=new Map,new Proxy(this,{get:(e,i)=>{if(i in e)return e[i];if(typeof i=="string")return this._getCollection(i)}})}_getCollection(t){if(!this.data.has(t)){let e={_rawItems:[],items:[],loading:!0,error:null,success:!1,_filter:null,_sort:null,where:i=>(e._filter=i,this._applyTransform(e),e),orderBy:(i,s="asc")=>(e._sort={key:i,direction:s},this._applyTransform(e),e),reset:()=>(e._filter=null,e._sort=null,this._applyTransform(e),e)};this.data.set(t,e),this._fetchAndSync(t)}return this.data.get(t)}async _fetchAndSync(t){let e=this.data.get(t);try{let i=await this.client.api.get(`/${t.toLowerCase()}`);if(e._rawItems=Array.isArray(i)?i:i.data||[],e.loading=!1,e.success=!0,e.error=null,this._applyTransform(e),!this.subscribed.has(t)){let s=()=>this.client.unsubscribe(`db:sync/${t.toLowerCase()}`,r),r=a=>{this._handleRemoteUpdate(t,a)};this.client.subscribe(`db:sync/${t.toLowerCase()}`,r),this._unsubscribers.set(t,s),this.subscribed.add(t)}}catch(i){e.loading=!1,e.success=!1,e.error=i.data?.error||i.message||"Fetch failed",this._notify()}}_applyTransform(t){let e=[...t._rawItems];if(t._filter&&(e=e.filter(t._filter)),t._sort){let{key:i,direction:s}=t._sort;e.sort((r,a)=>r[i]===a[i]?0:(r[i]>a[i]?1:-1)*(s==="asc"?1:-1))}t.items=e,this._notify()}_handleRemoteUpdate(t,e){let i=this.data.get(t);if(!i)return;let{type:s,data:r}=e,a=i._rawItems;s==="create"?a=[...a,r]:s==="update"?a=a.map(y=>y.id===r.id||y._id===r._id?{...y,...r}:y):s==="delete"&&(a=a.filter(y=>!(r.id!=null&&y.id===r.id||r._id!=null&&y._id===r._id))),i._rawItems=a,this._applyTransform(i)}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}getSnapshot(t){return this.data.get(t)||{items:[],loading:!1,error:null,success:!1}}_notify(){this.listeners.forEach(t=>t())}destroy(){this._unsubscribers.forEach(t=>{try{t()}catch{}}),this._unsubscribers.clear(),this.subscribed.clear(),this.listeners.clear(),this.data.clear()}};var $=class{host;httpUrl;deviceId;options;socket;storage;accessToken;api;auth;store;handlers;signalHandlers;fileHandlers;_offlineQueue;reconnectAttempts;_reconnectTimer;_attachedListeners;constructor(t="",e="",i={}){!t&&typeof window<"u"&&(t=window.location.host);let s="http:";t.startsWith("https://")?s="https:":t.startsWith("http://")?s="http:":typeof window<"u"&&(s=window.location.protocol),this.host=(t||"localhost").replace(/\/$/,"").replace(/^https?:\/\//,""),this.httpUrl=`${s}//${this.host}`,this.deviceId=e||(typeof crypto<"u"&&typeof crypto.randomUUID=="function"?"web_"+crypto.randomUUID().replace(/-/g,"").slice(0,8):"web_"+Math.random().toString(36).slice(2,10)),this.options={timeout:15e3,chunkSize:65536,maxReconnect:5,autoRefreshToken:!0,debug:!1,methodSpoofing:!1,routerViewport:"main, #viewport, body",routerTransitions:!0,...i},this.socket=null,this.storage=typeof localStorage<"u"?localStorage:{getItem:()=>null,setItem:()=>{},removeItem:()=>{}},this.accessToken=this.storage.getItem("dolphin_token"),this.api=new N(this),this.auth=new R(this),this.store=new O(this),this.handlers=new Map,this.signalHandlers=new Set,this.fileHandlers=new Set,this._offlineQueue=[],this.reconnectAttempts=0,this._reconnectTimer=null,this._attachedListeners=[],typeof window<"u"&&typeof this._initDOMBinding=="function"&&this._initDOMBinding(),typeof this._initOffline=="function"&&this._initOffline(),typeof this._initA11y=="function"&&this._initA11y(),typeof this._initI18n=="function"&&this._initI18n(),typeof this._initDragDrop=="function"&&this._initDragDrop(),typeof this._initCollab=="function"&&this._initCollab()}setToken(t){this.accessToken=t,t?this.storage.setItem("dolphin_token",t):this.storage.removeItem("dolphin_token")}async connect(){return this.socket&&(this.socket.readyState===WebSocket.OPEN||this.socket.readyState===WebSocket.CONNECTING)?Promise.resolve():new Promise((t,e)=>{let s=`${this.httpUrl.startsWith("https")?"wss:":"ws:"}//${this.host}/realtime?deviceId=${this.deviceId}`;console.log(`[Dolphin] Connecting to ${s}...`),this.socket=new WebSocket(s),this.socket.onopen=()=>{console.log(`[Dolphin] Connected as "${this.deviceId}" \u{1F42C}`),this.reconnectAttempts=0,this._flushOfflineQueue(),t()},this.socket.onmessage=r=>this._handleMessage(r.data),this.socket.onclose=()=>{console.warn("[Dolphin] Connection closed"),this._maybeReconnect()},this.socket.onerror=r=>{console.error("[Dolphin] WebSocket error:",r),e(r)}})}disconnect(){this._reconnectTimer!==null&&(clearTimeout(this._reconnectTimer),this._reconnectTimer=null),this.socket&&(this.socket.onclose=null,this.socket.close(),this.socket=null),typeof this._collabCleanup=="function"&&this._collabCleanup(),this.cleanupDomListeners()}_handleMessage(t){try{let e=JSON.parse(t);this.options.debug&&console.log("%c\u{1F4E5} [Dolphin WS Incoming]:","color: #eab308; font-weight: bold;",e),e.type&&e.from&&(e.to===this.deviceId||e.to==="all")&&(e.msgId&&e.type!=="ACK"&&this._sendAck(e.from,e.msgId),this.signalHandlers.forEach(i=>i(e))),e.type==="FILE_AVAILABLE"&&this.fileHandlers.forEach(i=>i(e)),e.type==="FILE_CHUNK"&&(this.saveFileProgress(e.fileId,e.chunkIndex),this._dispatch("file:chunk",e),this._dispatch(`file:chunk/${e.fileId}`,e)),e.type==="FILE_UPLOAD_ACK"&&this._dispatch(`file:upload:ack/${e.fileId}`,e),e.type==="PULL_RESPONSE"&&(this._dispatch("pull:response",e.payload,e.topic),this._dispatch(`pull:response/${e.topic}`,e.payload,e.topic)),e.topic&&e.payload!==void 0&&this.handlers.forEach((i,s)=>{this._matchTopic(s,e.topic)&&i.forEach(r=>r(e.payload,e.topic))})}catch{this._dispatch("raw",t)}}_dispatch(t,e,i){let s=this.handlers.get(t);s&&s.forEach(r=>r(e,i||t))}_sendRaw(t){this.options.debug&&console.log("%c\u{1F4E4} [Dolphin WS Outgoing]:","color: #8b5cf6; font-weight: bold;",t);let e=typeof t=="string"?t:JSON.stringify(t);this.socket&&this.socket.readyState===WebSocket.OPEN?this.socket.send(e):this._offlineQueue.length<100&&this._offlineQueue.push(e)}_flushOfflineQueue(){for(;this._offlineQueue.length>0;){let t=this._offlineQueue.shift();this.socket&&this.socket.readyState===WebSocket.OPEN&&this.socket.send(t)}}_sendAck(t,e){this._sendRaw({type:"ACK",from:this.deviceId,to:t,data:{ackId:e},timestamp:Date.now()})}_matchTopic(t,e){if(t===e||t==="#")return!0;let i=t.split("/"),s=e.split("/");if(i.length!==s.length&&!t.includes("#"))return!1;for(let r=0;r<i.length;r++){if(i[r]==="#")return!0;if(i[r]!=="+"&&i[r]!==s[r])return!1}return i.length===s.length}_maybeReconnect(){if(this.reconnectAttempts<this.options.maxReconnect){this.reconnectAttempts++;let t=Math.pow(2,this.reconnectAttempts)*1e3;console.log(`[Dolphin] Reconnecting in ${t/1e3}s (attempt ${this.reconnectAttempts})...`),this._reconnectTimer=setTimeout(()=>{this._reconnectTimer=null,this.connect().catch(()=>{})},t)}else console.error("[Dolphin] Max reconnect attempts reached.")}subscribe(t,e){this.handlers.has(t)||(this.handlers.set(t,new Set),this._sendRaw({type:"sub",topic:t})),this.handlers.get(t).add(e)}unsubscribe(t,e){if(this.handlers.has(t)){let i=this.handlers.get(t);i.delete(e),i.size===0&&(this.handlers.delete(t),this._sendRaw({type:"unsub",topic:t}))}}publish(t,e){this._sendRaw({topic:t,payload:e})}pubPush(t,e){this._sendRaw({type:"pub",topic:t,payload:e})}subPull(t,e=10){this._sendRaw({type:"PULL_REQUEST",topic:t,count:e})}async pubFile(t,e,i="",s){let r;e instanceof Blob?r=await e.arrayBuffer():e instanceof ArrayBuffer?r=e:r=e.buffer||e;let a=new Uint8Array(r),y=this.options.chunkSize,T=Math.ceil(a.length/y);this._sendRaw({type:"FILE_UPLOAD_START",fileId:t,name:i,size:a.length,totalChunks:T,chunkSize:y});for(let E=0;E<T;E++){let D=a.slice(E*y,(E+1)*y),k=this._uint8ToBase64(D);this._sendRaw({type:"FILE_UPLOAD_CHUNK",fileId:t,chunkIndex:E,totalChunks:T,data:k}),s&&s(Math.round((E+1)/T*100)),E%10===0&&await new Promise(M=>setTimeout(M,0))}this._sendRaw({type:"FILE_UPLOAD_DONE",fileId:t})}_uint8ToBase64(t){let e="";for(let i=0;i<t.length;i++)e+=String.fromCharCode(t[i]);return typeof btoa<"u"?btoa(e):Buffer.from(e,"binary").toString("base64")}subFile(t,e=0){this._sendRaw({type:"FILE_REQUEST",fileId:t,startChunk:e})}resumeFile(t){let e=parseInt(this.storage.getItem(`dolphin_file_${t}`)||"-1");this.subFile(t,e+1)}saveFileProgress(t,e){this.storage.setItem(`dolphin_file_${t}`,e.toString())}onSignal(t){this.signalHandlers.add(t)}offSignal(t){this.signalHandlers.delete(t)}onFileAvailable(t){this.fileHandlers.add(t)}offFileAvailable(t){this.fileHandlers.delete(t)}addDomListener(t,e,i){t&&(t.addEventListener(e,i),this._attachedListeners=this._attachedListeners||[],this._attachedListeners.push({target:t,event:e,cb:i}))}cleanupDomListeners(){this._attachedListeners&&(this._attachedListeners.forEach(({target:t,event:e,cb:i})=>{try{t.removeEventListener(e,i)}catch{}}),this._attachedListeners=[])}};function B(w){let t=new Map;function e(h){return h.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function i(h,d){if(!(!d||typeof d!="object"))try{let f=new Proxy(d,{has(o,c){return!0},get(o,c){if(typeof c=="string"){if(c in o)return o[c];if(typeof globalThis<"u"&&c in globalThis)return globalThis[c];if(typeof window<"u"&&c in window)return window[c]}}});return new Function("ctx",`with(ctx) { return (${h}); }`)(f)}catch{return d[h]}}function s(h,d){let f=[],n="",o=!1,c=!1,p=!1,l=0;for(let g=0;g<h.length;g++){let u=h[g];u==="'"&&!c&&!p?o=!o:u==='"'&&!o&&!p?c=!c:u==="`"&&!o&&!c?p=!p:u==="("||u==="["||u==="{"?!o&&!c&&!p&&l++:(u===")"||u==="]"||u==="}")&&!o&&!c&&!p&&l--,u===d&&!o&&!c&&!p&&l===0?(f.push(n),n=""):n+=u}return f.push(n),f}function r(h){let d=!1,f=!1,n=!1,o=0;for(let c=0;c<h.length;c++){let p=h[c];if(p==="'"&&!f&&!n?d=!d:p==='"'&&!d&&!n?f=!f:p==="`"&&!d&&!f?n=!n:p==="("||p==="["||p==="{"?!d&&!f&&!n&&o++:(p===")"||p==="]"||p==="}")&&!d&&!f&&!n&&o--,p===":"&&!d&&!f&&!n&&o===0)return[h.slice(0,c),h.slice(c+1)]}return null}function a(h){let d=h.getAttribute("data-rt-template");if(!d)return null;if(typeof document<"u"&&!d.includes("<"))try{let f=document.querySelector(d);if(f)return f.innerHTML}catch{}return d}function y(h,d){if(!h.includes("{#if")&&!h.includes("{#each")){let f=h;for(let n in d){let o=n.replace(/[.*+?^$${}()|[\]\\]/g,"\\$&");f=f.replace(new RegExp("\\{\\{"+o+"\\}}","g"),d[n]!==void 0&&d[n]!==null?d[n]:"")}return f=f.replace(/\{\{([\s\S]*?)\}\}/g,(n,o)=>{let c=o.trim();if(!c)return"";if(/^[a-zA-Z_$][a-zA-Z0-9_$]*(?:\??\.[a-zA-Z_$][a-zA-Z0-9_$]*)+$/.test(c)){let p=c.split(/\??\./),l=d;for(let g of p){if(l==null){l=void 0;break}l=l[g]}return l??""}return n}),f}try{let f=m=>m.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"),n=`let out = "";
|
|
2
|
+
`,o=0,c=/(\{\{([\s\S]*?)\}\}|\{#if\s+([\s\S]*?)\}|\{:else\s+if\s+([\s\S]*?)\}|\{:else\}|\{\/if\}|\{#each\s+([\s\S]*?)\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*,\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\}|\{\/each\}|\{([^{}]+?)\})/g,p=[],l;for(;(l=c.exec(h))!==null;){let m=h.slice(o,l.index);m&&(n+=`out += "${f(m)}";
|
|
3
|
+
`);let _=l[0];if(_.startsWith("{{")){let S=l[2];n+=`out += (${S} !== undefined && ${S} !== null ? ${S} : "");
|
|
4
|
+
`}else if(_.startsWith("{#if")){let S=l[3];n+=`if (${S}) {
|
|
5
|
+
`}else if(_.startsWith("{:else if")){let S=l[4];n+=`} else if (${S}) {
|
|
6
|
+
`}else if(_.startsWith("{:else}"))n+=`} else {
|
|
7
|
+
`;else if(_.startsWith("{/if}"))n+=`}
|
|
8
|
+
`;else if(_.startsWith("{#each")){let S=l[5],C=l[6],v=l[7];p.push({indexVar:v}),n+=`if (typeof ${S} !== "undefined" && ${S} !== null && Array.isArray(${S})) {
|
|
9
|
+
`,v&&(n+=` let ${v} = 0;
|
|
10
|
+
`),n+=` for (let ${C} of ${S}) {
|
|
11
|
+
`}else if(_.startsWith("{/each}")){let S=p.pop();S&&S.indexVar&&(n+=` ${S.indexVar}++;
|
|
12
|
+
`),n+=` }
|
|
13
13
|
}
|
|
14
|
-
`}else if(
|
|
15
|
-
`)}o=c.lastIndex}let
|
|
16
|
-
`),
|
|
17
|
-
`;let
|
|
14
|
+
`}else if(_.startsWith("{")){let S=l[8];S&&(n+=`out += (${S} !== undefined && ${S} !== null ? ${S} : "");
|
|
15
|
+
`)}o=c.lastIndex}let g=h.slice(o);g&&(n+=`out += "${f(g)}";
|
|
16
|
+
`),n+=`return out;
|
|
17
|
+
`;let u=`
|
|
18
18
|
with (context) {
|
|
19
19
|
try {
|
|
20
|
-
${
|
|
20
|
+
${n}
|
|
21
21
|
} catch (innerErr) {
|
|
22
22
|
console.warn('[Dolphin Template Eval Warning]:', innerErr);
|
|
23
23
|
return '';
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
`,A=f;return typeof Proxy<"u"&&f!==null&&typeof f=="object"&&(A=new Proxy(f,{has(y,w){return typeof w!="symbol"},get(y,w){if(w!==Symbol.unscopables){if(w in y)return y[w];if(typeof globalThis<"u"&&w in globalThis)return globalThis[w];if(typeof window<"u"&&w in window)return window[w]}}})),new Function("context",g)(A)}catch(l){console.error("[Dolphin Template Compiler Error]:",l);let s=d;for(let o in f){let c=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");s=s.replace(new RegExp(`\\{\\{${c}\\}\\}`,"g"),f[o]!==void 0&&f[o]!==null?f[o]:"")}return s}}function T(d){if(typeof document>"u")return d;try{let f=new DOMParser,s=/<\s*(?:body|html)\b/i.test(d)?d:`<body>${d}</body>`,c=f.parseFromString(s,"text/html").body,p=u=>{let b=u.tagName.toLowerCase();if(["script","iframe","object","embed","link","style","meta","applet","svg"].includes(b)){u.parentNode?.removeChild(u);return}let g=u.attributes;for(let A=g.length-1;A>=0;A--){let m=g[A].name.toLowerCase(),y=g[A].value.toLowerCase();(m.startsWith("on")||["src","href","data"].includes(m)&&(y.includes("javascript:")||y.includes("data:text/html")))&&u.removeAttribute(g[A].name)}Array.from(u.children).forEach(p)};return Array.from(c.children).forEach(p),c.innerHTML}catch{return d}}function E(d,f){if(d.nodeType!==f.nodeType){d.parentNode?.replaceChild(f.cloneNode(!0),d);return}if(d.nodeType===Node.TEXT_NODE){d.textContent!==f.textContent&&(d.textContent=f.textContent);return}if(d.nodeType===Node.ELEMENT_NODE){let l=d,s=f;if(l.tagName!==s.tagName){l.parentNode?.replaceChild(s.cloneNode(!0),l);return}let o=l.attributes,c=s.attributes;for(let m=o.length-1;m>=0;m--){let y=o[m].name;s.hasAttribute(y)||l.removeAttribute(y)}for(let m=0;m<c.length;m++){let y=c[m].name,w=c[m].value;l.getAttribute(y)!==w&&l.setAttribute(y,w)}l.tagName==="INPUT"||l.tagName==="TEXTAREA"?(l.value!==s.value&&(l.value=s.value),l.checked!==s.checked&&(l.checked=s.checked)):l.tagName==="SELECT"&&l.value!==s.value&&(l.value=s.value);let p=Array.from(l.childNodes),u=Array.from(s.childNodes),b=p.length,g=u.length,A=Math.max(b,g);for(let m=0;m<A;m++)m>=b?l.appendChild(u[m].cloneNode(!0)):m>=g?l.removeChild(p[m]):E(p[m],u[m])}}function D(d,f){if(typeof document>"u")return;let l=document.createElement(d.tagName);l.innerHTML=f;let s=Array.from(d.childNodes),o=Array.from(l.childNodes),c=s.length,p=o.length,u=Math.max(c,p);for(let b=0;b<u;b++)b>=c?d.appendChild(o[b].cloneNode(!0)):b>=p?d.removeChild(s[b]):E(s[b],o[b])}let k=new Map,M=!1;function $(d,f){k.set(d,f),M||(M=!0,(typeof requestAnimationFrame<"u"?requestAnimationFrame:s=>setTimeout(s,0))(()=>{k.forEach((s,o)=>{o.isConnected!==!1&&D(o,s)}),k.clear(),M=!1}))}_.setStoreState=function(d,f,l,s){this.uiStores=this.uiStores||new Map,this.uiStores.has(d)||this.uiStores.set(d,{});let o=this.uiStores.get(d);o[f]=l,this.options.debug&&console.log("%c\u{1F4BE} [Dolphin Store Update]:","color: #ec4899; font-weight: bold;",`${d}.${f}`,"=",l),typeof document<"u"&&document.querySelectorAll(`[data-store-read="${d}.${f}"]`).forEach(p=>{p!==s&&(p.tagName==="INPUT"||p.tagName==="TEXTAREA"?p.type==="checkbox"?p.checked=!!l:p.value=l??"":p.textContent=l??"")}),this.publish(`store/${d}`,o),typeof this._updateDOM=="function"&&this._updateDOM(`store/${d}`,o)},_.getStoreState=function(d,f){this.uiStores=this.uiStores||new Map;let l=this.uiStores.get(d);return l?l[f]:void 0},_._scanStoreBinds=function(){if(typeof document>"u")return;document.querySelectorAll("[data-store-write]").forEach(l=>{let s=l.getAttribute("data-store-write");if(s){let o=s.split(".");if(o.length===2){let c=o[0],p=o[1],u=l.type==="checkbox"?l.checked:l.value;this.uiStores=this.uiStores||new Map,this.uiStores.has(c)||this.uiStores.set(c,{});let b=this.uiStores.get(c);b[p]===void 0&&(b[p]=u)}}}),document.querySelectorAll("[data-store-read]").forEach(l=>{let s=l.getAttribute("data-store-read");if(s){let o=s.split(".");if(o.length===2){let c=o[0],p=o[1],u=this.getStoreState(c,p);u!=null&&(l.tagName==="INPUT"||l.tagName==="TEXTAREA"?l.type==="checkbox"?l.checked=!!u:l.value=u:l.textContent=u)}}})},_.getClosestContext=function(d,f){let l=d;for(;l;){if(l._rtContext){let s=l._rtContext;return f?s[f]:s}l=l.parentElement||l.parentNode}return null},_._executeStoreAction=function(d,f){this.uiStores=this.uiStores||new Map;let l=f&&typeof this.getClosestContext=="function"?this.getClosestContext(f):null,s=new Proxy({},{has:(o,c)=>!0,get:(o,c)=>{if(typeof c=="string")return c==="log"?p=>{if(p===void 0){let u={};this.uiStores.forEach((b,g)=>{u[g]={...b}}),console.log("%c\u{1F4CA} [Dolphin All UI Stores]:","color: #06b6d4; font-weight: bold;",u)}else if(p&&typeof p=="object"&&p.__isStoreProxy__){let u=p.__storeName__,b=this.uiStores.get(u);console.log(`%c\u{1F4CA} [Dolphin Store: ${u}]:`,"color: #06b6d4; font-weight: bold;",b?{...b}:{})}else console.log("%c\u{1F4CA} [Dolphin Log]:","color: #06b6d4; font-weight: bold;",p)}:l&&l[c]!==void 0?l[c]:typeof globalThis<"u"&&c in globalThis?globalThis[c]:typeof window<"u"&&c in window?window[c]:new Proxy({},{get:(p,u)=>{if(u==="__storeName__")return c;if(u==="__isStoreProxy__")return!0;if(typeof u=="string")return this.getStoreState(c,u)},set:(p,u,b)=>typeof u=="string"?(this.setStoreState(c,u,b),!0):!1})}});try{new Function("ctx",`with(ctx) { ${d} }`)(s)}catch(o){console.error("%c[Dolphin Store Action Error]:","color: #ef4444; font-weight: bold;",o),f&&console.error("%cFailed Element:","color: #f97316; font-weight: bold;",f),console.error("%cFailed Expression:","color: #3b82f6; font-style: italic;",d)}},_._initDOMBinding=function(){if(this._domInitialized)return;this._domInitialized=!0;let d=["input","change","keyup","paste","blur"],f=new WeakMap;d.forEach(s=>{this.addDomListener(document,s,o=>{if(!o.target||!o.target.getAttribute)return;let c=o.target.getAttribute("data-store-write");if(c){let g=c.split(".");if(g.length===2){let A=g[0],m=g[1],y=o.target.type==="checkbox"?o.target.checked:o.target.value;this.setStoreState(A,m,y,o.target)}}let p=o.target.getAttribute("data-rt-validate"),u=o.target.name;if(p&&u&&typeof this.validateField=="function"){let g=o.target.closest("form"),A=g?Object.fromEntries(new FormData(g).entries()):{},m=this.validateField(o.target.value,p,A);m?(o.target.classList.add("invalid"),this.publish(`errors/${u}`,m)):(o.target.classList.remove("invalid"),this.publish(`errors/${u}`,""))}let b=o.target.getAttribute("data-rt-push");if(b){let g=o.target.getAttribute("data-rt-debounce"),A=g?parseInt(g,10):0,m=()=>{let y={name:o.target.name,value:o.target.value};this.pubPush(b,y)};if(A>0){f.has(o.target)&&clearTimeout(f.get(o.target));let y=setTimeout(m,A);f.set(o.target,y)}else m()}})}),this.addDomListener(document,"submit",async s=>{if(!s.target||!s.target.getAttribute)return;let o=s.target.getAttribute("data-rt-submit"),c=s.target.getAttribute("data-api-submit");if(o||c){s.target.querySelectorAll("[name]").forEach(y=>{let w=y.name;w&&(this.publish(`errors/${w}`,""),y.classList.remove("invalid"))});let u=s.target.querySelectorAll("[data-rt-validate]"),b=!0;if(u.length>0&&typeof this.validateField=="function"){let y=Object.fromEntries(new FormData(s.target).entries());u.forEach(w=>{let S=w.getAttribute("data-rt-validate"),C=w.name;if(S&&C){let v=this.validateField(w.value,S,y);v&&(b=!1,w.classList.add("invalid"),this.publish(`errors/${C}`,v))}})}if(!b){s.preventDefault(),s.stopPropagation();return}s.preventDefault();let g=this.getClosestContext(s.target)||{},A=new FormData(s.target),m=Object.fromEntries(A.entries());if(o){let y=o;for(let w in g){let S=e(w);y=y.replace(new RegExp(`\\{\\{${S}\\}\\}`,"g"),g[w]!==void 0&&g[w]!==null?g[w]:"")}this.publish(y,m)}else if(c){let y=c;for(let v in g){let L=e(v);y=y.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),g[v]!==void 0&&g[v]!==null?g[v]:"")}let w=y.trim().split(" "),S=w.length>1?w[0].toUpperCase():"POST",C=w.length>1?w[1]:w[0];m._method&&(S=String(m._method).toUpperCase());try{let v=await this.api.request(S,C,m),L=s.target.getAttribute("data-api-result");L&&this._updateDOM(L,v);let P=s.target.getAttribute("data-api-redirect");P&&(window.location.href=P),s.target.hasAttribute("data-api-reload")&&window.location.reload()}catch(v){console.error("[Dolphin] API Submit Error:",v)}}}}),["click","change","submit","input","keydown","keyup","dblclick","focus","blur","mouseenter","mouseleave"].forEach(s=>{this.addDomListener(document,s,async o=>{if(!o.target||!o.target.closest)return;let c=o.target.closest(`[data-rt-${s}]`),p=o.target.closest(`[data-api-${s}]`);if(c){s==="submit"&&o.preventDefault();let b=c.getAttribute(`data-rt-${s}`),g=c.getAttribute("data-rt-payload"),A=this.getClosestContext(c)||{},m={};if(g){let y=g;for(let w in A){let S=e(w);y=y.replace(new RegExp(`\\{\\{${S}\\}\\}`,"g"),A[w]!==void 0&&A[w]!==null?A[w]:"")}try{m=JSON.parse(y)}catch{m={}}}this.publish(b,m)}if(p){s==="submit"&&o.preventDefault();let b=p.getAttribute(`data-api-${s}`),g=p.getAttribute("data-api-payload"),A=this.getClosestContext(p)||{},m=b.trim().split(" "),y=m.length>1?m[0].toUpperCase():"POST",w=m.length>1?m[1]:m[0],S=null;if(g){let C=g;for(let v in A){let L=e(v);C=C.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),A[v]!==void 0&&A[v]!==null?A[v]:"")}try{S=JSON.parse(C)}catch{S=null}}try{let C=await this.api.request(y,w,S),v=p.getAttribute("data-api-result");v&&this._updateDOM(v,C);let L=p.getAttribute("data-api-redirect");L&&(window.location.href=L),p.hasAttribute("data-api-reload")&&window.location.reload()}catch(C){console.error(`[Dolphin] API ${s} Error:`,C)}}let u=o.target.closest(`[data-store-${s}]`);if(u){s==="submit"&&o.preventDefault();let b=u.getAttribute(`data-store-${s}`);b&&this._executeStoreAction(b,u)}})}),this.subscribe("#",(s,o)=>{this._updateDOM(o,s)}),this._scanAndFetchAPIBinds(),this._scanStoreBinds(),this._resolveImports(),this._initSPARouter()},_._scanAndFetchAPIBinds=async function(){if(typeof document>"u")return;let d=document.querySelectorAll("[data-api-get]");for(let f of Array.from(d)){let l=f.getAttribute("data-api-get");if(l&&!(typeof f.hasAttribute=="function"&&f.hasAttribute("data-api-initialized"))){typeof f.setAttribute=="function"&&f.setAttribute("data-api-initialized","true");try{let s=await this.api.get(l),o=f.getAttribute("data-api-store");if(o){let p=o.split(".");p.length===2&&this.setStoreState(p[0],p[1],s)}let c=f.getAttribute("data-rt-bind");if(c&&!o)this._updateDOM(c,s);else if(!o){let p=a(f);if(p&&typeof s=="object"&&s!==null){let u=this._applyDeclarativeDirectives(f,s);if(Array.isArray(u)){let b="";for(let g of u)b+=h(p,g);$(f,b)}else $(f,h(p,u))}else if(f.tagName==="INPUT"||f.tagName==="TEXTAREA")f.value=typeof s=="object"?s.value!==void 0?s.value:"":s;else{let u=typeof s=="object"?s.html||s.text||JSON.stringify(s):String(s);f.innerHTML=T(u)}}}catch(s){console.error("[Dolphin] API Get Error:",s)}}}},_._applyDeclarativeDirectives=function(d,f){let l=f;if(typeof f=="object"&&f!==null){let s=o=>{let c=[...o],p=d.getAttribute("data-rt-filter");if(p){let g=p.split("==");if(g.length===2){let A=g[0].trim(),m=g[1].trim(),y,w=m.split(".");w.length===2?y=this.getStoreState(w[0],w[1]):y=f[m]!==void 0?f[m]:this.getStoreState("app",m),y!=null&&y!==""&&(c=c.filter(S=>S[A]===y))}}let u=d.getAttribute("data-rt-search");if(u){let g=u.split("==");if(g.length===2){let A=g[0].trim(),m=g[1].trim(),y,w=m.split(".");if(w.length===2?y=this.getStoreState(w[0],w[1]):y=f[m]!==void 0?f[m]:this.getStoreState("app",m),y!=null&&y!==""){let S=String(y).toLowerCase();c=c.filter(C=>{let v=C[A];return v!=null&&String(v).toLowerCase().includes(S)})}}}let b=d.getAttribute("data-rt-sort");if(b){let g,A=b.split(".");if(A.length===2?g=this.getStoreState(A[0],A[1]):g=f[b]!==void 0?f[b]:this.getStoreState("app",b),g&&g!=="")if(g==="popular")c.sort((m,y)=>{let w=m.rating?.rate||m.rate||0;return(y.rating?.rate||y.rate||0)-w});else{let m="",y="asc";g.endsWith("-low")||g.endsWith("-asc")?(m=g.replace("-low","").replace("-asc",""),y="asc"):(g.endsWith("-high")||g.endsWith("-desc"))&&(m=g.replace("-high","").replace("-desc",""),y="desc"),m&&c.sort((w,S)=>{let C=(G,Y)=>Y.split(".").reduce((F,tt)=>F&&F[tt],G),v=C(w,m),L=C(S,m);if(v===void 0&&(v=w[m]),L===void 0&&(L=S[m]),typeof v=="string"&&typeof L=="string")return y==="asc"?v.localeCompare(L):L.localeCompare(v);let P=Number(v),O=Number(L);return!isNaN(P)&&!isNaN(O)?y==="asc"?P-O:O-P:0})}}return c};if(Array.isArray(f))l=s(f);else{let o="";for(let c in f)if(Array.isArray(f[c])){o=c;break}if(o){let c=s(f[o]);l={...f,[o]:c}}}}return l},_._updateDOM=function(d,f){if(typeof document>"u")return;document.querySelectorAll(`[data-rt-bind="${d}"]`).forEach(s=>{let o=this._applyDeclarativeDirectives(s,f);if(s.getAttribute("data-rt-type")==="context"&&typeof o=="object"&&o!==null){s._rtContext=o;let p=u=>{if(u.hasAttribute("data-rt-text")){let b=u.getAttribute("data-rt-text");if(b){let g=n(b,o);g!=null&&(u.textContent=g)}}if(u.hasAttribute("data-rt-html")){let b=u.getAttribute("data-rt-html");if(b){let g=n(b,o);g!=null&&(u.innerHTML=T(g))}}if(u.hasAttribute("data-rt-attr")){let b=u.getAttribute("data-rt-attr");b&&i(b,",").forEach(g=>{let A=r(g);if(A){let m=A[0].trim(),y=A[1].trim();if(m&&y){let w=n(y,o);w!=null&&u.setAttribute(m,w)}}})}if(u.hasAttribute("data-rt-class")){let b=u.getAttribute("data-rt-class");b&&i(b,",").forEach(g=>{let A=r(g);if(A){let m=A[0].trim(),y=A[1].trim(),w=m.split(/\s+/).filter(Boolean);n(y,o)?w.forEach(S=>u.classList.add(S)):w.forEach(S=>u.classList.remove(S))}})}if(u.hasAttribute("data-rt-if")){let b=u.getAttribute("data-rt-if");b&&(n(b,o)?u.style.display="":u.style.display="none")}if(u.hasAttribute("data-rt-hide")){let b=u.getAttribute("data-rt-hide");b&&(n(b,o)?u.style.display="none":u.style.display="")}};p(s),s.querySelectorAll("[data-rt-text], [data-rt-html], [data-rt-attr], [data-rt-class], [data-rt-if], [data-rt-hide]").forEach(p);return}let c=a(s);if(c&&typeof o=="object"&&o!==null){if(Array.isArray(o)){let p="";for(let u of o)p+=h(c,u);$(s,p)}else $(s,h(c,o));return}if(s.tagName==="INPUT"||s.tagName==="TEXTAREA")s.value=typeof o=="object"?o.value!==void 0?o.value:"":o;else if(c)s.innerHTML=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);else{let p=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);s.innerHTML=T(p)}})},_._resolveImports=async function(d){if(typeof document>"u")return;let f=d||document.body||document;if(!f||typeof f.querySelectorAll!="function")return;let l=f.querySelectorAll("[data-import]");if(l.length===0)return;let s=async(c,p)=>{let u=c.getAttribute("data-import");if(!u)return;if(p.has(u)){console.warn(`[Dolphin Component Warning]: Circular import detected for "${u}". Skipping resolving.`),c.innerHTML=`<span style="color:red;font-weight:bold;">Circular import: ${u}</span>`;return}p.add(u);let b=u.indexOf("#"),g=b!==-1?u.substring(0,b):u,A=b!==-1?u.substring(b):null,m=t.get(g);m||(m=fetch(g).then(S=>{if(!S.ok)throw new Error(`HTTP ${S.status}`);return S.text()}),m.catch(()=>t.delete(g)),t.set(g,m));let y="";try{if(y=await m,A&&typeof DOMParser<"u"){let v=new DOMParser().parseFromString(y,"text/html").querySelector(A);v?y=v.outerHTML:(console.warn(`[Dolphin Component Warning]: Selector "${A}" not found in imported file "${g}".`),y=`<span style="color:orange;font-weight:bold;">Selector ${A} not found in ${g}</span>`)}}catch(S){console.error(`[Dolphin Component Error]: Failed to fetch component "${g}":`,S),y=`<span style="color:red;font-weight:bold;">Failed to import ${g}</span>`}c.innerHTML=T(y),c.removeAttribute("data-import");let w=c.querySelectorAll("[data-import]");if(w.length>0){let S=Array.from(w).map(C=>s(C,new Set(p)));await Promise.all(S)}this._scanStoreBinds(),this._scanAndFetchAPIBinds()},o=Array.from(l).map(c=>s(c,new Set));await Promise.all(o)},_._initSPARouter=function(){if(typeof window>"u"||typeof document>"u"||this._routerInitialized)return;this._routerInitialized=!0;let d=null,f=()=>{let o=(this.options.routerViewport||"main, #viewport, body").split(",").map(c=>c.trim());for(let c of o){let p=document.querySelector(c);if(p)return p}return document.body},l=async(s,o=!0)=>{try{this.options.debug&&console.log(`%c\u{1F6E3}\uFE0F [Dolphin Router]: Navigating to ${s}...`,"color: #3b82f6; font-weight: bold;"),d&&d.abort(),d=new AbortController;let c=d.signal,p=f();this.options.routerTransitions&&p&&(p.classList.add("dolphin-fade-out"),await new Promise(w=>setTimeout(w,150)));let u=await fetch(s,{signal:c});if(!u.ok)throw new Error(`HTTP ${u.status}`);let b=await u.text();d=null;let A=new DOMParser().parseFromString(b,"text/html");A.title&&(document.title=A.title);let m=A.querySelector(this.options.routerViewport||"main, #viewport, body"),y=f();m&&y?(y.innerHTML=m.innerHTML,Array.from(m.attributes).forEach(w=>{y.setAttribute(w.name,w.value)})):y&&(y.innerHTML=A.body.innerHTML),o&&window.history.pushState({dolphinSpa:!0,url:s},"",s),this.options.routerTransitions&&y&&(y.classList.remove("dolphin-fade-out"),y.classList.add("dolphin-fade-in"),setTimeout(()=>y.classList.remove("dolphin-fade-in"),300)),await this._resolveImports(y),this._scanStoreBinds(),this._scanAndFetchAPIBinds()}catch(c){if(c&&c.name==="AbortError")return;console.error("[Dolphin Router Error]: Failed to route page:",c),window.location.href=s}};if(this.addDomListener(document,"click",s=>{let o=s.target.closest("a");if(!o||!o.hasAttribute("data-spa")&&o.getAttribute("data-spa")!=="true")return;let c=o.getAttribute("href");!c||c.startsWith("#")||c.startsWith("javascript:")||c.startsWith("mailto:")||c.startsWith("tel:")||new URL(c,window.location.href).origin!==window.location.origin||(s.preventDefault(),l(c))}),this.addDomListener(window,"popstate",s=>{s.state&&s.state.dolphinSpa?l(s.state.url,!1):s.state===null&&l(window.location.pathname,!1)}),this.options.routerTransitions){let s=document.createElement("style");s.innerHTML=`
|
|
26
|
+
`,A=d;return typeof Proxy<"u"&&d!==null&&typeof d=="object"&&(A=new Proxy(d,{has(m,_){return typeof _!="symbol"},get(m,_){if(_!==Symbol.unscopables){if(_ in m)return m[_];if(typeof globalThis<"u"&&_ in globalThis)return globalThis[_];if(typeof window<"u"&&_ in window)return window[_]}}})),new Function("context",u)(A)}catch(f){console.error("[Dolphin Template Compiler Error]:",f);let n=h;for(let o in d){let c=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");n=n.replace(new RegExp(`\\{\\{${c}\\}\\}`,"g"),d[o]!==void 0&&d[o]!==null?d[o]:"")}return n}}function T(h){if(typeof document>"u")return h;try{let d=new DOMParser,n=/<\s*(?:body|html)\b/i.test(h)?h:`<body>${h}</body>`,c=d.parseFromString(n,"text/html").body,p=l=>{let g=l.tagName.toLowerCase();if(["script","iframe","object","embed","link","style","meta","applet","svg"].includes(g)){l.parentNode?.removeChild(l);return}let u=l.attributes;for(let A=u.length-1;A>=0;A--){let b=u[A].name.toLowerCase(),m=u[A].value.toLowerCase();(b.startsWith("on")||["src","href","data"].includes(b)&&(m.includes("javascript:")||m.includes("data:text/html")))&&l.removeAttribute(u[A].name)}Array.from(l.children).forEach(p)};return Array.from(c.children).forEach(p),c.innerHTML}catch{return h}}function E(h,d){if(h.nodeType!==d.nodeType){h.parentNode?.replaceChild(d.cloneNode(!0),h);return}if(h.nodeType===Node.TEXT_NODE){h.textContent!==d.textContent&&(h.textContent=d.textContent);return}if(h.nodeType===Node.ELEMENT_NODE){let f=h,n=d;if(f.tagName!==n.tagName){f.parentNode?.replaceChild(n.cloneNode(!0),f);return}let o=f.attributes,c=n.attributes;for(let b=o.length-1;b>=0;b--){let m=o[b].name;n.hasAttribute(m)||f.removeAttribute(m)}for(let b=0;b<c.length;b++){let m=c[b].name,_=c[b].value;f.getAttribute(m)!==_&&f.setAttribute(m,_)}f.tagName==="INPUT"||f.tagName==="TEXTAREA"?(f.value!==n.value&&(f.value=n.value),f.checked!==n.checked&&(f.checked=n.checked)):f.tagName==="SELECT"&&f.value!==n.value&&(f.value=n.value);let p=Array.from(f.childNodes),l=Array.from(n.childNodes),g=p.length,u=l.length,A=Math.max(g,u);for(let b=0;b<A;b++)b>=g?f.appendChild(l[b].cloneNode(!0)):b>=u?f.removeChild(p[b]):E(p[b],l[b])}}function D(h,d){if(typeof document>"u")return;let f=document.createElement(h.tagName);f.innerHTML=d;let n=Array.from(h.childNodes),o=Array.from(f.childNodes),c=n.length,p=o.length,l=Math.max(c,p);for(let g=0;g<l;g++)g>=c?h.appendChild(o[g].cloneNode(!0)):g>=p?h.removeChild(n[g]):E(n[g],o[g])}let k=new Map,M=!1;function x(h,d){k.set(h,d),M||(M=!0,(typeof requestAnimationFrame<"u"?requestAnimationFrame:n=>setTimeout(n,0))(()=>{k.forEach((n,o)=>{o.isConnected!==!1&&D(o,n)}),k.clear(),M=!1}))}w.setStoreState=function(h,d,f,n){this.uiStores=this.uiStores||new Map,this.uiStores.has(h)||this.uiStores.set(h,{});let o=this.uiStores.get(h);o[d]=f,this.options.debug&&console.log("%c\u{1F4BE} [Dolphin Store Update]:","color: #ec4899; font-weight: bold;",`${h}.${d}`,"=",f),typeof document<"u"&&document.querySelectorAll(`[data-store-read="${h}.${d}"]`).forEach(p=>{p!==n&&(p.tagName==="INPUT"||p.tagName==="TEXTAREA"?p.type==="checkbox"?p.checked=!!f:p.value=f??"":p.textContent=f??"")}),this.publish(`store/${h}`,o),typeof this._updateDOM=="function"&&this._updateDOM(`store/${h}`,o)},w.getStoreState=function(h,d){this.uiStores=this.uiStores||new Map;let f=this.uiStores.get(h);return f?f[d]:void 0},w._scanStoreBinds=function(){if(typeof document>"u")return;document.querySelectorAll("dolphin-store").forEach(n=>{if(typeof n.getAttribute!="function")return;let o=n.getAttribute("name")||n.getAttribute("data-store");if(!o)return;let c=n.children&&n.children.length>0;if(c?typeof n.setAttribute=="function"&&(n.setAttribute("data-rt-bind",`store/${o}`),n.setAttribute("data-rt-type","context")):n.style&&(n.style.display="none"),!c){let l=n.textContent?n.textContent.trim():"";if(l&&l.startsWith("{"))try{let g=JSON.parse(l);g&&typeof g=="object"&&Object.keys(g).forEach(u=>{this.setStoreState(o,u,g[u])})}catch(g){console.error(`[Dolphin Store Init Error] Failed to parse JSON inside <dolphin-store name="${o}">:`,g)}}let p=n.getAttribute("template");if(n.attributes){let l=["name","data-store","style","data-rt-bind","data-rt-type","template"];Array.from(n.attributes).forEach(g=>{if(!l.includes(g.name)){let u=g.value;u==="true"?u=!0:u==="false"?u=!1:u==="null"?u=null:!isNaN(Number(u))&&u.trim()!==""&&(u=Number(u)),this.setStoreState(o,g.name,u)}})}if(p&&!c&&n.parentNode&&typeof document<"u"){let l=`_ds_${o}_${p.replace(/[^a-z0-9]/gi,"_")}`,g=document.querySelector(`[data-ds-wired="${l}"]`);if(g||(g=document.createElement("div"),g.setAttribute("data-rt-bind",`store/${o}`),g.setAttribute("data-rt-template",p),g.setAttribute("data-ds-wired",l),n.parentNode.insertBefore(g,n.nextSibling)),typeof this._updateDOM=="function"){this.uiStores=this.uiStores||new Map;let u=this.uiStores.get(o)||{};this._updateDOM(`store/${o}`,u)}}if(c&&typeof this._updateDOM=="function"){this.uiStores=this.uiStores||new Map;let l=this.uiStores.get(o)||{};this._updateDOM(`store/${o}`,l)}}),document.querySelectorAll("[data-store-write]").forEach(n=>{let o=n.getAttribute("data-store-write");if(o){let c=o.split(".");if(c.length===2){let p=c[0],l=c[1],g=n.type==="checkbox"?n.checked:n.value;this.uiStores=this.uiStores||new Map,this.uiStores.has(p)||this.uiStores.set(p,{});let u=this.uiStores.get(p);u[l]===void 0&&(u[l]=g)}}}),document.querySelectorAll("[data-store-read]").forEach(n=>{let o=n.getAttribute("data-store-read");if(o){let c=o.split(".");if(c.length===2){let p=c[0],l=c[1],g=this.getStoreState(p,l);g!=null&&(n.tagName==="INPUT"||n.tagName==="TEXTAREA"?n.type==="checkbox"?n.checked=!!g:n.value=g:n.textContent=g)}}})},w.getClosestContext=function(h,d){let f=h;for(;f;){if(f._rtContext){let n=f._rtContext;return d?n[d]:n}f=f.parentElement||f.parentNode}return null},w._executeStoreAction=function(h,d){this.uiStores=this.uiStores||new Map;let f=d&&typeof this.getClosestContext=="function"?this.getClosestContext(d):null,n=new Proxy({},{has:(o,c)=>!0,get:(o,c)=>{if(typeof c=="string")return c==="log"?p=>{if(p===void 0){let l={};this.uiStores.forEach((g,u)=>{l[u]={...g}}),console.log("%c\u{1F4CA} [Dolphin All UI Stores]:","color: #06b6d4; font-weight: bold;",l)}else if(p&&typeof p=="object"&&p.__isStoreProxy__){let l=p.__storeName__,g=this.uiStores.get(l);console.log(`%c\u{1F4CA} [Dolphin Store: ${l}]:`,"color: #06b6d4; font-weight: bold;",g?{...g}:{})}else console.log("%c\u{1F4CA} [Dolphin Log]:","color: #06b6d4; font-weight: bold;",p)}:f&&f[c]!==void 0?f[c]:typeof globalThis<"u"&&c in globalThis?globalThis[c]:typeof window<"u"&&c in window?window[c]:new Proxy({},{get:(p,l)=>{if(l==="__storeName__")return c;if(l==="__isStoreProxy__")return!0;if(typeof l=="string")return this.getStoreState(c,l)},set:(p,l,g)=>typeof l=="string"?(this.setStoreState(c,l,g),!0):!1})}});try{new Function("ctx",`with(ctx) { ${h} }`)(n)}catch(o){console.error("%c[Dolphin Store Action Error]:","color: #ef4444; font-weight: bold;",o),d&&console.error("%cFailed Element:","color: #f97316; font-weight: bold;",d),console.error("%cFailed Expression:","color: #3b82f6; font-style: italic;",h)}},w._initDOMBinding=function(){if(this._domInitialized)return;this._domInitialized=!0;let h=["input","change","keyup","paste","blur"],d=new WeakMap;h.forEach(n=>{this.addDomListener(document,n,o=>{if(!o.target||!o.target.getAttribute)return;let c=o.target.getAttribute("data-store-write");if(c){let u=c.split(".");if(u.length===2){let A=u[0],b=u[1],m=o.target.type==="checkbox"?o.target.checked:o.target.value;this.setStoreState(A,b,m,o.target)}}let p=o.target.getAttribute("data-rt-validate"),l=o.target.name;if(p&&l&&typeof this.validateField=="function"){let u=o.target.closest("form"),A=u?Object.fromEntries(new FormData(u).entries()):{},b=this.validateField(o.target.value,p,A);b?(o.target.classList.add("invalid"),this.publish(`errors/${l}`,b)):(o.target.classList.remove("invalid"),this.publish(`errors/${l}`,""))}let g=o.target.getAttribute("data-rt-push");if(g){let u=o.target.getAttribute("data-rt-debounce"),A=u?parseInt(u,10):0,b=()=>{let m={name:o.target.name,value:o.target.value};this.pubPush(g,m)};if(A>0){d.has(o.target)&&clearTimeout(d.get(o.target));let m=setTimeout(b,A);d.set(o.target,m)}else b()}})}),this.addDomListener(document,"submit",async n=>{if(!n.target||!n.target.getAttribute)return;let o=n.target.getAttribute("data-rt-submit"),c=n.target.getAttribute("data-api-submit");if(o||c){n.target.querySelectorAll("[name]").forEach(m=>{let _=m.name;_&&(this.publish(`errors/${_}`,""),m.classList.remove("invalid"))});let l=n.target.querySelectorAll("[data-rt-validate]"),g=!0;if(l.length>0&&typeof this.validateField=="function"){let m=Object.fromEntries(new FormData(n.target).entries());l.forEach(_=>{let S=_.getAttribute("data-rt-validate"),C=_.name;if(S&&C){let v=this.validateField(_.value,S,m);v&&(g=!1,_.classList.add("invalid"),this.publish(`errors/${C}`,v))}})}if(!g){n.preventDefault(),n.stopPropagation();return}n.preventDefault();let u=this.getClosestContext(n.target)||{},A=new FormData(n.target),b=Object.fromEntries(A.entries());if(o){let m=o;for(let _ in u){let S=e(_);m=m.replace(new RegExp(`\\{\\{${S}\\}\\}`,"g"),u[_]!==void 0&&u[_]!==null?u[_]:"")}this.publish(m,b)}else if(c){let m=c;for(let v in u){let L=e(v);m=m.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),u[v]!==void 0&&u[v]!==null?u[v]:"")}let _=m.trim().split(" "),S=_.length>1?_[0].toUpperCase():"POST",C=_.length>1?_[1]:_[0];b._method&&(S=String(b._method).toUpperCase());try{let v=await this.api.request(S,C,b),L=n.target.getAttribute("data-api-result");L&&this._updateDOM(L,v);let P=n.target.getAttribute("data-api-redirect");P&&(window.location.href=P),n.target.hasAttribute("data-api-reload")&&window.location.reload()}catch(v){console.error("[Dolphin] API Submit Error:",v)}}}}),["click","change","submit","input","keydown","keyup","dblclick","focus","blur","mouseenter","mouseleave"].forEach(n=>{this.addDomListener(document,n,async o=>{if(!o.target||!o.target.closest)return;let c=o.target.closest(`[data-rt-${n}]`),p=o.target.closest(`[data-api-${n}]`);if(c){n==="submit"&&o.preventDefault();let g=c.getAttribute(`data-rt-${n}`),u=c.getAttribute("data-rt-payload"),A=this.getClosestContext(c)||{},b={};if(u){let m=u;for(let _ in A){let S=e(_);m=m.replace(new RegExp(`\\{\\{${S}\\}\\}`,"g"),A[_]!==void 0&&A[_]!==null?A[_]:"")}try{b=JSON.parse(m)}catch{b={}}}this.publish(g,b)}if(p){n==="submit"&&o.preventDefault();let g=p.getAttribute(`data-api-${n}`),u=p.getAttribute("data-api-payload"),A=this.getClosestContext(p)||{},b=g.trim().split(" "),m=b.length>1?b[0].toUpperCase():"POST",_=b.length>1?b[1]:b[0],S=null;if(u){let C=u;for(let v in A){let L=e(v);C=C.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),A[v]!==void 0&&A[v]!==null?A[v]:"")}try{S=JSON.parse(C)}catch{S=null}}try{let C=await this.api.request(m,_,S),v=p.getAttribute("data-api-result");v&&this._updateDOM(v,C);let L=p.getAttribute("data-api-redirect");L&&(window.location.href=L),p.hasAttribute("data-api-reload")&&window.location.reload()}catch(C){console.error(`[Dolphin] API ${n} Error:`,C)}}let l=o.target.closest(`[data-store-${n}]`);if(l){n==="submit"&&o.preventDefault();let g=l.getAttribute(`data-store-${n}`);g&&this._executeStoreAction(g,l)}})}),this.subscribe("#",(n,o)=>{this._updateDOM(o,n)}),this._scanAndFetchAPIBinds(),this._scanStoreBinds(),this._resolveImports(),this._initSPARouter()},w._scanAndFetchAPIBinds=async function(){if(typeof document>"u")return;let h=document.querySelectorAll("[data-api-get]");for(let d of Array.from(h)){let f=d.getAttribute("data-api-get");if(f&&!(typeof d.hasAttribute=="function"&&d.hasAttribute("data-api-initialized"))){typeof d.setAttribute=="function"&&d.setAttribute("data-api-initialized","true");try{let n=await this.api.get(f),o=d.getAttribute("data-api-store");if(o){let p=o.split(".");p.length===2&&this.setStoreState(p[0],p[1],n)}let c=d.getAttribute("data-rt-bind");if(c&&!o)this._updateDOM(c,n);else if(!o){let p=a(d);if(p&&typeof n=="object"&&n!==null){let l=this._applyDeclarativeDirectives(d,n);if(Array.isArray(l)){let g="";for(let u of l)g+=y(p,u);x(d,g)}else x(d,y(p,l))}else if(d.tagName==="INPUT"||d.tagName==="TEXTAREA")d.value=typeof n=="object"?n.value!==void 0?n.value:"":n;else{let l=typeof n=="object"?n.html||n.text||JSON.stringify(n):String(n);d.innerHTML=T(l)}}}catch(n){console.error("[Dolphin] API Get Error:",n)}}}},w._applyDeclarativeDirectives=function(h,d){let f=d;if(typeof d=="object"&&d!==null){let n=o=>{let c=[...o],p=h.getAttribute("data-rt-filter");if(p){let u=p.split("==");if(u.length===2){let A=u[0].trim(),b=u[1].trim(),m,_=b.split(".");_.length===2?m=this.getStoreState(_[0],_[1]):m=d[b]!==void 0?d[b]:this.getStoreState("app",b),m!=null&&m!==""&&(c=c.filter(S=>S[A]===m))}}let l=h.getAttribute("data-rt-search");if(l){let u=l.split("==");if(u.length===2){let A=u[0].trim(),b=u[1].trim(),m,_=b.split(".");if(_.length===2?m=this.getStoreState(_[0],_[1]):m=d[b]!==void 0?d[b]:this.getStoreState("app",b),m!=null&&m!==""){let S=String(m).toLowerCase();c=c.filter(C=>{let v=C[A];return v!=null&&String(v).toLowerCase().includes(S)})}}}let g=h.getAttribute("data-rt-sort");if(g){let u,A=g.split(".");if(A.length===2?u=this.getStoreState(A[0],A[1]):u=d[g]!==void 0?d[g]:this.getStoreState("app",g),u&&u!=="")if(u==="popular")c.sort((b,m)=>{let _=b.rating?.rate||b.rate||0;return(m.rating?.rate||m.rate||0)-_});else{let b="",m="asc";u.endsWith("-low")||u.endsWith("-asc")?(b=u.replace("-low","").replace("-asc",""),m="asc"):(u.endsWith("-high")||u.endsWith("-desc"))&&(b=u.replace("-high","").replace("-desc",""),m="desc"),b&&c.sort((_,S)=>{let C=(Y,tt)=>tt.split(".").reduce((W,et)=>W&&W[et],Y),v=C(_,b),L=C(S,b);if(v===void 0&&(v=_[b]),L===void 0&&(L=S[b]),typeof v=="string"&&typeof L=="string")return m==="asc"?v.localeCompare(L):L.localeCompare(v);let P=Number(v),q=Number(L);return!isNaN(P)&&!isNaN(q)?m==="asc"?P-q:q-P:0})}}return c};if(Array.isArray(d))f=n(d);else{let o="";for(let c in d)if(Array.isArray(d[c])){o=c;break}if(o){let c=n(d[o]);f={...d,[o]:c}}}}return f},w._updateDOM=function(h,d){if(typeof document>"u")return;document.querySelectorAll(`[data-rt-bind="${h}"]`).forEach(n=>{let o=this._applyDeclarativeDirectives(n,d);if(n.getAttribute("data-rt-type")==="context"&&typeof o=="object"&&o!==null){n._rtContext=o;let p=l=>{if(l.hasAttribute("data-rt-text")){let g=l.getAttribute("data-rt-text");if(g){let u=i(g,o);u!=null&&(l.textContent=u)}}if(l.hasAttribute("data-rt-html")){let g=l.getAttribute("data-rt-html");if(g){let u=i(g,o);u!=null&&(l.innerHTML=T(u))}}if(l.hasAttribute("data-rt-attr")){let g=l.getAttribute("data-rt-attr");g&&s(g,",").forEach(u=>{let A=r(u);if(A){let b=A[0].trim(),m=A[1].trim();if(b&&m){let _=i(m,o);_!=null&&l.setAttribute(b,_)}}})}if(l.hasAttribute("data-rt-class")){let g=l.getAttribute("data-rt-class");g&&s(g,",").forEach(u=>{let A=r(u);if(A){let b=A[0].trim(),m=A[1].trim(),_=b.split(/\s+/).filter(Boolean);i(m,o)?_.forEach(S=>l.classList.add(S)):_.forEach(S=>l.classList.remove(S))}})}if(l.hasAttribute("data-rt-if")){let g=l.getAttribute("data-rt-if");g&&(i(g,o)?l.style.display="":l.style.display="none")}if(l.hasAttribute("data-rt-hide")){let g=l.getAttribute("data-rt-hide");g&&(i(g,o)?l.style.display="none":l.style.display="")}};p(n),n.querySelectorAll("[data-rt-text], [data-rt-html], [data-rt-attr], [data-rt-class], [data-rt-if], [data-rt-hide]").forEach(p);return}let c=a(n);if(c&&typeof o=="object"&&o!==null){if(Array.isArray(o)){let p="";for(let l of o)p+=y(c,l);x(n,p)}else x(n,y(c,o));return}if(n.tagName==="INPUT"||n.tagName==="TEXTAREA")n.value=typeof o=="object"?o.value!==void 0?o.value:"":o;else if(c)n.innerHTML=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);else{let p=typeof o=="object"?o.html||o.text||JSON.stringify(o):String(o);n.innerHTML=T(p)}})},w._resolveImports=async function(h){if(typeof document>"u")return;let d=h||document.body||document;if(!d||typeof d.querySelectorAll!="function")return;let f=d.querySelectorAll("[data-import]");if(f.length===0)return;let n=async(c,p)=>{let l=c.getAttribute("data-import");if(!l)return;if(p.has(l)){console.warn(`[Dolphin Component Warning]: Circular import detected for "${l}". Skipping resolving.`),c.innerHTML=`<span style="color:red;font-weight:bold;">Circular import: ${l}</span>`;return}p.add(l);let g=l.indexOf("#"),u=g!==-1?l.substring(0,g):l,A=g!==-1?l.substring(g):null,b=t.get(u);b||(b=fetch(u).then(S=>{if(!S.ok)throw new Error(`HTTP ${S.status}`);return S.text()}),b.catch(()=>t.delete(u)),t.set(u,b));let m="";try{if(m=await b,A&&typeof DOMParser<"u"){let v=new DOMParser().parseFromString(m,"text/html").querySelector(A);v?m=v.outerHTML:(console.warn(`[Dolphin Component Warning]: Selector "${A}" not found in imported file "${u}".`),m=`<span style="color:orange;font-weight:bold;">Selector ${A} not found in ${u}</span>`)}}catch(S){console.error(`[Dolphin Component Error]: Failed to fetch component "${u}":`,S),m=`<span style="color:red;font-weight:bold;">Failed to import ${u}</span>`}c.innerHTML=T(m),c.removeAttribute("data-import");let _=c.querySelectorAll("[data-import]");if(_.length>0){let S=Array.from(_).map(C=>n(C,new Set(p)));await Promise.all(S)}this._scanStoreBinds(),this._scanAndFetchAPIBinds()},o=Array.from(f).map(c=>n(c,new Set));await Promise.all(o)},w._initSPARouter=function(){if(typeof window>"u"||typeof document>"u"||this._routerInitialized)return;this._routerInitialized=!0;let h=null,d=()=>{let o=(this.options.routerViewport||"main, #viewport, body").split(",").map(c=>c.trim());for(let c of o){let p=document.querySelector(c);if(p)return p}return document.body},f=async(n,o=!0)=>{try{this.options.debug&&console.log(`%c\u{1F6E3}\uFE0F [Dolphin Router]: Navigating to ${n}...`,"color: #3b82f6; font-weight: bold;"),h&&h.abort(),h=new AbortController;let c=h.signal,p=d();this.options.routerTransitions&&p&&(p.classList.add("dolphin-fade-out"),await new Promise(_=>setTimeout(_,150)));let l=await fetch(n,{signal:c});if(!l.ok)throw new Error(`HTTP ${l.status}`);let g=await l.text();h=null;let A=new DOMParser().parseFromString(g,"text/html");A.title&&(document.title=A.title);let b=A.querySelector(this.options.routerViewport||"main, #viewport, body"),m=d();b&&m?(m.innerHTML=b.innerHTML,Array.from(b.attributes).forEach(_=>{m.setAttribute(_.name,_.value)})):m&&(m.innerHTML=A.body.innerHTML),o&&window.history.pushState({dolphinSpa:!0,url:n},"",n),this.options.routerTransitions&&m&&(m.classList.remove("dolphin-fade-out"),m.classList.add("dolphin-fade-in"),setTimeout(()=>m.classList.remove("dolphin-fade-in"),300)),await this._resolveImports(m),this._scanStoreBinds(),this._scanAndFetchAPIBinds()}catch(c){if(c&&c.name==="AbortError")return;console.error("[Dolphin Router Error]: Failed to route page:",c),window.location.href=n}};if(this.addDomListener(document,"click",n=>{let o=n.target.closest("a");if(!o||!o.hasAttribute("data-spa")&&o.getAttribute("data-spa")!=="true")return;let c=o.getAttribute("href");!c||c.startsWith("#")||c.startsWith("javascript:")||c.startsWith("mailto:")||c.startsWith("tel:")||new URL(c,window.location.href).origin!==window.location.origin||(n.preventDefault(),f(c))}),this.addDomListener(window,"popstate",n=>{n.state&&n.state.dolphinSpa?f(n.state.url,!1):n.state===null&&f(window.location.pathname,!1)}),this.options.routerTransitions){let n=document.createElement("style");n.innerHTML=`
|
|
27
27
|
.dolphin-fade-out {
|
|
28
28
|
opacity: 0;
|
|
29
29
|
transition: opacity 0.15s ease-in-out;
|
|
@@ -34,4 +34,4 @@ var DolphinModule=(()=>{var q=Object.defineProperty;var et=Object.getOwnProperty
|
|
|
34
34
|
main, #viewport, body {
|
|
35
35
|
transition: opacity 0.15s ease-in-out;
|
|
36
36
|
}
|
|
37
|
-
`,document.head.appendChild(s)}}}var U=class{client;db;isOnline;memoryCache=new Map;memoryMutations=[];constructor(t){this.client=t,this.isOnline=typeof window<"u"&&typeof navigator<"u"?navigator.onLine:!0,this.initDB(),this.setupNetworkListeners()}initDB(){if(!(typeof indexedDB>"u"))try{let t=indexedDB.open("dolphin_offline",1);t.onupgradeneeded=e=>{let n=e.target.result;n.objectStoreNames.contains("cache")||n.createObjectStore("cache"),n.objectStoreNames.contains("mutations")||n.createObjectStore("mutations",{keyPath:"id",autoIncrement:!0})},t.onsuccess=e=>{this.db=e.target.result,this.isOnline&&this.syncMutations()}}catch(t){console.warn("[Dolphin Offline] Failed to initialize IndexedDB:",t)}}setupNetworkListeners(){typeof window>"u"||(this.client.addDomListener(window,"online",()=>{this.isOnline=!0,this.client._dispatch("network:status",{online:!0}),this.syncMutations()}),this.client.addDomListener(window,"offline",()=>{this.isOnline=!1,this.client._dispatch("network:status",{online:!1})}))}async getCache(t){return this.db?new Promise(e=>{try{let r=this.db.transaction("cache","readonly").objectStore("cache").get(t);r.onsuccess=()=>e(r.result?r.result.data:null),r.onerror=()=>e(null)}catch{e(null)}}):this.memoryCache.get(t)}async setCache(t,e){if(!this.db){this.memoryCache.set(t,e);return}return new Promise(n=>{try{let i=this.db.transaction("cache","readwrite");i.objectStore("cache").put({data:e,timestamp:Date.now()},t),i.oncomplete=()=>n(),i.onerror=()=>{console.warn("[Dolphin Offline] setCache write failed for key:",t),n()}}catch{n()}})}async queueMutation(t,e,n){let i={method:t,path:e,payload:n,timestamp:Date.now()};if(!this.db){this.memoryMutations.push(i);return}return new Promise(r=>{try{let a=this.db.transaction("mutations","readwrite");a.objectStore("mutations").add(i),a.oncomplete=()=>r(),a.onerror=()=>{console.warn("[Dolphin Offline] queueMutation write failed:",t,e),this.memoryMutations.push(i),r()}}catch{r()}})}async getMutations(){return this.db?new Promise(t=>{try{let i=this.db.transaction("mutations","readonly").objectStore("mutations").getAll();i.onsuccess=()=>t(i.result||[]),i.onerror=()=>t([])}catch{t([])}}):[...this.memoryMutations]}async removeMutation(t){if(!this.db){this.memoryMutations=this.memoryMutations.filter(e=>e.id!==t);return}return new Promise(e=>{try{let n=this.db.transaction("mutations","readwrite");n.objectStore("mutations").delete(t),n.oncomplete=()=>e(),n.onerror=()=>{console.warn("[Dolphin Offline] removeMutation failed for id:",t),e()}}catch{e()}})}async syncMutations(){let t=await this.getMutations();if(t.length!==0){console.log(`[Dolphin Offline] Syncing ${t.length} queued mutations...`);for(let e of t)try{await this.client.api.requestDirect(e.method,e.path,e.payload),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift()}catch(n){if(console.error(`[Dolphin Offline] Sync failed for mutation ${e.method} ${e.path}:`,n),n&&n.status&&n.status>=400&&n.status<500)console.warn("[Dolphin Offline] Discarding invalid mutation."),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift();else break}}}};function B(_){_._initOffline=function(){this.offline=new U(this)}}function at(_,t,e){let n=t.split(",");for(let i of n){let r=i.trim().split(":"),a=r[0],h=r[1];if(a==="required"){if(!_||_.trim()==="")return"This field is required"}else if(a==="email"){if(_&&_.trim()!==""&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(_))return"Please enter a valid email address"}else if(a==="min"){let T=parseInt(h,10);if(!_||_.length<T)return`Must be at least ${T} characters`}else if(a==="match"&&e&&_!==e[h])return`Must match ${h}`}return null}function j(_){_.validateField=at}function z(_){_.animateElement=function(t,e,n=300){if(typeof t.animate!="function"){t.classList.add(e),setTimeout(()=>t.classList.remove(e),n);return}e==="fade-in"?t.animate([{opacity:0,transform:"translateY(10px)"},{opacity:1,transform:"translateY(0)"}],{duration:n,easing:"ease-out"}):e==="fade-out"&&t.animate([{opacity:1,transform:"translateY(0)"},{opacity:0,transform:"translateY(10px)"}],{duration:n,easing:"ease-in"})},_.staggerListItems=function(t,e,n=50){if(typeof document>"u")return;t.querySelectorAll(e).forEach((r,a)=>{r.style.animationDelay=`${a*n}ms`,r.classList.add("staggered-item")})}}function V(_){_._initA11y=function(){typeof document>"u"||(this.addDomListener(document,"keydown",t=>{if(t.key!=="Tab")return;document.querySelectorAll("[data-rt-a11y-focus-trap]").forEach(n=>{if(n.style.display==="none"||n.hasAttribute("aria-hidden")&&n.getAttribute("aria-hidden")==="true")return;let r=Array.from(n.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'));if(r.length===0)return;let a=r[0],h=r[r.length-1];t.shiftKey?document.activeElement===a&&(h.focus(),t.preventDefault()):document.activeElement===h&&(a.focus(),t.preventDefault())})}),this.addDomListener(document,"keydown",t=>{if(!["ArrowUp","ArrowDown","Enter"].includes(t.key))return;document.querySelectorAll("[data-rt-keynav]").forEach(n=>{let i=Array.from(n.children);if(i.length===0)return;let r=i.findIndex(a=>a.classList.contains("active")||document.activeElement===a);t.key==="ArrowDown"?(r=(r+1)%i.length,i[r].focus(),i.forEach((a,h)=>{h===r?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="ArrowUp"?(r=(r-1+i.length)%i.length,i[r].focus(),i.forEach((a,h)=>{h===r?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="Enter"&&r!==-1&&(i[r].click(),t.preventDefault())})}))},_.autoAriaModal=function(t,e){e?(t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),t.setAttribute("aria-hidden","false"),t.focus()):t.setAttribute("aria-hidden","true")}}function K(_){_._initI18n=function(){if(this.i18n=this.i18n||{locale:"en",dicts:{}},typeof document>"u")return;if(document.querySelectorAll("[data-i18n-dict]").forEach(e=>{let n=e.getAttribute("data-i18n-dict");if(n)try{let i=JSON.parse(e.textContent||"{}");this.i18n.dicts[n]={...this.i18n.dicts[n]||{},...i}}catch(i){console.warn("[Dolphin i18n] Failed to parse dictionary for locale:",n,i)}}),!this.i18n.locale&&typeof navigator<"u"){let e=navigator.language.split("-")[0];this.i18n.dicts[e]&&(this.i18n.locale=e)}this.addDomListener(document,"click",e=>{let n=e.target.closest("[data-i18n-switch]");if(n){let i=n.getAttribute("data-i18n-switch");i&&this.setLocale(i)}}),this.translateDOM()},_.setLocale=function(t){this.i18n=this.i18n||{locale:"en",dicts:{}},this.i18n.locale=t,this.translateDOM(),this.publish("i18n/locale",t)},_.translateDOM=function(){if(typeof document>"u")return;this.i18n=this.i18n||{locale:"en",dicts:{}};let t=this.i18n.locale||"en",e=this.i18n.dicts[t]||{};document.querySelectorAll("[data-i18n-key]").forEach(i=>{let r=i.getAttribute("data-i18n-key");if(!r)return;let a=r.split(".").reduce((T,E)=>T?T[E]:null,e);a==null&&(a=r);let h=i.getAttribute("data-i18n-params");if(h)try{let T=JSON.parse(h),E=D=>D.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");for(let D in T){let k=E(D);a=a.replace(new RegExp(`\\{\\{${k}\\}\\}`,"g"),T[D])}}catch{}i.tagName==="INPUT"||i.tagName==="TEXTAREA"?i.placeholder=a:i.textContent=a})}}function X(_){_._initDragDrop=function(){typeof document>"u"||(this.addDomListener(document,"dragstart",t=>{let e=t.target.closest("[data-drag]");if(!e)return;let n=e.getAttribute("data-drag");n&&(t.dataTransfer.setData("text/plain",n),t.dataTransfer.effectAllowed="move",e.classList.add("dragging"))}),this.addDomListener(document,"dragend",t=>{let e=t.target.closest("[data-drag]");e&&e.classList.remove("dragging")}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-drop]");e&&(t.preventDefault(),e.classList.add("drag-over"))}),this.addDomListener(document,"dragleave",t=>{let e=t.target.closest("[data-drop]");e&&e.classList.remove("drag-over")}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-drop]");if(!e)return;t.preventDefault(),e.classList.remove("drag-over");let n=e.getAttribute("data-drop"),i=t.dataTransfer.getData("text/plain");if(n&&i)try{let r=JSON.parse(i);this.publish(n,r)}catch{this.publish(n,{value:i})}}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;t.preventDefault();let n=e.querySelector(".dragging");if(!n)return;let r=Array.from(e.querySelectorAll("[data-drag]:not(.dragging)")).find(a=>{let h=a.getBoundingClientRect();return t.clientY-h.top-h.height/2<0});r?e.insertBefore(n,r):e.appendChild(n)}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;let n=e.getAttribute("data-sortable");if(!n)return;let r=Array.from(e.querySelectorAll("[data-drag]")).map((a,h)=>{let T=a.getAttribute("data-drag");try{return{index:h,payload:JSON.parse(T||"{}")}}catch{return{index:h,payload:T}}});this.publish(n,r)}))}}function Q(_){_._initCollab=function(){if(typeof document>"u")return;let t=new Map,e=new Map,n=5e3;this.addDomListener(document,"mousemove",i=>{document.querySelectorAll("[data-rt-cursor-share]").forEach(a=>{let h=a.getAttribute("data-rt-cursor-share");if(!h)return;let T=a.getBoundingClientRect(),E=(i.clientX-T.left)/T.width,D=(i.clientY-T.top)/T.height,k=Date.now();(!a._lastSent||k-a._lastSent>50)&&(a._lastSent=k,this.pubPush(`collab/${h}/cursor/${this.deviceId}`,{deviceId:this.deviceId,x:E,y:D}))})}),this.addDomListener(document,"input",i=>{let r=i.target.getAttribute("data-rt-typing");if(!r)return;let a=r,h=T=>{this.pubPush(`collab/${a}/typing/${this.deviceId}`,{deviceId:this.deviceId,typing:T})};i.target._isTyping||(i.target._isTyping=!0,h(!0)),i.target._typingTimer&&clearTimeout(i.target._typingTimer),i.target._typingTimer=setTimeout(()=>{i.target._isTyping=!1,i.target._typingTimer=null,h(!1)},2e3)}),this.addDomListener(document,"input",i=>{let r=i.target.getAttribute("data-rt-crdt");if(!r)return;let a=r,h=i.target.value,T=Date.now();this.publish(`collab/${a}/crdt`,{deviceId:this.deviceId,value:h,timestamp:T,cursorPos:i.target.selectionStart})}),this.subscribe("collab/+/cursor/+",(i,r)=>{let a=r.split("/"),h=a[1],T=a[3];if(T===this.deviceId)return;let E=document.querySelector(`[data-rt-cursor-share="${h}"]`);if(!E)return;let D=`${h}::${T}`,k=t.get(D);(!k||!document.contains(k))&&(k=document.createElement("div"),k.className=`rt-cursor rt-cursor-${T}`,k.style.position="absolute",k.style.width="10px",k.style.height="10px",k.style.borderRadius="50%",k.style.backgroundColor="#"+Math.floor(Math.random()*16777215).toString(16).padStart(6,"0"),k.style.pointerEvents="none",E.appendChild(k),t.set(D,k));let M=E.getBoundingClientRect();k.style.left=i.x*M.width+"px",k.style.top=i.y*M.height+"px",e.has(D)&&clearTimeout(e.get(D)),e.set(D,setTimeout(()=>{let $=t.get(D);$&&$.parentNode&&$.parentNode.removeChild($),t.delete(D),e.delete(D)},n))}),this.subscribe("collab/+/crdt",(i,r)=>{if(i.deviceId===this.deviceId)return;let h=r.split("/")[1];document.querySelectorAll(`[data-rt-crdt="${h}"]`).forEach(E=>{if(!E._lastUpdate||i.timestamp>E._lastUpdate){E._lastUpdate=i.timestamp;let D=E.selectionStart;E.value=i.value,document.activeElement===E&&E.setSelectionRange(D,D)}})}),this._collabCleanup=()=>{e.forEach(i=>clearTimeout(i)),e.clear(),t.forEach(i=>{i&&i.parentNode&&i.parentNode.removeChild(i)}),t.clear()}}}function J(_){_.registerServiceWorker=async function(t="/sw.js"){if(typeof navigator>"u"||!("serviceWorker"in navigator))return console.warn("[Dolphin PWA] Service Workers are not supported in this browser."),null;try{let e=await navigator.serviceWorker.register(t);return console.log("[Dolphin PWA] Service Worker registered successfully with scope:",e.scope),e}catch(e){return console.error("[Dolphin PWA] Service Worker registration failed:",e),null}},_.subscribePushNotifications=async function(t){if(typeof window>"u"||!("serviceWorker"in navigator)||!("PushManager"in window))return console.warn("[Dolphin PWA] Push notifications are not supported in this browser."),null;try{let e=await navigator.serviceWorker.ready,n=await e.pushManager.getSubscription();if(!n){let i="=".repeat((4-t.length%4)%4),r=(t+i).replace(/\-/g,"+").replace(/_/g,"/"),a=window.atob(r),h=new Uint8Array(a.length);for(let T=0;T<a.length;++T)h[T]=a.charCodeAt(T);n=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:h})}return console.log("[Dolphin PWA] Subscribed to push notifications:",n),n}catch(e){return console.error("[Dolphin PWA] Push notification subscription failed:",e),null}}}var H=class{static render(t){if(typeof document>"u")throw new Error("DolphinTestUtils.render requires a DOM document environment to execute.");let e=document.createElement("div");return e.innerHTML=t,document.body.appendChild(e),{container:e,find:n=>e.querySelector(n),fireEvent:(n,i)=>{let r=document.createEvent("Event");r.initEvent(i,!0,!0),n.dispatchEvent(r)}}}static mockWebSocket(){let t=[],e={readyState:1,send:n=>{t.push(n)},close:jest.fn(),onopen:jest.fn(),onmessage:jest.fn(),onclose:jest.fn(),onerror:jest.fn(),sentMessages:t};return global.WebSocket=class{static OPEN=1;readyState=e.readyState;send=e.send;close=e.close;set onopen(n){e.onopen=n}get onopen(){return e.onopen}set onmessage(n){e.onmessage=n}get onmessage(){return e.onmessage}set onclose(n){e.onclose=n}get getonclose(){return e.onclose}constructor(){setTimeout(()=>e.onopen&&e.onopen(),0)}},e}static simulateClick(t){let e={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.click||[]).forEach(i=>i(e))}static simulateChange(t,e){t.value=e;let n={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.change||[]).forEach(r=>r(n))}};function Z(_){_.testing=H}W(x.prototype);B(x.prototype);j(x.prototype);z(x.prototype);V(x.prototype);K(x.prototype);X(x.prototype);Q(x.prototype);J(x.prototype);Z(x.prototype);typeof window<"u"&&(window.DolphinClient=x,document.addEventListener("DOMContentLoaded",()=>{if(!window.dolphin){let _=document.querySelector('script[src*="dolphin-client"]'),t=_?_.getAttribute("data-debug")==="true":!1,e=new x(void 0,void 0,{debug:t});window.dolphin=e,t&&(console.log("%c\u{1F42C} [Dolphin Client] Auto-initialized local reactive engine!","color: #06b6d4; font-weight: bold; font-size: 14px;"),console.log('%c\u{1F449} Tip: You can access the client instance via "window.dolphin" in console.',"color: #94a3b8; font-style: italic;")),document.querySelector('[data-store-write="app.username"]')&&e.setStoreState("app","username","\u0928\u092E\u0938\u094D\u0924\u0947 \u0938\u093E\u0925\u0940!")}}));return ot(ct);})();
|
|
37
|
+
`,document.head.appendChild(n)}}}var F=class{client;db;isOnline;memoryCache=new Map;memoryMutations=[];constructor(t){this.client=t,this.isOnline=typeof window<"u"&&typeof navigator<"u"?navigator.onLine:!0,this.initDB(),this.setupNetworkListeners()}initDB(){if(!(typeof indexedDB>"u"))try{let t=indexedDB.open("dolphin_offline",1);t.onupgradeneeded=e=>{let i=e.target.result;i.objectStoreNames.contains("cache")||i.createObjectStore("cache"),i.objectStoreNames.contains("mutations")||i.createObjectStore("mutations",{keyPath:"id",autoIncrement:!0})},t.onsuccess=e=>{this.db=e.target.result,this.isOnline&&this.syncMutations()}}catch(t){console.warn("[Dolphin Offline] Failed to initialize IndexedDB:",t)}}setupNetworkListeners(){typeof window>"u"||(this.client.addDomListener(window,"online",()=>{this.isOnline=!0,this.client._dispatch("network:status",{online:!0}),this.syncMutations()}),this.client.addDomListener(window,"offline",()=>{this.isOnline=!1,this.client._dispatch("network:status",{online:!1})}))}async getCache(t){return this.db?new Promise(e=>{try{let r=this.db.transaction("cache","readonly").objectStore("cache").get(t);r.onsuccess=()=>e(r.result?r.result.data:null),r.onerror=()=>e(null)}catch{e(null)}}):this.memoryCache.get(t)}async setCache(t,e){if(!this.db){this.memoryCache.set(t,e);return}return new Promise(i=>{try{let s=this.db.transaction("cache","readwrite");s.objectStore("cache").put({data:e,timestamp:Date.now()},t),s.oncomplete=()=>i(),s.onerror=()=>{console.warn("[Dolphin Offline] setCache write failed for key:",t),i()}}catch{i()}})}async queueMutation(t,e,i){let s={method:t,path:e,payload:i,timestamp:Date.now()};if(!this.db){this.memoryMutations.push(s);return}return new Promise(r=>{try{let a=this.db.transaction("mutations","readwrite");a.objectStore("mutations").add(s),a.oncomplete=()=>r(),a.onerror=()=>{console.warn("[Dolphin Offline] queueMutation write failed:",t,e),this.memoryMutations.push(s),r()}}catch{r()}})}async getMutations(){return this.db?new Promise(t=>{try{let s=this.db.transaction("mutations","readonly").objectStore("mutations").getAll();s.onsuccess=()=>t(s.result||[]),s.onerror=()=>t([])}catch{t([])}}):[...this.memoryMutations]}async removeMutation(t){if(!this.db){this.memoryMutations=this.memoryMutations.filter(e=>e.id!==t);return}return new Promise(e=>{try{let i=this.db.transaction("mutations","readwrite");i.objectStore("mutations").delete(t),i.oncomplete=()=>e(),i.onerror=()=>{console.warn("[Dolphin Offline] removeMutation failed for id:",t),e()}}catch{e()}})}async syncMutations(){let t=await this.getMutations();if(t.length!==0){console.log(`[Dolphin Offline] Syncing ${t.length} queued mutations...`);for(let e of t)try{await this.client.api.requestDirect(e.method,e.path,e.payload),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift()}catch(i){if(console.error(`[Dolphin Offline] Sync failed for mutation ${e.method} ${e.path}:`,i),i&&i.status&&i.status>=400&&i.status<500)console.warn("[Dolphin Offline] Discarding invalid mutation."),e.id!==void 0?await this.removeMutation(e.id):this.memoryMutations.shift();else break}}}};function j(w){w._initOffline=function(){this.offline=new F(this)}}function ct(w,t,e){let i=t.split(",");for(let s of i){let r=s.trim().split(":"),a=r[0],y=r[1];if(a==="required"){if(!w||w.trim()==="")return"This field is required"}else if(a==="email"){if(w&&w.trim()!==""&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(w))return"Please enter a valid email address"}else if(a==="min"){let T=parseInt(y,10);if(!w||w.length<T)return`Must be at least ${T} characters`}else if(a==="match"&&e&&w!==e[y])return`Must match ${y}`}return null}function V(w){w.validateField=ct}function z(w){w.animateElement=function(t,e,i=300){if(typeof t.animate!="function"){t.classList.add(e),setTimeout(()=>t.classList.remove(e),i);return}e==="fade-in"?t.animate([{opacity:0,transform:"translateY(10px)"},{opacity:1,transform:"translateY(0)"}],{duration:i,easing:"ease-out"}):e==="fade-out"&&t.animate([{opacity:1,transform:"translateY(0)"},{opacity:0,transform:"translateY(10px)"}],{duration:i,easing:"ease-in"})},w.staggerListItems=function(t,e,i=50){if(typeof document>"u")return;t.querySelectorAll(e).forEach((r,a)=>{r.style.animationDelay=`${a*i}ms`,r.classList.add("staggered-item")})}}function K(w){w._initA11y=function(){typeof document>"u"||(this.addDomListener(document,"keydown",t=>{if(t.key!=="Tab")return;document.querySelectorAll("[data-rt-a11y-focus-trap]").forEach(i=>{if(i.style.display==="none"||i.hasAttribute("aria-hidden")&&i.getAttribute("aria-hidden")==="true")return;let r=Array.from(i.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'));if(r.length===0)return;let a=r[0],y=r[r.length-1];t.shiftKey?document.activeElement===a&&(y.focus(),t.preventDefault()):document.activeElement===y&&(a.focus(),t.preventDefault())})}),this.addDomListener(document,"keydown",t=>{if(!["ArrowUp","ArrowDown","Enter"].includes(t.key))return;document.querySelectorAll("[data-rt-keynav]").forEach(i=>{let s=Array.from(i.children);if(s.length===0)return;let r=s.findIndex(a=>a.classList.contains("active")||document.activeElement===a);t.key==="ArrowDown"?(r=(r+1)%s.length,s[r].focus(),s.forEach((a,y)=>{y===r?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="ArrowUp"?(r=(r-1+s.length)%s.length,s[r].focus(),s.forEach((a,y)=>{y===r?a.classList.add("active"):a.classList.remove("active")}),t.preventDefault()):t.key==="Enter"&&r!==-1&&(s[r].click(),t.preventDefault())})}))},w.autoAriaModal=function(t,e){e?(t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),t.setAttribute("aria-hidden","false"),t.focus()):t.setAttribute("aria-hidden","true")}}function X(w){w._initI18n=function(){if(this.i18n=this.i18n||{locale:"en",dicts:{}},typeof document>"u")return;if(document.querySelectorAll("[data-i18n-dict]").forEach(e=>{let i=e.getAttribute("data-i18n-dict");if(i)try{let s=JSON.parse(e.textContent||"{}");this.i18n.dicts[i]={...this.i18n.dicts[i]||{},...s}}catch(s){console.warn("[Dolphin i18n] Failed to parse dictionary for locale:",i,s)}}),!this.i18n.locale&&typeof navigator<"u"){let e=navigator.language.split("-")[0];this.i18n.dicts[e]&&(this.i18n.locale=e)}this.addDomListener(document,"click",e=>{let i=e.target.closest("[data-i18n-switch]");if(i){let s=i.getAttribute("data-i18n-switch");s&&this.setLocale(s)}}),this.translateDOM()},w.setLocale=function(t){this.i18n=this.i18n||{locale:"en",dicts:{}},this.i18n.locale=t,this.translateDOM(),this.publish("i18n/locale",t)},w.translateDOM=function(){if(typeof document>"u")return;this.i18n=this.i18n||{locale:"en",dicts:{}};let t=this.i18n.locale||"en",e=this.i18n.dicts[t]||{};document.querySelectorAll("[data-i18n-key]").forEach(s=>{let r=s.getAttribute("data-i18n-key");if(!r)return;let a=r.split(".").reduce((T,E)=>T?T[E]:null,e);a==null&&(a=r);let y=s.getAttribute("data-i18n-params");if(y)try{let T=JSON.parse(y),E=D=>D.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");for(let D in T){let k=E(D);a=a.replace(new RegExp(`\\{\\{${k}\\}\\}`,"g"),T[D])}}catch{}s.tagName==="INPUT"||s.tagName==="TEXTAREA"?s.placeholder=a:s.textContent=a})}}function J(w){w._initDragDrop=function(){typeof document>"u"||(this.addDomListener(document,"dragstart",t=>{let e=t.target.closest("[data-drag]");if(!e)return;let i=e.getAttribute("data-drag");i&&(t.dataTransfer.setData("text/plain",i),t.dataTransfer.effectAllowed="move",e.classList.add("dragging"))}),this.addDomListener(document,"dragend",t=>{let e=t.target.closest("[data-drag]");e&&e.classList.remove("dragging")}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-drop]");e&&(t.preventDefault(),e.classList.add("drag-over"))}),this.addDomListener(document,"dragleave",t=>{let e=t.target.closest("[data-drop]");e&&e.classList.remove("drag-over")}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-drop]");if(!e)return;t.preventDefault(),e.classList.remove("drag-over");let i=e.getAttribute("data-drop"),s=t.dataTransfer.getData("text/plain");if(i&&s)try{let r=JSON.parse(s);this.publish(i,r)}catch{this.publish(i,{value:s})}}),this.addDomListener(document,"dragover",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;t.preventDefault();let i=e.querySelector(".dragging");if(!i)return;let r=Array.from(e.querySelectorAll("[data-drag]:not(.dragging)")).find(a=>{let y=a.getBoundingClientRect();return t.clientY-y.top-y.height/2<0});r?e.insertBefore(i,r):e.appendChild(i)}),this.addDomListener(document,"drop",t=>{let e=t.target.closest("[data-sortable]");if(!e)return;let i=e.getAttribute("data-sortable");if(!i)return;let r=Array.from(e.querySelectorAll("[data-drag]")).map((a,y)=>{let T=a.getAttribute("data-drag");try{return{index:y,payload:JSON.parse(T||"{}")}}catch{return{index:y,payload:T}}});this.publish(i,r)}))}}function Q(w){w._initCollab=function(){if(typeof document>"u")return;let t=new Map,e=new Map,i=5e3;this.addDomListener(document,"mousemove",s=>{document.querySelectorAll("[data-rt-cursor-share]").forEach(a=>{let y=a.getAttribute("data-rt-cursor-share");if(!y)return;let T=a.getBoundingClientRect(),E=(s.clientX-T.left)/T.width,D=(s.clientY-T.top)/T.height,k=Date.now();(!a._lastSent||k-a._lastSent>50)&&(a._lastSent=k,this.pubPush(`collab/${y}/cursor/${this.deviceId}`,{deviceId:this.deviceId,x:E,y:D}))})}),this.addDomListener(document,"input",s=>{let r=s.target.getAttribute("data-rt-typing");if(!r)return;let a=r,y=T=>{this.pubPush(`collab/${a}/typing/${this.deviceId}`,{deviceId:this.deviceId,typing:T})};s.target._isTyping||(s.target._isTyping=!0,y(!0)),s.target._typingTimer&&clearTimeout(s.target._typingTimer),s.target._typingTimer=setTimeout(()=>{s.target._isTyping=!1,s.target._typingTimer=null,y(!1)},2e3)}),this.addDomListener(document,"input",s=>{let r=s.target.getAttribute("data-rt-crdt");if(!r)return;let a=r,y=s.target.value,T=Date.now();this.publish(`collab/${a}/crdt`,{deviceId:this.deviceId,value:y,timestamp:T,cursorPos:s.target.selectionStart})}),this.subscribe("collab/+/cursor/+",(s,r)=>{let a=r.split("/"),y=a[1],T=a[3];if(T===this.deviceId)return;let E=document.querySelector(`[data-rt-cursor-share="${y}"]`);if(!E)return;let D=`${y}::${T}`,k=t.get(D);(!k||!document.contains(k))&&(k=document.createElement("div"),k.className=`rt-cursor rt-cursor-${T}`,k.style.position="absolute",k.style.width="10px",k.style.height="10px",k.style.borderRadius="50%",k.style.backgroundColor="#"+Math.floor(Math.random()*16777215).toString(16).padStart(6,"0"),k.style.pointerEvents="none",E.appendChild(k),t.set(D,k));let M=E.getBoundingClientRect();k.style.left=s.x*M.width+"px",k.style.top=s.y*M.height+"px",e.has(D)&&clearTimeout(e.get(D)),e.set(D,setTimeout(()=>{let x=t.get(D);x&&x.parentNode&&x.parentNode.removeChild(x),t.delete(D),e.delete(D)},i))}),this.subscribe("collab/+/crdt",(s,r)=>{if(s.deviceId===this.deviceId)return;let y=r.split("/")[1];document.querySelectorAll(`[data-rt-crdt="${y}"]`).forEach(E=>{if(!E._lastUpdate||s.timestamp>E._lastUpdate){E._lastUpdate=s.timestamp;let D=E.selectionStart;E.value=s.value,document.activeElement===E&&E.setSelectionRange(D,D)}})}),this._collabCleanup=()=>{e.forEach(s=>clearTimeout(s)),e.clear(),t.forEach(s=>{s&&s.parentNode&&s.parentNode.removeChild(s)}),t.clear()}}}function Z(w){w.registerServiceWorker=async function(t="/sw.js"){if(typeof navigator>"u"||!("serviceWorker"in navigator))return console.warn("[Dolphin PWA] Service Workers are not supported in this browser."),null;try{let e=await navigator.serviceWorker.register(t);return console.log("[Dolphin PWA] Service Worker registered successfully with scope:",e.scope),e}catch(e){return console.error("[Dolphin PWA] Service Worker registration failed:",e),null}},w.subscribePushNotifications=async function(t){if(typeof window>"u"||!("serviceWorker"in navigator)||!("PushManager"in window))return console.warn("[Dolphin PWA] Push notifications are not supported in this browser."),null;try{let e=await navigator.serviceWorker.ready,i=await e.pushManager.getSubscription();if(!i){let s="=".repeat((4-t.length%4)%4),r=(t+s).replace(/\-/g,"+").replace(/_/g,"/"),a=window.atob(r),y=new Uint8Array(a.length);for(let T=0;T<a.length;++T)y[T]=a.charCodeAt(T);i=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:y})}return console.log("[Dolphin PWA] Subscribed to push notifications:",i),i}catch(e){return console.error("[Dolphin PWA] Push notification subscription failed:",e),null}}}function I(){if(typeof jest<"u"&&typeof jest.fn=="function")return jest.fn();let w=(...t)=>(w.mock.calls.push(t),w._implementation?w._implementation(...t):w._returnValue);return w.mock={calls:[]},w._returnValue=void 0,w._implementation=null,w.mockReturnValue=t=>(w._returnValue=t,w),w.mockImplementation=t=>(w._implementation=t,w),w}var H=class{static render(t){if(typeof document>"u")throw new Error("DolphinTestUtils.render requires a DOM document environment to execute.");let e=document.createElement("div");return e.innerHTML=t,document.body.appendChild(e),{container:e,find:i=>e.querySelector(i),fireEvent:(i,s)=>{let r=document.createEvent("Event");r.initEvent(s,!0,!0),i.dispatchEvent(r)}}}static mockWebSocket(){let t=[],e={readyState:1,send:i=>{t.push(i)},close:I(),onopen:I(),onmessage:I(),onclose:I(),onerror:I(),sentMessages:t};return global.WebSocket=class{static OPEN=1;readyState=e.readyState;send=e.send;close=e.close;set onopen(i){e.onopen=i}get onopen(){return e.onopen}set onmessage(i){e.onmessage=i}get onmessage(){return e.onmessage}set onclose(i){e.onclose=i}get getonclose(){return e.onclose}constructor(){setTimeout(()=>e.onopen&&e.onopen(),0)}},e}static simulateClick(t){let e={target:t,preventDefault:I(),stopPropagation:I()};(global.document._listeners?.click||[]).forEach(s=>s(e))}static simulateChange(t,e){t.value=e;let i={target:t,preventDefault:I(),stopPropagation:I()};(global.document._listeners?.change||[]).forEach(r=>r(i))}};function G(w){w.testing=H}B($.prototype);j($.prototype);V($.prototype);z($.prototype);K($.prototype);X($.prototype);J($.prototype);Q($.prototype);Z($.prototype);G($.prototype);typeof window<"u"&&(window.DolphinClient=$,document.addEventListener("DOMContentLoaded",()=>{if(!window.dolphin){let w=document.querySelector('script[src*="dolphin-client"]'),t=w?w.getAttribute("data-debug")==="true":!1,e=new $(void 0,void 0,{debug:t});window.dolphin=e,t&&(console.log("%c\u{1F42C} [Dolphin Client] Auto-initialized local reactive engine!","color: #06b6d4; font-weight: bold; font-size: 14px;"),console.log('%c\u{1F449} Tip: You can access the client instance via "window.dolphin" in console.',"color: #94a3b8; font-style: italic;")),document.querySelector('[data-store-write="app.username"]')&&e.setStoreState("app","username","\u0928\u092E\u0938\u094D\u0924\u0947 \u0938\u093E\u0925\u0940!")}}));return at(lt);})();
|
package/dist/index.cjs
CHANGED
|
@@ -1381,6 +1381,73 @@ function attachDOMBinding(clientProto) {
|
|
|
1381
1381
|
};
|
|
1382
1382
|
clientProto._scanStoreBinds = function() {
|
|
1383
1383
|
if (typeof document === "undefined") return;
|
|
1384
|
+
const storeElements = document.querySelectorAll("dolphin-store");
|
|
1385
|
+
storeElements.forEach((el) => {
|
|
1386
|
+
if (typeof el.getAttribute !== "function") return;
|
|
1387
|
+
const storeName = el.getAttribute("name") || el.getAttribute("data-store");
|
|
1388
|
+
if (!storeName) return;
|
|
1389
|
+
const hasChildren = el.children && el.children.length > 0;
|
|
1390
|
+
if (hasChildren) {
|
|
1391
|
+
if (typeof el.setAttribute === "function") {
|
|
1392
|
+
el.setAttribute("data-rt-bind", `store/${storeName}`);
|
|
1393
|
+
el.setAttribute("data-rt-type", "context");
|
|
1394
|
+
}
|
|
1395
|
+
} else {
|
|
1396
|
+
if (el.style) {
|
|
1397
|
+
el.style.display = "none";
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
if (!hasChildren) {
|
|
1401
|
+
const content = el.textContent ? el.textContent.trim() : "";
|
|
1402
|
+
if (content && content.startsWith("{")) {
|
|
1403
|
+
try {
|
|
1404
|
+
const parsed = JSON.parse(content);
|
|
1405
|
+
if (parsed && typeof parsed === "object") {
|
|
1406
|
+
Object.keys(parsed).forEach((key) => {
|
|
1407
|
+
this.setStoreState(storeName, key, parsed[key]);
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
} catch (err) {
|
|
1411
|
+
console.error(`[Dolphin Store Init Error] Failed to parse JSON inside <dolphin-store name="${storeName}">:`, err);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
const templateSelector = el.getAttribute("template");
|
|
1416
|
+
if (el.attributes) {
|
|
1417
|
+
const excludeAttrs = ["name", "data-store", "style", "data-rt-bind", "data-rt-type", "template"];
|
|
1418
|
+
Array.from(el.attributes).forEach((attr) => {
|
|
1419
|
+
if (!excludeAttrs.includes(attr.name)) {
|
|
1420
|
+
let val = attr.value;
|
|
1421
|
+
if (val === "true") val = true;
|
|
1422
|
+
else if (val === "false") val = false;
|
|
1423
|
+
else if (val === "null") val = null;
|
|
1424
|
+
else if (!isNaN(Number(val)) && val.trim() !== "") val = Number(val);
|
|
1425
|
+
this.setStoreState(storeName, attr.name, val);
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
if (templateSelector && !hasChildren && el.parentNode && typeof document !== "undefined") {
|
|
1430
|
+
const markerId = `_ds_${storeName}_${templateSelector.replace(/[^a-z0-9]/gi, "_")}`;
|
|
1431
|
+
let wrapper = document.querySelector(`[data-ds-wired="${markerId}"]`);
|
|
1432
|
+
if (!wrapper) {
|
|
1433
|
+
wrapper = document.createElement("div");
|
|
1434
|
+
wrapper.setAttribute("data-rt-bind", `store/${storeName}`);
|
|
1435
|
+
wrapper.setAttribute("data-rt-template", templateSelector);
|
|
1436
|
+
wrapper.setAttribute("data-ds-wired", markerId);
|
|
1437
|
+
el.parentNode.insertBefore(wrapper, el.nextSibling);
|
|
1438
|
+
}
|
|
1439
|
+
if (typeof this._updateDOM === "function") {
|
|
1440
|
+
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1441
|
+
const currentStore = this.uiStores.get(storeName) || {};
|
|
1442
|
+
this._updateDOM(`store/${storeName}`, currentStore);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
if (hasChildren && typeof this._updateDOM === "function") {
|
|
1446
|
+
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1447
|
+
const currentStore = this.uiStores.get(storeName) || {};
|
|
1448
|
+
this._updateDOM(`store/${storeName}`, currentStore);
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1384
1451
|
const writeEls = document.querySelectorAll("[data-store-write]");
|
|
1385
1452
|
writeEls.forEach((el) => {
|
|
1386
1453
|
const writeBind = el.getAttribute("data-store-write");
|
|
@@ -2786,6 +2853,32 @@ function attachPwa(clientProto) {
|
|
|
2786
2853
|
}
|
|
2787
2854
|
|
|
2788
2855
|
// src/testing.ts
|
|
2856
|
+
function createMockFn() {
|
|
2857
|
+
if (typeof jest !== "undefined" && typeof jest.fn === "function") {
|
|
2858
|
+
return jest.fn();
|
|
2859
|
+
}
|
|
2860
|
+
const fn = (...args) => {
|
|
2861
|
+
fn.mock.calls.push(args);
|
|
2862
|
+
if (fn._implementation) {
|
|
2863
|
+
return fn._implementation(...args);
|
|
2864
|
+
}
|
|
2865
|
+
return fn._returnValue;
|
|
2866
|
+
};
|
|
2867
|
+
fn.mock = {
|
|
2868
|
+
calls: []
|
|
2869
|
+
};
|
|
2870
|
+
fn._returnValue = void 0;
|
|
2871
|
+
fn._implementation = null;
|
|
2872
|
+
fn.mockReturnValue = (val) => {
|
|
2873
|
+
fn._returnValue = val;
|
|
2874
|
+
return fn;
|
|
2875
|
+
};
|
|
2876
|
+
fn.mockImplementation = (impl) => {
|
|
2877
|
+
fn._implementation = impl;
|
|
2878
|
+
return fn;
|
|
2879
|
+
};
|
|
2880
|
+
return fn;
|
|
2881
|
+
}
|
|
2789
2882
|
var DolphinTestUtils = class {
|
|
2790
2883
|
static render(html) {
|
|
2791
2884
|
if (typeof document === "undefined") {
|
|
@@ -2812,11 +2905,11 @@ var DolphinTestUtils = class {
|
|
|
2812
2905
|
send: (data) => {
|
|
2813
2906
|
sentMessages.push(data);
|
|
2814
2907
|
},
|
|
2815
|
-
close:
|
|
2816
|
-
onopen:
|
|
2817
|
-
onmessage:
|
|
2818
|
-
onclose:
|
|
2819
|
-
onerror:
|
|
2908
|
+
close: createMockFn(),
|
|
2909
|
+
onopen: createMockFn(),
|
|
2910
|
+
onmessage: createMockFn(),
|
|
2911
|
+
onclose: createMockFn(),
|
|
2912
|
+
onerror: createMockFn(),
|
|
2820
2913
|
sentMessages
|
|
2821
2914
|
};
|
|
2822
2915
|
global.WebSocket = class {
|
|
@@ -2851,8 +2944,8 @@ var DolphinTestUtils = class {
|
|
|
2851
2944
|
static simulateClick(el) {
|
|
2852
2945
|
const clickEvt = {
|
|
2853
2946
|
target: el,
|
|
2854
|
-
preventDefault:
|
|
2855
|
-
stopPropagation:
|
|
2947
|
+
preventDefault: createMockFn(),
|
|
2948
|
+
stopPropagation: createMockFn()
|
|
2856
2949
|
};
|
|
2857
2950
|
const clickListeners = global.document._listeners?.["click"] || [];
|
|
2858
2951
|
clickListeners.forEach((listener) => listener(clickEvt));
|
|
@@ -2861,8 +2954,8 @@ var DolphinTestUtils = class {
|
|
|
2861
2954
|
el.value = value;
|
|
2862
2955
|
const changeEvt = {
|
|
2863
2956
|
target: el,
|
|
2864
|
-
preventDefault:
|
|
2865
|
-
stopPropagation:
|
|
2957
|
+
preventDefault: createMockFn(),
|
|
2958
|
+
stopPropagation: createMockFn()
|
|
2866
2959
|
};
|
|
2867
2960
|
const changeListeners = global.document._listeners?.["change"] || [];
|
|
2868
2961
|
changeListeners.forEach((listener) => listener(changeEvt));
|
package/dist/index.js
CHANGED
|
@@ -1356,6 +1356,73 @@ function attachDOMBinding(clientProto) {
|
|
|
1356
1356
|
};
|
|
1357
1357
|
clientProto._scanStoreBinds = function() {
|
|
1358
1358
|
if (typeof document === "undefined") return;
|
|
1359
|
+
const storeElements = document.querySelectorAll("dolphin-store");
|
|
1360
|
+
storeElements.forEach((el) => {
|
|
1361
|
+
if (typeof el.getAttribute !== "function") return;
|
|
1362
|
+
const storeName = el.getAttribute("name") || el.getAttribute("data-store");
|
|
1363
|
+
if (!storeName) return;
|
|
1364
|
+
const hasChildren = el.children && el.children.length > 0;
|
|
1365
|
+
if (hasChildren) {
|
|
1366
|
+
if (typeof el.setAttribute === "function") {
|
|
1367
|
+
el.setAttribute("data-rt-bind", `store/${storeName}`);
|
|
1368
|
+
el.setAttribute("data-rt-type", "context");
|
|
1369
|
+
}
|
|
1370
|
+
} else {
|
|
1371
|
+
if (el.style) {
|
|
1372
|
+
el.style.display = "none";
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
if (!hasChildren) {
|
|
1376
|
+
const content = el.textContent ? el.textContent.trim() : "";
|
|
1377
|
+
if (content && content.startsWith("{")) {
|
|
1378
|
+
try {
|
|
1379
|
+
const parsed = JSON.parse(content);
|
|
1380
|
+
if (parsed && typeof parsed === "object") {
|
|
1381
|
+
Object.keys(parsed).forEach((key) => {
|
|
1382
|
+
this.setStoreState(storeName, key, parsed[key]);
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
} catch (err) {
|
|
1386
|
+
console.error(`[Dolphin Store Init Error] Failed to parse JSON inside <dolphin-store name="${storeName}">:`, err);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
const templateSelector = el.getAttribute("template");
|
|
1391
|
+
if (el.attributes) {
|
|
1392
|
+
const excludeAttrs = ["name", "data-store", "style", "data-rt-bind", "data-rt-type", "template"];
|
|
1393
|
+
Array.from(el.attributes).forEach((attr) => {
|
|
1394
|
+
if (!excludeAttrs.includes(attr.name)) {
|
|
1395
|
+
let val = attr.value;
|
|
1396
|
+
if (val === "true") val = true;
|
|
1397
|
+
else if (val === "false") val = false;
|
|
1398
|
+
else if (val === "null") val = null;
|
|
1399
|
+
else if (!isNaN(Number(val)) && val.trim() !== "") val = Number(val);
|
|
1400
|
+
this.setStoreState(storeName, attr.name, val);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
if (templateSelector && !hasChildren && el.parentNode && typeof document !== "undefined") {
|
|
1405
|
+
const markerId = `_ds_${storeName}_${templateSelector.replace(/[^a-z0-9]/gi, "_")}`;
|
|
1406
|
+
let wrapper = document.querySelector(`[data-ds-wired="${markerId}"]`);
|
|
1407
|
+
if (!wrapper) {
|
|
1408
|
+
wrapper = document.createElement("div");
|
|
1409
|
+
wrapper.setAttribute("data-rt-bind", `store/${storeName}`);
|
|
1410
|
+
wrapper.setAttribute("data-rt-template", templateSelector);
|
|
1411
|
+
wrapper.setAttribute("data-ds-wired", markerId);
|
|
1412
|
+
el.parentNode.insertBefore(wrapper, el.nextSibling);
|
|
1413
|
+
}
|
|
1414
|
+
if (typeof this._updateDOM === "function") {
|
|
1415
|
+
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1416
|
+
const currentStore = this.uiStores.get(storeName) || {};
|
|
1417
|
+
this._updateDOM(`store/${storeName}`, currentStore);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
if (hasChildren && typeof this._updateDOM === "function") {
|
|
1421
|
+
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1422
|
+
const currentStore = this.uiStores.get(storeName) || {};
|
|
1423
|
+
this._updateDOM(`store/${storeName}`, currentStore);
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1359
1426
|
const writeEls = document.querySelectorAll("[data-store-write]");
|
|
1360
1427
|
writeEls.forEach((el) => {
|
|
1361
1428
|
const writeBind = el.getAttribute("data-store-write");
|
|
@@ -2761,6 +2828,32 @@ function attachPwa(clientProto) {
|
|
|
2761
2828
|
}
|
|
2762
2829
|
|
|
2763
2830
|
// src/testing.ts
|
|
2831
|
+
function createMockFn() {
|
|
2832
|
+
if (typeof jest !== "undefined" && typeof jest.fn === "function") {
|
|
2833
|
+
return jest.fn();
|
|
2834
|
+
}
|
|
2835
|
+
const fn = (...args) => {
|
|
2836
|
+
fn.mock.calls.push(args);
|
|
2837
|
+
if (fn._implementation) {
|
|
2838
|
+
return fn._implementation(...args);
|
|
2839
|
+
}
|
|
2840
|
+
return fn._returnValue;
|
|
2841
|
+
};
|
|
2842
|
+
fn.mock = {
|
|
2843
|
+
calls: []
|
|
2844
|
+
};
|
|
2845
|
+
fn._returnValue = void 0;
|
|
2846
|
+
fn._implementation = null;
|
|
2847
|
+
fn.mockReturnValue = (val) => {
|
|
2848
|
+
fn._returnValue = val;
|
|
2849
|
+
return fn;
|
|
2850
|
+
};
|
|
2851
|
+
fn.mockImplementation = (impl) => {
|
|
2852
|
+
fn._implementation = impl;
|
|
2853
|
+
return fn;
|
|
2854
|
+
};
|
|
2855
|
+
return fn;
|
|
2856
|
+
}
|
|
2764
2857
|
var DolphinTestUtils = class {
|
|
2765
2858
|
static render(html) {
|
|
2766
2859
|
if (typeof document === "undefined") {
|
|
@@ -2787,11 +2880,11 @@ var DolphinTestUtils = class {
|
|
|
2787
2880
|
send: (data) => {
|
|
2788
2881
|
sentMessages.push(data);
|
|
2789
2882
|
},
|
|
2790
|
-
close:
|
|
2791
|
-
onopen:
|
|
2792
|
-
onmessage:
|
|
2793
|
-
onclose:
|
|
2794
|
-
onerror:
|
|
2883
|
+
close: createMockFn(),
|
|
2884
|
+
onopen: createMockFn(),
|
|
2885
|
+
onmessage: createMockFn(),
|
|
2886
|
+
onclose: createMockFn(),
|
|
2887
|
+
onerror: createMockFn(),
|
|
2795
2888
|
sentMessages
|
|
2796
2889
|
};
|
|
2797
2890
|
global.WebSocket = class {
|
|
@@ -2826,8 +2919,8 @@ var DolphinTestUtils = class {
|
|
|
2826
2919
|
static simulateClick(el) {
|
|
2827
2920
|
const clickEvt = {
|
|
2828
2921
|
target: el,
|
|
2829
|
-
preventDefault:
|
|
2830
|
-
stopPropagation:
|
|
2922
|
+
preventDefault: createMockFn(),
|
|
2923
|
+
stopPropagation: createMockFn()
|
|
2831
2924
|
};
|
|
2832
2925
|
const clickListeners = global.document._listeners?.["click"] || [];
|
|
2833
2926
|
clickListeners.forEach((listener) => listener(clickEvt));
|
|
@@ -2836,8 +2929,8 @@ var DolphinTestUtils = class {
|
|
|
2836
2929
|
el.value = value;
|
|
2837
2930
|
const changeEvt = {
|
|
2838
2931
|
target: el,
|
|
2839
|
-
preventDefault:
|
|
2840
|
-
stopPropagation:
|
|
2932
|
+
preventDefault: createMockFn(),
|
|
2933
|
+
stopPropagation: createMockFn()
|
|
2841
2934
|
};
|
|
2842
2935
|
const changeListeners = global.document._listeners?.["change"] || [];
|
|
2843
2936
|
changeListeners.forEach((listener) => listener(changeEvt));
|
package/dist/testing.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ export declare class DolphinTestUtils {
|
|
|
7
7
|
static mockWebSocket(): {
|
|
8
8
|
readyState: number;
|
|
9
9
|
send: (data: string) => void;
|
|
10
|
-
close:
|
|
11
|
-
onopen:
|
|
12
|
-
onmessage:
|
|
13
|
-
onclose:
|
|
14
|
-
onerror:
|
|
10
|
+
close: any;
|
|
11
|
+
onopen: any;
|
|
12
|
+
onmessage: any;
|
|
13
|
+
onclose: any;
|
|
14
|
+
onerror: any;
|
|
15
15
|
sentMessages: string[];
|
|
16
16
|
};
|
|
17
17
|
static simulateClick(el: any): void;
|
package/fulltutorial.md
CHANGED
|
@@ -503,6 +503,61 @@ Ternary operator (`condition ? true : false`) वा बहु-लाइन `if/
|
|
|
503
503
|
<button data-store-click="app.darkMode = !app.darkMode">डार्क मोड अन/अफ</button>
|
|
504
504
|
```
|
|
505
505
|
|
|
506
|
+
### १०.१.३. Declarative Store Initialization & Context Containers (`<dolphin-store>`) - स्टोर घोषणा, प्रारम्भिकरण र अटो-बाइन्डिङ
|
|
507
|
+
|
|
508
|
+
Dolphin Client v2.0 introduces the `<dolphin-store>` tag for declaring, seeding, and auto-wiring reactive stores directly in HTML without writing custom JavaScript scripts.
|
|
509
|
+
|
|
510
|
+
#### क) Seed-Only Mode (विशुद्ध डेटा सीडिङ)
|
|
511
|
+
If the `<dolphin-store>` tag does not contain child elements, it seeds the store and remains hidden (`display: none`). You can seed data via tag attributes or inline JSON content:
|
|
512
|
+
|
|
513
|
+
**Attribute-Based Seeding:**
|
|
514
|
+
```html
|
|
515
|
+
<!-- Automatically seeds store "app" with boolean, numeric, null, and string types -->
|
|
516
|
+
<dolphin-store name="app" count="0" isAdding="false" user="null" theme="dark"></dolphin-store>
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**JSON-Based Seeding:**
|
|
520
|
+
```html
|
|
521
|
+
<!-- Seed complex objects/arrays by putting JSON content inside the tag -->
|
|
522
|
+
<dolphin-store name="app">
|
|
523
|
+
{
|
|
524
|
+
"count": 10,
|
|
525
|
+
"user": { "name": "Shankar", "role": "admin" },
|
|
526
|
+
"loggedIn": true
|
|
527
|
+
}
|
|
528
|
+
</dolphin-store>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### ख) Context Container Dual-Mode (डबल-मोड - डेटा कन्टेनर)
|
|
532
|
+
If the `<dolphin-store>` tag contains child elements, it acts as a **reactive context container** and remains visible. Its children can read and write to the store dynamically using `data-store-*` directives:
|
|
533
|
+
|
|
534
|
+
```html
|
|
535
|
+
<!-- Acts as the reactive container for 'store/profile' -->
|
|
536
|
+
<dolphin-store name="profile" username="Shankar" role="Admin">
|
|
537
|
+
<div class="card">
|
|
538
|
+
<h3 data-rt-text="username"></h3>
|
|
539
|
+
<span class="badge" data-rt-text="role"></span>
|
|
540
|
+
</div>
|
|
541
|
+
</dolphin-store>
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
#### ग) Template Auto-Wiring (टेम्पलेट स्वतः-कनेक्सन)
|
|
545
|
+
By setting the `template` attribute on a `<dolphin-store>`, Dolphin will automatically generate a reactive binding `div` right after it, compile the specified template selector, and render it with the store's seeded state immediately on page load:
|
|
546
|
+
|
|
547
|
+
```html
|
|
548
|
+
<!-- 1. The Svelte-style template -->
|
|
549
|
+
<template id="counter-ui">
|
|
550
|
+
<div class="counter-box">
|
|
551
|
+
<p>Count: {{count}}</p>
|
|
552
|
+
<button data-rt-click="app.count = app.count + 1">Increment</button>
|
|
553
|
+
</div>
|
|
554
|
+
</template>
|
|
555
|
+
|
|
556
|
+
<!-- 2. Auto-wire the store app to the template (No separate wrapper div required!) -->
|
|
557
|
+
<dolphin-store name="app" template="#counter-ui" count="5"></dolphin-store>
|
|
558
|
+
```
|
|
559
|
+
Under the hood, this dynamically injects a binding container that subscribes to `store/app` and updates the UI in real-time.
|
|
560
|
+
|
|
506
561
|
### १०.२. Declarative Validations (`data-validate`)
|
|
507
562
|
Apply robust validation rules to inputs and forms instantly. Tagged invalid inputs automatically receive `.invalid` classes and publish error text:
|
|
508
563
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dolphin-client",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "HTML is back! Hookless, framework-agnostic real-time reactive DOM-binding client for Dolphin Server with WebSockets, WebRTC signaling, and offline REST API fallbacks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|