dolphin-client 1.0.4 → 1.0.5
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/dist/dolphin-client.js +138 -19
- package/dist/dolphin-client.min.js +13 -13
- package/dist/index.cjs +138 -19
- package/dist/index.js +138 -19
- package/package.json +1 -1
package/dist/dolphin-client.js
CHANGED
|
@@ -1380,14 +1380,15 @@ var DolphinModule = (() => {
|
|
|
1380
1380
|
} else if (!apiStore) {
|
|
1381
1381
|
const template = resolveTemplate(el);
|
|
1382
1382
|
if (template && typeof result === "object" && result !== null) {
|
|
1383
|
-
|
|
1383
|
+
const processedResult = this._applyDeclarativeDirectives(el, result);
|
|
1384
|
+
if (Array.isArray(processedResult)) {
|
|
1384
1385
|
let combinedHTML = "";
|
|
1385
|
-
for (const item of
|
|
1386
|
+
for (const item of processedResult) {
|
|
1386
1387
|
combinedHTML += renderTemplate(template, item);
|
|
1387
1388
|
}
|
|
1388
1389
|
scheduleDOMUpdate(el, combinedHTML);
|
|
1389
1390
|
} else {
|
|
1390
|
-
scheduleDOMUpdate(el, renderTemplate(template,
|
|
1391
|
+
scheduleDOMUpdate(el, renderTemplate(template, processedResult));
|
|
1391
1392
|
}
|
|
1392
1393
|
} else {
|
|
1393
1394
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
@@ -1402,21 +1403,139 @@ var DolphinModule = (() => {
|
|
|
1402
1403
|
}
|
|
1403
1404
|
}
|
|
1404
1405
|
};
|
|
1406
|
+
clientProto._applyDeclarativeDirectives = function(el, payload) {
|
|
1407
|
+
let processedPayload = payload;
|
|
1408
|
+
if (typeof payload === "object" && payload !== null) {
|
|
1409
|
+
const applyFilterSearchSort = (arr) => {
|
|
1410
|
+
let result = [...arr];
|
|
1411
|
+
const filterAttr = el.getAttribute("data-rt-filter");
|
|
1412
|
+
if (filterAttr) {
|
|
1413
|
+
const parts = filterAttr.split("==");
|
|
1414
|
+
if (parts.length === 2) {
|
|
1415
|
+
const itemProp = parts[0].trim();
|
|
1416
|
+
const storeExpr = parts[1].trim();
|
|
1417
|
+
let filterVal = void 0;
|
|
1418
|
+
const storeParts = storeExpr.split(".");
|
|
1419
|
+
if (storeParts.length === 2) {
|
|
1420
|
+
filterVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1421
|
+
} else {
|
|
1422
|
+
filterVal = payload[storeExpr] !== void 0 ? payload[storeExpr] : this.getStoreState("app", storeExpr);
|
|
1423
|
+
}
|
|
1424
|
+
if (filterVal !== void 0 && filterVal !== null && filterVal !== "") {
|
|
1425
|
+
result = result.filter((item) => item[itemProp] === filterVal);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
const searchAttr = el.getAttribute("data-rt-search");
|
|
1430
|
+
if (searchAttr) {
|
|
1431
|
+
const parts = searchAttr.split("==");
|
|
1432
|
+
if (parts.length === 2) {
|
|
1433
|
+
const itemProp = parts[0].trim();
|
|
1434
|
+
const storeExpr = parts[1].trim();
|
|
1435
|
+
let searchVal = void 0;
|
|
1436
|
+
const storeParts = storeExpr.split(".");
|
|
1437
|
+
if (storeParts.length === 2) {
|
|
1438
|
+
searchVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1439
|
+
} else {
|
|
1440
|
+
searchVal = payload[storeExpr] !== void 0 ? payload[storeExpr] : this.getStoreState("app", storeExpr);
|
|
1441
|
+
}
|
|
1442
|
+
if (searchVal !== void 0 && searchVal !== null && searchVal !== "") {
|
|
1443
|
+
const query = String(searchVal).toLowerCase();
|
|
1444
|
+
result = result.filter((item) => {
|
|
1445
|
+
const val = item[itemProp];
|
|
1446
|
+
return val !== void 0 && val !== null && String(val).toLowerCase().includes(query);
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
const sortAttr = el.getAttribute("data-rt-sort");
|
|
1452
|
+
if (sortAttr) {
|
|
1453
|
+
let sortByVal = void 0;
|
|
1454
|
+
const storeParts = sortAttr.split(".");
|
|
1455
|
+
if (storeParts.length === 2) {
|
|
1456
|
+
sortByVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1457
|
+
} else {
|
|
1458
|
+
sortByVal = payload[sortAttr] !== void 0 ? payload[sortAttr] : this.getStoreState("app", sortAttr);
|
|
1459
|
+
}
|
|
1460
|
+
if (sortByVal && sortByVal !== "") {
|
|
1461
|
+
if (sortByVal === "popular") {
|
|
1462
|
+
result.sort((a, b) => {
|
|
1463
|
+
const rateA = a.rating?.rate || a.rate || 0;
|
|
1464
|
+
const rateB = b.rating?.rate || b.rate || 0;
|
|
1465
|
+
return rateB - rateA;
|
|
1466
|
+
});
|
|
1467
|
+
} else {
|
|
1468
|
+
let field = "";
|
|
1469
|
+
let direction = "asc";
|
|
1470
|
+
if (sortByVal.endsWith("-low") || sortByVal.endsWith("-asc")) {
|
|
1471
|
+
field = sortByVal.replace("-low", "").replace("-asc", "");
|
|
1472
|
+
direction = "asc";
|
|
1473
|
+
} else if (sortByVal.endsWith("-high") || sortByVal.endsWith("-desc")) {
|
|
1474
|
+
field = sortByVal.replace("-high", "").replace("-desc", "");
|
|
1475
|
+
direction = "desc";
|
|
1476
|
+
}
|
|
1477
|
+
if (field) {
|
|
1478
|
+
result.sort((a, b) => {
|
|
1479
|
+
const resolveVal = (obj, path) => {
|
|
1480
|
+
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
1481
|
+
};
|
|
1482
|
+
let valA = resolveVal(a, field);
|
|
1483
|
+
let valB = resolveVal(b, field);
|
|
1484
|
+
if (valA === void 0) valA = a[field];
|
|
1485
|
+
if (valB === void 0) valB = b[field];
|
|
1486
|
+
if (typeof valA === "string" && typeof valB === "string") {
|
|
1487
|
+
return direction === "asc" ? valA.localeCompare(valB) : valB.localeCompare(valA);
|
|
1488
|
+
}
|
|
1489
|
+
const numA = Number(valA);
|
|
1490
|
+
const numB = Number(valB);
|
|
1491
|
+
if (!isNaN(numA) && !isNaN(numB)) {
|
|
1492
|
+
return direction === "asc" ? numA - numB : numB - numA;
|
|
1493
|
+
}
|
|
1494
|
+
return 0;
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return result;
|
|
1501
|
+
};
|
|
1502
|
+
if (Array.isArray(payload)) {
|
|
1503
|
+
processedPayload = applyFilterSearchSort(payload);
|
|
1504
|
+
} else {
|
|
1505
|
+
let foundArrayKey = "";
|
|
1506
|
+
for (const key in payload) {
|
|
1507
|
+
if (Array.isArray(payload[key])) {
|
|
1508
|
+
foundArrayKey = key;
|
|
1509
|
+
break;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (foundArrayKey) {
|
|
1513
|
+
const processedArray = applyFilterSearchSort(payload[foundArrayKey]);
|
|
1514
|
+
processedPayload = {
|
|
1515
|
+
...payload,
|
|
1516
|
+
[foundArrayKey]: processedArray
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return processedPayload;
|
|
1522
|
+
};
|
|
1405
1523
|
clientProto._updateDOM = function(topic, payload) {
|
|
1406
1524
|
if (typeof document === "undefined") return;
|
|
1407
1525
|
const elements = document.querySelectorAll(`[data-rt-bind="${topic}"]`);
|
|
1408
1526
|
elements.forEach((el) => {
|
|
1409
|
-
|
|
1410
|
-
|
|
1527
|
+
const processedPayload = this._applyDeclarativeDirectives(el, payload);
|
|
1528
|
+
if (el.getAttribute("data-rt-type") === "context" && typeof processedPayload === "object" && processedPayload !== null) {
|
|
1529
|
+
el._rtContext = processedPayload;
|
|
1411
1530
|
const processNode = (node) => {
|
|
1412
1531
|
if (node.hasAttribute("data-rt-text")) {
|
|
1413
1532
|
const key = node.getAttribute("data-rt-text");
|
|
1414
|
-
if (key &&
|
|
1533
|
+
if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) node.textContent = processedPayload[key];
|
|
1415
1534
|
}
|
|
1416
1535
|
if (node.hasAttribute("data-rt-html")) {
|
|
1417
1536
|
const key = node.getAttribute("data-rt-html");
|
|
1418
|
-
if (key &&
|
|
1419
|
-
node.innerHTML = sanitizeHTML(
|
|
1537
|
+
if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
|
|
1538
|
+
node.innerHTML = sanitizeHTML(processedPayload[key]);
|
|
1420
1539
|
}
|
|
1421
1540
|
}
|
|
1422
1541
|
if (node.hasAttribute("data-rt-attr")) {
|
|
@@ -1427,8 +1546,8 @@ var DolphinModule = (() => {
|
|
|
1427
1546
|
if (parts.length === 2) {
|
|
1428
1547
|
const attrName = parts[0].trim();
|
|
1429
1548
|
const key = parts[1].trim();
|
|
1430
|
-
if (attrName && key &&
|
|
1431
|
-
node.setAttribute(attrName,
|
|
1549
|
+
if (attrName && key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
|
|
1550
|
+
node.setAttribute(attrName, processedPayload[key]);
|
|
1432
1551
|
}
|
|
1433
1552
|
}
|
|
1434
1553
|
});
|
|
@@ -1443,7 +1562,7 @@ var DolphinModule = (() => {
|
|
|
1443
1562
|
const className = parts[0].trim();
|
|
1444
1563
|
const key = parts[1].trim();
|
|
1445
1564
|
const classNames = className.split(/\s+/).filter(Boolean);
|
|
1446
|
-
if (
|
|
1565
|
+
if (processedPayload[key]) {
|
|
1447
1566
|
classNames.forEach((c) => node.classList.add(c));
|
|
1448
1567
|
} else {
|
|
1449
1568
|
classNames.forEach((c) => node.classList.remove(c));
|
|
@@ -1455,7 +1574,7 @@ var DolphinModule = (() => {
|
|
|
1455
1574
|
if (node.hasAttribute("data-rt-if")) {
|
|
1456
1575
|
const key = node.getAttribute("data-rt-if");
|
|
1457
1576
|
if (key) {
|
|
1458
|
-
if (
|
|
1577
|
+
if (processedPayload[key]) {
|
|
1459
1578
|
node.style.display = "";
|
|
1460
1579
|
} else {
|
|
1461
1580
|
node.style.display = "none";
|
|
@@ -1465,7 +1584,7 @@ var DolphinModule = (() => {
|
|
|
1465
1584
|
if (node.hasAttribute("data-rt-hide")) {
|
|
1466
1585
|
const key = node.getAttribute("data-rt-hide");
|
|
1467
1586
|
if (key) {
|
|
1468
|
-
if (
|
|
1587
|
+
if (processedPayload[key]) {
|
|
1469
1588
|
node.style.display = "none";
|
|
1470
1589
|
} else {
|
|
1471
1590
|
node.style.display = "";
|
|
@@ -1478,22 +1597,22 @@ var DolphinModule = (() => {
|
|
|
1478
1597
|
return;
|
|
1479
1598
|
}
|
|
1480
1599
|
const template = resolveTemplate(el);
|
|
1481
|
-
if (template && typeof
|
|
1482
|
-
if (Array.isArray(
|
|
1600
|
+
if (template && typeof processedPayload === "object" && processedPayload !== null) {
|
|
1601
|
+
if (Array.isArray(processedPayload)) {
|
|
1483
1602
|
let combinedHTML = "";
|
|
1484
|
-
for (const item of
|
|
1603
|
+
for (const item of processedPayload) {
|
|
1485
1604
|
combinedHTML += renderTemplate(template, item);
|
|
1486
1605
|
}
|
|
1487
1606
|
scheduleDOMUpdate(el, combinedHTML);
|
|
1488
1607
|
} else {
|
|
1489
|
-
scheduleDOMUpdate(el, renderTemplate(template,
|
|
1608
|
+
scheduleDOMUpdate(el, renderTemplate(template, processedPayload));
|
|
1490
1609
|
}
|
|
1491
1610
|
return;
|
|
1492
1611
|
}
|
|
1493
1612
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
1494
|
-
el.value = typeof
|
|
1613
|
+
el.value = typeof processedPayload === "object" ? processedPayload.value !== void 0 ? processedPayload.value : "" : processedPayload;
|
|
1495
1614
|
} else {
|
|
1496
|
-
el.innerHTML = typeof
|
|
1615
|
+
el.innerHTML = typeof processedPayload === "object" ? processedPayload.html || processedPayload.text || JSON.stringify(processedPayload) : processedPayload;
|
|
1497
1616
|
}
|
|
1498
1617
|
});
|
|
1499
1618
|
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
var DolphinModule=(()=>{var $=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var J=(p,t)=>{for(var e in t)$(p,e,{get:t[e],enumerable:!0})},Q=(p,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of K(t))!V.call(p,r)&&r!==e&&$(p,r,{get:()=>t[r],enumerable:!(n=z(t,r))||n.enumerable});return p};var X=p=>Q($({},"__esModule",{value:!0}),p);var Z={};J(Z,{DolphinClient:()=>k});var x=class{client;constructor(t){return this.client=t,this._createProxy([])}_createProxy(t){let e=t.join("/"),n=i=>this.request("GET",e,null,i);n.get=(i,o)=>typeof i=="string"?this.request("GET",i,null,o):this.request("GET",e,null,i),n.post=(i,o,l)=>typeof i=="string"?this.request("POST",i,o,l):this.request("POST",e,i,o),n.put=(i,o,l)=>typeof i=="string"?this.request("PUT",i,o,l):this.request("PUT",e,i,o),n.patch=(i,o,l)=>typeof i=="string"?this.request("PATCH",i,o,l):this.request("PATCH",e,i,o),n.del=(i,o)=>typeof i=="string"?this.request("DELETE",i,null,o):this.request("DELETE",e,null,i),n.request=(i,o,l,w)=>{let T=o?`${e}/${o.startsWith("/")?o.slice(1):o}`:e;return this.request(i,T,l,w)},n.requestDirect=(i,o,l,w)=>this.requestDirect(i,o,l,w);let r=["get","post","put","patch","del","request","requestDirect"];return new Proxy(n,{get:(i,o)=>typeof o=="string"&&!r.includes(o)?this._createProxy([...t,o]):i[o]})}async request(t,e,n=null,r={}){if(this.client.offline){let i=this.client.offline.isOnline,o=`${t.toUpperCase()}:${e}`;if(t.toUpperCase()==="GET")if(i)try{let l=await this.requestDirect(t,e,n,r);return await this.client.offline.setCache(o,l),l}catch(l){let w=await this.client.offline.getCache(o);if(w!=null)return w;throw l}else{let l=await this.client.offline.getCache(o);if(l!=null)return l;throw{status:503,data:{error:"Offline, no cache available"}}}else return i?this.requestDirect(t,e,n,r):(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,r)}async requestDirect(t,e,n=null,r={}){let i=r._isRetry===!0,o=e.startsWith("http://")||e.startsWith("https://")?e:`${this.client.httpUrl}${e.startsWith("/")?e:"/"+e}`;this.client.options.debug&&console.log("%c\u{1F680} [Dolphin API Request]:","color: #3b82f6; font-weight: bold;",t.toUpperCase(),e,n||"");let l=new AbortController,w=setTimeout(()=>l.abort(),this.client.options.timeout),T={"Content-Type":"application/json",...r.headers||{}};this.client.accessToken&&(T.Authorization=`Bearer ${this.client.accessToken}`);let h={...r};delete h._isRetry;try{let a=await fetch(o,{method:t,headers:T,signal:l.signal,...n?{body:JSON.stringify(n)}:{},...h});if(clearTimeout(w),a.status===401&&!i&&this.client.options.autoRefreshToken&&await this.client.auth._silentRefresh())return this.request(t,e,n,{...r,_isRetry:!0});let s=(a.headers.get("content-type")||"").includes("application/json")?await a.json():await a.text();if(!a.ok)throw{status:a.status,data:s};if(this.client.options.debug&&console.log("%c\u2705 [Dolphin API Success]:","color: #10b981; font-weight: bold;",t.toUpperCase(),e,s),s&&typeof s=="object"&&s.accessToken&&(this.client.setToken(s.accessToken),s.user&&(this.client.auth.user=s.user)),this.client.options.autoBroadcast&&["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let c=e.startsWith("/")?e.substring(1):e;this.client.publish(c,{method:t.toUpperCase(),payload:n,result:s})}return s}catch(a){throw clearTimeout(w),this.client.options.debug&&console.error("%c\u274C [Dolphin API Error]:","color: #ef4444; font-weight: bold;",t.toUpperCase(),e,a),a.name==="AbortError"?{status:408,data:{error:"Request timed out"}}:a}}};var C=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},r=await this.client.api.post("/api/auth/2fa/verify",n);return r.accessToken&&(this.client.setToken(r.accessToken),r.user&&(this.user=r.user)),r}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 M=class{client;data;listeners;subscribed;constructor(t){return this.client=t,this.data=new Map,this.listeners=new Set,this.subscribed=new Set,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,r="asc")=>(e._sort={key:n,direction:r},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()}`);e._rawItems=Array.isArray(n)?n:n.data||[],e.loading=!1,e.success=!0,e.error=null,this._applyTransform(e),this.subscribed.has(t)||(this.client.subscribe(`db:sync/${t.toLowerCase()}`,r=>{this._handleRemoteUpdate(t,r)}),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:r}=t._sort;e.sort((i,o)=>i[n]===o[n]?0:(i[n]>o[n]?1:-1)*(r==="asc"?1:-1))}t.items=e,this._notify()}_handleRemoteUpdate(t,e){let n=this.data.get(t);if(!n)return;let{type:r,data:i}=e,o=n._rawItems;r==="create"?o=[...o,i]:r==="update"?o=o.map(l=>l.id===i.id||l._id===i._id?{...l,...i}:l):r==="delete"&&(o=o.filter(l=>!(i.id!=null&&l.id===i.id||i._id!=null&&l._id===i._id))),n._rawItems=o,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())}};var k=class{host;httpUrl;deviceId;options;socket;storage;accessToken;api;auth;store;handlers;signalHandlers;fileHandlers;_offlineQueue;reconnectAttempts;_attachedListeners;constructor(t="",e="",n={}){!t&&typeof window<"u"&&(t=window.location.host);let r="http:";t.startsWith("https://")?r="https:":t.startsWith("http://")?r="http:":typeof window<"u"&&(r=window.location.protocol),this.host=(t||"localhost").replace(/\/$/,"").replace(/^https?:\/\//,""),this.httpUrl=`${r}//${this.host}`,this.deviceId=e||"web_"+Math.random().toString(36).substr(2,8),this.options={timeout:15e3,chunkSize:65536,maxReconnect:5,autoRefreshToken:!0,debug:!1,...n},this.socket=null,this.storage=typeof localStorage<"u"?localStorage:{getItem:()=>null,setItem:()=>{},removeItem:()=>{}},this.accessToken=this.storage.getItem("dolphin_token"),this.api=new x(this),this.auth=new C(this),this.store=new M(this),this.handlers=new Map,this.signalHandlers=new Set,this.fileHandlers=new Set,this._offlineQueue=[],this.reconnectAttempts=0,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 new Promise((t,e)=>{let r=`${this.httpUrl.startsWith("https")?"wss:":"ws:"}//${this.host}/realtime?deviceId=${this.deviceId}`;console.log(`[Dolphin] Connecting to ${r}...`),this.socket=new WebSocket(r),this.socket.onopen=()=>{console.log(`[Dolphin] Connected as "${this.deviceId}" \u{1F42C}`),this.reconnectAttempts=0,this._flushOfflineQueue(),t()},this.socket.onmessage=i=>this._handleMessage(i.data),this.socket.onclose=()=>{console.warn("[Dolphin] Connection closed"),this._maybeReconnect()},this.socket.onerror=i=>{console.error("[Dolphin] WebSocket error:",i),e(i)}})}disconnect(){this.socket&&(this.socket.onclose=null,this.socket.close(),this.socket=null),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,r)=>{this._matchTopic(r,e.topic)&&n.forEach(i=>i(e.payload,e.topic))})}catch{this._dispatch("raw",t)}}_dispatch(t,e,n){let r=this.handlers.get(t);r&&r.forEach(i=>i(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("/"),r=e.split("/");if(n.length!==r.length&&!t.includes("#"))return!1;for(let i=0;i<n.length;i++){if(n[i]==="#")return!0;if(n[i]!=="+"&&n[i]!==r[i])return!1}return n.length===r.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})...`),setTimeout(()=>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="",r){let i;e instanceof Blob?i=await e.arrayBuffer():e instanceof ArrayBuffer?i=e:i=e.buffer||e;let o=new Uint8Array(i),l=this.options.chunkSize,w=Math.ceil(o.length/l);this._sendRaw({type:"FILE_UPLOAD_START",fileId:t,name:n,size:o.length,totalChunks:w,chunkSize:l});for(let T=0;T<w;T++){let h=o.slice(T*l,(T+1)*l),a=this._uint8ToBase64(h);this._sendRaw({type:"FILE_UPLOAD_CHUNK",fileId:t,chunkIndex:T,totalChunks:w,data:a}),r&&r(Math.round((T+1)/w*100)),T%10===0&&await new Promise(u=>setTimeout(u,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 R(p){function t(h){return h.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function e(h){let a=h.getAttribute("data-rt-template");if(!a)return null;if(typeof document<"u"&&!a.includes("<"))try{let u=document.querySelector(a);if(u)return u.innerHTML}catch{}return a}function n(h,a){if(!h.includes("{#if")&&!h.includes("{#each")){let u=h;for(let s in a){let c=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");u=u.replace(new RegExp(`\\{\\{${c}\\}\\}`,"g"),a[s]!==void 0&&a[s]!==null?a[s]:"")}return u}try{let u=b=>b.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"),s=`let out = "";
|
|
2
|
-
`,
|
|
3
|
-
`);let A=
|
|
4
|
-
`}else if(A.startsWith("{#if")){let
|
|
5
|
-
`}else if(A.startsWith("{:else if")){let
|
|
1
|
+
var DolphinModule=(()=>{var P=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var G=Object.prototype.hasOwnProperty;var tt=(m,t)=>{for(var e in t)P(m,e,{get:t[e],enumerable:!0})},et=(m,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Z(t))!G.call(m,r)&&r!==e&&P(m,r,{get:()=>t[r],enumerable:!(n=Y(t,r))||n.enumerable});return m};var nt=m=>et(P({},"__esModule",{value:!0}),m);var it={};tt(it,{DolphinClient:()=>k});var x=class{client;constructor(t){return this.client=t,this._createProxy([])}_createProxy(t){let e=t.join("/"),n=i=>this.request("GET",e,null,i);n.get=(i,o)=>typeof i=="string"?this.request("GET",i,null,o):this.request("GET",e,null,i),n.post=(i,o,u)=>typeof i=="string"?this.request("POST",i,o,u):this.request("POST",e,i,o),n.put=(i,o,u)=>typeof i=="string"?this.request("PUT",i,o,u):this.request("PUT",e,i,o),n.patch=(i,o,u)=>typeof i=="string"?this.request("PATCH",i,o,u):this.request("PATCH",e,i,o),n.del=(i,o)=>typeof i=="string"?this.request("DELETE",i,null,o):this.request("DELETE",e,null,i),n.request=(i,o,u,S)=>{let D=o?`${e}/${o.startsWith("/")?o.slice(1):o}`:e;return this.request(i,D,u,S)},n.requestDirect=(i,o,u,S)=>this.requestDirect(i,o,u,S);let r=["get","post","put","patch","del","request","requestDirect"];return new Proxy(n,{get:(i,o)=>typeof o=="string"&&!r.includes(o)?this._createProxy([...t,o]):i[o]})}async request(t,e,n=null,r={}){if(this.client.offline){let i=this.client.offline.isOnline,o=`${t.toUpperCase()}:${e}`;if(t.toUpperCase()==="GET")if(i)try{let u=await this.requestDirect(t,e,n,r);return await this.client.offline.setCache(o,u),u}catch(u){let S=await this.client.offline.getCache(o);if(S!=null)return S;throw u}else{let u=await this.client.offline.getCache(o);if(u!=null)return u;throw{status:503,data:{error:"Offline, no cache available"}}}else return i?this.requestDirect(t,e,n,r):(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,r)}async requestDirect(t,e,n=null,r={}){let i=r._isRetry===!0,o=e.startsWith("http://")||e.startsWith("https://")?e:`${this.client.httpUrl}${e.startsWith("/")?e:"/"+e}`;this.client.options.debug&&console.log("%c\u{1F680} [Dolphin API Request]:","color: #3b82f6; font-weight: bold;",t.toUpperCase(),e,n||"");let u=new AbortController,S=setTimeout(()=>u.abort(),this.client.options.timeout),D={"Content-Type":"application/json",...r.headers||{}};this.client.accessToken&&(D.Authorization=`Bearer ${this.client.accessToken}`);let g={...r};delete g._isRetry;try{let c=await fetch(o,{method:t,headers:D,signal:u.signal,...n?{body:JSON.stringify(n)}:{},...g});if(clearTimeout(S),c.status===401&&!i&&this.client.options.autoRefreshToken&&await this.client.auth._silentRefresh())return this.request(t,e,n,{...r,_isRetry:!0});let s=(c.headers.get("content-type")||"").includes("application/json")?await c.json():await c.text();if(!c.ok)throw{status:c.status,data:s};if(this.client.options.debug&&console.log("%c\u2705 [Dolphin API Success]:","color: #10b981; font-weight: bold;",t.toUpperCase(),e,s),s&&typeof s=="object"&&s.accessToken&&(this.client.setToken(s.accessToken),s.user&&(this.client.auth.user=s.user)),this.client.options.autoBroadcast&&["POST","PUT","PATCH","DELETE"].includes(t.toUpperCase())){let a=e.startsWith("/")?e.substring(1):e;this.client.publish(a,{method:t.toUpperCase(),payload:n,result:s})}return s}catch(c){throw clearTimeout(S),this.client.options.debug&&console.error("%c\u274C [Dolphin API Error]:","color: #ef4444; font-weight: bold;",t.toUpperCase(),e,c),c.name==="AbortError"?{status:408,data:{error:"Request timed out"}}:c}}};var C=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},r=await this.client.api.post("/api/auth/2fa/verify",n);return r.accessToken&&(this.client.setToken(r.accessToken),r.user&&(this.user=r.user)),r}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 M=class{client;data;listeners;subscribed;constructor(t){return this.client=t,this.data=new Map,this.listeners=new Set,this.subscribed=new Set,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,r="asc")=>(e._sort={key:n,direction:r},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()}`);e._rawItems=Array.isArray(n)?n:n.data||[],e.loading=!1,e.success=!0,e.error=null,this._applyTransform(e),this.subscribed.has(t)||(this.client.subscribe(`db:sync/${t.toLowerCase()}`,r=>{this._handleRemoteUpdate(t,r)}),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:r}=t._sort;e.sort((i,o)=>i[n]===o[n]?0:(i[n]>o[n]?1:-1)*(r==="asc"?1:-1))}t.items=e,this._notify()}_handleRemoteUpdate(t,e){let n=this.data.get(t);if(!n)return;let{type:r,data:i}=e,o=n._rawItems;r==="create"?o=[...o,i]:r==="update"?o=o.map(u=>u.id===i.id||u._id===i._id?{...u,...i}:u):r==="delete"&&(o=o.filter(u=>!(i.id!=null&&u.id===i.id||i._id!=null&&u._id===i._id))),n._rawItems=o,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())}};var k=class{host;httpUrl;deviceId;options;socket;storage;accessToken;api;auth;store;handlers;signalHandlers;fileHandlers;_offlineQueue;reconnectAttempts;_attachedListeners;constructor(t="",e="",n={}){!t&&typeof window<"u"&&(t=window.location.host);let r="http:";t.startsWith("https://")?r="https:":t.startsWith("http://")?r="http:":typeof window<"u"&&(r=window.location.protocol),this.host=(t||"localhost").replace(/\/$/,"").replace(/^https?:\/\//,""),this.httpUrl=`${r}//${this.host}`,this.deviceId=e||"web_"+Math.random().toString(36).substr(2,8),this.options={timeout:15e3,chunkSize:65536,maxReconnect:5,autoRefreshToken:!0,debug:!1,...n},this.socket=null,this.storage=typeof localStorage<"u"?localStorage:{getItem:()=>null,setItem:()=>{},removeItem:()=>{}},this.accessToken=this.storage.getItem("dolphin_token"),this.api=new x(this),this.auth=new C(this),this.store=new M(this),this.handlers=new Map,this.signalHandlers=new Set,this.fileHandlers=new Set,this._offlineQueue=[],this.reconnectAttempts=0,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 new Promise((t,e)=>{let r=`${this.httpUrl.startsWith("https")?"wss:":"ws:"}//${this.host}/realtime?deviceId=${this.deviceId}`;console.log(`[Dolphin] Connecting to ${r}...`),this.socket=new WebSocket(r),this.socket.onopen=()=>{console.log(`[Dolphin] Connected as "${this.deviceId}" \u{1F42C}`),this.reconnectAttempts=0,this._flushOfflineQueue(),t()},this.socket.onmessage=i=>this._handleMessage(i.data),this.socket.onclose=()=>{console.warn("[Dolphin] Connection closed"),this._maybeReconnect()},this.socket.onerror=i=>{console.error("[Dolphin] WebSocket error:",i),e(i)}})}disconnect(){this.socket&&(this.socket.onclose=null,this.socket.close(),this.socket=null),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,r)=>{this._matchTopic(r,e.topic)&&n.forEach(i=>i(e.payload,e.topic))})}catch{this._dispatch("raw",t)}}_dispatch(t,e,n){let r=this.handlers.get(t);r&&r.forEach(i=>i(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("/"),r=e.split("/");if(n.length!==r.length&&!t.includes("#"))return!1;for(let i=0;i<n.length;i++){if(n[i]==="#")return!0;if(n[i]!=="+"&&n[i]!==r[i])return!1}return n.length===r.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})...`),setTimeout(()=>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="",r){let i;e instanceof Blob?i=await e.arrayBuffer():e instanceof ArrayBuffer?i=e:i=e.buffer||e;let o=new Uint8Array(i),u=this.options.chunkSize,S=Math.ceil(o.length/u);this._sendRaw({type:"FILE_UPLOAD_START",fileId:t,name:n,size:o.length,totalChunks:S,chunkSize:u});for(let D=0;D<S;D++){let g=o.slice(D*u,(D+1)*u),c=this._uint8ToBase64(g);this._sendRaw({type:"FILE_UPLOAD_CHUNK",fileId:t,chunkIndex:D,totalChunks:S,data:c}),r&&r(Math.round((D+1)/S*100)),D%10===0&&await new Promise(l=>setTimeout(l,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 O(m){function t(g){return g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function e(g){let c=g.getAttribute("data-rt-template");if(!c)return null;if(typeof document<"u"&&!c.includes("<"))try{let l=document.querySelector(c);if(l)return l.innerHTML}catch{}return c}function n(g,c){if(!g.includes("{#if")&&!g.includes("{#each")){let l=g;for(let s in c){let a=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");l=l.replace(new RegExp(`\\{\\{${a}\\}\\}`,"g"),c[s]!==void 0&&c[s]!==null?c[s]:"")}return l}try{let l=p=>p.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t"),s=`let out = "";
|
|
2
|
+
`,a=0,y=/(\{\{([\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,b=[],d;for(;(d=y.exec(g))!==null;){let p=g.slice(a,d.index);p&&(s+=`out += "${l(p)}";
|
|
3
|
+
`);let A=d[0];if(A.startsWith("{{")){let _=d[2];s+=`out += (${_} !== undefined && ${_} !== null ? ${_} : "");
|
|
4
|
+
`}else if(A.startsWith("{#if")){let _=d[3];s+=`if (${_}) {
|
|
5
|
+
`}else if(A.startsWith("{:else if")){let _=d[4];s+=`} else if (${_}) {
|
|
6
6
|
`}else if(A.startsWith("{:else}"))s+=`} else {
|
|
7
7
|
`;else if(A.startsWith("{/if}"))s+=`}
|
|
8
|
-
`;else if(A.startsWith("{#each")){let
|
|
9
|
-
`,
|
|
10
|
-
`),s+=` for (let ${
|
|
11
|
-
`}else if(A.startsWith("{/each}")){let
|
|
8
|
+
`;else if(A.startsWith("{#each")){let _=d[5],T=d[6],v=d[7];b.push({indexVar:v}),s+=`if (typeof ${_} !== "undefined" && ${_} !== null && Array.isArray(${_})) {
|
|
9
|
+
`,v&&(s+=` let ${v} = 0;
|
|
10
|
+
`),s+=` for (let ${T} of ${_}) {
|
|
11
|
+
`}else if(A.startsWith("{/each}")){let _=b.pop();_&&_.indexVar&&(s+=` ${_.indexVar}++;
|
|
12
12
|
`),s+=` }
|
|
13
13
|
}
|
|
14
|
-
`}else if(A.startsWith("{")){let
|
|
15
|
-
`)}
|
|
14
|
+
`}else if(A.startsWith("{")){let _=d[8];_&&(s+=`out += (${_} !== undefined && ${_} !== null ? ${_} : "");
|
|
15
|
+
`)}a=y.lastIndex}let f=g.slice(a);f&&(s+=`out += "${l(f)}";
|
|
16
16
|
`),s+=`return out;
|
|
17
|
-
`;let
|
|
17
|
+
`;let w=`
|
|
18
18
|
with (context) {
|
|
19
19
|
try {
|
|
20
20
|
${s}
|
|
@@ -23,4 +23,4 @@ var DolphinModule=(()=>{var $=Object.defineProperty;var z=Object.getOwnPropertyD
|
|
|
23
23
|
return '';
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
`,S=a;return typeof Proxy<"u"&&a!==null&&typeof a=="object"&&(S=new Proxy(a,{has(b,A){return typeof A!="symbol"},get(b,A){if(A!==Symbol.unscopables){if(A in b)return b[A];if(typeof globalThis<"u"&&A in globalThis)return globalThis[A];if(typeof window<"u"&&A in window)return window[A]}}})),new Function("context",_)(S)}catch(u){console.error("[Dolphin Template Compiler Error]:",u);let s=h;for(let c in a){let g=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");s=s.replace(new RegExp(`\\{\\{${g}\\}\\}`,"g"),a[c]!==void 0&&a[c]!==null?a[c]:"")}return s}}function r(h){if(typeof document>"u")return h;try{let s=new DOMParser().parseFromString(h,"text/html").body,c=g=>{let d=g.tagName.toLowerCase();if(["script","iframe","object","embed","link","style","meta","applet","svg"].includes(d)){g.parentNode?.removeChild(g);return}let f=g.attributes;for(let m=f.length-1;m>=0;m--){let _=f[m].name.toLowerCase(),S=f[m].value.toLowerCase();(_.startsWith("on")||["src","href","data"].includes(_)&&(S.includes("javascript:")||S.includes("data:text/html")))&&g.removeAttribute(f[m].name)}Array.from(g.children).forEach(c)};return Array.from(s.children).forEach(c),s.innerHTML}catch{return h}}function i(h,a){if(h.nodeType!==a.nodeType){h.parentNode?.replaceChild(a.cloneNode(!0),h);return}if(h.nodeType===Node.TEXT_NODE){h.textContent!==a.textContent&&(h.textContent=a.textContent);return}if(h.nodeType===Node.ELEMENT_NODE){let u=h,s=a;if(u.tagName!==s.tagName){u.parentNode?.replaceChild(s.cloneNode(!0),u);return}let c=u.attributes,g=s.attributes;for(let y=c.length-1;y>=0;y--){let b=c[y].name;s.hasAttribute(b)||u.removeAttribute(b)}for(let y=0;y<g.length;y++){let b=g[y].name,A=g[y].value;u.getAttribute(b)!==A&&u.setAttribute(b,A)}u.tagName==="INPUT"||u.tagName==="TEXTAREA"?(u.value!==s.value&&(u.value=s.value),u.checked!==s.checked&&(u.checked=s.checked)):u.tagName==="SELECT"&&u.value!==s.value&&(u.value=s.value);let d=Array.from(u.childNodes),f=Array.from(s.childNodes),m=d.length,_=f.length,S=Math.max(m,_);for(let y=0;y<S;y++)y>=m?u.appendChild(f[y].cloneNode(!0)):y>=_?u.removeChild(d[y]):i(d[y],f[y])}}function o(h,a){if(typeof document>"u")return;let u=document.createElement(h.tagName);u.innerHTML=a;let s=Array.from(h.childNodes),c=Array.from(u.childNodes),g=s.length,d=c.length,f=Math.max(g,d);for(let m=0;m<f;m++)m>=g?h.appendChild(c[m].cloneNode(!0)):m>=d?h.removeChild(s[m]):i(s[m],c[m])}let l=new Map,w=!1;function T(h,a){l.set(h,a),w||(w=!0,(typeof requestAnimationFrame<"u"?requestAnimationFrame:s=>setTimeout(s,0))(()=>{l.forEach((s,c)=>{o(c,s)}),l.clear(),w=!1}))}p.setStoreState=function(h,a,u){this.uiStores=this.uiStores||new Map,this.uiStores.has(h)||this.uiStores.set(h,{});let s=this.uiStores.get(h);s[a]=u,this.options.debug&&console.log("%c\u{1F4BE} [Dolphin Store Update]:","color: #ec4899; font-weight: bold;",`${h}.${a}`,"=",u),typeof document<"u"&&document.querySelectorAll(`[data-store-read="${h}.${a}"]`).forEach(g=>{g.tagName==="INPUT"||g.tagName==="TEXTAREA"?g.type==="checkbox"?g.checked=!!u:g.value=u??"":g.textContent=u??""}),this.publish(`store/${h}`,s),typeof this._updateDOM=="function"&&this._updateDOM(`store/${h}`,s)},p.getStoreState=function(h,a){this.uiStores=this.uiStores||new Map;let u=this.uiStores.get(h);return u?u[a]:void 0},p._scanStoreBinds=function(){if(typeof document>"u")return;document.querySelectorAll("[data-store-write]").forEach(u=>{let s=u.getAttribute("data-store-write");if(s){let c=s.split(".");if(c.length===2){let g=c[0],d=c[1],f=u.type==="checkbox"?u.checked:u.value;this.uiStores=this.uiStores||new Map,this.uiStores.has(g)||this.uiStores.set(g,{});let m=this.uiStores.get(g);m[d]===void 0&&(m[d]=f)}}}),document.querySelectorAll("[data-store-read]").forEach(u=>{let s=u.getAttribute("data-store-read");if(s){let c=s.split(".");if(c.length===2){let g=c[0],d=c[1],f=this.getStoreState(g,d);f!=null&&(u.tagName==="INPUT"||u.tagName==="TEXTAREA"?u.type==="checkbox"?u.checked=!!f:u.value=f:u.textContent=f)}}})},p.getClosestContext=function(h,a){let u=h;for(;u;){if(u._rtContext){let s=u._rtContext;return a?s[a]:s}u=u.parentElement}return null},p._executeStoreAction=function(h,a){this.uiStores=this.uiStores||new Map;let u=new Proxy({},{has:(s,c)=>!0,get:(s,c)=>{if(typeof c=="string")return new Proxy({},{get:(g,d)=>{if(typeof d=="string")return this.getStoreState(c,d)},set:(g,d,f)=>typeof d=="string"?(this.setStoreState(c,d,f),!0):!1})}});try{new Function("ctx",`with(ctx) { ${h} }`)(u)}catch(s){console.error("%c[Dolphin Store Action Error]:","color: #ef4444; font-weight: bold;",s),a&&console.error("%cFailed Element:","color: #f97316; font-weight: bold;",a),console.error("%cFailed Expression:","color: #3b82f6; font-style: italic;",h)}},p._initDOMBinding=function(){if(this._domInitialized)return;this._domInitialized=!0;let h=["input","change","keyup","paste","blur"],a=new Map;h.forEach(s=>{this.addDomListener(document,s,c=>{if(!c.target||!c.target.getAttribute)return;let g=c.target.getAttribute("data-store-write");if(g){let _=g.split(".");if(_.length===2){let S=_[0],y=_[1],b=c.target.type==="checkbox"?c.target.checked:c.target.value;this.setStoreState(S,y,b)}}let d=c.target.getAttribute("data-rt-validate"),f=c.target.name;if(d&&f&&typeof this.validateField=="function"){let _=c.target.closest("form"),S=_?Object.fromEntries(new FormData(_).entries()):{},y=this.validateField(c.target.value,d,S);y?(c.target.classList.add("invalid"),this.publish(`errors/${f}`,y)):(c.target.classList.remove("invalid"),this.publish(`errors/${f}`,""))}let m=c.target.getAttribute("data-rt-push");if(m){let _=c.target.getAttribute("data-rt-debounce"),S=_?parseInt(_,10):0,y=()=>{let b={name:c.target.name,value:c.target.value};this.pubPush(m,b)};if(S>0){a.has(c.target)&&clearTimeout(a.get(c.target));let b=setTimeout(y,S);a.set(c.target,b)}else y()}})}),this.addDomListener(document,"submit",async s=>{if(!s.target||!s.target.getAttribute)return;let c=s.target.getAttribute("data-rt-submit"),g=s.target.getAttribute("data-api-submit");if(c||g){let d=s.target.querySelectorAll("[data-rt-validate]"),f=!0;if(d.length>0&&typeof this.validateField=="function"){let y=Object.fromEntries(new FormData(s.target).entries());d.forEach(b=>{let A=b.getAttribute("data-rt-validate"),E=b.name;if(A&&E){let v=this.validateField(b.value,A,y);v?(f=!1,b.classList.add("invalid"),this.publish(`errors/${E}`,v)):(b.classList.remove("invalid"),this.publish(`errors/${E}`,""))}})}if(!f){s.preventDefault(),s.stopPropagation();return}s.preventDefault();let m=this.getClosestContext(s.target)||{},_=new FormData(s.target),S=Object.fromEntries(_.entries());if(c){let y=c;for(let b in m){let A=t(b);y=y.replace(new RegExp(`\\{\\{${A}\\}\\}`,"g"),m[b]!==void 0&&m[b]!==null?m[b]:"")}this.publish(y,S)}else if(g){let y=g;for(let v in m){let D=t(v);y=y.replace(new RegExp(`\\{\\{${D}\\}\\}`,"g"),m[v]!==void 0&&m[v]!==null?m[v]:"")}let b=y.trim().split(" "),A=b.length>1?b[0].toUpperCase():"POST",E=b.length>1?b[1]:b[0];try{let v=await this.api.request(A,E,S),D=s.target.getAttribute("data-api-result");D&&this._updateDOM(D,v);let L=s.target.getAttribute("data-api-redirect");L&&(window.location.href=L),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 c=>{if(!c.target||!c.target.closest)return;let g=c.target.closest(`[data-rt-${s}]`),d=c.target.closest(`[data-api-${s}]`);if(g){s==="submit"&&c.preventDefault();let m=g.getAttribute(`data-rt-${s}`),_=g.getAttribute("data-rt-payload"),S=this.getClosestContext(g)||{},y={};if(_){let b=_;for(let A in S){let E=t(A);b=b.replace(new RegExp(`\\{\\{${E}\\}\\}`,"g"),S[A]!==void 0&&S[A]!==null?S[A]:"")}try{y=JSON.parse(b)}catch{y={}}}this.publish(m,y)}if(d){s==="submit"&&c.preventDefault();let m=d.getAttribute(`data-api-${s}`),_=d.getAttribute("data-api-payload"),S=this.getClosestContext(d)||{},y=m.trim().split(" "),b=y.length>1?y[0].toUpperCase():"POST",A=y.length>1?y[1]:y[0],E=null;if(_){let v=_;for(let D in S){let L=t(D);v=v.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),S[D]!==void 0&&S[D]!==null?S[D]:"")}try{E=JSON.parse(v)}catch{E=null}}try{let v=await this.api.request(b,A,E),D=d.getAttribute("data-api-result");D&&this._updateDOM(D,v);let L=d.getAttribute("data-api-redirect");L&&(window.location.href=L),d.hasAttribute("data-api-reload")&&window.location.reload()}catch(v){console.error(`[Dolphin] API ${s} Error:`,v)}}let f=c.target.closest(`[data-store-${s}]`);if(f){s==="submit"&&c.preventDefault();let m=f.getAttribute(`data-store-${s}`);m&&this._executeStoreAction(m,f)}})}),this.subscribe("#",(s,c)=>{this._updateDOM(c,s)}),this._scanAndFetchAPIBinds(),this._scanStoreBinds()},p._scanAndFetchAPIBinds=async function(){if(typeof document>"u")return;let h=document.querySelectorAll("[data-api-get]");for(let a of Array.from(h)){let u=a.getAttribute("data-api-get");if(u)try{let s=await this.api.get(u),c=a.getAttribute("data-api-store");if(c){let d=c.split(".");d.length===2&&this.setStoreState(d[0],d[1],s)}let g=a.getAttribute("data-rt-bind");if(g&&!c)this._updateDOM(g,s);else if(!c){let d=e(a);if(d&&typeof s=="object"&&s!==null)if(Array.isArray(s)){let f="";for(let m of s)f+=n(d,m);T(a,f)}else T(a,n(d,s));else a.tagName==="INPUT"||a.tagName==="TEXTAREA"?a.value=typeof s=="object"?s.value!==void 0?s.value:"":s:a.innerHTML=typeof s=="object"?s.html||s.text||JSON.stringify(s):s}}catch(s){console.error("[Dolphin] API Get Error:",s)}}},p._updateDOM=function(h,a){if(typeof document>"u")return;document.querySelectorAll(`[data-rt-bind="${h}"]`).forEach(s=>{if(s.getAttribute("data-rt-type")==="context"&&typeof a=="object"&&a!==null){s._rtContext=a;let g=d=>{if(d.hasAttribute("data-rt-text")){let f=d.getAttribute("data-rt-text");f&&a[f]!==void 0&&a[f]!==null&&(d.textContent=a[f])}if(d.hasAttribute("data-rt-html")){let f=d.getAttribute("data-rt-html");f&&a[f]!==void 0&&a[f]!==null&&(d.innerHTML=r(a[f]))}if(d.hasAttribute("data-rt-attr")){let f=d.getAttribute("data-rt-attr");f&&f.split(",").forEach(m=>{let _=m.split(":");if(_.length===2){let S=_[0].trim(),y=_[1].trim();S&&y&&a[y]!==void 0&&a[y]!==null&&d.setAttribute(S,a[y])}})}if(d.hasAttribute("data-rt-class")){let f=d.getAttribute("data-rt-class");f&&f.split(",").forEach(m=>{let _=m.split(":");if(_.length===2){let S=_[0].trim(),y=_[1].trim(),b=S.split(/\s+/).filter(Boolean);a[y]?b.forEach(A=>d.classList.add(A)):b.forEach(A=>d.classList.remove(A))}})}if(d.hasAttribute("data-rt-if")){let f=d.getAttribute("data-rt-if");f&&(a[f]?d.style.display="":d.style.display="none")}if(d.hasAttribute("data-rt-hide")){let f=d.getAttribute("data-rt-hide");f&&(a[f]?d.style.display="none":d.style.display="")}};g(s),s.querySelectorAll("[data-rt-text], [data-rt-html], [data-rt-attr], [data-rt-class], [data-rt-if], [data-rt-hide]").forEach(g);return}let c=e(s);if(c&&typeof a=="object"&&a!==null){if(Array.isArray(a)){let g="";for(let d of a)g+=n(c,d);T(s,g)}else T(s,n(c,a));return}s.tagName==="INPUT"||s.tagName==="TEXTAREA"?s.value=typeof a=="object"?a.value!==void 0?a.value:"":a:s.innerHTML=typeof a=="object"?a.html||a.text||JSON.stringify(a):a})}}var I=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 i=this.db.transaction("cache","readonly").objectStore("cache").get(t);i.onsuccess=()=>e(i.result?i.result.data:null),i.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 r=this.db.transaction("cache","readwrite");r.objectStore("cache").put({data:e,timestamp:Date.now()},t),r.oncomplete=()=>n()}catch{n()}})}async queueMutation(t,e,n){let r={method:t,path:e,payload:n,timestamp:Date.now()};if(!this.db){this.memoryMutations.push(r);return}return new Promise(i=>{try{let o=this.db.transaction("mutations","readwrite");o.objectStore("mutations").add(r),o.oncomplete=()=>i()}catch{i()}})}async getMutations(){return this.db?new Promise(t=>{try{let r=this.db.transaction("mutations","readonly").objectStore("mutations").getAll();r.onsuccess=()=>t(r.result||[]),r.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()}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 q(p){p._initOffline=function(){this.offline=new I(this)}}function Y(p,t,e){let n=t.split(",");for(let r of n){let i=r.trim().split(":"),o=i[0],l=i[1];if(o==="required"){if(!p||p.trim()==="")return"This field is required"}else if(o==="email"){if(p&&p.trim()!==""&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(p))return"Please enter a valid email address"}else if(o==="min"){let w=parseInt(l,10);if(!p||p.length<w)return`Must be at least ${w} characters`}else if(o==="match"&&e&&p!==e[l])return`Must match ${l}`}return null}function O(p){p.validateField=Y}function N(p){p.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"})},p.staggerListItems=function(t,e,n=50){if(typeof document>"u")return;t.querySelectorAll(e).forEach((i,o)=>{i.style.animationDelay=`${o*n}ms`,i.classList.add("staggered-item")})}}function U(p){p._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 i=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(i.length===0)return;let o=i[0],l=i[i.length-1];t.shiftKey?document.activeElement===o&&(l.focus(),t.preventDefault()):document.activeElement===l&&(o.focus(),t.preventDefault())})}),this.addDomListener(document,"keydown",t=>{if(!["ArrowUp","ArrowDown","Enter"].includes(t.key))return;document.querySelectorAll("[data-rt-keynav]").forEach(n=>{let r=Array.from(n.children);if(r.length===0)return;let i=r.findIndex(o=>o.classList.contains("active")||document.activeElement===o);t.key==="ArrowDown"?(i=(i+1)%r.length,r[i].focus(),r.forEach((o,l)=>{l===i?o.classList.add("active"):o.classList.remove("active")}),t.preventDefault()):t.key==="ArrowUp"?(i=(i-1+r.length)%r.length,r[i].focus(),r.forEach((o,l)=>{l===i?o.classList.add("active"):o.classList.remove("active")}),t.preventDefault()):t.key==="Enter"&&i!==-1&&(r[i].click(),t.preventDefault())})}))},p.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 F(p){p._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 r=JSON.parse(e.textContent||"{}");this.i18n.dicts[n]={...this.i18n.dicts[n]||{},...r}}catch(r){console.warn("[Dolphin i18n] Failed to parse dictionary for locale:",n,r)}}),!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 r=n.getAttribute("data-i18n-switch");r&&this.setLocale(r)}}),this.translateDOM()},p.setLocale=function(t){this.i18n=this.i18n||{locale:"en",dicts:{}},this.i18n.locale=t,this.translateDOM(),this.publish("i18n/locale",t)},p.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(r=>{let i=r.getAttribute("data-i18n-key");if(!i)return;let o=i.split(".").reduce((w,T)=>w?w[T]:null,e);o==null&&(o=i);let l=r.getAttribute("data-i18n-params");if(l)try{let w=JSON.parse(l),T=h=>h.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");for(let h in w){let a=T(h);o=o.replace(new RegExp(`\\{\\{${a}\\}\\}`,"g"),w[h])}}catch{}r.tagName==="INPUT"||r.tagName==="TEXTAREA"?r.placeholder=o:r.textContent=o})}}function H(p){p._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"),r=t.dataTransfer.getData("text/plain");if(n&&r)try{let i=JSON.parse(r);this.publish(n,i)}catch{this.publish(n,{value:r})}}),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 i=Array.from(e.querySelectorAll("[data-drag]:not(.dragging)")).find(o=>{let l=o.getBoundingClientRect();return t.clientY-l.top-l.height/2<0});i?e.insertBefore(n,i):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 i=Array.from(e.querySelectorAll("[data-drag]")).map((o,l)=>{let w=o.getAttribute("data-drag");try{return{index:l,payload:JSON.parse(w||"{}")}}catch{return{index:l,payload:w}}});this.publish(n,i)}))}}function W(p){p._initCollab=function(){typeof document>"u"||(this.addDomListener(document,"mousemove",t=>{document.querySelectorAll("[data-rt-cursor-share]").forEach(n=>{let r=n.getAttribute("data-rt-cursor-share");if(!r)return;let i=n.getBoundingClientRect(),o=(t.clientX-i.left)/i.width,l=(t.clientY-i.top)/i.height,w=Date.now();(!n._lastSent||w-n._lastSent>50)&&(n._lastSent=w,this.pubPush(`collab/${r}/cursor/${this.deviceId}`,{deviceId:this.deviceId,x:o,y:l}))})}),this.addDomListener(document,"input",t=>{let e=t.target.getAttribute("data-rt-typing");if(!e)return;let n=e,r=i=>{this.pubPush(`collab/${n}/typing/${this.deviceId}`,{deviceId:this.deviceId,typing:i})};t.target._isTyping||(t.target._isTyping=!0,r(!0)),t.target._typingTimer&&clearTimeout(t.target._typingTimer),t.target._typingTimer=setTimeout(()=>{t.target._isTyping=!1,r(!1)},2e3)}),this.addDomListener(document,"input",t=>{let e=t.target.getAttribute("data-rt-crdt");if(!e)return;let n=e,r=t.target.value,i=Date.now();this.publish(`collab/${n}/crdt`,{deviceId:this.deviceId,value:r,timestamp:i,cursorPos:t.target.selectionStart})}),this.subscribe("collab/+/cursor/+",(t,e)=>{let n=e.split("/"),r=n[1],i=n[3];if(i===this.deviceId)return;let o=document.querySelector(`[data-rt-cursor-share="${r}"]`);if(!o)return;let l=o.querySelector(`.rt-cursor-${i}`);l||(l=document.createElement("div"),l.className=`rt-cursor rt-cursor-${i}`,l.style.position="absolute",l.style.width="10px",l.style.height="10px",l.style.borderRadius="50%",l.style.backgroundColor="#"+Math.floor(Math.random()*16777215).toString(16),l.style.pointerEvents="none",o.appendChild(l));let w=o.getBoundingClientRect();l.style.left=t.x*w.width+"px",l.style.top=t.y*w.height+"px"}),this.subscribe("collab/+/crdt",(t,e)=>{if(t.deviceId===this.deviceId)return;let r=e.split("/")[1];document.querySelectorAll(`[data-rt-crdt="${r}"]`).forEach(o=>{if(!o._lastUpdate||t.timestamp>o._lastUpdate){o._lastUpdate=t.timestamp;let l=o.selectionStart;o.value=t.value,document.activeElement===o&&o.setSelectionRange(l,l)}})}))}}function j(p){p.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}},p.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 r="=".repeat((4-t.length%4)%4),i=(t+r).replace(/\-/g,"+").replace(/_/g,"/"),o=window.atob(i),l=new Uint8Array(o.length);for(let w=0;w<o.length;++w)l[w]=o.charCodeAt(w);n=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:l})}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 P=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,r)=>{let i=document.createEvent("Event");i.initEvent(r,!0,!0),n.dispatchEvent(i)}}}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(r=>r(e))}static simulateChange(t,e){t.value=e;let n={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.change||[]).forEach(i=>i(n))}};function B(p){p.testing=P}R(k.prototype);q(k.prototype);O(k.prototype);N(k.prototype);U(k.prototype);F(k.prototype);H(k.prototype);W(k.prototype);j(k.prototype);B(k.prototype);typeof window<"u"&&(window.DolphinClient=k,document.addEventListener("DOMContentLoaded",()=>{if(!window.dolphin){let p=document.querySelector('script[src*="dolphin-client"]'),t=p?p.getAttribute("data-debug")==="true":!1,e=new k(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 X(Z);})();
|
|
26
|
+
`,E=c;return typeof Proxy<"u"&&c!==null&&typeof c=="object"&&(E=new Proxy(c,{has(p,A){return typeof A!="symbol"},get(p,A){if(A!==Symbol.unscopables){if(A in p)return p[A];if(typeof globalThis<"u"&&A in globalThis)return globalThis[A];if(typeof window<"u"&&A in window)return window[A]}}})),new Function("context",w)(E)}catch(l){console.error("[Dolphin Template Compiler Error]:",l);let s=g;for(let a in c){let y=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");s=s.replace(new RegExp(`\\{\\{${y}\\}\\}`,"g"),c[a]!==void 0&&c[a]!==null?c[a]:"")}return s}}function r(g){if(typeof document>"u")return g;try{let s=new DOMParser().parseFromString(g,"text/html").body,a=y=>{let b=y.tagName.toLowerCase();if(["script","iframe","object","embed","link","style","meta","applet","svg"].includes(b)){y.parentNode?.removeChild(y);return}let d=y.attributes;for(let f=d.length-1;f>=0;f--){let w=d[f].name.toLowerCase(),E=d[f].value.toLowerCase();(w.startsWith("on")||["src","href","data"].includes(w)&&(E.includes("javascript:")||E.includes("data:text/html")))&&y.removeAttribute(d[f].name)}Array.from(y.children).forEach(a)};return Array.from(s.children).forEach(a),s.innerHTML}catch{return g}}function i(g,c){if(g.nodeType!==c.nodeType){g.parentNode?.replaceChild(c.cloneNode(!0),g);return}if(g.nodeType===Node.TEXT_NODE){g.textContent!==c.textContent&&(g.textContent=c.textContent);return}if(g.nodeType===Node.ELEMENT_NODE){let l=g,s=c;if(l.tagName!==s.tagName){l.parentNode?.replaceChild(s.cloneNode(!0),l);return}let a=l.attributes,y=s.attributes;for(let h=a.length-1;h>=0;h--){let p=a[h].name;s.hasAttribute(p)||l.removeAttribute(p)}for(let h=0;h<y.length;h++){let p=y[h].name,A=y[h].value;l.getAttribute(p)!==A&&l.setAttribute(p,A)}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 b=Array.from(l.childNodes),d=Array.from(s.childNodes),f=b.length,w=d.length,E=Math.max(f,w);for(let h=0;h<E;h++)h>=f?l.appendChild(d[h].cloneNode(!0)):h>=w?l.removeChild(b[h]):i(b[h],d[h])}}function o(g,c){if(typeof document>"u")return;let l=document.createElement(g.tagName);l.innerHTML=c;let s=Array.from(g.childNodes),a=Array.from(l.childNodes),y=s.length,b=a.length,d=Math.max(y,b);for(let f=0;f<d;f++)f>=y?g.appendChild(a[f].cloneNode(!0)):f>=b?g.removeChild(s[f]):i(s[f],a[f])}let u=new Map,S=!1;function D(g,c){u.set(g,c),S||(S=!0,(typeof requestAnimationFrame<"u"?requestAnimationFrame:s=>setTimeout(s,0))(()=>{u.forEach((s,a)=>{o(a,s)}),u.clear(),S=!1}))}m.setStoreState=function(g,c,l){this.uiStores=this.uiStores||new Map,this.uiStores.has(g)||this.uiStores.set(g,{});let s=this.uiStores.get(g);s[c]=l,this.options.debug&&console.log("%c\u{1F4BE} [Dolphin Store Update]:","color: #ec4899; font-weight: bold;",`${g}.${c}`,"=",l),typeof document<"u"&&document.querySelectorAll(`[data-store-read="${g}.${c}"]`).forEach(y=>{y.tagName==="INPUT"||y.tagName==="TEXTAREA"?y.type==="checkbox"?y.checked=!!l:y.value=l??"":y.textContent=l??""}),this.publish(`store/${g}`,s),typeof this._updateDOM=="function"&&this._updateDOM(`store/${g}`,s)},m.getStoreState=function(g,c){this.uiStores=this.uiStores||new Map;let l=this.uiStores.get(g);return l?l[c]:void 0},m._scanStoreBinds=function(){if(typeof document>"u")return;document.querySelectorAll("[data-store-write]").forEach(l=>{let s=l.getAttribute("data-store-write");if(s){let a=s.split(".");if(a.length===2){let y=a[0],b=a[1],d=l.type==="checkbox"?l.checked:l.value;this.uiStores=this.uiStores||new Map,this.uiStores.has(y)||this.uiStores.set(y,{});let f=this.uiStores.get(y);f[b]===void 0&&(f[b]=d)}}}),document.querySelectorAll("[data-store-read]").forEach(l=>{let s=l.getAttribute("data-store-read");if(s){let a=s.split(".");if(a.length===2){let y=a[0],b=a[1],d=this.getStoreState(y,b);d!=null&&(l.tagName==="INPUT"||l.tagName==="TEXTAREA"?l.type==="checkbox"?l.checked=!!d:l.value=d:l.textContent=d)}}})},m.getClosestContext=function(g,c){let l=g;for(;l;){if(l._rtContext){let s=l._rtContext;return c?s[c]:s}l=l.parentElement}return null},m._executeStoreAction=function(g,c){this.uiStores=this.uiStores||new Map;let l=new Proxy({},{has:(s,a)=>!0,get:(s,a)=>{if(typeof a=="string")return new Proxy({},{get:(y,b)=>{if(typeof b=="string")return this.getStoreState(a,b)},set:(y,b,d)=>typeof b=="string"?(this.setStoreState(a,b,d),!0):!1})}});try{new Function("ctx",`with(ctx) { ${g} }`)(l)}catch(s){console.error("%c[Dolphin Store Action Error]:","color: #ef4444; font-weight: bold;",s),c&&console.error("%cFailed Element:","color: #f97316; font-weight: bold;",c),console.error("%cFailed Expression:","color: #3b82f6; font-style: italic;",g)}},m._initDOMBinding=function(){if(this._domInitialized)return;this._domInitialized=!0;let g=["input","change","keyup","paste","blur"],c=new Map;g.forEach(s=>{this.addDomListener(document,s,a=>{if(!a.target||!a.target.getAttribute)return;let y=a.target.getAttribute("data-store-write");if(y){let w=y.split(".");if(w.length===2){let E=w[0],h=w[1],p=a.target.type==="checkbox"?a.target.checked:a.target.value;this.setStoreState(E,h,p)}}let b=a.target.getAttribute("data-rt-validate"),d=a.target.name;if(b&&d&&typeof this.validateField=="function"){let w=a.target.closest("form"),E=w?Object.fromEntries(new FormData(w).entries()):{},h=this.validateField(a.target.value,b,E);h?(a.target.classList.add("invalid"),this.publish(`errors/${d}`,h)):(a.target.classList.remove("invalid"),this.publish(`errors/${d}`,""))}let f=a.target.getAttribute("data-rt-push");if(f){let w=a.target.getAttribute("data-rt-debounce"),E=w?parseInt(w,10):0,h=()=>{let p={name:a.target.name,value:a.target.value};this.pubPush(f,p)};if(E>0){c.has(a.target)&&clearTimeout(c.get(a.target));let p=setTimeout(h,E);c.set(a.target,p)}else h()}})}),this.addDomListener(document,"submit",async s=>{if(!s.target||!s.target.getAttribute)return;let a=s.target.getAttribute("data-rt-submit"),y=s.target.getAttribute("data-api-submit");if(a||y){let b=s.target.querySelectorAll("[data-rt-validate]"),d=!0;if(b.length>0&&typeof this.validateField=="function"){let h=Object.fromEntries(new FormData(s.target).entries());b.forEach(p=>{let A=p.getAttribute("data-rt-validate"),_=p.name;if(A&&_){let T=this.validateField(p.value,A,h);T?(d=!1,p.classList.add("invalid"),this.publish(`errors/${_}`,T)):(p.classList.remove("invalid"),this.publish(`errors/${_}`,""))}})}if(!d){s.preventDefault(),s.stopPropagation();return}s.preventDefault();let f=this.getClosestContext(s.target)||{},w=new FormData(s.target),E=Object.fromEntries(w.entries());if(a){let h=a;for(let p in f){let A=t(p);h=h.replace(new RegExp(`\\{\\{${A}\\}\\}`,"g"),f[p]!==void 0&&f[p]!==null?f[p]:"")}this.publish(h,E)}else if(y){let h=y;for(let T in f){let v=t(T);h=h.replace(new RegExp(`\\{\\{${v}\\}\\}`,"g"),f[T]!==void 0&&f[T]!==null?f[T]:"")}let p=h.trim().split(" "),A=p.length>1?p[0].toUpperCase():"POST",_=p.length>1?p[1]:p[0];try{let T=await this.api.request(A,_,E),v=s.target.getAttribute("data-api-result");v&&this._updateDOM(v,T);let L=s.target.getAttribute("data-api-redirect");L&&(window.location.href=L),s.target.hasAttribute("data-api-reload")&&window.location.reload()}catch(T){console.error("[Dolphin] API Submit Error:",T)}}}}),["click","change","submit","input","keydown","keyup","dblclick","focus","blur","mouseenter","mouseleave"].forEach(s=>{this.addDomListener(document,s,async a=>{if(!a.target||!a.target.closest)return;let y=a.target.closest(`[data-rt-${s}]`),b=a.target.closest(`[data-api-${s}]`);if(y){s==="submit"&&a.preventDefault();let f=y.getAttribute(`data-rt-${s}`),w=y.getAttribute("data-rt-payload"),E=this.getClosestContext(y)||{},h={};if(w){let p=w;for(let A in E){let _=t(A);p=p.replace(new RegExp(`\\{\\{${_}\\}\\}`,"g"),E[A]!==void 0&&E[A]!==null?E[A]:"")}try{h=JSON.parse(p)}catch{h={}}}this.publish(f,h)}if(b){s==="submit"&&a.preventDefault();let f=b.getAttribute(`data-api-${s}`),w=b.getAttribute("data-api-payload"),E=this.getClosestContext(b)||{},h=f.trim().split(" "),p=h.length>1?h[0].toUpperCase():"POST",A=h.length>1?h[1]:h[0],_=null;if(w){let T=w;for(let v in E){let L=t(v);T=T.replace(new RegExp(`\\{\\{${L}\\}\\}`,"g"),E[v]!==void 0&&E[v]!==null?E[v]:"")}try{_=JSON.parse(T)}catch{_=null}}try{let T=await this.api.request(p,A,_),v=b.getAttribute("data-api-result");v&&this._updateDOM(v,T);let L=b.getAttribute("data-api-redirect");L&&(window.location.href=L),b.hasAttribute("data-api-reload")&&window.location.reload()}catch(T){console.error(`[Dolphin] API ${s} Error:`,T)}}let d=a.target.closest(`[data-store-${s}]`);if(d){s==="submit"&&a.preventDefault();let f=d.getAttribute(`data-store-${s}`);f&&this._executeStoreAction(f,d)}})}),this.subscribe("#",(s,a)=>{this._updateDOM(a,s)}),this._scanAndFetchAPIBinds(),this._scanStoreBinds()},m._scanAndFetchAPIBinds=async function(){if(typeof document>"u")return;let g=document.querySelectorAll("[data-api-get]");for(let c of Array.from(g)){let l=c.getAttribute("data-api-get");if(l)try{let s=await this.api.get(l),a=c.getAttribute("data-api-store");if(a){let b=a.split(".");b.length===2&&this.setStoreState(b[0],b[1],s)}let y=c.getAttribute("data-rt-bind");if(y&&!a)this._updateDOM(y,s);else if(!a){let b=e(c);if(b&&typeof s=="object"&&s!==null){let d=this._applyDeclarativeDirectives(c,s);if(Array.isArray(d)){let f="";for(let w of d)f+=n(b,w);D(c,f)}else D(c,n(b,d))}else c.tagName==="INPUT"||c.tagName==="TEXTAREA"?c.value=typeof s=="object"?s.value!==void 0?s.value:"":s:c.innerHTML=typeof s=="object"?s.html||s.text||JSON.stringify(s):s}}catch(s){console.error("[Dolphin] API Get Error:",s)}}},m._applyDeclarativeDirectives=function(g,c){let l=c;if(typeof c=="object"&&c!==null){let s=a=>{let y=[...a],b=g.getAttribute("data-rt-filter");if(b){let w=b.split("==");if(w.length===2){let E=w[0].trim(),h=w[1].trim(),p,A=h.split(".");A.length===2?p=this.getStoreState(A[0],A[1]):p=c[h]!==void 0?c[h]:this.getStoreState("app",h),p!=null&&p!==""&&(y=y.filter(_=>_[E]===p))}}let d=g.getAttribute("data-rt-search");if(d){let w=d.split("==");if(w.length===2){let E=w[0].trim(),h=w[1].trim(),p,A=h.split(".");if(A.length===2?p=this.getStoreState(A[0],A[1]):p=c[h]!==void 0?c[h]:this.getStoreState("app",h),p!=null&&p!==""){let _=String(p).toLowerCase();y=y.filter(T=>{let v=T[E];return v!=null&&String(v).toLowerCase().includes(_)})}}}let f=g.getAttribute("data-rt-sort");if(f){let w,E=f.split(".");if(E.length===2?w=this.getStoreState(E[0],E[1]):w=c[f]!==void 0?c[f]:this.getStoreState("app",f),w&&w!=="")if(w==="popular")y.sort((h,p)=>{let A=h.rating?.rate||h.rate||0;return(p.rating?.rate||p.rate||0)-A});else{let h="",p="asc";w.endsWith("-low")||w.endsWith("-asc")?(h=w.replace("-low","").replace("-asc",""),p="asc"):(w.endsWith("-high")||w.endsWith("-desc"))&&(h=w.replace("-high","").replace("-desc",""),p="desc"),h&&y.sort((A,_)=>{let T=(J,Q)=>Q.split(".").reduce((N,X)=>N&&N[X],J),v=T(A,h),L=T(_,h);if(v===void 0&&(v=A[h]),L===void 0&&(L=_[h]),typeof v=="string"&&typeof L=="string")return p==="asc"?v.localeCompare(L):L.localeCompare(v);let $=Number(v),I=Number(L);return!isNaN($)&&!isNaN(I)?p==="asc"?$-I:I-$:0})}}return y};if(Array.isArray(c))l=s(c);else{let a="";for(let y in c)if(Array.isArray(c[y])){a=y;break}if(a){let y=s(c[a]);l={...c,[a]:y}}}}return l},m._updateDOM=function(g,c){if(typeof document>"u")return;document.querySelectorAll(`[data-rt-bind="${g}"]`).forEach(s=>{let a=this._applyDeclarativeDirectives(s,c);if(s.getAttribute("data-rt-type")==="context"&&typeof a=="object"&&a!==null){s._rtContext=a;let b=d=>{if(d.hasAttribute("data-rt-text")){let f=d.getAttribute("data-rt-text");f&&a[f]!==void 0&&a[f]!==null&&(d.textContent=a[f])}if(d.hasAttribute("data-rt-html")){let f=d.getAttribute("data-rt-html");f&&a[f]!==void 0&&a[f]!==null&&(d.innerHTML=r(a[f]))}if(d.hasAttribute("data-rt-attr")){let f=d.getAttribute("data-rt-attr");f&&f.split(",").forEach(w=>{let E=w.split(":");if(E.length===2){let h=E[0].trim(),p=E[1].trim();h&&p&&a[p]!==void 0&&a[p]!==null&&d.setAttribute(h,a[p])}})}if(d.hasAttribute("data-rt-class")){let f=d.getAttribute("data-rt-class");f&&f.split(",").forEach(w=>{let E=w.split(":");if(E.length===2){let h=E[0].trim(),p=E[1].trim(),A=h.split(/\s+/).filter(Boolean);a[p]?A.forEach(_=>d.classList.add(_)):A.forEach(_=>d.classList.remove(_))}})}if(d.hasAttribute("data-rt-if")){let f=d.getAttribute("data-rt-if");f&&(a[f]?d.style.display="":d.style.display="none")}if(d.hasAttribute("data-rt-hide")){let f=d.getAttribute("data-rt-hide");f&&(a[f]?d.style.display="none":d.style.display="")}};b(s),s.querySelectorAll("[data-rt-text], [data-rt-html], [data-rt-attr], [data-rt-class], [data-rt-if], [data-rt-hide]").forEach(b);return}let y=e(s);if(y&&typeof a=="object"&&a!==null){if(Array.isArray(a)){let b="";for(let d of a)b+=n(y,d);D(s,b)}else D(s,n(y,a));return}s.tagName==="INPUT"||s.tagName==="TEXTAREA"?s.value=typeof a=="object"?a.value!==void 0?a.value:"":a:s.innerHTML=typeof a=="object"?a.html||a.text||JSON.stringify(a):a})}}var R=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 i=this.db.transaction("cache","readonly").objectStore("cache").get(t);i.onsuccess=()=>e(i.result?i.result.data:null),i.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 r=this.db.transaction("cache","readwrite");r.objectStore("cache").put({data:e,timestamp:Date.now()},t),r.oncomplete=()=>n()}catch{n()}})}async queueMutation(t,e,n){let r={method:t,path:e,payload:n,timestamp:Date.now()};if(!this.db){this.memoryMutations.push(r);return}return new Promise(i=>{try{let o=this.db.transaction("mutations","readwrite");o.objectStore("mutations").add(r),o.oncomplete=()=>i()}catch{i()}})}async getMutations(){return this.db?new Promise(t=>{try{let r=this.db.transaction("mutations","readonly").objectStore("mutations").getAll();r.onsuccess=()=>t(r.result||[]),r.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()}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 U(m){m._initOffline=function(){this.offline=new R(this)}}function st(m,t,e){let n=t.split(",");for(let r of n){let i=r.trim().split(":"),o=i[0],u=i[1];if(o==="required"){if(!m||m.trim()==="")return"This field is required"}else if(o==="email"){if(m&&m.trim()!==""&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(m))return"Please enter a valid email address"}else if(o==="min"){let S=parseInt(u,10);if(!m||m.length<S)return`Must be at least ${S} characters`}else if(o==="match"&&e&&m!==e[u])return`Must match ${u}`}return null}function W(m){m.validateField=st}function F(m){m.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"})},m.staggerListItems=function(t,e,n=50){if(typeof document>"u")return;t.querySelectorAll(e).forEach((i,o)=>{i.style.animationDelay=`${o*n}ms`,i.classList.add("staggered-item")})}}function H(m){m._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 i=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(i.length===0)return;let o=i[0],u=i[i.length-1];t.shiftKey?document.activeElement===o&&(u.focus(),t.preventDefault()):document.activeElement===u&&(o.focus(),t.preventDefault())})}),this.addDomListener(document,"keydown",t=>{if(!["ArrowUp","ArrowDown","Enter"].includes(t.key))return;document.querySelectorAll("[data-rt-keynav]").forEach(n=>{let r=Array.from(n.children);if(r.length===0)return;let i=r.findIndex(o=>o.classList.contains("active")||document.activeElement===o);t.key==="ArrowDown"?(i=(i+1)%r.length,r[i].focus(),r.forEach((o,u)=>{u===i?o.classList.add("active"):o.classList.remove("active")}),t.preventDefault()):t.key==="ArrowUp"?(i=(i-1+r.length)%r.length,r[i].focus(),r.forEach((o,u)=>{u===i?o.classList.add("active"):o.classList.remove("active")}),t.preventDefault()):t.key==="Enter"&&i!==-1&&(r[i].click(),t.preventDefault())})}))},m.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 j(m){m._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 r=JSON.parse(e.textContent||"{}");this.i18n.dicts[n]={...this.i18n.dicts[n]||{},...r}}catch(r){console.warn("[Dolphin i18n] Failed to parse dictionary for locale:",n,r)}}),!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 r=n.getAttribute("data-i18n-switch");r&&this.setLocale(r)}}),this.translateDOM()},m.setLocale=function(t){this.i18n=this.i18n||{locale:"en",dicts:{}},this.i18n.locale=t,this.translateDOM(),this.publish("i18n/locale",t)},m.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(r=>{let i=r.getAttribute("data-i18n-key");if(!i)return;let o=i.split(".").reduce((S,D)=>S?S[D]:null,e);o==null&&(o=i);let u=r.getAttribute("data-i18n-params");if(u)try{let S=JSON.parse(u),D=g=>g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");for(let g in S){let c=D(g);o=o.replace(new RegExp(`\\{\\{${c}\\}\\}`,"g"),S[g])}}catch{}r.tagName==="INPUT"||r.tagName==="TEXTAREA"?r.placeholder=o:r.textContent=o})}}function B(m){m._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"),r=t.dataTransfer.getData("text/plain");if(n&&r)try{let i=JSON.parse(r);this.publish(n,i)}catch{this.publish(n,{value:r})}}),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 i=Array.from(e.querySelectorAll("[data-drag]:not(.dragging)")).find(o=>{let u=o.getBoundingClientRect();return t.clientY-u.top-u.height/2<0});i?e.insertBefore(n,i):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 i=Array.from(e.querySelectorAll("[data-drag]")).map((o,u)=>{let S=o.getAttribute("data-drag");try{return{index:u,payload:JSON.parse(S||"{}")}}catch{return{index:u,payload:S}}});this.publish(n,i)}))}}function V(m){m._initCollab=function(){typeof document>"u"||(this.addDomListener(document,"mousemove",t=>{document.querySelectorAll("[data-rt-cursor-share]").forEach(n=>{let r=n.getAttribute("data-rt-cursor-share");if(!r)return;let i=n.getBoundingClientRect(),o=(t.clientX-i.left)/i.width,u=(t.clientY-i.top)/i.height,S=Date.now();(!n._lastSent||S-n._lastSent>50)&&(n._lastSent=S,this.pubPush(`collab/${r}/cursor/${this.deviceId}`,{deviceId:this.deviceId,x:o,y:u}))})}),this.addDomListener(document,"input",t=>{let e=t.target.getAttribute("data-rt-typing");if(!e)return;let n=e,r=i=>{this.pubPush(`collab/${n}/typing/${this.deviceId}`,{deviceId:this.deviceId,typing:i})};t.target._isTyping||(t.target._isTyping=!0,r(!0)),t.target._typingTimer&&clearTimeout(t.target._typingTimer),t.target._typingTimer=setTimeout(()=>{t.target._isTyping=!1,r(!1)},2e3)}),this.addDomListener(document,"input",t=>{let e=t.target.getAttribute("data-rt-crdt");if(!e)return;let n=e,r=t.target.value,i=Date.now();this.publish(`collab/${n}/crdt`,{deviceId:this.deviceId,value:r,timestamp:i,cursorPos:t.target.selectionStart})}),this.subscribe("collab/+/cursor/+",(t,e)=>{let n=e.split("/"),r=n[1],i=n[3];if(i===this.deviceId)return;let o=document.querySelector(`[data-rt-cursor-share="${r}"]`);if(!o)return;let u=o.querySelector(`.rt-cursor-${i}`);u||(u=document.createElement("div"),u.className=`rt-cursor rt-cursor-${i}`,u.style.position="absolute",u.style.width="10px",u.style.height="10px",u.style.borderRadius="50%",u.style.backgroundColor="#"+Math.floor(Math.random()*16777215).toString(16),u.style.pointerEvents="none",o.appendChild(u));let S=o.getBoundingClientRect();u.style.left=t.x*S.width+"px",u.style.top=t.y*S.height+"px"}),this.subscribe("collab/+/crdt",(t,e)=>{if(t.deviceId===this.deviceId)return;let r=e.split("/")[1];document.querySelectorAll(`[data-rt-crdt="${r}"]`).forEach(o=>{if(!o._lastUpdate||t.timestamp>o._lastUpdate){o._lastUpdate=t.timestamp;let u=o.selectionStart;o.value=t.value,document.activeElement===o&&o.setSelectionRange(u,u)}})}))}}function z(m){m.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}},m.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 r="=".repeat((4-t.length%4)%4),i=(t+r).replace(/\-/g,"+").replace(/_/g,"/"),o=window.atob(i),u=new Uint8Array(o.length);for(let S=0;S<o.length;++S)u[S]=o.charCodeAt(S);n=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:u})}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 q=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,r)=>{let i=document.createEvent("Event");i.initEvent(r,!0,!0),n.dispatchEvent(i)}}}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(r=>r(e))}static simulateChange(t,e){t.value=e;let n={target:t,preventDefault:jest.fn(),stopPropagation:jest.fn()};(global.document._listeners?.change||[]).forEach(i=>i(n))}};function K(m){m.testing=q}O(k.prototype);U(k.prototype);W(k.prototype);F(k.prototype);H(k.prototype);j(k.prototype);B(k.prototype);V(k.prototype);z(k.prototype);K(k.prototype);typeof window<"u"&&(window.DolphinClient=k,document.addEventListener("DOMContentLoaded",()=>{if(!window.dolphin){let m=document.querySelector('script[src*="dolphin-client"]'),t=m?m.getAttribute("data-debug")==="true":!1,e=new k(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 nt(it);})();
|
package/dist/index.cjs
CHANGED
|
@@ -1380,14 +1380,15 @@ function attachDOMBinding(clientProto) {
|
|
|
1380
1380
|
} else if (!apiStore) {
|
|
1381
1381
|
const template = resolveTemplate(el);
|
|
1382
1382
|
if (template && typeof result === "object" && result !== null) {
|
|
1383
|
-
|
|
1383
|
+
const processedResult = this._applyDeclarativeDirectives(el, result);
|
|
1384
|
+
if (Array.isArray(processedResult)) {
|
|
1384
1385
|
let combinedHTML = "";
|
|
1385
|
-
for (const item of
|
|
1386
|
+
for (const item of processedResult) {
|
|
1386
1387
|
combinedHTML += renderTemplate(template, item);
|
|
1387
1388
|
}
|
|
1388
1389
|
scheduleDOMUpdate(el, combinedHTML);
|
|
1389
1390
|
} else {
|
|
1390
|
-
scheduleDOMUpdate(el, renderTemplate(template,
|
|
1391
|
+
scheduleDOMUpdate(el, renderTemplate(template, processedResult));
|
|
1391
1392
|
}
|
|
1392
1393
|
} else {
|
|
1393
1394
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
@@ -1402,21 +1403,139 @@ function attachDOMBinding(clientProto) {
|
|
|
1402
1403
|
}
|
|
1403
1404
|
}
|
|
1404
1405
|
};
|
|
1406
|
+
clientProto._applyDeclarativeDirectives = function(el, payload) {
|
|
1407
|
+
let processedPayload = payload;
|
|
1408
|
+
if (typeof payload === "object" && payload !== null) {
|
|
1409
|
+
const applyFilterSearchSort = (arr) => {
|
|
1410
|
+
let result = [...arr];
|
|
1411
|
+
const filterAttr = el.getAttribute("data-rt-filter");
|
|
1412
|
+
if (filterAttr) {
|
|
1413
|
+
const parts = filterAttr.split("==");
|
|
1414
|
+
if (parts.length === 2) {
|
|
1415
|
+
const itemProp = parts[0].trim();
|
|
1416
|
+
const storeExpr = parts[1].trim();
|
|
1417
|
+
let filterVal = void 0;
|
|
1418
|
+
const storeParts = storeExpr.split(".");
|
|
1419
|
+
if (storeParts.length === 2) {
|
|
1420
|
+
filterVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1421
|
+
} else {
|
|
1422
|
+
filterVal = payload[storeExpr] !== void 0 ? payload[storeExpr] : this.getStoreState("app", storeExpr);
|
|
1423
|
+
}
|
|
1424
|
+
if (filterVal !== void 0 && filterVal !== null && filterVal !== "") {
|
|
1425
|
+
result = result.filter((item) => item[itemProp] === filterVal);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
const searchAttr = el.getAttribute("data-rt-search");
|
|
1430
|
+
if (searchAttr) {
|
|
1431
|
+
const parts = searchAttr.split("==");
|
|
1432
|
+
if (parts.length === 2) {
|
|
1433
|
+
const itemProp = parts[0].trim();
|
|
1434
|
+
const storeExpr = parts[1].trim();
|
|
1435
|
+
let searchVal = void 0;
|
|
1436
|
+
const storeParts = storeExpr.split(".");
|
|
1437
|
+
if (storeParts.length === 2) {
|
|
1438
|
+
searchVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1439
|
+
} else {
|
|
1440
|
+
searchVal = payload[storeExpr] !== void 0 ? payload[storeExpr] : this.getStoreState("app", storeExpr);
|
|
1441
|
+
}
|
|
1442
|
+
if (searchVal !== void 0 && searchVal !== null && searchVal !== "") {
|
|
1443
|
+
const query = String(searchVal).toLowerCase();
|
|
1444
|
+
result = result.filter((item) => {
|
|
1445
|
+
const val = item[itemProp];
|
|
1446
|
+
return val !== void 0 && val !== null && String(val).toLowerCase().includes(query);
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
const sortAttr = el.getAttribute("data-rt-sort");
|
|
1452
|
+
if (sortAttr) {
|
|
1453
|
+
let sortByVal = void 0;
|
|
1454
|
+
const storeParts = sortAttr.split(".");
|
|
1455
|
+
if (storeParts.length === 2) {
|
|
1456
|
+
sortByVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1457
|
+
} else {
|
|
1458
|
+
sortByVal = payload[sortAttr] !== void 0 ? payload[sortAttr] : this.getStoreState("app", sortAttr);
|
|
1459
|
+
}
|
|
1460
|
+
if (sortByVal && sortByVal !== "") {
|
|
1461
|
+
if (sortByVal === "popular") {
|
|
1462
|
+
result.sort((a, b) => {
|
|
1463
|
+
const rateA = a.rating?.rate || a.rate || 0;
|
|
1464
|
+
const rateB = b.rating?.rate || b.rate || 0;
|
|
1465
|
+
return rateB - rateA;
|
|
1466
|
+
});
|
|
1467
|
+
} else {
|
|
1468
|
+
let field = "";
|
|
1469
|
+
let direction = "asc";
|
|
1470
|
+
if (sortByVal.endsWith("-low") || sortByVal.endsWith("-asc")) {
|
|
1471
|
+
field = sortByVal.replace("-low", "").replace("-asc", "");
|
|
1472
|
+
direction = "asc";
|
|
1473
|
+
} else if (sortByVal.endsWith("-high") || sortByVal.endsWith("-desc")) {
|
|
1474
|
+
field = sortByVal.replace("-high", "").replace("-desc", "");
|
|
1475
|
+
direction = "desc";
|
|
1476
|
+
}
|
|
1477
|
+
if (field) {
|
|
1478
|
+
result.sort((a, b) => {
|
|
1479
|
+
const resolveVal = (obj, path) => {
|
|
1480
|
+
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
1481
|
+
};
|
|
1482
|
+
let valA = resolveVal(a, field);
|
|
1483
|
+
let valB = resolveVal(b, field);
|
|
1484
|
+
if (valA === void 0) valA = a[field];
|
|
1485
|
+
if (valB === void 0) valB = b[field];
|
|
1486
|
+
if (typeof valA === "string" && typeof valB === "string") {
|
|
1487
|
+
return direction === "asc" ? valA.localeCompare(valB) : valB.localeCompare(valA);
|
|
1488
|
+
}
|
|
1489
|
+
const numA = Number(valA);
|
|
1490
|
+
const numB = Number(valB);
|
|
1491
|
+
if (!isNaN(numA) && !isNaN(numB)) {
|
|
1492
|
+
return direction === "asc" ? numA - numB : numB - numA;
|
|
1493
|
+
}
|
|
1494
|
+
return 0;
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return result;
|
|
1501
|
+
};
|
|
1502
|
+
if (Array.isArray(payload)) {
|
|
1503
|
+
processedPayload = applyFilterSearchSort(payload);
|
|
1504
|
+
} else {
|
|
1505
|
+
let foundArrayKey = "";
|
|
1506
|
+
for (const key in payload) {
|
|
1507
|
+
if (Array.isArray(payload[key])) {
|
|
1508
|
+
foundArrayKey = key;
|
|
1509
|
+
break;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (foundArrayKey) {
|
|
1513
|
+
const processedArray = applyFilterSearchSort(payload[foundArrayKey]);
|
|
1514
|
+
processedPayload = {
|
|
1515
|
+
...payload,
|
|
1516
|
+
[foundArrayKey]: processedArray
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return processedPayload;
|
|
1522
|
+
};
|
|
1405
1523
|
clientProto._updateDOM = function(topic, payload) {
|
|
1406
1524
|
if (typeof document === "undefined") return;
|
|
1407
1525
|
const elements = document.querySelectorAll(`[data-rt-bind="${topic}"]`);
|
|
1408
1526
|
elements.forEach((el) => {
|
|
1409
|
-
|
|
1410
|
-
|
|
1527
|
+
const processedPayload = this._applyDeclarativeDirectives(el, payload);
|
|
1528
|
+
if (el.getAttribute("data-rt-type") === "context" && typeof processedPayload === "object" && processedPayload !== null) {
|
|
1529
|
+
el._rtContext = processedPayload;
|
|
1411
1530
|
const processNode = (node) => {
|
|
1412
1531
|
if (node.hasAttribute("data-rt-text")) {
|
|
1413
1532
|
const key = node.getAttribute("data-rt-text");
|
|
1414
|
-
if (key &&
|
|
1533
|
+
if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) node.textContent = processedPayload[key];
|
|
1415
1534
|
}
|
|
1416
1535
|
if (node.hasAttribute("data-rt-html")) {
|
|
1417
1536
|
const key = node.getAttribute("data-rt-html");
|
|
1418
|
-
if (key &&
|
|
1419
|
-
node.innerHTML = sanitizeHTML(
|
|
1537
|
+
if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
|
|
1538
|
+
node.innerHTML = sanitizeHTML(processedPayload[key]);
|
|
1420
1539
|
}
|
|
1421
1540
|
}
|
|
1422
1541
|
if (node.hasAttribute("data-rt-attr")) {
|
|
@@ -1427,8 +1546,8 @@ function attachDOMBinding(clientProto) {
|
|
|
1427
1546
|
if (parts.length === 2) {
|
|
1428
1547
|
const attrName = parts[0].trim();
|
|
1429
1548
|
const key = parts[1].trim();
|
|
1430
|
-
if (attrName && key &&
|
|
1431
|
-
node.setAttribute(attrName,
|
|
1549
|
+
if (attrName && key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
|
|
1550
|
+
node.setAttribute(attrName, processedPayload[key]);
|
|
1432
1551
|
}
|
|
1433
1552
|
}
|
|
1434
1553
|
});
|
|
@@ -1443,7 +1562,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1443
1562
|
const className = parts[0].trim();
|
|
1444
1563
|
const key = parts[1].trim();
|
|
1445
1564
|
const classNames = className.split(/\s+/).filter(Boolean);
|
|
1446
|
-
if (
|
|
1565
|
+
if (processedPayload[key]) {
|
|
1447
1566
|
classNames.forEach((c) => node.classList.add(c));
|
|
1448
1567
|
} else {
|
|
1449
1568
|
classNames.forEach((c) => node.classList.remove(c));
|
|
@@ -1455,7 +1574,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1455
1574
|
if (node.hasAttribute("data-rt-if")) {
|
|
1456
1575
|
const key = node.getAttribute("data-rt-if");
|
|
1457
1576
|
if (key) {
|
|
1458
|
-
if (
|
|
1577
|
+
if (processedPayload[key]) {
|
|
1459
1578
|
node.style.display = "";
|
|
1460
1579
|
} else {
|
|
1461
1580
|
node.style.display = "none";
|
|
@@ -1465,7 +1584,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1465
1584
|
if (node.hasAttribute("data-rt-hide")) {
|
|
1466
1585
|
const key = node.getAttribute("data-rt-hide");
|
|
1467
1586
|
if (key) {
|
|
1468
|
-
if (
|
|
1587
|
+
if (processedPayload[key]) {
|
|
1469
1588
|
node.style.display = "none";
|
|
1470
1589
|
} else {
|
|
1471
1590
|
node.style.display = "";
|
|
@@ -1478,22 +1597,22 @@ function attachDOMBinding(clientProto) {
|
|
|
1478
1597
|
return;
|
|
1479
1598
|
}
|
|
1480
1599
|
const template = resolveTemplate(el);
|
|
1481
|
-
if (template && typeof
|
|
1482
|
-
if (Array.isArray(
|
|
1600
|
+
if (template && typeof processedPayload === "object" && processedPayload !== null) {
|
|
1601
|
+
if (Array.isArray(processedPayload)) {
|
|
1483
1602
|
let combinedHTML = "";
|
|
1484
|
-
for (const item of
|
|
1603
|
+
for (const item of processedPayload) {
|
|
1485
1604
|
combinedHTML += renderTemplate(template, item);
|
|
1486
1605
|
}
|
|
1487
1606
|
scheduleDOMUpdate(el, combinedHTML);
|
|
1488
1607
|
} else {
|
|
1489
|
-
scheduleDOMUpdate(el, renderTemplate(template,
|
|
1608
|
+
scheduleDOMUpdate(el, renderTemplate(template, processedPayload));
|
|
1490
1609
|
}
|
|
1491
1610
|
return;
|
|
1492
1611
|
}
|
|
1493
1612
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
1494
|
-
el.value = typeof
|
|
1613
|
+
el.value = typeof processedPayload === "object" ? processedPayload.value !== void 0 ? processedPayload.value : "" : processedPayload;
|
|
1495
1614
|
} else {
|
|
1496
|
-
el.innerHTML = typeof
|
|
1615
|
+
el.innerHTML = typeof processedPayload === "object" ? processedPayload.html || processedPayload.text || JSON.stringify(processedPayload) : processedPayload;
|
|
1497
1616
|
}
|
|
1498
1617
|
});
|
|
1499
1618
|
};
|
package/dist/index.js
CHANGED
|
@@ -1355,14 +1355,15 @@ function attachDOMBinding(clientProto) {
|
|
|
1355
1355
|
} else if (!apiStore) {
|
|
1356
1356
|
const template = resolveTemplate(el);
|
|
1357
1357
|
if (template && typeof result === "object" && result !== null) {
|
|
1358
|
-
|
|
1358
|
+
const processedResult = this._applyDeclarativeDirectives(el, result);
|
|
1359
|
+
if (Array.isArray(processedResult)) {
|
|
1359
1360
|
let combinedHTML = "";
|
|
1360
|
-
for (const item of
|
|
1361
|
+
for (const item of processedResult) {
|
|
1361
1362
|
combinedHTML += renderTemplate(template, item);
|
|
1362
1363
|
}
|
|
1363
1364
|
scheduleDOMUpdate(el, combinedHTML);
|
|
1364
1365
|
} else {
|
|
1365
|
-
scheduleDOMUpdate(el, renderTemplate(template,
|
|
1366
|
+
scheduleDOMUpdate(el, renderTemplate(template, processedResult));
|
|
1366
1367
|
}
|
|
1367
1368
|
} else {
|
|
1368
1369
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
@@ -1377,21 +1378,139 @@ function attachDOMBinding(clientProto) {
|
|
|
1377
1378
|
}
|
|
1378
1379
|
}
|
|
1379
1380
|
};
|
|
1381
|
+
clientProto._applyDeclarativeDirectives = function(el, payload) {
|
|
1382
|
+
let processedPayload = payload;
|
|
1383
|
+
if (typeof payload === "object" && payload !== null) {
|
|
1384
|
+
const applyFilterSearchSort = (arr) => {
|
|
1385
|
+
let result = [...arr];
|
|
1386
|
+
const filterAttr = el.getAttribute("data-rt-filter");
|
|
1387
|
+
if (filterAttr) {
|
|
1388
|
+
const parts = filterAttr.split("==");
|
|
1389
|
+
if (parts.length === 2) {
|
|
1390
|
+
const itemProp = parts[0].trim();
|
|
1391
|
+
const storeExpr = parts[1].trim();
|
|
1392
|
+
let filterVal = void 0;
|
|
1393
|
+
const storeParts = storeExpr.split(".");
|
|
1394
|
+
if (storeParts.length === 2) {
|
|
1395
|
+
filterVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1396
|
+
} else {
|
|
1397
|
+
filterVal = payload[storeExpr] !== void 0 ? payload[storeExpr] : this.getStoreState("app", storeExpr);
|
|
1398
|
+
}
|
|
1399
|
+
if (filterVal !== void 0 && filterVal !== null && filterVal !== "") {
|
|
1400
|
+
result = result.filter((item) => item[itemProp] === filterVal);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
const searchAttr = el.getAttribute("data-rt-search");
|
|
1405
|
+
if (searchAttr) {
|
|
1406
|
+
const parts = searchAttr.split("==");
|
|
1407
|
+
if (parts.length === 2) {
|
|
1408
|
+
const itemProp = parts[0].trim();
|
|
1409
|
+
const storeExpr = parts[1].trim();
|
|
1410
|
+
let searchVal = void 0;
|
|
1411
|
+
const storeParts = storeExpr.split(".");
|
|
1412
|
+
if (storeParts.length === 2) {
|
|
1413
|
+
searchVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1414
|
+
} else {
|
|
1415
|
+
searchVal = payload[storeExpr] !== void 0 ? payload[storeExpr] : this.getStoreState("app", storeExpr);
|
|
1416
|
+
}
|
|
1417
|
+
if (searchVal !== void 0 && searchVal !== null && searchVal !== "") {
|
|
1418
|
+
const query = String(searchVal).toLowerCase();
|
|
1419
|
+
result = result.filter((item) => {
|
|
1420
|
+
const val = item[itemProp];
|
|
1421
|
+
return val !== void 0 && val !== null && String(val).toLowerCase().includes(query);
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
const sortAttr = el.getAttribute("data-rt-sort");
|
|
1427
|
+
if (sortAttr) {
|
|
1428
|
+
let sortByVal = void 0;
|
|
1429
|
+
const storeParts = sortAttr.split(".");
|
|
1430
|
+
if (storeParts.length === 2) {
|
|
1431
|
+
sortByVal = this.getStoreState(storeParts[0], storeParts[1]);
|
|
1432
|
+
} else {
|
|
1433
|
+
sortByVal = payload[sortAttr] !== void 0 ? payload[sortAttr] : this.getStoreState("app", sortAttr);
|
|
1434
|
+
}
|
|
1435
|
+
if (sortByVal && sortByVal !== "") {
|
|
1436
|
+
if (sortByVal === "popular") {
|
|
1437
|
+
result.sort((a, b) => {
|
|
1438
|
+
const rateA = a.rating?.rate || a.rate || 0;
|
|
1439
|
+
const rateB = b.rating?.rate || b.rate || 0;
|
|
1440
|
+
return rateB - rateA;
|
|
1441
|
+
});
|
|
1442
|
+
} else {
|
|
1443
|
+
let field = "";
|
|
1444
|
+
let direction = "asc";
|
|
1445
|
+
if (sortByVal.endsWith("-low") || sortByVal.endsWith("-asc")) {
|
|
1446
|
+
field = sortByVal.replace("-low", "").replace("-asc", "");
|
|
1447
|
+
direction = "asc";
|
|
1448
|
+
} else if (sortByVal.endsWith("-high") || sortByVal.endsWith("-desc")) {
|
|
1449
|
+
field = sortByVal.replace("-high", "").replace("-desc", "");
|
|
1450
|
+
direction = "desc";
|
|
1451
|
+
}
|
|
1452
|
+
if (field) {
|
|
1453
|
+
result.sort((a, b) => {
|
|
1454
|
+
const resolveVal = (obj, path) => {
|
|
1455
|
+
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
1456
|
+
};
|
|
1457
|
+
let valA = resolveVal(a, field);
|
|
1458
|
+
let valB = resolveVal(b, field);
|
|
1459
|
+
if (valA === void 0) valA = a[field];
|
|
1460
|
+
if (valB === void 0) valB = b[field];
|
|
1461
|
+
if (typeof valA === "string" && typeof valB === "string") {
|
|
1462
|
+
return direction === "asc" ? valA.localeCompare(valB) : valB.localeCompare(valA);
|
|
1463
|
+
}
|
|
1464
|
+
const numA = Number(valA);
|
|
1465
|
+
const numB = Number(valB);
|
|
1466
|
+
if (!isNaN(numA) && !isNaN(numB)) {
|
|
1467
|
+
return direction === "asc" ? numA - numB : numB - numA;
|
|
1468
|
+
}
|
|
1469
|
+
return 0;
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
return result;
|
|
1476
|
+
};
|
|
1477
|
+
if (Array.isArray(payload)) {
|
|
1478
|
+
processedPayload = applyFilterSearchSort(payload);
|
|
1479
|
+
} else {
|
|
1480
|
+
let foundArrayKey = "";
|
|
1481
|
+
for (const key in payload) {
|
|
1482
|
+
if (Array.isArray(payload[key])) {
|
|
1483
|
+
foundArrayKey = key;
|
|
1484
|
+
break;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
if (foundArrayKey) {
|
|
1488
|
+
const processedArray = applyFilterSearchSort(payload[foundArrayKey]);
|
|
1489
|
+
processedPayload = {
|
|
1490
|
+
...payload,
|
|
1491
|
+
[foundArrayKey]: processedArray
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return processedPayload;
|
|
1497
|
+
};
|
|
1380
1498
|
clientProto._updateDOM = function(topic, payload) {
|
|
1381
1499
|
if (typeof document === "undefined") return;
|
|
1382
1500
|
const elements = document.querySelectorAll(`[data-rt-bind="${topic}"]`);
|
|
1383
1501
|
elements.forEach((el) => {
|
|
1384
|
-
|
|
1385
|
-
|
|
1502
|
+
const processedPayload = this._applyDeclarativeDirectives(el, payload);
|
|
1503
|
+
if (el.getAttribute("data-rt-type") === "context" && typeof processedPayload === "object" && processedPayload !== null) {
|
|
1504
|
+
el._rtContext = processedPayload;
|
|
1386
1505
|
const processNode = (node) => {
|
|
1387
1506
|
if (node.hasAttribute("data-rt-text")) {
|
|
1388
1507
|
const key = node.getAttribute("data-rt-text");
|
|
1389
|
-
if (key &&
|
|
1508
|
+
if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) node.textContent = processedPayload[key];
|
|
1390
1509
|
}
|
|
1391
1510
|
if (node.hasAttribute("data-rt-html")) {
|
|
1392
1511
|
const key = node.getAttribute("data-rt-html");
|
|
1393
|
-
if (key &&
|
|
1394
|
-
node.innerHTML = sanitizeHTML(
|
|
1512
|
+
if (key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
|
|
1513
|
+
node.innerHTML = sanitizeHTML(processedPayload[key]);
|
|
1395
1514
|
}
|
|
1396
1515
|
}
|
|
1397
1516
|
if (node.hasAttribute("data-rt-attr")) {
|
|
@@ -1402,8 +1521,8 @@ function attachDOMBinding(clientProto) {
|
|
|
1402
1521
|
if (parts.length === 2) {
|
|
1403
1522
|
const attrName = parts[0].trim();
|
|
1404
1523
|
const key = parts[1].trim();
|
|
1405
|
-
if (attrName && key &&
|
|
1406
|
-
node.setAttribute(attrName,
|
|
1524
|
+
if (attrName && key && processedPayload[key] !== void 0 && processedPayload[key] !== null) {
|
|
1525
|
+
node.setAttribute(attrName, processedPayload[key]);
|
|
1407
1526
|
}
|
|
1408
1527
|
}
|
|
1409
1528
|
});
|
|
@@ -1418,7 +1537,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1418
1537
|
const className = parts[0].trim();
|
|
1419
1538
|
const key = parts[1].trim();
|
|
1420
1539
|
const classNames = className.split(/\s+/).filter(Boolean);
|
|
1421
|
-
if (
|
|
1540
|
+
if (processedPayload[key]) {
|
|
1422
1541
|
classNames.forEach((c) => node.classList.add(c));
|
|
1423
1542
|
} else {
|
|
1424
1543
|
classNames.forEach((c) => node.classList.remove(c));
|
|
@@ -1430,7 +1549,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1430
1549
|
if (node.hasAttribute("data-rt-if")) {
|
|
1431
1550
|
const key = node.getAttribute("data-rt-if");
|
|
1432
1551
|
if (key) {
|
|
1433
|
-
if (
|
|
1552
|
+
if (processedPayload[key]) {
|
|
1434
1553
|
node.style.display = "";
|
|
1435
1554
|
} else {
|
|
1436
1555
|
node.style.display = "none";
|
|
@@ -1440,7 +1559,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1440
1559
|
if (node.hasAttribute("data-rt-hide")) {
|
|
1441
1560
|
const key = node.getAttribute("data-rt-hide");
|
|
1442
1561
|
if (key) {
|
|
1443
|
-
if (
|
|
1562
|
+
if (processedPayload[key]) {
|
|
1444
1563
|
node.style.display = "none";
|
|
1445
1564
|
} else {
|
|
1446
1565
|
node.style.display = "";
|
|
@@ -1453,22 +1572,22 @@ function attachDOMBinding(clientProto) {
|
|
|
1453
1572
|
return;
|
|
1454
1573
|
}
|
|
1455
1574
|
const template = resolveTemplate(el);
|
|
1456
|
-
if (template && typeof
|
|
1457
|
-
if (Array.isArray(
|
|
1575
|
+
if (template && typeof processedPayload === "object" && processedPayload !== null) {
|
|
1576
|
+
if (Array.isArray(processedPayload)) {
|
|
1458
1577
|
let combinedHTML = "";
|
|
1459
|
-
for (const item of
|
|
1578
|
+
for (const item of processedPayload) {
|
|
1460
1579
|
combinedHTML += renderTemplate(template, item);
|
|
1461
1580
|
}
|
|
1462
1581
|
scheduleDOMUpdate(el, combinedHTML);
|
|
1463
1582
|
} else {
|
|
1464
|
-
scheduleDOMUpdate(el, renderTemplate(template,
|
|
1583
|
+
scheduleDOMUpdate(el, renderTemplate(template, processedPayload));
|
|
1465
1584
|
}
|
|
1466
1585
|
return;
|
|
1467
1586
|
}
|
|
1468
1587
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
1469
|
-
el.value = typeof
|
|
1588
|
+
el.value = typeof processedPayload === "object" ? processedPayload.value !== void 0 ? processedPayload.value : "" : processedPayload;
|
|
1470
1589
|
} else {
|
|
1471
|
-
el.innerHTML = typeof
|
|
1590
|
+
el.innerHTML = typeof processedPayload === "object" ? processedPayload.html || processedPayload.text || JSON.stringify(processedPayload) : processedPayload;
|
|
1472
1591
|
}
|
|
1473
1592
|
});
|
|
1474
1593
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dolphin-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
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",
|