@broberg/cms-inline-edit 0.6.0 → 0.6.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/dist/index.cjs +35 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +35 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -67,6 +67,10 @@ function isExternalHost(raw, currentHost) {
|
|
|
67
67
|
return false;
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
function isDangerousUrl(raw) {
|
|
71
|
+
const v = (raw || "").replace(/[\u0000-\u0020]/g, "");
|
|
72
|
+
return /^(?:javascript|data|vbscript):/i.test(v);
|
|
73
|
+
}
|
|
70
74
|
|
|
71
75
|
// src/token-safe.ts
|
|
72
76
|
var TEXT_NODE = 3;
|
|
@@ -125,6 +129,7 @@ var DEFAULT_LABELS = {
|
|
|
125
129
|
linkNewTab: "\xC5bn i ny fane",
|
|
126
130
|
linkSchemeless: "<b>{v}</b> l\xE6ses som en side p\xE5 <b>dette</b> site \u2014 ikke som en adresse ude p\xE5 nettet.",
|
|
127
131
|
linkSchemelessFix: "Tilf\xF8j https://",
|
|
132
|
+
linkBlocked: "<b>{v}</b> kan ikke bruges som link \u2014 adresser af den type kan k\xF8re kode p\xE5 sitet.",
|
|
128
133
|
linkInsert: "Inds\xE6t link",
|
|
129
134
|
linkSave: "Gem \xE6ndring",
|
|
130
135
|
linkCancel: "Annull\xE9r",
|
|
@@ -327,10 +332,12 @@ function showIdlePill() {
|
|
|
327
332
|
removeIdlePill();
|
|
328
333
|
const wrap = document.createElement("div");
|
|
329
334
|
wrap.setAttribute("data-cms-inline-edit-idle", "");
|
|
335
|
+
wrap.setAttribute("data-testid", "inline-edit-idle");
|
|
330
336
|
wrap.style.cssText = "position:fixed;bottom:16px;left:16px;display:inline-flex;align-items:stretch;z-index:2147483647;box-shadow:0 4px 16px rgba(0,0,0,.3);border-radius:999px;overflow:hidden;";
|
|
331
337
|
const edit = document.createElement("button");
|
|
332
338
|
edit.type = "button";
|
|
333
339
|
edit.setAttribute("data-cms-inline-edit-enter", "");
|
|
340
|
+
edit.setAttribute("data-testid", "inline-edit-enter");
|
|
334
341
|
edit.style.cssText = "display:inline-flex;align-items:center;gap:7px;background:#1c2027;color:#fff;font:600 12px system-ui,sans-serif;padding:8px 14px;border:none;cursor:pointer;";
|
|
335
342
|
const editText = document.createElement("span");
|
|
336
343
|
editText.textContent = options.connectLabel;
|
|
@@ -339,6 +346,7 @@ function showIdlePill() {
|
|
|
339
346
|
const out = document.createElement("button");
|
|
340
347
|
out.type = "button";
|
|
341
348
|
out.setAttribute("data-cms-inline-edit-logout", "");
|
|
349
|
+
out.setAttribute("data-testid", "inline-edit-logout");
|
|
342
350
|
out.textContent = options.logoutLabel;
|
|
343
351
|
out.style.cssText = "background:#141821;color:#9aa4b2;font:600 11px system-ui,sans-serif;padding:8px 12px;border:none;border-left:1px solid rgba(255,255,255,.12);cursor:pointer;";
|
|
344
352
|
out.addEventListener("click", () => {
|
|
@@ -591,7 +599,7 @@ function renderLinkDialog() {
|
|
|
591
599
|
}
|
|
592
600
|
const existingRef = linkEditing?.getAttribute("data-cms-ref") ?? "";
|
|
593
601
|
const onPageTab = !editing || !!existingRef;
|
|
594
|
-
d.innerHTML = `<div style="display:flex;gap:4px;padding:8px 8px 0"><button type="button" data-testid="inline-link-tab-page" data-tab="page" style="${tabCss(onPageTab)}">${L.linkTabPage}</button><button type="button" data-testid="inline-link-tab-url" data-tab="url" style="${tabCss(!onPageTab)}">${L.linkTabUrl}</button></div><div style="padding:10px 12px 12px"><div data-pane="page" style="display:${onPageTab ? "block" : "none"}"><input data-testid="inline-link-search" data-role="search" placeholder="${L.linkSearch}" style="${fieldCss()}"><div data-role="current" data-testid="inline-link-current" style="display:none;margin-top:8px;padding:7px 10px;background:rgba(0,178,255,.08);border:1px solid rgba(0,178,255,.28);border-radius:8px;font-size:11.5px;color:#c9d1dd;line-height:1.45"></div><div data-role="list" data-testid="inline-link-list" style="margin-top:8px;max-height:196px;overflow-y:auto;border:1px solid #3a3f4a;border-radius:8px;background:#141821"></div></div><div data-pane="url" style="display:${onPageTab ? "none" : "block"}"><label style="${labelCss()}">${L.linkUrl}</label><input data-testid="inline-link-url" data-role="url" placeholder="https://\u2026" style="${fieldCss()}"><div data-role="schemeless" data-testid="inline-link-schemeless" style="
|
|
602
|
+
d.innerHTML = `<div style="display:flex;gap:4px;padding:8px 8px 0"><button type="button" data-testid="inline-link-tab-page" data-tab="page" style="${tabCss(onPageTab)}">${L.linkTabPage}</button><button type="button" data-testid="inline-link-tab-url" data-tab="url" style="${tabCss(!onPageTab)}">${L.linkTabUrl}</button></div><div style="padding:10px 12px 12px"><div data-pane="page" style="display:${onPageTab ? "block" : "none"}"><input data-testid="inline-link-search" data-role="search" placeholder="${L.linkSearch}" style="${fieldCss()}"><div data-role="current" data-testid="inline-link-current" style="display:none;margin-top:8px;padding:7px 10px;background:rgba(0,178,255,.08);border:1px solid rgba(0,178,255,.28);border-radius:8px;font-size:11.5px;color:#c9d1dd;line-height:1.45"></div><div data-role="list" data-testid="inline-link-list" style="margin-top:8px;max-height:196px;overflow-y:auto;border:1px solid #3a3f4a;border-radius:8px;background:#141821"></div></div><div data-pane="url" style="display:${onPageTab ? "none" : "block"}"><label style="${labelCss()}">${L.linkUrl}</label><input data-testid="inline-link-url" data-role="url" placeholder="https://\u2026" style="${fieldCss()}"><div data-role="schemeless" data-testid="inline-link-schemeless" style="${noticeCss("schemeless")}"><p data-role="schemeless-text" style="margin:0;font-size:11.5px;line-height:1.5"></p><button type="button" data-testid="inline-link-add-scheme" data-role="add-scheme" style="${btnCss(false)};margin-top:7px;height:26px;font-size:12px">${L.linkSchemelessFix}</button></div></div><label style="${labelCss()}">${L.linkText}</label><input data-testid="inline-link-text" data-role="text" placeholder="${L.linkTextAuto}" style="${fieldCss()}"><p data-role="hint" style="font-size:11.5px;color:#9aa3b2;margin:6px 0 0;line-height:1.5"></p><button type="button" data-role="newtab" data-testid="inline-link-newtab" aria-pressed="false" style="display:flex;align-items:center;gap:8px;margin-top:12px;background:none;border:none;padding:0;cursor:pointer;font-family:inherit;font-size:12.5px;color:#c9d1dd"><span data-role="newtab-box" style="${checkboxCss(false)}"></span><span>${L.linkNewTab}</span></button><div data-role="live" style="display:flex;gap:8px;margin-top:12px;padding:9px 10px;background:rgba(0,178,255,.08);border:1px solid rgba(0,178,255,.28);border-radius:8px"><p style="margin:0;font-size:11.5px;color:#c9d1dd;line-height:1.55">${L.linkLiveHint}</p></div><div style="display:flex;gap:8px;align-items:center;margin-top:14px"><div data-role="remove"></div><div style="flex:1"></div><button type="button" data-testid="inline-link-cancel" data-role="cancel" style="${btnCss(false)}">${L.linkCancel}</button><button type="button" data-testid="inline-link-submit" data-role="submit" style="${btnCss(true)}" disabled>${editing ? L.linkSave : L.linkInsert}</button></div></div>`;
|
|
595
603
|
const q = (role) => d.querySelector(`[data-role="${role}"]`);
|
|
596
604
|
const text = q("text");
|
|
597
605
|
const urlIn = q("url");
|
|
@@ -736,14 +744,29 @@ function syncLinkDialog() {
|
|
|
736
744
|
q("live").style.display = onPage ? "flex" : "none";
|
|
737
745
|
q("submit").disabled = onPage ? !linkPicked : !q("url").value.trim();
|
|
738
746
|
const raw = q("url").value;
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
747
|
+
const dangerous = !onPage && isDangerousUrl(raw);
|
|
748
|
+
const doubtful = !onPage && !dangerous && isSchemeless(raw);
|
|
749
|
+
const notice = q("schemeless");
|
|
750
|
+
const show = dangerous || doubtful;
|
|
751
|
+
if (dangerous) {
|
|
752
|
+
notice.setAttribute("style", noticeCss("blocked"));
|
|
753
|
+
notice.setAttribute("data-variant", "blocked");
|
|
754
|
+
q("schemeless-text").innerHTML = uiLabels.linkBlocked.replace(
|
|
755
|
+
"{v}",
|
|
756
|
+
escapeHtml(raw.trim())
|
|
757
|
+
);
|
|
758
|
+
q("submit").disabled = true;
|
|
759
|
+
} else if (doubtful) {
|
|
760
|
+
notice.setAttribute("style", noticeCss("schemeless"));
|
|
761
|
+
notice.setAttribute("data-variant", "schemeless");
|
|
742
762
|
q("schemeless-text").innerHTML = uiLabels.linkSchemeless.replace(
|
|
743
763
|
"{v}",
|
|
744
764
|
escapeHtml(raw.trim())
|
|
745
765
|
);
|
|
746
766
|
}
|
|
767
|
+
notice.style.display = show ? "block" : "none";
|
|
768
|
+
q("schemeless-text").style.color = dangerous ? "#f5c6c6" : "#e8d6b0";
|
|
769
|
+
q("add-scheme").style.display = doubtful ? "" : "none";
|
|
747
770
|
if (!onPage && !linkNewTabTouched) {
|
|
748
771
|
linkNewTab = isExternalHost(raw, typeof location !== "undefined" ? location.host : "");
|
|
749
772
|
}
|
|
@@ -768,6 +791,7 @@ function applyLink() {
|
|
|
768
791
|
const own = q("text").value.trim();
|
|
769
792
|
const href = onPage ? linkPicked?.path ?? "" : q("url").value.trim();
|
|
770
793
|
if (!href) return;
|
|
794
|
+
if (isDangerousUrl(href)) return;
|
|
771
795
|
const ref = onPage && linkPicked ? `${linkPicked.collection}:${linkPicked.slug}` : "";
|
|
772
796
|
const label = own || (onPage ? linkPicked?.title ?? href : href);
|
|
773
797
|
const applyTarget = (el) => setLinkTarget(el, linkNewTab);
|
|
@@ -809,6 +833,7 @@ function escapeHtml(v) {
|
|
|
809
833
|
return v.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
810
834
|
}
|
|
811
835
|
var tabCss = (on) => "flex:1;background:" + (on ? "#2a2f38" : "none") + ";border:1px solid " + (on ? "#3a3f4a" : "transparent") + ";color:" + (on ? "#fff" : "#9aa3b2") + ";height:32px;border-radius:7px;font-size:13px;cursor:pointer;font-weight:500;font-family:inherit;";
|
|
836
|
+
var noticeCss = (variant) => "display:none;margin-top:8px;padding:8px 10px;border-radius:8px;background:" + (variant === "blocked" ? "rgba(255,90,90,.10)" : "rgba(255,176,32,.10)") + ";border:1px solid " + (variant === "blocked" ? "rgba(255,90,90,.38)" : "rgba(255,176,32,.34)") + ";";
|
|
812
837
|
var checkboxCss = (on) => "width:16px;height:16px;flex:0 0 16px;border-radius:4px;box-sizing:border-box;display:inline-block;" + (on ? `background:#00b2ff;border:1px solid #00b2ff;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M4 8.5l2.6 2.6L12 5.8' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");background-size:100% 100%;` : "background:#141821;border:1px solid #3a3f4a;");
|
|
813
838
|
var fieldCss = () => "width:100%;background:#141821;border:1px solid #3a3f4a;color:#fff;height:34px;border-radius:7px;padding:0 10px;font-size:13.5px;outline:none;font-family:inherit;box-sizing:border-box;";
|
|
814
839
|
var labelCss = () => "font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:#9aa3b2;margin:12px 0 5px;display:block;";
|
|
@@ -1100,6 +1125,10 @@ function serializeInlineNode(child) {
|
|
|
1100
1125
|
break;
|
|
1101
1126
|
case "a": {
|
|
1102
1127
|
const href = el.getAttribute("href") || "";
|
|
1128
|
+
if (isDangerousUrl(href)) {
|
|
1129
|
+
out += inner;
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1103
1132
|
const extras = preservedLinkAttrs(el);
|
|
1104
1133
|
if (extras.length > 0) {
|
|
1105
1134
|
out += `<a ${[`href="${escapeAttr(href)}"`, ...extras].join(" ")}>${inner}</a>`;
|
|
@@ -1173,6 +1202,7 @@ function showPill(el, state) {
|
|
|
1173
1202
|
if (!pill) {
|
|
1174
1203
|
pill = document.createElement("span");
|
|
1175
1204
|
pill.setAttribute("data-cms-inline-edit-pill", "");
|
|
1205
|
+
pill.setAttribute("data-testid", "inline-edit-pill");
|
|
1176
1206
|
document.body.appendChild(pill);
|
|
1177
1207
|
pills.set(el, pill);
|
|
1178
1208
|
}
|
|
@@ -1200,6 +1230,7 @@ function showActiveBadge(options) {
|
|
|
1200
1230
|
const badge = document.createElement("button");
|
|
1201
1231
|
badge.type = "button";
|
|
1202
1232
|
badge.setAttribute("data-cms-inline-edit-badge", "");
|
|
1233
|
+
badge.setAttribute("data-testid", "inline-edit-badge");
|
|
1203
1234
|
badge.style.cssText = "position:fixed;bottom:16px;left:16px;display:inline-flex;align-items:center;gap:7px;background:#1c2027;color:#fff;font:600 12px system-ui,sans-serif;padding:8px 14px;border-radius:999px;z-index:2147483647;box-shadow:0 4px 16px rgba(0,0,0,.3);border:none;cursor:pointer;opacity:.9;transition:opacity .15s;";
|
|
1204
1235
|
const text = document.createElement("span");
|
|
1205
1236
|
text.textContent = options.disconnectLabel;
|