dolphin-client 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +5 -0
- package/dist/dolphin-client.js +181 -171
- package/dist/dolphin-client.min.js +19 -19
- package/dist/index.cjs +181 -171
- package/dist/index.js +181 -171
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -972,6 +972,82 @@ function attachDOMBinding(clientProto) {
|
|
|
972
972
|
function escapeRegExp(str) {
|
|
973
973
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
974
974
|
}
|
|
975
|
+
function evaluateExpression(expr, ctx) {
|
|
976
|
+
if (!ctx || typeof ctx !== "object") return void 0;
|
|
977
|
+
try {
|
|
978
|
+
const safeCtx = new Proxy(ctx, {
|
|
979
|
+
has(target, prop) {
|
|
980
|
+
return true;
|
|
981
|
+
},
|
|
982
|
+
get(target, prop) {
|
|
983
|
+
if (typeof prop === "string") {
|
|
984
|
+
if (prop in target) return target[prop];
|
|
985
|
+
if (typeof globalThis !== "undefined" && prop in globalThis) return globalThis[prop];
|
|
986
|
+
if (typeof window !== "undefined" && prop in window) return window[prop];
|
|
987
|
+
}
|
|
988
|
+
return void 0;
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
|
|
992
|
+
return fn(safeCtx);
|
|
993
|
+
} catch {
|
|
994
|
+
return ctx[expr];
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function splitByUnquotedChar(str, char) {
|
|
998
|
+
const parts = [];
|
|
999
|
+
let current = "";
|
|
1000
|
+
let inSingleQuote = false;
|
|
1001
|
+
let inDoubleQuote = false;
|
|
1002
|
+
let inBacktick = false;
|
|
1003
|
+
let depth = 0;
|
|
1004
|
+
for (let i = 0; i < str.length; i++) {
|
|
1005
|
+
const c = str[i];
|
|
1006
|
+
if (c === "'" && !inDoubleQuote && !inBacktick) {
|
|
1007
|
+
inSingleQuote = !inSingleQuote;
|
|
1008
|
+
} else if (c === '"' && !inSingleQuote && !inBacktick) {
|
|
1009
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1010
|
+
} else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
|
|
1011
|
+
inBacktick = !inBacktick;
|
|
1012
|
+
} else if (c === "(" || c === "[" || c === "{") {
|
|
1013
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
|
|
1014
|
+
} else if (c === ")" || c === "]" || c === "}") {
|
|
1015
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
|
|
1016
|
+
}
|
|
1017
|
+
if (c === char && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
|
|
1018
|
+
parts.push(current);
|
|
1019
|
+
current = "";
|
|
1020
|
+
} else {
|
|
1021
|
+
current += c;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
parts.push(current);
|
|
1025
|
+
return parts;
|
|
1026
|
+
}
|
|
1027
|
+
function splitFirstUnquotedColon(str) {
|
|
1028
|
+
let inSingleQuote = false;
|
|
1029
|
+
let inDoubleQuote = false;
|
|
1030
|
+
let inBacktick = false;
|
|
1031
|
+
let depth = 0;
|
|
1032
|
+
for (let i = 0; i < str.length; i++) {
|
|
1033
|
+
const c = str[i];
|
|
1034
|
+
if (c === "'" && !inDoubleQuote && !inBacktick) {
|
|
1035
|
+
inSingleQuote = !inSingleQuote;
|
|
1036
|
+
} else if (c === '"' && !inSingleQuote && !inBacktick) {
|
|
1037
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1038
|
+
} else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
|
|
1039
|
+
inBacktick = !inBacktick;
|
|
1040
|
+
} else if (c === "(" || c === "[" || c === "{") {
|
|
1041
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
|
|
1042
|
+
} else if (c === ")" || c === "]" || c === "}") {
|
|
1043
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
|
|
1044
|
+
}
|
|
1045
|
+
if (c === ":" && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
|
|
1046
|
+
return [str.slice(0, i), str.slice(i + 1)];
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return null;
|
|
1050
|
+
}
|
|
975
1051
|
function resolveTemplate(el) {
|
|
976
1052
|
const template = el.getAttribute("data-rt-template");
|
|
977
1053
|
if (!template) return null;
|
|
@@ -988,9 +1064,26 @@ function attachDOMBinding(clientProto) {
|
|
|
988
1064
|
if (!templateStr.includes("{#if") && !templateStr.includes("{#each")) {
|
|
989
1065
|
let result = templateStr;
|
|
990
1066
|
for (let key in context) {
|
|
991
|
-
const escapedKey = key.replace(/[
|
|
992
|
-
result = result.replace(new RegExp(
|
|
993
|
-
}
|
|
1067
|
+
const escapedKey = key.replace(/[.*+?^$${}()|[\]\\]/g, "\\$&");
|
|
1068
|
+
result = result.replace(new RegExp("\\{\\{" + escapedKey + "\\}}", "g"), context[key] !== void 0 && context[key] !== null ? context[key] : "");
|
|
1069
|
+
}
|
|
1070
|
+
result = result.replace(/\{\{([\s\S]*?)\}\}/g, (match, expr) => {
|
|
1071
|
+
const trimmed = expr.trim();
|
|
1072
|
+
if (!trimmed) return "";
|
|
1073
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*(?:\??\.[a-zA-Z_$][a-zA-Z0-9_$]*)+$/.test(trimmed)) {
|
|
1074
|
+
const parts = trimmed.split(/\??\./);
|
|
1075
|
+
let val = context;
|
|
1076
|
+
for (const part of parts) {
|
|
1077
|
+
if (val === void 0 || val === null) {
|
|
1078
|
+
val = void 0;
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
val = val[part];
|
|
1082
|
+
}
|
|
1083
|
+
return val !== void 0 && val !== null ? val : "";
|
|
1084
|
+
}
|
|
1085
|
+
return match;
|
|
1086
|
+
});
|
|
994
1087
|
return result;
|
|
995
1088
|
}
|
|
996
1089
|
try {
|
|
@@ -1110,7 +1203,9 @@ function attachDOMBinding(clientProto) {
|
|
|
1110
1203
|
if (typeof document === "undefined") return html;
|
|
1111
1204
|
try {
|
|
1112
1205
|
const parser = new DOMParser();
|
|
1113
|
-
const
|
|
1206
|
+
const hasBodyOrHtml = /<\s*(?:body|html)\b/i.test(html);
|
|
1207
|
+
const parseString = hasBodyOrHtml ? html : `<body>${html}</body>`;
|
|
1208
|
+
const doc = parser.parseFromString(parseString, "text/html");
|
|
1114
1209
|
const body = doc.body;
|
|
1115
1210
|
const sanitizeNode = (el) => {
|
|
1116
1211
|
const tag = el.tagName.toLowerCase();
|
|
@@ -1224,7 +1319,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1224
1319
|
});
|
|
1225
1320
|
}
|
|
1226
1321
|
}
|
|
1227
|
-
clientProto.setStoreState = function(storeName, key, val) {
|
|
1322
|
+
clientProto.setStoreState = function(storeName, key, val, originEl) {
|
|
1228
1323
|
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1229
1324
|
if (!this.uiStores.has(storeName)) {
|
|
1230
1325
|
this.uiStores.set(storeName, {});
|
|
@@ -1237,6 +1332,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1237
1332
|
if (typeof document !== "undefined") {
|
|
1238
1333
|
const readElements = document.querySelectorAll(`[data-store-read="${storeName}.${key}"]`);
|
|
1239
1334
|
readElements.forEach((el) => {
|
|
1335
|
+
if (el === originEl) return;
|
|
1240
1336
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
1241
1337
|
if (el.type === "checkbox") {
|
|
1242
1338
|
el.checked = !!val;
|
|
@@ -1312,20 +1408,49 @@ function attachDOMBinding(clientProto) {
|
|
|
1312
1408
|
if (key) return ctx[key];
|
|
1313
1409
|
return ctx;
|
|
1314
1410
|
}
|
|
1315
|
-
current = current.parentElement;
|
|
1411
|
+
current = current.parentElement || current.parentNode;
|
|
1316
1412
|
}
|
|
1317
1413
|
return null;
|
|
1318
1414
|
};
|
|
1319
1415
|
clientProto._executeStoreAction = function(expression, element) {
|
|
1320
1416
|
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1417
|
+
const parentCtx = element && typeof this.getClosestContext === "function" ? this.getClosestContext(element) : null;
|
|
1321
1418
|
const context = new Proxy({}, {
|
|
1322
1419
|
has: (target, prop) => {
|
|
1323
1420
|
return true;
|
|
1324
1421
|
},
|
|
1325
1422
|
get: (target, prop) => {
|
|
1326
1423
|
if (typeof prop === "string") {
|
|
1424
|
+
if (prop === "log") {
|
|
1425
|
+
return (arg) => {
|
|
1426
|
+
if (arg === void 0) {
|
|
1427
|
+
const allStores = {};
|
|
1428
|
+
this.uiStores.forEach((val, key) => {
|
|
1429
|
+
allStores[key] = { ...val };
|
|
1430
|
+
});
|
|
1431
|
+
console.log(`%c\u{1F4CA} [Dolphin All UI Stores]:`, "color: #06b6d4; font-weight: bold;", allStores);
|
|
1432
|
+
} else if (arg && typeof arg === "object" && arg.__isStoreProxy__) {
|
|
1433
|
+
const storeName = arg.__storeName__;
|
|
1434
|
+
const store = this.uiStores.get(storeName);
|
|
1435
|
+
console.log(`%c\u{1F4CA} [Dolphin Store: ${storeName}]:`, "color: #06b6d4; font-weight: bold;", store ? { ...store } : {});
|
|
1436
|
+
} else {
|
|
1437
|
+
console.log(`%c\u{1F4CA} [Dolphin Log]:`, "color: #06b6d4; font-weight: bold;", arg);
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
if (parentCtx && parentCtx[prop] !== void 0) {
|
|
1442
|
+
return parentCtx[prop];
|
|
1443
|
+
}
|
|
1444
|
+
if (typeof globalThis !== "undefined" && prop in globalThis) {
|
|
1445
|
+
return globalThis[prop];
|
|
1446
|
+
}
|
|
1447
|
+
if (typeof window !== "undefined" && prop in window) {
|
|
1448
|
+
return window[prop];
|
|
1449
|
+
}
|
|
1327
1450
|
return new Proxy({}, {
|
|
1328
1451
|
get: (subTarget, subProp) => {
|
|
1452
|
+
if (subProp === "__storeName__") return prop;
|
|
1453
|
+
if (subProp === "__isStoreProxy__") return true;
|
|
1329
1454
|
if (typeof subProp === "string") {
|
|
1330
1455
|
return this.getStoreState(prop, subProp);
|
|
1331
1456
|
}
|
|
@@ -1352,86 +1477,6 @@ function attachDOMBinding(clientProto) {
|
|
|
1352
1477
|
console.error("%cFailed Expression:", "color: #3b82f6; font-style: italic;", expression);
|
|
1353
1478
|
}
|
|
1354
1479
|
};
|
|
1355
|
-
clientProto._showAlert = function(id, duration) {
|
|
1356
|
-
if (typeof document === "undefined") return;
|
|
1357
|
-
const el = document.getElementById(id) || document.querySelector(`[id="${id}"]`);
|
|
1358
|
-
if (!el) return;
|
|
1359
|
-
el.removeAttribute("hidden");
|
|
1360
|
-
el.style.display = "";
|
|
1361
|
-
if (duration && duration > 0) {
|
|
1362
|
-
setTimeout(() => {
|
|
1363
|
-
el.setAttribute("hidden", "");
|
|
1364
|
-
}, duration);
|
|
1365
|
-
}
|
|
1366
|
-
};
|
|
1367
|
-
clientProto._showToast = function(message, type = "success") {
|
|
1368
|
-
if (typeof document === "undefined") return;
|
|
1369
|
-
const colors = {
|
|
1370
|
-
success: { bg: "rgba(16,185,129,0.15)", border: "#10b981", icon: "\u2705" },
|
|
1371
|
-
error: { bg: "rgba(239,68,68,0.15)", border: "#ef4444", icon: "\u274C" },
|
|
1372
|
-
info: { bg: "rgba(59,130,246,0.15)", border: "#3b82f6", icon: "\u2139\uFE0F" }
|
|
1373
|
-
};
|
|
1374
|
-
const c = colors[type] || colors.success;
|
|
1375
|
-
const toast = document.createElement("div");
|
|
1376
|
-
toast.setAttribute("data-dolphin-toast", "");
|
|
1377
|
-
const existing = document.querySelectorAll("[data-dolphin-toast]");
|
|
1378
|
-
const offset = existing.length * 72;
|
|
1379
|
-
toast.style.cssText = [
|
|
1380
|
-
"position:fixed",
|
|
1381
|
-
`bottom:${24 + offset}px`,
|
|
1382
|
-
"right:24px",
|
|
1383
|
-
"z-index:2147483647",
|
|
1384
|
-
`background:${c.bg}`,
|
|
1385
|
-
`border:1px solid ${c.border}`,
|
|
1386
|
-
"color:#fff",
|
|
1387
|
-
"padding:14px 20px",
|
|
1388
|
-
"border-radius:14px",
|
|
1389
|
-
"font-size:14px",
|
|
1390
|
-
"font-weight:600",
|
|
1391
|
-
"font-family:system-ui,sans-serif",
|
|
1392
|
-
"box-shadow:0 8px 32px rgba(0,0,0,0.4)",
|
|
1393
|
-
"display:flex",
|
|
1394
|
-
"align-items:center",
|
|
1395
|
-
"gap:10px",
|
|
1396
|
-
"max-width:380px",
|
|
1397
|
-
"word-break:break-word",
|
|
1398
|
-
"transform:translateY(80px)",
|
|
1399
|
-
"opacity:0",
|
|
1400
|
-
"transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1),opacity 0.3s ease",
|
|
1401
|
-
"pointer-events:auto",
|
|
1402
|
-
"backdrop-filter:blur(12px)"
|
|
1403
|
-
].join(";");
|
|
1404
|
-
toast.innerHTML = `<span style="font-size:18px">${c.icon}</span><span>${message}</span>`;
|
|
1405
|
-
document.body.appendChild(toast);
|
|
1406
|
-
const restack = () => {
|
|
1407
|
-
const remaining = document.querySelectorAll("[data-dolphin-toast]");
|
|
1408
|
-
remaining.forEach((el, i) => {
|
|
1409
|
-
el.style.bottom = `${24 + i * 72}px`;
|
|
1410
|
-
});
|
|
1411
|
-
};
|
|
1412
|
-
const raf = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : (cb) => setTimeout(cb, 0);
|
|
1413
|
-
raf(() => raf(() => {
|
|
1414
|
-
toast.style.transform = "translateY(0)";
|
|
1415
|
-
toast.style.opacity = "1";
|
|
1416
|
-
}));
|
|
1417
|
-
const removeToast = () => {
|
|
1418
|
-
clearTimeout(toast._removeTimer);
|
|
1419
|
-
if (toast.parentNode) {
|
|
1420
|
-
toast.parentNode.removeChild(toast);
|
|
1421
|
-
restack();
|
|
1422
|
-
}
|
|
1423
|
-
};
|
|
1424
|
-
const hideTimer = setTimeout(() => {
|
|
1425
|
-
toast.style.transform = "translateY(80px)";
|
|
1426
|
-
toast.style.opacity = "0";
|
|
1427
|
-
toast._removeTimer = setTimeout(removeToast, 400);
|
|
1428
|
-
}, 3500);
|
|
1429
|
-
toast._hideTimer = hideTimer;
|
|
1430
|
-
toast.addEventListener("click", () => {
|
|
1431
|
-
clearTimeout(toast._hideTimer);
|
|
1432
|
-
removeToast();
|
|
1433
|
-
}, { once: true });
|
|
1434
|
-
};
|
|
1435
1480
|
clientProto._initDOMBinding = function() {
|
|
1436
1481
|
if (this._domInitialized) return;
|
|
1437
1482
|
this._domInitialized = true;
|
|
@@ -1447,7 +1492,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1447
1492
|
const storeName = parts[0];
|
|
1448
1493
|
const key = parts[1];
|
|
1449
1494
|
const val = e.target.type === "checkbox" ? e.target.checked : e.target.value;
|
|
1450
|
-
this.setStoreState(storeName, key, val);
|
|
1495
|
+
this.setStoreState(storeName, key, val, e.target);
|
|
1451
1496
|
}
|
|
1452
1497
|
}
|
|
1453
1498
|
const rules = e.target.getAttribute("data-rt-validate");
|
|
@@ -1530,11 +1575,6 @@ function attachDOMBinding(clientProto) {
|
|
|
1530
1575
|
resolvedTopic = resolvedTopic.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
|
|
1531
1576
|
}
|
|
1532
1577
|
this.publish(resolvedTopic, data);
|
|
1533
|
-
const rtSuccessId = e.target.getAttribute("data-rt-api-success");
|
|
1534
|
-
if (rtSuccessId) {
|
|
1535
|
-
const rtSuccessEl = document.getElementById(rtSuccessId);
|
|
1536
|
-
if (rtSuccessEl) this._showToast(rtSuccessEl.textContent || rtSuccessEl.innerText || "", "success");
|
|
1537
|
-
}
|
|
1538
1578
|
} else if (apiTarget) {
|
|
1539
1579
|
let resolvedTarget = apiTarget;
|
|
1540
1580
|
for (const k in parentCtx) {
|
|
@@ -1551,40 +1591,16 @@ function attachDOMBinding(clientProto) {
|
|
|
1551
1591
|
const result = await this.api.request(method, path, data);
|
|
1552
1592
|
const resultBind = e.target.getAttribute("data-api-result");
|
|
1553
1593
|
if (resultBind) this._updateDOM(resultBind, result);
|
|
1554
|
-
const successToast = e.target.getAttribute("data-api-toast");
|
|
1555
|
-
if (successToast) this._showToast(successToast, "success");
|
|
1556
|
-
const successElId = e.target.getAttribute("data-rt-api-success");
|
|
1557
|
-
if (successElId) {
|
|
1558
|
-
const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
|
|
1559
|
-
this._showAlert(successElId, duration);
|
|
1560
|
-
const errElId = e.target.getAttribute("data-rt-api-error");
|
|
1561
|
-
if (errElId) {
|
|
1562
|
-
const errEl = document.getElementById(errElId);
|
|
1563
|
-
if (errEl) errEl.setAttribute("hidden", "");
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
1594
|
const redirect = e.target.getAttribute("data-api-redirect");
|
|
1567
1595
|
if (redirect) window.location.href = redirect;
|
|
1568
1596
|
if (e.target.hasAttribute("data-api-reload")) window.location.reload();
|
|
1569
1597
|
} catch (err) {
|
|
1570
1598
|
console.error("[Dolphin] API Submit Error:", err);
|
|
1571
|
-
const errorToast = e.target.getAttribute("data-api-error-toast");
|
|
1572
|
-
if (errorToast) this._showToast(errorToast, "error");
|
|
1573
|
-
const errorElId = e.target.getAttribute("data-rt-api-error");
|
|
1574
|
-
if (errorElId) {
|
|
1575
|
-
const duration = parseInt(e.target.getAttribute("data-rt-alert-duration") || "0", 10);
|
|
1576
|
-
this._showAlert(errorElId, duration);
|
|
1577
|
-
const sucElId = e.target.getAttribute("data-rt-api-success");
|
|
1578
|
-
if (sucElId) {
|
|
1579
|
-
const sucEl = document.getElementById(sucElId);
|
|
1580
|
-
if (sucEl) sucEl.setAttribute("hidden", "");
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
1599
|
}
|
|
1584
1600
|
}
|
|
1585
1601
|
}
|
|
1586
1602
|
});
|
|
1587
|
-
const INTERACTION_EVENTS = ["click", "change", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
|
|
1603
|
+
const INTERACTION_EVENTS = ["click", "change", "submit", "input", "keydown", "keyup", "dblclick", "focus", "blur", "mouseenter", "mouseleave"];
|
|
1588
1604
|
INTERACTION_EVENTS.forEach((evtName) => {
|
|
1589
1605
|
this.addDomListener(document, evtName, async (e) => {
|
|
1590
1606
|
if (!e.target || !e.target.closest) return;
|
|
@@ -1615,12 +1631,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1615
1631
|
const apiTarget = apiBtn.getAttribute(`data-api-${evtName}`);
|
|
1616
1632
|
const actionData = apiBtn.getAttribute("data-api-payload");
|
|
1617
1633
|
const parentCtx = this.getClosestContext(apiBtn) || {};
|
|
1618
|
-
|
|
1619
|
-
for (const k in parentCtx) {
|
|
1620
|
-
const escapedK = escapeRegExp(k);
|
|
1621
|
-
resolvedApiTarget = resolvedApiTarget.replace(new RegExp(`\\{\\{${escapedK}\\}\\}`, "g"), parentCtx[k] !== void 0 && parentCtx[k] !== null ? parentCtx[k] : "");
|
|
1622
|
-
}
|
|
1623
|
-
const parts = resolvedApiTarget.trim().split(" ");
|
|
1634
|
+
const parts = apiTarget.trim().split(" ");
|
|
1624
1635
|
const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
|
|
1625
1636
|
const path = parts.length > 1 ? parts[1] : parts[0];
|
|
1626
1637
|
let payload = null;
|
|
@@ -1640,35 +1651,11 @@ function attachDOMBinding(clientProto) {
|
|
|
1640
1651
|
const result = await this.api.request(method, path, payload);
|
|
1641
1652
|
const resultBind = apiBtn.getAttribute("data-api-result");
|
|
1642
1653
|
if (resultBind) this._updateDOM(resultBind, result);
|
|
1643
|
-
const successToast = apiBtn.getAttribute("data-api-toast");
|
|
1644
|
-
if (successToast) this._showToast(successToast, "success");
|
|
1645
|
-
const successElId = apiBtn.getAttribute("data-rt-api-success");
|
|
1646
|
-
if (successElId) {
|
|
1647
|
-
const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
|
|
1648
|
-
this._showAlert(successElId, duration);
|
|
1649
|
-
const errElId = apiBtn.getAttribute("data-rt-api-error");
|
|
1650
|
-
if (errElId) {
|
|
1651
|
-
const errEl = document.getElementById(errElId);
|
|
1652
|
-
if (errEl) errEl.setAttribute("hidden", "");
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
1654
|
const redirect = apiBtn.getAttribute("data-api-redirect");
|
|
1656
1655
|
if (redirect) window.location.href = redirect;
|
|
1657
1656
|
if (apiBtn.hasAttribute("data-api-reload")) window.location.reload();
|
|
1658
1657
|
} catch (err) {
|
|
1659
1658
|
console.error(`[Dolphin] API ${evtName} Error:`, err);
|
|
1660
|
-
const errorToast = apiBtn.getAttribute("data-api-error-toast");
|
|
1661
|
-
if (errorToast) this._showToast(errorToast, "error");
|
|
1662
|
-
const errorElId = apiBtn.getAttribute("data-rt-api-error");
|
|
1663
|
-
if (errorElId) {
|
|
1664
|
-
const duration = parseInt(apiBtn.getAttribute("data-rt-alert-duration") || "0", 10);
|
|
1665
|
-
this._showAlert(errorElId, duration);
|
|
1666
|
-
const sucElId = apiBtn.getAttribute("data-rt-api-success");
|
|
1667
|
-
if (sucElId) {
|
|
1668
|
-
const sucEl = document.getElementById(sucElId);
|
|
1669
|
-
if (sucEl) sucEl.setAttribute("hidden", "");
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
1659
|
}
|
|
1673
1660
|
}
|
|
1674
1661
|
const storeActionBtn = e.target.closest(`[data-store-${evtName}]`);
|
|
@@ -1865,24 +1852,33 @@ function attachDOMBinding(clientProto) {
|
|
|
1865
1852
|
const processNode = (node) => {
|
|
1866
1853
|
if (node.hasAttribute("data-rt-text")) {
|
|
1867
1854
|
const key = node.getAttribute("data-rt-text");
|
|
1868
|
-
if (key
|
|
1855
|
+
if (key) {
|
|
1856
|
+
const val = evaluateExpression(key, processedPayload);
|
|
1857
|
+
if (val !== void 0 && val !== null) node.textContent = val;
|
|
1858
|
+
}
|
|
1869
1859
|
}
|
|
1870
1860
|
if (node.hasAttribute("data-rt-html")) {
|
|
1871
1861
|
const key = node.getAttribute("data-rt-html");
|
|
1872
|
-
if (key
|
|
1873
|
-
|
|
1862
|
+
if (key) {
|
|
1863
|
+
const val = evaluateExpression(key, processedPayload);
|
|
1864
|
+
if (val !== void 0 && val !== null) {
|
|
1865
|
+
node.innerHTML = sanitizeHTML(val);
|
|
1866
|
+
}
|
|
1874
1867
|
}
|
|
1875
1868
|
}
|
|
1876
1869
|
if (node.hasAttribute("data-rt-attr")) {
|
|
1877
1870
|
const attrStr = node.getAttribute("data-rt-attr");
|
|
1878
1871
|
if (attrStr) {
|
|
1879
|
-
attrStr
|
|
1880
|
-
const
|
|
1881
|
-
if (
|
|
1882
|
-
const attrName =
|
|
1883
|
-
const key =
|
|
1884
|
-
if (attrName && key
|
|
1885
|
-
|
|
1872
|
+
splitByUnquotedChar(attrStr, ",").forEach((b) => {
|
|
1873
|
+
const pair = splitFirstUnquotedColon(b);
|
|
1874
|
+
if (pair) {
|
|
1875
|
+
const attrName = pair[0].trim();
|
|
1876
|
+
const key = pair[1].trim();
|
|
1877
|
+
if (attrName && key) {
|
|
1878
|
+
const val = evaluateExpression(key, processedPayload);
|
|
1879
|
+
if (val !== void 0 && val !== null) {
|
|
1880
|
+
node.setAttribute(attrName, val);
|
|
1881
|
+
}
|
|
1886
1882
|
}
|
|
1887
1883
|
}
|
|
1888
1884
|
});
|
|
@@ -1891,13 +1887,13 @@ function attachDOMBinding(clientProto) {
|
|
|
1891
1887
|
if (node.hasAttribute("data-rt-class")) {
|
|
1892
1888
|
const classStr = node.getAttribute("data-rt-class");
|
|
1893
1889
|
if (classStr) {
|
|
1894
|
-
classStr
|
|
1895
|
-
const
|
|
1896
|
-
if (
|
|
1897
|
-
const className =
|
|
1898
|
-
const key =
|
|
1890
|
+
splitByUnquotedChar(classStr, ",").forEach((b) => {
|
|
1891
|
+
const pair = splitFirstUnquotedColon(b);
|
|
1892
|
+
if (pair) {
|
|
1893
|
+
const className = pair[0].trim();
|
|
1894
|
+
const key = pair[1].trim();
|
|
1899
1895
|
const classNames = className.split(/\s+/).filter(Boolean);
|
|
1900
|
-
if (processedPayload
|
|
1896
|
+
if (evaluateExpression(key, processedPayload)) {
|
|
1901
1897
|
classNames.forEach((c) => node.classList.add(c));
|
|
1902
1898
|
} else {
|
|
1903
1899
|
classNames.forEach((c) => node.classList.remove(c));
|
|
@@ -1909,7 +1905,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1909
1905
|
if (node.hasAttribute("data-rt-if")) {
|
|
1910
1906
|
const key = node.getAttribute("data-rt-if");
|
|
1911
1907
|
if (key) {
|
|
1912
|
-
if (processedPayload
|
|
1908
|
+
if (evaluateExpression(key, processedPayload)) {
|
|
1913
1909
|
node.style.display = "";
|
|
1914
1910
|
} else {
|
|
1915
1911
|
node.style.display = "none";
|
|
@@ -1919,7 +1915,7 @@ function attachDOMBinding(clientProto) {
|
|
|
1919
1915
|
if (node.hasAttribute("data-rt-hide")) {
|
|
1920
1916
|
const key = node.getAttribute("data-rt-hide");
|
|
1921
1917
|
if (key) {
|
|
1922
|
-
if (processedPayload
|
|
1918
|
+
if (evaluateExpression(key, processedPayload)) {
|
|
1923
1919
|
node.style.display = "none";
|
|
1924
1920
|
} else {
|
|
1925
1921
|
node.style.display = "";
|
|
@@ -1969,21 +1965,35 @@ function attachDOMBinding(clientProto) {
|
|
|
1969
1965
|
return;
|
|
1970
1966
|
}
|
|
1971
1967
|
resolvingSet.add(src);
|
|
1972
|
-
|
|
1968
|
+
const hashIndex = src.indexOf("#");
|
|
1969
|
+
const url = hashIndex !== -1 ? src.substring(0, hashIndex) : src;
|
|
1970
|
+
const selector = hashIndex !== -1 ? src.substring(hashIndex) : null;
|
|
1971
|
+
let promise = componentPromiseCache.get(url);
|
|
1973
1972
|
if (!promise) {
|
|
1974
|
-
promise = fetch(
|
|
1973
|
+
promise = fetch(url).then((res) => {
|
|
1975
1974
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1976
1975
|
return res.text();
|
|
1977
1976
|
});
|
|
1978
|
-
promise.catch(() => componentPromiseCache.delete(
|
|
1979
|
-
componentPromiseCache.set(
|
|
1977
|
+
promise.catch(() => componentPromiseCache.delete(url));
|
|
1978
|
+
componentPromiseCache.set(url, promise);
|
|
1980
1979
|
}
|
|
1981
1980
|
let content = "";
|
|
1982
1981
|
try {
|
|
1983
1982
|
content = await promise;
|
|
1983
|
+
if (selector && typeof DOMParser !== "undefined") {
|
|
1984
|
+
const parser = new DOMParser();
|
|
1985
|
+
const doc = parser.parseFromString(content, "text/html");
|
|
1986
|
+
const targetEl = doc.querySelector(selector);
|
|
1987
|
+
if (targetEl) {
|
|
1988
|
+
content = targetEl.outerHTML;
|
|
1989
|
+
} else {
|
|
1990
|
+
console.warn(`[Dolphin Component Warning]: Selector "${selector}" not found in imported file "${url}".`);
|
|
1991
|
+
content = `<span style="color:orange;font-weight:bold;">Selector ${selector} not found in ${url}</span>`;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1984
1994
|
} catch (err) {
|
|
1985
|
-
console.error(`[Dolphin Component Error]: Failed to fetch component "${
|
|
1986
|
-
content = `<span style="color:red;font-weight:bold;">Failed to import ${
|
|
1995
|
+
console.error(`[Dolphin Component Error]: Failed to fetch component "${url}":`, err);
|
|
1996
|
+
content = `<span style="color:red;font-weight:bold;">Failed to import ${url}</span>`;
|
|
1987
1997
|
}
|
|
1988
1998
|
el.innerHTML = sanitizeHTML(content);
|
|
1989
1999
|
el.removeAttribute("data-import");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dolphin-client",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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",
|