dolphin-client 1.0.9 → 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/dolphin-tags.json +28 -0
- package/.vscode/settings.json +5 -0
- package/dist/dolphin-client.js +182 -33
- package/dist/dolphin-client.min.js +19 -19
- package/dist/index.cjs +182 -33
- package/dist/index.js +182 -33
- package/package.json +1 -1
|
@@ -390,6 +390,34 @@
|
|
|
390
390
|
{
|
|
391
391
|
"name": "data-rt-sort",
|
|
392
392
|
"description": "Sorts the rendered data array (e.g., price.asc or popular)."
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"name": "data-api-toast",
|
|
396
|
+
"description": "Inline success message to show as toast after a successful API request."
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"name": "data-api-error-toast",
|
|
400
|
+
"description": "Inline error message to show as toast after a failed API request."
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"name": "data-rt-api-success",
|
|
404
|
+
"description": "ID of a hidden element whose text content is shown as a success toast after a successful API request. Example: data-rt-api-success=\"my-success-msg\""
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
"name": "data-rt-api-error",
|
|
408
|
+
"description": "ID of a hidden element whose text content is shown as an error toast after a failed API request. Example: data-rt-api-error=\"my-error-msg\""
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
"name": "data-rt-api-success-show",
|
|
412
|
+
"description": "ID of an inline alert element to reveal (remove hidden) on API success. Pair with data-rt-alert-duration for auto-hide. Example: data-rt-api-success-show=\"success-alert\""
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"name": "data-rt-api-error-show",
|
|
416
|
+
"description": "ID of an inline alert element to reveal (remove hidden) on API error. Pair with data-rt-alert-duration for auto-hide. Example: data-rt-api-error-show=\"error-alert\""
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"name": "data-rt-alert-duration",
|
|
420
|
+
"description": "Duration in milliseconds before the inline alert auto-hides. Use with data-rt-api-success-show or data-rt-api-error-show. Example: data-rt-alert-duration=\"3000\""
|
|
393
421
|
}
|
|
394
422
|
],
|
|
395
423
|
"valueSets": [
|
package/dist/dolphin-client.js
CHANGED
|
@@ -41,7 +41,7 @@ var DolphinModule = (() => {
|
|
|
41
41
|
target.patch = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("PATCH", pathOrBody, bodyOrOptions, options) : this.request("PATCH", joined, pathOrBody, bodyOrOptions);
|
|
42
42
|
target.del = (pathOrOptions, options) => typeof pathOrOptions === "string" ? this.request("DELETE", pathOrOptions, null, options) : this.request("DELETE", joined, null, pathOrOptions);
|
|
43
43
|
target.request = (method, subPath, body, options) => {
|
|
44
|
-
const finalPath = subPath ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : joined;
|
|
44
|
+
const finalPath = subPath ? joined ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : subPath : joined;
|
|
45
45
|
return this.request(method, finalPath, body, options);
|
|
46
46
|
};
|
|
47
47
|
target.requestDirect = (method, path, body, options) => {
|
|
@@ -225,8 +225,9 @@ var DolphinModule = (() => {
|
|
|
225
225
|
const _isRetry = options._isRetry === true;
|
|
226
226
|
let finalMethod = method.toUpperCase();
|
|
227
227
|
let finalBody = body;
|
|
228
|
+
const hasBody = !["GET", "HEAD"].includes(finalMethod);
|
|
228
229
|
const headers = {
|
|
229
|
-
"Content-Type": "application/json",
|
|
230
|
+
...hasBody ? { "Content-Type": "application/json" } : {},
|
|
230
231
|
...options.headers || {}
|
|
231
232
|
};
|
|
232
233
|
if (["PUT", "PATCH", "DELETE"].includes(finalMethod)) {
|
|
@@ -996,6 +997,82 @@ var DolphinModule = (() => {
|
|
|
996
997
|
function escapeRegExp(str) {
|
|
997
998
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
998
999
|
}
|
|
1000
|
+
function evaluateExpression(expr, ctx) {
|
|
1001
|
+
if (!ctx || typeof ctx !== "object") return void 0;
|
|
1002
|
+
try {
|
|
1003
|
+
const safeCtx = new Proxy(ctx, {
|
|
1004
|
+
has(target, prop) {
|
|
1005
|
+
return true;
|
|
1006
|
+
},
|
|
1007
|
+
get(target, prop) {
|
|
1008
|
+
if (typeof prop === "string") {
|
|
1009
|
+
if (prop in target) return target[prop];
|
|
1010
|
+
if (typeof globalThis !== "undefined" && prop in globalThis) return globalThis[prop];
|
|
1011
|
+
if (typeof window !== "undefined" && prop in window) return window[prop];
|
|
1012
|
+
}
|
|
1013
|
+
return void 0;
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
const fn = new Function("ctx", `with(ctx) { return (${expr}); }`);
|
|
1017
|
+
return fn(safeCtx);
|
|
1018
|
+
} catch {
|
|
1019
|
+
return ctx[expr];
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
function splitByUnquotedChar(str, char) {
|
|
1023
|
+
const parts = [];
|
|
1024
|
+
let current = "";
|
|
1025
|
+
let inSingleQuote = false;
|
|
1026
|
+
let inDoubleQuote = false;
|
|
1027
|
+
let inBacktick = false;
|
|
1028
|
+
let depth = 0;
|
|
1029
|
+
for (let i = 0; i < str.length; i++) {
|
|
1030
|
+
const c = str[i];
|
|
1031
|
+
if (c === "'" && !inDoubleQuote && !inBacktick) {
|
|
1032
|
+
inSingleQuote = !inSingleQuote;
|
|
1033
|
+
} else if (c === '"' && !inSingleQuote && !inBacktick) {
|
|
1034
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1035
|
+
} else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
|
|
1036
|
+
inBacktick = !inBacktick;
|
|
1037
|
+
} else if (c === "(" || c === "[" || c === "{") {
|
|
1038
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
|
|
1039
|
+
} else if (c === ")" || c === "]" || c === "}") {
|
|
1040
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
|
|
1041
|
+
}
|
|
1042
|
+
if (c === char && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
|
|
1043
|
+
parts.push(current);
|
|
1044
|
+
current = "";
|
|
1045
|
+
} else {
|
|
1046
|
+
current += c;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
parts.push(current);
|
|
1050
|
+
return parts;
|
|
1051
|
+
}
|
|
1052
|
+
function splitFirstUnquotedColon(str) {
|
|
1053
|
+
let inSingleQuote = false;
|
|
1054
|
+
let inDoubleQuote = false;
|
|
1055
|
+
let inBacktick = false;
|
|
1056
|
+
let depth = 0;
|
|
1057
|
+
for (let i = 0; i < str.length; i++) {
|
|
1058
|
+
const c = str[i];
|
|
1059
|
+
if (c === "'" && !inDoubleQuote && !inBacktick) {
|
|
1060
|
+
inSingleQuote = !inSingleQuote;
|
|
1061
|
+
} else if (c === '"' && !inSingleQuote && !inBacktick) {
|
|
1062
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1063
|
+
} else if (c === "`" && !inSingleQuote && !inDoubleQuote) {
|
|
1064
|
+
inBacktick = !inBacktick;
|
|
1065
|
+
} else if (c === "(" || c === "[" || c === "{") {
|
|
1066
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth++;
|
|
1067
|
+
} else if (c === ")" || c === "]" || c === "}") {
|
|
1068
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick) depth--;
|
|
1069
|
+
}
|
|
1070
|
+
if (c === ":" && !inSingleQuote && !inDoubleQuote && !inBacktick && depth === 0) {
|
|
1071
|
+
return [str.slice(0, i), str.slice(i + 1)];
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return null;
|
|
1075
|
+
}
|
|
999
1076
|
function resolveTemplate(el) {
|
|
1000
1077
|
const template = el.getAttribute("data-rt-template");
|
|
1001
1078
|
if (!template) return null;
|
|
@@ -1012,9 +1089,26 @@ var DolphinModule = (() => {
|
|
|
1012
1089
|
if (!templateStr.includes("{#if") && !templateStr.includes("{#each")) {
|
|
1013
1090
|
let result = templateStr;
|
|
1014
1091
|
for (let key in context) {
|
|
1015
|
-
const escapedKey = key.replace(/[
|
|
1016
|
-
result = result.replace(new RegExp(
|
|
1017
|
-
}
|
|
1092
|
+
const escapedKey = key.replace(/[.*+?^$${}()|[\]\\]/g, "\\$&");
|
|
1093
|
+
result = result.replace(new RegExp("\\{\\{" + escapedKey + "\\}}", "g"), context[key] !== void 0 && context[key] !== null ? context[key] : "");
|
|
1094
|
+
}
|
|
1095
|
+
result = result.replace(/\{\{([\s\S]*?)\}\}/g, (match, expr) => {
|
|
1096
|
+
const trimmed = expr.trim();
|
|
1097
|
+
if (!trimmed) return "";
|
|
1098
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*(?:\??\.[a-zA-Z_$][a-zA-Z0-9_$]*)+$/.test(trimmed)) {
|
|
1099
|
+
const parts = trimmed.split(/\??\./);
|
|
1100
|
+
let val = context;
|
|
1101
|
+
for (const part of parts) {
|
|
1102
|
+
if (val === void 0 || val === null) {
|
|
1103
|
+
val = void 0;
|
|
1104
|
+
break;
|
|
1105
|
+
}
|
|
1106
|
+
val = val[part];
|
|
1107
|
+
}
|
|
1108
|
+
return val !== void 0 && val !== null ? val : "";
|
|
1109
|
+
}
|
|
1110
|
+
return match;
|
|
1111
|
+
});
|
|
1018
1112
|
return result;
|
|
1019
1113
|
}
|
|
1020
1114
|
try {
|
|
@@ -1134,7 +1228,9 @@ var DolphinModule = (() => {
|
|
|
1134
1228
|
if (typeof document === "undefined") return html;
|
|
1135
1229
|
try {
|
|
1136
1230
|
const parser = new DOMParser();
|
|
1137
|
-
const
|
|
1231
|
+
const hasBodyOrHtml = /<\s*(?:body|html)\b/i.test(html);
|
|
1232
|
+
const parseString = hasBodyOrHtml ? html : `<body>${html}</body>`;
|
|
1233
|
+
const doc = parser.parseFromString(parseString, "text/html");
|
|
1138
1234
|
const body = doc.body;
|
|
1139
1235
|
const sanitizeNode = (el) => {
|
|
1140
1236
|
const tag = el.tagName.toLowerCase();
|
|
@@ -1248,7 +1344,7 @@ var DolphinModule = (() => {
|
|
|
1248
1344
|
});
|
|
1249
1345
|
}
|
|
1250
1346
|
}
|
|
1251
|
-
clientProto.setStoreState = function(storeName, key, val) {
|
|
1347
|
+
clientProto.setStoreState = function(storeName, key, val, originEl) {
|
|
1252
1348
|
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1253
1349
|
if (!this.uiStores.has(storeName)) {
|
|
1254
1350
|
this.uiStores.set(storeName, {});
|
|
@@ -1261,6 +1357,7 @@ var DolphinModule = (() => {
|
|
|
1261
1357
|
if (typeof document !== "undefined") {
|
|
1262
1358
|
const readElements = document.querySelectorAll(`[data-store-read="${storeName}.${key}"]`);
|
|
1263
1359
|
readElements.forEach((el) => {
|
|
1360
|
+
if (el === originEl) return;
|
|
1264
1361
|
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
1265
1362
|
if (el.type === "checkbox") {
|
|
1266
1363
|
el.checked = !!val;
|
|
@@ -1336,20 +1433,49 @@ var DolphinModule = (() => {
|
|
|
1336
1433
|
if (key) return ctx[key];
|
|
1337
1434
|
return ctx;
|
|
1338
1435
|
}
|
|
1339
|
-
current = current.parentElement;
|
|
1436
|
+
current = current.parentElement || current.parentNode;
|
|
1340
1437
|
}
|
|
1341
1438
|
return null;
|
|
1342
1439
|
};
|
|
1343
1440
|
clientProto._executeStoreAction = function(expression, element) {
|
|
1344
1441
|
this.uiStores = this.uiStores || /* @__PURE__ */ new Map();
|
|
1442
|
+
const parentCtx = element && typeof this.getClosestContext === "function" ? this.getClosestContext(element) : null;
|
|
1345
1443
|
const context = new Proxy({}, {
|
|
1346
1444
|
has: (target, prop) => {
|
|
1347
1445
|
return true;
|
|
1348
1446
|
},
|
|
1349
1447
|
get: (target, prop) => {
|
|
1350
1448
|
if (typeof prop === "string") {
|
|
1449
|
+
if (prop === "log") {
|
|
1450
|
+
return (arg) => {
|
|
1451
|
+
if (arg === void 0) {
|
|
1452
|
+
const allStores = {};
|
|
1453
|
+
this.uiStores.forEach((val, key) => {
|
|
1454
|
+
allStores[key] = { ...val };
|
|
1455
|
+
});
|
|
1456
|
+
console.log(`%c\u{1F4CA} [Dolphin All UI Stores]:`, "color: #06b6d4; font-weight: bold;", allStores);
|
|
1457
|
+
} else if (arg && typeof arg === "object" && arg.__isStoreProxy__) {
|
|
1458
|
+
const storeName = arg.__storeName__;
|
|
1459
|
+
const store = this.uiStores.get(storeName);
|
|
1460
|
+
console.log(`%c\u{1F4CA} [Dolphin Store: ${storeName}]:`, "color: #06b6d4; font-weight: bold;", store ? { ...store } : {});
|
|
1461
|
+
} else {
|
|
1462
|
+
console.log(`%c\u{1F4CA} [Dolphin Log]:`, "color: #06b6d4; font-weight: bold;", arg);
|
|
1463
|
+
}
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
if (parentCtx && parentCtx[prop] !== void 0) {
|
|
1467
|
+
return parentCtx[prop];
|
|
1468
|
+
}
|
|
1469
|
+
if (typeof globalThis !== "undefined" && prop in globalThis) {
|
|
1470
|
+
return globalThis[prop];
|
|
1471
|
+
}
|
|
1472
|
+
if (typeof window !== "undefined" && prop in window) {
|
|
1473
|
+
return window[prop];
|
|
1474
|
+
}
|
|
1351
1475
|
return new Proxy({}, {
|
|
1352
1476
|
get: (subTarget, subProp) => {
|
|
1477
|
+
if (subProp === "__storeName__") return prop;
|
|
1478
|
+
if (subProp === "__isStoreProxy__") return true;
|
|
1353
1479
|
if (typeof subProp === "string") {
|
|
1354
1480
|
return this.getStoreState(prop, subProp);
|
|
1355
1481
|
}
|
|
@@ -1391,7 +1517,7 @@ var DolphinModule = (() => {
|
|
|
1391
1517
|
const storeName = parts[0];
|
|
1392
1518
|
const key = parts[1];
|
|
1393
1519
|
const val = e.target.type === "checkbox" ? e.target.checked : e.target.value;
|
|
1394
|
-
this.setStoreState(storeName, key, val);
|
|
1520
|
+
this.setStoreState(storeName, key, val, e.target);
|
|
1395
1521
|
}
|
|
1396
1522
|
}
|
|
1397
1523
|
const rules = e.target.getAttribute("data-rt-validate");
|
|
@@ -1751,24 +1877,33 @@ var DolphinModule = (() => {
|
|
|
1751
1877
|
const processNode = (node) => {
|
|
1752
1878
|
if (node.hasAttribute("data-rt-text")) {
|
|
1753
1879
|
const key = node.getAttribute("data-rt-text");
|
|
1754
|
-
if (key
|
|
1880
|
+
if (key) {
|
|
1881
|
+
const val = evaluateExpression(key, processedPayload);
|
|
1882
|
+
if (val !== void 0 && val !== null) node.textContent = val;
|
|
1883
|
+
}
|
|
1755
1884
|
}
|
|
1756
1885
|
if (node.hasAttribute("data-rt-html")) {
|
|
1757
1886
|
const key = node.getAttribute("data-rt-html");
|
|
1758
|
-
if (key
|
|
1759
|
-
|
|
1887
|
+
if (key) {
|
|
1888
|
+
const val = evaluateExpression(key, processedPayload);
|
|
1889
|
+
if (val !== void 0 && val !== null) {
|
|
1890
|
+
node.innerHTML = sanitizeHTML(val);
|
|
1891
|
+
}
|
|
1760
1892
|
}
|
|
1761
1893
|
}
|
|
1762
1894
|
if (node.hasAttribute("data-rt-attr")) {
|
|
1763
1895
|
const attrStr = node.getAttribute("data-rt-attr");
|
|
1764
1896
|
if (attrStr) {
|
|
1765
|
-
attrStr
|
|
1766
|
-
const
|
|
1767
|
-
if (
|
|
1768
|
-
const attrName =
|
|
1769
|
-
const key =
|
|
1770
|
-
if (attrName && key
|
|
1771
|
-
|
|
1897
|
+
splitByUnquotedChar(attrStr, ",").forEach((b) => {
|
|
1898
|
+
const pair = splitFirstUnquotedColon(b);
|
|
1899
|
+
if (pair) {
|
|
1900
|
+
const attrName = pair[0].trim();
|
|
1901
|
+
const key = pair[1].trim();
|
|
1902
|
+
if (attrName && key) {
|
|
1903
|
+
const val = evaluateExpression(key, processedPayload);
|
|
1904
|
+
if (val !== void 0 && val !== null) {
|
|
1905
|
+
node.setAttribute(attrName, val);
|
|
1906
|
+
}
|
|
1772
1907
|
}
|
|
1773
1908
|
}
|
|
1774
1909
|
});
|
|
@@ -1777,13 +1912,13 @@ var DolphinModule = (() => {
|
|
|
1777
1912
|
if (node.hasAttribute("data-rt-class")) {
|
|
1778
1913
|
const classStr = node.getAttribute("data-rt-class");
|
|
1779
1914
|
if (classStr) {
|
|
1780
|
-
classStr
|
|
1781
|
-
const
|
|
1782
|
-
if (
|
|
1783
|
-
const className =
|
|
1784
|
-
const key =
|
|
1915
|
+
splitByUnquotedChar(classStr, ",").forEach((b) => {
|
|
1916
|
+
const pair = splitFirstUnquotedColon(b);
|
|
1917
|
+
if (pair) {
|
|
1918
|
+
const className = pair[0].trim();
|
|
1919
|
+
const key = pair[1].trim();
|
|
1785
1920
|
const classNames = className.split(/\s+/).filter(Boolean);
|
|
1786
|
-
if (processedPayload
|
|
1921
|
+
if (evaluateExpression(key, processedPayload)) {
|
|
1787
1922
|
classNames.forEach((c) => node.classList.add(c));
|
|
1788
1923
|
} else {
|
|
1789
1924
|
classNames.forEach((c) => node.classList.remove(c));
|
|
@@ -1795,7 +1930,7 @@ var DolphinModule = (() => {
|
|
|
1795
1930
|
if (node.hasAttribute("data-rt-if")) {
|
|
1796
1931
|
const key = node.getAttribute("data-rt-if");
|
|
1797
1932
|
if (key) {
|
|
1798
|
-
if (processedPayload
|
|
1933
|
+
if (evaluateExpression(key, processedPayload)) {
|
|
1799
1934
|
node.style.display = "";
|
|
1800
1935
|
} else {
|
|
1801
1936
|
node.style.display = "none";
|
|
@@ -1805,7 +1940,7 @@ var DolphinModule = (() => {
|
|
|
1805
1940
|
if (node.hasAttribute("data-rt-hide")) {
|
|
1806
1941
|
const key = node.getAttribute("data-rt-hide");
|
|
1807
1942
|
if (key) {
|
|
1808
|
-
if (processedPayload
|
|
1943
|
+
if (evaluateExpression(key, processedPayload)) {
|
|
1809
1944
|
node.style.display = "none";
|
|
1810
1945
|
} else {
|
|
1811
1946
|
node.style.display = "";
|
|
@@ -1855,21 +1990,35 @@ var DolphinModule = (() => {
|
|
|
1855
1990
|
return;
|
|
1856
1991
|
}
|
|
1857
1992
|
resolvingSet.add(src);
|
|
1858
|
-
|
|
1993
|
+
const hashIndex = src.indexOf("#");
|
|
1994
|
+
const url = hashIndex !== -1 ? src.substring(0, hashIndex) : src;
|
|
1995
|
+
const selector = hashIndex !== -1 ? src.substring(hashIndex) : null;
|
|
1996
|
+
let promise = componentPromiseCache.get(url);
|
|
1859
1997
|
if (!promise) {
|
|
1860
|
-
promise = fetch(
|
|
1998
|
+
promise = fetch(url).then((res) => {
|
|
1861
1999
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1862
2000
|
return res.text();
|
|
1863
2001
|
});
|
|
1864
|
-
promise.catch(() => componentPromiseCache.delete(
|
|
1865
|
-
componentPromiseCache.set(
|
|
2002
|
+
promise.catch(() => componentPromiseCache.delete(url));
|
|
2003
|
+
componentPromiseCache.set(url, promise);
|
|
1866
2004
|
}
|
|
1867
2005
|
let content = "";
|
|
1868
2006
|
try {
|
|
1869
2007
|
content = await promise;
|
|
2008
|
+
if (selector && typeof DOMParser !== "undefined") {
|
|
2009
|
+
const parser = new DOMParser();
|
|
2010
|
+
const doc = parser.parseFromString(content, "text/html");
|
|
2011
|
+
const targetEl = doc.querySelector(selector);
|
|
2012
|
+
if (targetEl) {
|
|
2013
|
+
content = targetEl.outerHTML;
|
|
2014
|
+
} else {
|
|
2015
|
+
console.warn(`[Dolphin Component Warning]: Selector "${selector}" not found in imported file "${url}".`);
|
|
2016
|
+
content = `<span style="color:orange;font-weight:bold;">Selector ${selector} not found in ${url}</span>`;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
1870
2019
|
} catch (err) {
|
|
1871
|
-
console.error(`[Dolphin Component Error]: Failed to fetch component "${
|
|
1872
|
-
content = `<span style="color:red;font-weight:bold;">Failed to import ${
|
|
2020
|
+
console.error(`[Dolphin Component Error]: Failed to fetch component "${url}":`, err);
|
|
2021
|
+
content = `<span style="color:red;font-weight:bold;">Failed to import ${url}</span>`;
|
|
1873
2022
|
}
|
|
1874
2023
|
el.innerHTML = sanitizeHTML(content);
|
|
1875
2024
|
el.removeAttribute("data-import");
|