word-marker
Version:
The library used to tag web page text, which can store tag information.
507 lines (506 loc) • 15.9 kB
JavaScript
/** wordMarker v1.1.16
* (c) 2025-7-23
* @license MIT
*/
const ot = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
__proto__: null
}, Symbol.toStringTag, { value: "Module" })), E = "data-mark-id", B = (e) => {
let r = "";
for (let t = 0; t < e; t++) {
const s = Math.random() * 62 | 0;
s < 10 ? r += s : s < 36 ? r += String.fromCharCode(65 + s - 10) : r += String.fromCharCode(97 + s - 36);
}
return r;
}, y = (e) => Object.prototype.toString.call(e) === "[object Text]", K = (e, r = 50) => {
let t = null;
return () => {
t && clearTimeout(t), t = setTimeout(() => {
e(), t = null;
}, r);
};
}, Y = (e) => {
const r = /translateY\((\d+(\.\d+)?)px\)/.exec(e.style.transform);
return r ? Number(r[1]) : 0;
}, z = (e, r) => {
let t = e.parentElement;
return t != null && t.parentElement && t.parentElement !== r && (t = t.parentElement), Array.from(t.children).map((s) => s.localName).join(",");
}, _ = (e, r) => {
var s;
let t = e.parentElement;
return (t == null ? void 0 : t.parentElement) === r ? "" : ((s = t.parentElement) == null ? void 0 : s.textContent) || "";
}, Q = (e, r, t = 1) => {
const s = document.createElement("div");
s.setAttribute("style", `position: absolute; top: 0; left: 0; z-index: ${t}; pointer-events: none;`);
const n = document.createElement("canvas");
return n.width = e.scrollWidth, n.height = r ? window.innerHeight * 3 : e.scrollHeight, s.appendChild(n), e.appendChild(s), n;
}, V = (e) => {
const r = [];
let t = e.parentElement;
for (; t; )
r.push(t), t = (t == null ? void 0 : t.parentElement) || null;
return r;
}, Z = (e, r) => {
let t = e.parentElement;
for (; t; ) {
if (r.includes(t))
return t;
t = (t == null ? void 0 : t.parentElement) || null;
}
return null;
}, q = (e, r) => {
let t = e == null ? void 0 : e.parentElement;
for (; t; ) {
if (t === r)
return t;
t = (t == null ? void 0 : t.parentElement) || null;
}
return null;
};
let R = !1, M = !1;
const X = (e, r, t, s) => {
const n = [];
return !e || R || Array.from(e.childNodes).forEach((o) => {
R || (o.nodeType === 1 ? n.push(...X(o, r, t, s)) : o.nodeType === 3 && !(s != null && s(o)) && (!M && o === r && (M = !0), M && n.push(o)), o === t && (R = !0));
}), n;
}, tt = (e, r, t) => {
if (!e || !r)
return [];
if (e === r)
return [e];
const s = V(e), n = Z(r, s), i = q(e, n), o = q(r, n);
if (!i || !o)
return [];
const l = [];
let c = i;
for (; c !== o.nextSibling; )
R = !1, M = !1, l.push(...X(c, e, r, t)), c = c == null ? void 0 : c.nextSibling;
return l;
}, H = (e, r, t) => {
const s = e.canvas;
e.fillStyle = t.color, e.globalAlpha = t.globalAlpha, e.clearRect(0, 0, s.width, s.height);
const n = Y(s);
r.forEach((i) => {
i.range.forEach((o) => {
const { x: l, y: c, width: h, height: d } = o;
if (c >= n && c <= n + window.innerHeight * 3) {
const w = c - n;
t.mark ? t.mark(e, { x: l, y: w, width: h, height: d }) : e.fillRect(l, w, h, d);
}
});
});
}, F = (e, r, t, s, n) => {
const i = r.findIndex((l) => l.id === s);
if (i > -1) {
const l = r.splice(i, 1)[0];
if (l.startEle) {
const c = k(l.startEle.parentElement, n.attribute || E);
r.find((h) => h.startEleId === c) || $(l.startEle.parentElement, E);
}
if (l.endEle) {
const c = k(l.endEle.parentElement, n.attribute || E);
r.find((h) => h.endEleId === c) || $(l.endEle.parentElement, E);
}
}
const o = t.findIndex((l) => l.id === s);
o > -1 && (t.splice(o, 1), H(e, t, n));
}, G = (e, r, t, s) => {
const n = r.getBoundingClientRect(), i = t.getClientRects(), o = [];
for (let l = 0; l < i.length; l++) {
const c = i[l], h = c.left - n.left, d = c.top - n.top, w = c.right - c.left, b = c.bottom - c.top;
if (w === 0 || b === 0)
continue;
const A = Y(e.canvas), a = d - A;
s == null || s({ x: h, y: a, width: w, height: b }), o.push({ x: h, y: d, width: w, height: b });
}
return o;
}, L = (e, r, t, s, n) => {
const i = tt(r.startEle, r.endEle, n.ignoreNode), o = [];
i.forEach((l, c) => {
const h = document.createRange();
h.setStart(l, c === 0 ? r.startOffset : 0);
let d = c === i.length - 1 ? r.endOffset : l.data.length;
d > l.length && (d = l.length), h.setEnd(l, d);
const w = G(e, s, h, (b) => {
const { x: A, y: a, width: f, height: g } = b;
n.mark ? n.mark(e, { x: A, y: a, width: f, height: g }) : e.fillRect(A, a, f, g);
});
o.push(...w);
}), t.push({
id: r.id,
range: o,
message: r.message
});
}, k = (e, r) => {
var t;
return !e || !r ? "" : ((t = e.getAttribute) == null ? void 0 : t.call(e, r)) || "";
}, $ = (e, r) => {
if (!(!e || !r))
return e.removeAttribute(r);
}, J = (e = E, r) => !!document.querySelector(`[${e}="${r}"]`), O = (e, r, t) => {
e && r && (e == null || e.setAttribute(r, t || ""));
};
function et(e, r) {
const t = nt(e, r);
if (!t)
return;
const { firstNode: s, endNode: n } = t;
let i = document.createRange();
i.setStart(s.node, s.index), i.setEnd(n.node, n.index);
let o = window.getSelection();
o && (o.removeAllRanges(), o.addRange(i));
}
function D(e, r) {
var t;
if ((t = e.textContent) != null && t.includes(r)) {
for (const s of Array.from(e.children)) {
let n = D(s, r);
if (n)
return D(n, r);
}
return e;
}
}
function j(e, r, t, s = 0) {
var n, i, o;
for (const l of Array.from(e.childNodes)) {
if (y(l))
s += ((n = l.textContent) == null ? void 0 : n.length) || 0;
else {
const c = j(l, r, t, s);
if (typeof c == "number")
s = c;
else
return c;
}
if (y(l)) {
if (t === "start" && s > r)
return {
index: r - (s - (((i = l.textContent) == null ? void 0 : i.length) || 0)),
node: l
};
if (t === "end" && s >= r)
return {
index: r - (s - (((o = l.textContent) == null ? void 0 : o.length) || 0)),
node: l
};
}
}
return s;
}
function nt(e, r) {
var o, l;
if (!r || !((o = e.textContent) != null && o.includes(r)))
return;
const t = D(e, r);
if (!t)
return;
const s = ((l = t.textContent) == null ? void 0 : l.indexOf(r)) || 0, n = j(t, s, "start"), i = j(t, s + r.length, "end");
if (!(typeof n == "number" || typeof i == "number"))
return {
firstNode: n,
endNode: i
};
}
const U = (e, r) => {
const { startEle: t, endEle: s } = e;
if (!t || !s || !y(t) || !y(s))
return;
let n = "", i = "";
r ? (n = k(t.parentElement, r), i = k(s.parentElement, r)) : (n = k(t.parentElement, E), n || (n = B(10), O(t.parentElement, E, n)), i = k(s.parentElement, E), i || (i = B(10), O(s.parentElement, E, i))), e.startEleId = n, e.endEleId = i;
}, rt = (e, r) => {
const t = r.getRangeAt(0), s = t.startContainer, n = t.endContainer;
return !y(s) || !y(n) ? void 0 : {
id: B(10),
startEle: s,
startOffset: t.startOffset,
startText: s.data,
startBrother: z(s, e),
startParentText: _(s, e),
endEle: n,
endOffset: t.endOffset,
endText: n.data,
endBrother: z(n, e),
endParentText: _(n, e),
text: r.toString(),
message: "",
single: s === n
};
}, I = (e) => {
const r = [];
return Array.from(e.childNodes).forEach((s) => {
s.nodeType === 1 ? r.push(...I(s)) : s.nodeType === 3 && r.push(s);
}), r;
}, S = (e, r) => {
const { startEle: t, startEleId: s, endEle: n, endEleId: i } = e;
if (t && n && y(t) && y(n) && r === E) {
const o = k(t.parentElement, E);
s && !o && O(t.parentElement, E, s);
const l = k(n.parentElement, E);
i && !l && O(n.parentElement, E, i);
}
}, W = (e, r, t) => {
if (e.nodeType !== 1)
return;
const s = e.textContent;
for (const n of r) {
if (!y(n.startEle)) {
if (n.startEleId && J(t, n.startEleId)) {
if (n.startEleId === k(e, t)) {
const i = I(e);
for (const o of i)
if (o.data === n.startText) {
n.startEle = o, n.single && (n.endEle = n.startEle, S(n, t));
break;
}
}
} else if (!n.startParentText || n.startParentText === s) {
if (Array.from(e == null ? void 0 : e.children).map((l) => l.localName).join(",") !== n.startBrother)
continue;
const o = I(e);
for (const l of o)
if (l.data === n.startText) {
n.startEle = l, n.single && (n.endEle = n.startEle, S(n, t));
break;
}
}
}
if (!n.single && !y(n.endEle)) {
if (n.endEleId && J(t, n.endEleId)) {
if (n.endEleId === k(e, t)) {
const i = I(e);
for (const o of i)
if (o.data === n.endText) {
n.endEle = o, S(n, t);
break;
}
}
} else if (!n.endParentText || n.endParentText === s) {
if (Array.from(e == null ? void 0 : e.children).map((l) => l.localName).join(",") !== n.endBrother)
continue;
const o = I(e);
for (const l of o)
if (l.data === n.endText) {
n.endEle = l, S(n, t);
break;
}
}
}
}
}, P = (e, r, t) => {
var o;
const s = t.attribute || E;
let n = !1;
if (r.forEach((l) => {
const c = e.querySelector(`[${s}="${l.startEleId}"]`), h = e.querySelector(`[${s}="${l.endEleId}"]`);
c && c.childNodes.forEach((d) => {
y(d) && d.textContent === l.startText && (l.startEle = d, l.single && (l.endEle = d));
}), h && !l.endEle && h.childNodes.forEach((d) => {
y(d) && d.textContent === l.endText && (l.endEle = d);
}), (!l.startEle || !l.endEle) && (n = !0);
}), !n && !t.tag && !t.attribute)
return;
const i = Array.from(e.childNodes);
for (r.length && W(e, r, t.attribute || E); i.length; ) {
const l = i.shift();
l && ((o = t.tag) == null || o.call(t, l)), r.length && W(l, r, t.attribute || E);
for (let c = 0; c < (l == null ? void 0 : l.childNodes.length); c++)
i.push(l == null ? void 0 : l.childNodes[c]);
}
}, lt = (e, r, t, s, n) => {
const i = e.getContext("2d");
i.clearRect(0, 0, e.width, e.height), r.forEach((o) => {
L(i, o, t, s, n);
});
}, st = {
scrollBy: document,
color: "rgba(224, 108, 117)",
globalAlpha: 0.3,
data: []
};
function it(e, r) {
const t = { ...st, ...r };
let s = t.lazy === void 0 ? e.scrollHeight / 4 > window.innerHeight : t.lazy;
s && e.scrollHeight <= window.innerHeight * 3 && (s = !1);
const n = [], i = Q(e, s, t.zIndex), o = i.getContext("2d");
o.fillStyle = t.color, o.globalAlpha = t.globalAlpha;
let l = JSON.parse(JSON.stringify(t.data));
(t.tag || l.length) && (P(e, l, t), l = l.filter((a) => a.startEle && a.endEle)), l.length && lt(i, l, n, e, t);
let c = "", h = !1;
const d = () => {
var f, g, x;
const a = window.getSelection();
if (a != null && a.toString()) {
h = !0;
const p = rt(e, a);
if (!p)
(f = t.callback) == null || f.call(t);
else {
const m = e.getBoundingClientRect(), v = a.getRangeAt(0), u = v.getBoundingClientRect(), T = G(o, e, v);
(g = t.callback) == null || g.call(t, p, {
x: u.x - m.x,
y: u.y - m.y,
width: u.width,
height: u.height,
range: T
});
}
} else
(x = t.callback) == null || x.call(t);
}, w = (a, f) => {
var g;
if (c !== a || f) {
if (H(o, n, t), a) {
const x = n.find((m) => m.id === a), p = Y(i);
if (x)
for (const m of x.range) {
const { x: v, y: u, width: T, height: C } = m;
let N = u;
u >= p && u <= p + window.innerHeight * 3 && (N = u - p), (g = t.highlight) == null || g.call(t, o, { x: v, y: N, width: T, height: C });
}
}
c = a;
}
}, b = K(() => {
const a = e.getBoundingClientRect();
let f = 0;
a.y >= -window.innerHeight ? f = 0 : a.y <= window.innerHeight * 3 - e.scrollHeight ? f = e.scrollHeight - window.innerHeight * 3 : f = -window.innerHeight - a.y, i.style.transform = `translateY(${f}px)`, H(o, n, t), c && w(c, !0);
});
t.callback && e.addEventListener("mouseup", d), s && (t.scrollBy.addEventListener("scroll", b), b());
let A = "";
return {
// 自动选中文本
selectText: et,
// 触发标记动作
triggerMarker: d,
// 获取完整的标记数据
getActualMarkData() {
return l.map((a) => ({ ...a }));
},
/**
* 获取所有的标记数据
* @returns 获取标记数据
*/
getMarkData() {
return JSON.parse(JSON.stringify(l, (a, f) => {
if ((f == null ? void 0 : f.nodeType) !== 3)
return f;
}));
},
/**
* 添加标记
* @param data
*/
addMark(a) {
if (Array.isArray(a)) {
l.push(...a);
for (const f of a)
(!y(f.startEle) || !y(f.endEle)) && P(e, l, t), U(f, t.attribute), L(o, f, n, e, t);
} else
l.push(a), (!y(a.startEle) || !y(a.endEle)) && P(e, l, t), U(a, t.attribute), L(o, a, n, e, t);
},
/**
* 修改标记备注
* @param id
* @param msg
*/
modifyMark(a, f) {
const g = l.find((p) => p.id === a);
g && (g.message = f);
const x = n.find((p) => p.id === a);
x && (x.message = f);
},
/**
* 根据 ID 获取标记位置
* @param id
* @returns
*/
getPosition(a) {
const f = n.find((g) => g.id === a);
if (f) {
let g = 1 / 0, x = 1 / 0;
for (const p of f.range)
g = Math.min(g, p.x), x = Math.min(x, p.y);
return { x: g, y: x, range: f.range };
}
},
/**
* 根据 ID 删除标记
* @param id
*/
deleteMark(a) {
if (Array.isArray(a))
for (const f of a)
F(o, l, n, f, t);
else
F(o, l, n, a, t);
},
/**
* 根据 x y 获取该位置是否有标记
* @param x
* @param y
* @returns
*/
checkMark(a, f) {
if (h) {
h = !1;
return;
}
const g = e.getBoundingClientRect(), x = a - window.scrollX - g.left, p = f - window.scrollY - g.top, m = [];
let v = !1;
for (const u of n)
for (const T of u.range)
if (x >= T.x && x <= T.x + T.width && p >= T.y && p <= T.y + T.height) {
const C = l.find((N) => N.id === u.id);
C && (m.push(C), v || (v = A === C.id));
}
if (!m.length) {
A = "";
return;
}
if (!v)
return A = m[0].id, { ...m[0] };
for (let u = 0; u < m.length; u++)
if (m[u].id === A)
return u === m.length - 1 ? (A = m[0].id, { ...m[0] }) : (A = m[u + 1].id, { ...m[u + 1] });
},
/**
* 高亮标记
* @param id
*/
highlightMark: w,
/**
* 重新刷新标记
*/
refresh() {
H(o, n, t);
},
/**
* 清除所有标记
*/
clear() {
l = [], n.length = 0, o.clearRect(0, 0, i.width, i.height);
const a = document.querySelectorAll(`[${E}]`);
a.length && Array.from(a).forEach((f) => {
$(f, E);
});
},
/**
* 销毁所有事件
*/
destroy() {
var a;
t.callback && e.removeEventListener("mouseup", d), s && t.scrollBy.removeEventListener("scroll", b), this.clear(), (a = i.parentElement) == null || a.remove();
},
/**
* @deprecated 请使用 destroy 方法
*/
destory() {
var a;
t.callback && e.removeEventListener("mouseup", d), s && t.scrollBy.removeEventListener("scroll", b), this.clear(), (a = i.parentElement) == null || a.remove();
}
};
}
export {
ot as WordMarker,
it as default
};