UNPKG

@triabin/vue-book-reader

Version:

Forked from jinhuan138/vue-book-reader, add some features

230 lines (229 loc) 7.03 kB
const h = { XML: "http://www.w3.org/XML/1998/namespace", SSML: "http://www.w3.org/2001/10/synthesis" }, A = /* @__PURE__ */ new Set([ "article", "aside", "audio", "blockquote", "caption", "details", "dialog", "div", "dl", "dt", "dd", "figure", "footer", "form", "figcaption", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "li", "main", "math", "nav", "ol", "p", "pre", "section", "tr" ]), N = (n) => { var e; const t = n.lang || ((e = n == null ? void 0 : n.getAttributeNS) == null ? void 0 : e.call(n, h.XML, "lang")); return t || (n.parentElement ? N(n.parentElement) : null); }, E = (n) => { var e; const t = (e = n == null ? void 0 : n.getAttributeNS) == null ? void 0 : e.call(n, h.XML, "lang"); return t || (n.parentElement ? E(n.parentElement) : null); }, k = (n = "en", t = "word") => { const e = new Intl.Segmenter(n, { granularity: t }), s = t === "word"; return function* (i, r) { var m; const l = i.join("").replace(/\r\n/g, " ").replace(/\r/g, " ").replace(/\n/g, " "); let g = 0, o = -1, c = 0; const f = Array.from(e.segment(l)), u = []; for (let a = 0; a < f.length; a++) { const S = f[a], d = f[a + 1], b = S.segment.trim(), w = (m = d == null ? void 0 : d.segment) == null ? void 0 : m.trim(), p = /(?:^|\s)([A-Z][a-z]{1,5})\.$/.test(b), x = /^[A-Z]/.test(w || ""); if (p && x) { const L = { index: S.index, segment: S.segment + ((d == null ? void 0 : d.segment) || ""), isWordLike: !0 }; u.push(L), a++; } else u.push(S); } for (const { index: a, segment: S, isWordLike: d } of u) { if (s && !d) continue; for (; c <= a; ) c += i[++o].length; const b = o, w = a - (c - i[o].length), p = a + S.length - 1; if (p < l.length) for (; c <= p; ) c += i[++o].length; const x = o, L = p - (c - i[o].length) + 1; yield [ (g++).toString(), r(b, w, x, L) ]; } }; }, T = (n, t) => { const e = document.implementation.createDocument(h.SSML, "speak"), { lang: s } = t; s && e.documentElement.setAttributeNS(h.XML, "lang", s); const i = (r, l, g) => { if (!r) return; if (r.nodeType === 3) return e.createTextNode(r.textContent); if (r.nodeType === 4) return e.createCDATASection(r.textContent); if (r.nodeType !== 1) return; let o; const c = r.nodeName.toLowerCase(); c === "foliate-mark" ? (o = e.createElementNS(h.SSML, "mark"), o.setAttribute("name", r.dataset.name)) : c === "br" ? o = e.createElementNS(h.SSML, "break") : (c === "em" || c === "strong") && (o = e.createElementNS(h.SSML, "emphasis")); const f = r.lang || r.getAttributeNS(h.XML, "lang"); f && (o || (o = e.createElementNS(h.SSML, "lang")), o.setAttributeNS(h.XML, "lang", f)); const u = r.getAttributeNS(h.SSML, "alphabet") || g; if (!o) { const a = r.getAttributeNS(h.SSML, "ph"); a && (o = e.createElementNS(h.SSML, "phoneme"), u && o.setAttribute("alphabet", u), o.setAttribute("ph", a)); } o || (o = l); let m = r.firstChild; for (; m; ) { const a = i(m, o, u); a && o !== a && o.append(a), m = m.nextSibling; } return o; }; return i(n.firstChild, e.documentElement, t.alphabet), e; }, y = (n, t, e) => { const s = N(n.commonAncestorContainer), i = E(n.commonAncestorContainer), r = k(s, e), l = n.cloneContents(), g = [...t(n, r)], o = [...t(l, r)]; for (const [f, u] of o) { const m = document.createElement("foliate-mark"); m.dataset.name = f, u.insertNode(m); } const c = T(l, { lang: s, alphabet: i }); return { entries: g, ssml: c }; }, M = (n) => !n.toString().trim(); function* C(n) { let t; const e = n.createTreeWalker(n.body, NodeFilter.SHOW_ELEMENT); for (let s = e.nextNode(); s; s = e.nextNode()) { const i = s.tagName.toLowerCase(); A.has(i) && (t && (t.setEndBefore(s), M(t) || (yield t)), t = n.createRange(), t.setStart(s, 0)); } t || (t = n.createRange(), t.setStart(n.body.firstChild ?? n.body, 0)), t.setEndAfter(n.body.lastChild ?? n.body), M(t) || (yield t); } class I { #t = []; #s; #e = -1; #n; constructor(t, e = (s) => s) { this.#s = t, this.#n = e; } current() { if (this.#t[this.#e]) return this.#n(this.#t[this.#e]); } first() { if (this.#t[0]) return this.#e = 0, this.#n(this.#t[0]); } prev() { const t = this.#e - 1; if (this.#t[t]) return this.#e = t, this.#n(this.#t[t]); } next() { const t = this.#e + 1; if (this.#t[t]) return this.#e = t, this.#n(this.#t[t]); for (; ; ) { const { done: e, value: s } = this.#s.next(); if (e) break; if (this.#t.push(s), this.#t[t]) return this.#e = t, this.#n(this.#t[t]); } } find(t) { const e = this.#t.findIndex((s) => t(s)); if (e > -1) return this.#e = e, this.#n(this.#t[e]); for (; ; ) { const { done: s, value: i } = this.#s.next(); if (s) break; if (this.#t.push(i), t(i)) return this.#e = this.#t.length - 1, this.#n(i); } } } class v { #t; #s; #e; #n = new XMLSerializer(); constructor(t, e, s, i) { this.doc = t, this.highlight = s, this.#t = new I(C(t), (r) => { const { entries: l, ssml: g } = y(r, e, i); return this.#s = new Map(l), [g, r]; }); } #r(t, e) { return e ? t.querySelector(`mark[name="${CSS.escape(e)}"`) : null; } #i(t, e) { var r, l; if (!t) return; if (!e) return this.#n.serializeToString(t); const s = document.implementation.createDocument(h.SSML, "speak"); s.documentElement.replaceWith(s.importNode(t.documentElement, !0)); let i = (r = e(s)) == null ? void 0 : r.previousSibling; for (; i; ) { const g = i.previousSibling ?? ((l = i.parentNode) == null ? void 0 : l.previousSibling); i.parentNode.removeChild(i), i = g; } return this.#n.serializeToString(s); } start() { this.#e = null; const [t] = this.#t.first() ?? []; return t ? this.#i(t, (e) => this.#r(e, this.#e)) : this.next(); } resume() { const [t] = this.#t.current() ?? []; return t ? this.#i(t, (e) => this.#r(e, this.#e)) : this.next(); } prev(t) { this.#e = null; const [e, s] = this.#t.prev() ?? []; return t && s && this.highlight(s.cloneRange()), this.#i(e); } next(t) { this.#e = null; const [e, s] = this.#t.next() ?? []; return t && s && this.highlight(s.cloneRange()), this.#i(e); } from(t) { this.#e = null; const [e] = this.#t.find((i) => t.compareBoundaryPoints(Range.END_TO_START, i) <= 0); let s; for (const [i, r] of this.#s.entries()) if (t.compareBoundaryPoints(Range.START_TO_START, r) <= 0) { s = i; break; } return this.#i(e, (i) => this.#r(i, s)); } setMark(t) { const e = this.#s.get(t); e && (this.#e = t, this.highlight(e.cloneRange())); } } export { v as TTS };