website-highlighter 1.3.0 → 1.4.0

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/README.md CHANGED
@@ -83,7 +83,7 @@ highlightInIframe(iframe, 'text inside the iframe', 'https://example.com')
83
83
 
84
84
  ### `WebsiteHighlighter(text, options)`
85
85
 
86
- Finds the best fuzzy match for `text`, applies the `website-highlighter` custom highlight, and resolves to `{ range, value }`.
86
+ Finds the best fuzzy match for `text`, applies the `website-highlighter` custom highlight, scrolls the matched range into view, and resolves to `{ range, value }`.
87
87
 
88
88
  - `text`: string to search for.
89
89
  - `options.root`: optional DOM node to search. Defaults to `document.body`.
@@ -10,11 +10,11 @@ class I {
10
10
  */
11
11
  getEditDistances(e, t) {
12
12
  var r = new Array(t.length + 1).fill([0, 0]);
13
- for (let i = 0; i < e.length; i++) {
14
- let o = [[i + 1, 0]];
15
- for (let s = 0; s < t.length; s++) {
16
- let a = e[i] != t[s], l = r[s + 1][0] + 1, c = o[s][0] + 1, g = r[s][0] + a, f = Math.min(l, Math.min(c, g)), d = [f, r[s][1]];
17
- l === f ? d[1] = r[s + 1][1] - 1 : c === f && (d[1] = o[s][1] + 1), o.push(d);
13
+ for (let s = 0; s < e.length; s++) {
14
+ let o = [[s + 1, 0]];
15
+ for (let i = 0; i < t.length; i++) {
16
+ let a = e[s] != t[i], l = r[i + 1][0] + 1, c = o[i][0] + 1, g = r[i][0] + a, f = Math.min(l, Math.min(c, g)), d = [f, r[i][1]];
17
+ l === f ? d[1] = r[i + 1][1] - 1 : c === f && (d[1] = o[i][1] + 1), o.push(d);
18
18
  }
19
19
  r = o;
20
20
  }
@@ -30,41 +30,41 @@ class I {
30
30
  * @return {array} Array of best substring matches.
31
31
  */
32
32
  getMatches(e, t) {
33
- let r = this.getEditDistances(e, t), i = [0], o = r[0][0];
33
+ let r = this.getEditDistances(e, t), s = [0], o = r[0][0];
34
34
  for (let a = 1; a < r.length; a++) {
35
35
  let l = r[a][0];
36
- l < o ? (i = [a], o = l) : l == o && i.push(a);
36
+ l < o ? (s = [a], o = l) : l == o && s.push(a);
37
37
  }
38
- let s = [];
39
- for (let a of i) {
38
+ let i = [];
39
+ for (let a of s) {
40
40
  let l = r[a], c = {
41
41
  distance: l[0],
42
42
  start: a - e.length - l[1],
43
43
  //simplification of startPos = endPos − (needleLength + insertions − deletions)
44
44
  end: a
45
45
  };
46
- s.push(c);
46
+ i.push(c);
47
47
  }
48
- return s;
48
+ return i;
49
49
  }
50
50
  }
51
- function T(n, e) {
51
+ function v(n, e) {
52
52
  return new I().getMatches(n, e);
53
53
  }
54
54
  function x(n, e) {
55
- const t = T(n, e);
55
+ const t = v(n, e);
56
56
  let r;
57
57
  for (const a of t)
58
58
  (r === void 0 || a.distance < r.distance) && (r = a);
59
- const i = Math.max(r.start, 0), o = Math.min(Math.max(r.end, i), e.length);
60
- return { value: e.slice(i, o), start: i, end: o, distance: r.distance };
59
+ const s = Math.max(r.start, 0), o = Math.min(Math.max(r.end, s), e.length);
60
+ return { value: e.slice(s, o), start: s, end: o, distance: r.distance };
61
61
  }
62
62
  const y = `(function(){"use strict";class h{getEditDistances(s,i){var t=new Array(i.length+1).fill([0,0]);for(let e=0;e<s.length;e++){let r=[[e+1,0]];for(let l=0;l<i.length;l++){let n=s[e]!=i[l],a=t[l+1][0]+1,o=r[l][0]+1,m=t[l][0]+n,u=Math.min(a,Math.min(o,m)),f=[u,t[l][1]];a===u?f[1]=t[l+1][1]-1:o===u&&(f[1]=r[l][1]+1),r.push(f)}t=r}return t}getMatches(s,i){let t=this.getEditDistances(s,i),e=[0],r=t[0][0];for(let n=1;n<t.length;n++){let a=t[n][0];a<r?(e=[n],r=a):a==r&&e.push(n)}let l=[];for(let n of e){let a=t[n],o={distance:a[0],start:n-s.length-a[1],end:n};l.push(o)}return l}}function g(c,s){return new h().getMatches(c,s)}function d(c,s){const i=g(c,s);let t;for(const n of i)(t===void 0||n.distance<t.distance)&&(t=n);const e=Math.max(t.start,0),r=Math.min(Math.max(t.end,e),s.length);return{value:s.slice(e,r),start:e,end:r,distance:t.distance}}self.addEventListener("message",c=>{const{id:s,query:i,source:t}=c.data;try{self.postMessage({id:s,result:d(i,t)})}catch(e){self.postMessage({id:s,error:e instanceof Error?e.message:String(e)})}})})();
63
- `, E = typeof self < "u" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", y], { type: "text/javascript;charset=utf-8" });
63
+ `, b = typeof self < "u" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", y], { type: "text/javascript;charset=utf-8" });
64
64
  function W(n) {
65
65
  let e;
66
66
  try {
67
- if (e = E && (self.URL || self.webkitURL).createObjectURL(E), !e) throw "";
67
+ if (e = b && (self.URL || self.webkitURL).createObjectURL(b), !e) throw "";
68
68
  const t = new Worker(e, {
69
69
  name: n?.name
70
70
  });
@@ -80,15 +80,15 @@ function W(n) {
80
80
  );
81
81
  }
82
82
  }
83
- let u, S = 0, p = !1, M = "sync";
83
+ let u, H = 0, p = !1, M = "sync";
84
84
  const h = /* @__PURE__ */ new Map();
85
- function H(n) {
85
+ function S(n) {
86
86
  for (const { reject: e } of h.values())
87
87
  e(n);
88
88
  h.clear();
89
89
  }
90
- function b(n) {
91
- p = !0, u && (u.terminate(), u = void 0), H(n);
90
+ function E(n) {
91
+ p = !0, u && (u.terminate(), u = void 0), S(n);
92
92
  }
93
93
  function C() {
94
94
  if (!(p || typeof Worker > "u")) {
@@ -103,76 +103,76 @@ function C() {
103
103
  const e = h.get(n.data?.id);
104
104
  e && (h.delete(n.data.id), n.data.error ? e.reject(new Error(n.data.error)) : e.resolve(n.data.result));
105
105
  }), u.addEventListener("error", (n) => {
106
- b(n.error ?? new Error(n.message || "Worker matcher failed."));
106
+ E(n.error ?? new Error(n.message || "Worker matcher failed."));
107
107
  }), u.addEventListener("messageerror", () => {
108
- b(new Error("Worker matcher could not deserialize a message."));
108
+ E(new Error("Worker matcher could not deserialize a message."));
109
109
  }), u;
110
110
  }
111
111
  }
112
112
  function O(n, e) {
113
113
  const t = C();
114
114
  if (!t) return;
115
- const r = S++;
116
- return new Promise((i, o) => {
117
- h.set(r, { resolve: i, reject: o });
115
+ const r = H++;
116
+ return new Promise((s, o) => {
117
+ h.set(r, { resolve: s, reject: o });
118
118
  try {
119
119
  t.postMessage({ id: r, query: n, source: e });
120
- } catch (s) {
121
- h.delete(r), o(s);
120
+ } catch (i) {
121
+ h.delete(r), o(i);
122
122
  }
123
123
  });
124
124
  }
125
125
  function D() {
126
126
  return M;
127
127
  }
128
- async function U(n, e) {
128
+ async function N(n, e) {
129
129
  const t = O(n, e);
130
130
  if (t)
131
131
  try {
132
132
  const r = await t;
133
133
  return M = "worker", r;
134
134
  } catch (r) {
135
- b(r);
135
+ E(r);
136
136
  }
137
137
  return M = "sync", x(n, e);
138
138
  }
139
- function j(n, e, t) {
139
+ function U(n, e, t) {
140
140
  const r = n.textContent ?? "";
141
141
  if (!Number.isInteger(e) || !Number.isInteger(t) || e < 0 || e >= t || t > r.length)
142
142
  throw new RangeError(
143
143
  `Invalid range [${e}, ${t}) for text length ${r.length}`
144
144
  );
145
- const i = n.ownerDocument, o = i.createTreeWalker(
145
+ const s = n.ownerDocument, o = s.createTreeWalker(
146
146
  n,
147
- i.defaultView.NodeFilter.SHOW_TEXT
147
+ s.defaultView.NodeFilter.SHOW_TEXT
148
148
  );
149
- let s = 0, a = null, l = 0, c = null, g = 0;
149
+ let i = 0, a = null, l = 0, c = null, g = 0;
150
150
  for (let d = o.nextNode(); d; d = o.nextNode()) {
151
- const m = s + d.data.length;
152
- if (a === null && e >= s && e < m && (a = d, l = e - s), c === null && t > s && t <= m && (c = d, g = t - s), a && c)
151
+ const m = i + d.data.length;
152
+ if (a === null && e >= i && e < m && (a = d, l = e - i), c === null && t > i && t <= m && (c = d, g = t - i), a && c)
153
153
  break;
154
- s = m;
154
+ i = m;
155
155
  }
156
156
  if (!a || !c)
157
157
  throw new Error(
158
158
  "Could not map offsets to the DOM. The DOM may have changed."
159
159
  );
160
- const f = i.createRange();
160
+ const f = s.createRange();
161
161
  return f.setStart(a, l), f.setEnd(c, g), f;
162
162
  }
163
- const w = "website-highlighter", k = `${w}:response`, R = 500;
164
- let N = 0;
163
+ const w = "website-highlighter", R = `${w}:response`, k = 500;
164
+ let j = 0;
165
165
  function $(n) {
166
166
  return new Promise((e) => globalThis.setTimeout(e, n));
167
167
  }
168
168
  function q(n, e) {
169
169
  const t = n.ownerDocument?.defaultView ?? window;
170
170
  return t.MutationObserver ? new Promise((r) => {
171
- let i;
171
+ let s;
172
172
  const o = new t.MutationObserver(() => {
173
- t.clearTimeout(i), o.disconnect(), r(!0);
173
+ t.clearTimeout(s), o.disconnect(), r(!0);
174
174
  });
175
- i = t.setTimeout(() => {
175
+ s = t.setTimeout(() => {
176
176
  o.disconnect(), r(!1);
177
177
  }, e), o.observe(n, {
178
178
  childList: !0,
@@ -185,76 +185,96 @@ function P(n, e, t) {
185
185
  const r = Math.max(n.length, e.length);
186
186
  return r === 0 ? 1 : (r - t) / r;
187
187
  }
188
+ function _(n) {
189
+ const { startContainer: e } = n;
190
+ return e.nodeType === e.ELEMENT_NODE ? e : e.parentElement;
191
+ }
192
+ function V(n, e) {
193
+ const t = n.getClientRects()[0];
194
+ if (!t) {
195
+ _(n)?.scrollIntoView({
196
+ block: "center",
197
+ inline: "nearest",
198
+ behavior: "smooth"
199
+ });
200
+ return;
201
+ }
202
+ const r = e.innerHeight || e.document.documentElement.clientHeight, s = t.top + e.scrollY - r / 2 + t.height / 2;
203
+ e.scrollTo({
204
+ top: Math.max(0, s),
205
+ behavior: "smooth"
206
+ });
207
+ }
188
208
  async function L(n, e) {
189
209
  const t = e.textContent ?? "", r = e.ownerDocument?.defaultView ?? window;
190
210
  if (!r.CSS?.highlights || !r.Highlight) throw new Error("This browser does not support the CSS Custom Highlight API.");
191
- const { start: i, end: o, value: s, distance: a } = await U(n, t);
211
+ const { start: s, end: o, value: i, distance: a } = await N(n, t);
192
212
  return {
193
213
  haystack: t,
194
- range: i < o ? j(e, i, o) : null,
195
- value: s,
214
+ range: s < o ? U(e, s, o) : null,
215
+ value: i,
196
216
  view: r,
197
- score: P(n, s, a)
217
+ score: P(n, i, a)
198
218
  };
199
219
  }
200
- function v({ range: n, value: e, view: t }) {
220
+ function T({ range: n, value: e, view: t }) {
201
221
  if (!n) throw new Error("Could not find text to highlight.");
202
- return t.CSS.highlights.set(w, new t.Highlight(n)), { range: n, value: e };
222
+ return t.CSS.highlights.set(w, new t.Highlight(n)), V(n, t), { range: n, value: e };
203
223
  }
204
- async function _(n, e, t, r, i) {
224
+ async function z(n, e, t, r, s) {
205
225
  let o = 0;
206
226
  for (; ; ) {
207
- const s = await L(n, e);
208
- if (s.score >= t) return v(s);
227
+ const i = await L(n, e);
228
+ if (i.score >= t) return T(i);
209
229
  for (; o < r; ) {
210
- const a = await q(e, i);
230
+ const a = await q(e, s);
211
231
  if (o += 1, a) break;
212
232
  }
213
- if (o >= r) throw new Error(`Could not find "${n}" with threshold ${t}. Best match was "${s.value}".`);
233
+ if (o >= r) throw new Error(`Could not find "${n}" with threshold ${t}. Best match was "${i.value}".`);
214
234
  }
215
235
  }
216
- function A() {
236
+ function F() {
217
237
  return D();
218
238
  }
219
- async function z(n, {
239
+ async function A(n, {
220
240
  root: e = document.body,
221
241
  threshold: t = 0,
222
242
  retries: r = 6,
223
- retryInterval: i = R
243
+ retryInterval: s = k
224
244
  } = {}) {
225
- return t > 0 ? _(n, e, t, r, i) : v(await L(n, e));
245
+ return t > 0 ? z(n, e, t, r, s) : T(await L(n, e));
226
246
  }
227
- function F(n, e, t = "*") {
247
+ function B(n, e, t = "*") {
228
248
  if (!n?.contentWindow) throw new TypeError("Expected an iframe with a contentWindow");
229
- const r = `${Date.now()}-${N++}`, i = n.contentWindow;
249
+ const r = `${Date.now()}-${j++}`, s = n.contentWindow;
230
250
  let o;
231
- function s() {
232
- i.postMessage({
251
+ function i() {
252
+ s.postMessage({
233
253
  type: w,
234
254
  id: r,
235
255
  text: e
236
256
  }, t);
237
257
  }
238
258
  function a(l) {
239
- l.source === i && l.data?.type === k && l.data.id === r && (window.clearInterval(o), window.removeEventListener("message", a));
259
+ l.source === s && l.data?.type === R && l.data.id === r && (window.clearInterval(o), window.removeEventListener("message", a));
240
260
  }
241
- window.addEventListener("message", a), s(), o = window.setInterval(s, R);
261
+ window.addEventListener("message", a), i(), o = window.setInterval(i, k);
242
262
  }
243
263
  if (typeof window < "u") {
244
264
  let n = function(t) {
245
265
  return t.origin === "null" ? "*" : t.origin;
246
266
  }, e = function(t) {
247
267
  t.source?.postMessage({
248
- type: k,
268
+ type: R,
249
269
  id: t.data.id
250
270
  }, n(t));
251
271
  };
252
272
  window.addEventListener("message", (t) => {
253
- t.data?.type === w && (e(t), z(t.data.text, { threshold: 0.9 }).catch((r) => console.error(r)));
273
+ t.data?.type === w && (e(t), A(t.data.text, { threshold: 0.9 }).catch((r) => console.error(r)));
254
274
  });
255
275
  }
256
276
  export {
257
- z as default,
258
- A as getMatcherMode,
259
- F as highlightInIframe
277
+ A as default,
278
+ F as getMatcherMode,
279
+ B as highlightInIframe
260
280
  };
@@ -1,2 +1,2 @@
1
- (function(f,w){typeof exports=="object"&&typeof module<"u"?w(exports):typeof define=="function"&&define.amd?define(["exports"],w):(f=typeof globalThis<"u"?globalThis:f||self,w(f.WebsiteHighlighter={}))})(this,(function(f){"use strict";class w{getEditDistances(e,t){var r=new Array(t.length+1).fill([0,0]);for(let s=0;s<e.length;s++){let o=[[s+1,0]];for(let i=0;i<t.length;i++){let a=e[s]!=t[i],l=r[i+1][0]+1,u=o[i][0]+1,p=r[i][0]+a,h=Math.min(l,Math.min(u,p)),d=[h,r[i][1]];l===h?d[1]=r[i+1][1]-1:u===h&&(d[1]=o[i][1]+1),o.push(d)}r=o}return r}getMatches(e,t){let r=this.getEditDistances(e,t),s=[0],o=r[0][0];for(let a=1;a<r.length;a++){let l=r[a][0];l<o?(s=[a],o=l):l==o&&s.push(a)}let i=[];for(let a of s){let l=r[a],u={distance:l[0],start:a-e.length-l[1],end:a};i.push(u)}return i}}function x(n,e){return new w().getMatches(n,e)}function S(n,e){const t=x(n,e);let r;for(const a of t)(r===void 0||a.distance<r.distance)&&(r=a);const s=Math.max(r.start,0),o=Math.min(Math.max(r.end,s),e.length);return{value:e.slice(s,o),start:s,end:o,distance:r.distance}}const k=`(function(){"use strict";class h{getEditDistances(s,i){var t=new Array(i.length+1).fill([0,0]);for(let e=0;e<s.length;e++){let r=[[e+1,0]];for(let l=0;l<i.length;l++){let n=s[e]!=i[l],a=t[l+1][0]+1,o=r[l][0]+1,m=t[l][0]+n,u=Math.min(a,Math.min(o,m)),f=[u,t[l][1]];a===u?f[1]=t[l+1][1]-1:o===u&&(f[1]=r[l][1]+1),r.push(f)}t=r}return t}getMatches(s,i){let t=this.getEditDistances(s,i),e=[0],r=t[0][0];for(let n=1;n<t.length;n++){let a=t[n][0];a<r?(e=[n],r=a):a==r&&e.push(n)}let l=[];for(let n of e){let a=t[n],o={distance:a[0],start:n-s.length-a[1],end:n};l.push(o)}return l}}function g(c,s){return new h().getMatches(c,s)}function d(c,s){const i=g(c,s);let t;for(const n of i)(t===void 0||n.distance<t.distance)&&(t=n);const e=Math.max(t.start,0),r=Math.min(Math.max(t.end,e),s.length);return{value:s.slice(e,r),start:e,end:r,distance:t.distance}}self.addEventListener("message",c=>{const{id:s,query:i,source:t}=c.data;try{self.postMessage({id:s,result:d(i,t)})}catch(e){self.postMessage({id:s,error:e instanceof Error?e.message:String(e)})}})})();
2
- `,R=typeof self<"u"&&self.Blob&&new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);",k],{type:"text/javascript;charset=utf-8"});function H(n){let e;try{if(e=R&&(self.URL||self.webkitURL).createObjectURL(R),!e)throw"";const t=new Worker(e,{name:n?.name});return t.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),t}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(k),{name:n?.name})}}let c,O=0,M=!1,b="sync";const g=new Map;function C(n){for(const{reject:e}of g.values())e(n);g.clear()}function y(n){M=!0,c&&(c.terminate(),c=void 0),C(n)}function j(){if(!(M||typeof Worker>"u")){if(c)return c;try{c=new H}catch{M=!0;return}return c.addEventListener("message",n=>{const e=g.get(n.data?.id);e&&(g.delete(n.data.id),n.data.error?e.reject(new Error(n.data.error)):e.resolve(n.data.result))}),c.addEventListener("error",n=>{y(n.error??new Error(n.message||"Worker matcher failed."))}),c.addEventListener("messageerror",()=>{y(new Error("Worker matcher could not deserialize a message."))}),c}}function D(n,e){const t=j();if(!t)return;const r=O++;return new Promise((s,o)=>{g.set(r,{resolve:s,reject:o});try{t.postMessage({id:r,query:n,source:e})}catch(i){g.delete(r),o(i)}})}function U(){return b}async function N(n,e){const t=D(n,e);if(t)try{const r=await t;return b="worker",r}catch(r){y(r)}return b="sync",S(n,e)}function P(n,e,t){const r=n.textContent??"";if(!Number.isInteger(e)||!Number.isInteger(t)||e<0||e>=t||t>r.length)throw new RangeError(`Invalid range [${e}, ${t}) for text length ${r.length}`);const s=n.ownerDocument,o=s.createTreeWalker(n,s.defaultView.NodeFilter.SHOW_TEXT);let i=0,a=null,l=0,u=null,p=0;for(let d=o.nextNode();d;d=o.nextNode()){const E=i+d.data.length;if(a===null&&e>=i&&e<E&&(a=d,l=e-i),u===null&&t>i&&t<=E&&(u=d,p=t-i),a&&u)break;i=E}if(!a||!u)throw new Error("Could not map offsets to the DOM. The DOM may have changed.");const h=s.createRange();return h.setStart(a,l),h.setEnd(u,p),h}const m="website-highlighter",v=`${m}:response`,L=500;let $=0;function q(n){return new Promise(e=>globalThis.setTimeout(e,n))}function _(n,e){const t=n.ownerDocument?.defaultView??window;return t.MutationObserver?new Promise(r=>{let s;const o=new t.MutationObserver(()=>{t.clearTimeout(s),o.disconnect(),r(!0)});s=t.setTimeout(()=>{o.disconnect(),r(!1)},e),o.observe(n,{childList:!0,characterData:!0,subtree:!0})}):q(e).then(()=>!1)}function z(n,e,t){const r=Math.max(n.length,e.length);return r===0?1:(r-t)/r}async function T(n,e){const t=e.textContent??"",r=e.ownerDocument?.defaultView??window;if(!r.CSS?.highlights||!r.Highlight)throw new Error("This browser does not support the CSS Custom Highlight API.");const{start:s,end:o,value:i,distance:a}=await N(n,t);return{haystack:t,range:s<o?P(e,s,o):null,value:i,view:r,score:z(n,i,a)}}function I({range:n,value:e,view:t}){if(!n)throw new Error("Could not find text to highlight.");return t.CSS.highlights.set(m,new t.Highlight(n)),{range:n,value:e}}async function A(n,e,t,r,s){let o=0;for(;;){const i=await T(n,e);if(i.score>=t)return I(i);for(;o<r;){const a=await _(e,s);if(o+=1,a)break}if(o>=r)throw new Error(`Could not find "${n}" with threshold ${t}. Best match was "${i.value}".`)}}function F(){return U()}async function W(n,{root:e=document.body,threshold:t=0,retries:r=6,retryInterval:s=L}={}){return t>0?A(n,e,t,r,s):I(await T(n,e))}function B(n,e,t="*"){if(!n?.contentWindow)throw new TypeError("Expected an iframe with a contentWindow");const r=`${Date.now()}-${$++}`,s=n.contentWindow;let o;function i(){s.postMessage({type:m,id:r,text:e},t)}function a(l){l.source===s&&l.data?.type===v&&l.data.id===r&&(window.clearInterval(o),window.removeEventListener("message",a))}window.addEventListener("message",a),i(),o=window.setInterval(i,L)}if(typeof window<"u"){let n=function(t){return t.origin==="null"?"*":t.origin},e=function(t){t.source?.postMessage({type:v,id:t.data.id},n(t))};window.addEventListener("message",t=>{t.data?.type===m&&(e(t),W(t.data.text,{threshold:.9}).catch(r=>console.error(r)))})}f.default=W,f.getMatcherMode=F,f.highlightInIframe=B,Object.defineProperties(f,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(f,w){typeof exports=="object"&&typeof module<"u"?w(exports):typeof define=="function"&&define.amd?define(["exports"],w):(f=typeof globalThis<"u"?globalThis:f||self,w(f.WebsiteHighlighter={}))})(this,(function(f){"use strict";class w{getEditDistances(e,t){var r=new Array(t.length+1).fill([0,0]);for(let i=0;i<e.length;i++){let o=[[i+1,0]];for(let s=0;s<t.length;s++){let a=e[i]!=t[s],l=r[s+1][0]+1,u=o[s][0]+1,p=r[s][0]+a,h=Math.min(l,Math.min(u,p)),d=[h,r[s][1]];l===h?d[1]=r[s+1][1]-1:u===h&&(d[1]=o[s][1]+1),o.push(d)}r=o}return r}getMatches(e,t){let r=this.getEditDistances(e,t),i=[0],o=r[0][0];for(let a=1;a<r.length;a++){let l=r[a][0];l<o?(i=[a],o=l):l==o&&i.push(a)}let s=[];for(let a of i){let l=r[a],u={distance:l[0],start:a-e.length-l[1],end:a};s.push(u)}return s}}function x(n,e){return new w().getMatches(n,e)}function H(n,e){const t=x(n,e);let r;for(const a of t)(r===void 0||a.distance<r.distance)&&(r=a);const i=Math.max(r.start,0),o=Math.min(Math.max(r.end,i),e.length);return{value:e.slice(i,o),start:i,end:o,distance:r.distance}}const R=`(function(){"use strict";class h{getEditDistances(s,i){var t=new Array(i.length+1).fill([0,0]);for(let e=0;e<s.length;e++){let r=[[e+1,0]];for(let l=0;l<i.length;l++){let n=s[e]!=i[l],a=t[l+1][0]+1,o=r[l][0]+1,m=t[l][0]+n,u=Math.min(a,Math.min(o,m)),f=[u,t[l][1]];a===u?f[1]=t[l+1][1]-1:o===u&&(f[1]=r[l][1]+1),r.push(f)}t=r}return t}getMatches(s,i){let t=this.getEditDistances(s,i),e=[0],r=t[0][0];for(let n=1;n<t.length;n++){let a=t[n][0];a<r?(e=[n],r=a):a==r&&e.push(n)}let l=[];for(let n of e){let a=t[n],o={distance:a[0],start:n-s.length-a[1],end:n};l.push(o)}return l}}function g(c,s){return new h().getMatches(c,s)}function d(c,s){const i=g(c,s);let t;for(const n of i)(t===void 0||n.distance<t.distance)&&(t=n);const e=Math.max(t.start,0),r=Math.min(Math.max(t.end,e),s.length);return{value:s.slice(e,r),start:e,end:r,distance:t.distance}}self.addEventListener("message",c=>{const{id:s,query:i,source:t}=c.data;try{self.postMessage({id:s,result:d(i,t)})}catch(e){self.postMessage({id:s,error:e instanceof Error?e.message:String(e)})}})})();
2
+ `,k=typeof self<"u"&&self.Blob&&new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);",R],{type:"text/javascript;charset=utf-8"});function S(n){let e;try{if(e=k&&(self.URL||self.webkitURL).createObjectURL(k),!e)throw"";const t=new Worker(e,{name:n?.name});return t.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),t}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(R),{name:n?.name})}}let c,C=0,M=!1,b="sync";const g=new Map;function O(n){for(const{reject:e}of g.values())e(n);g.clear()}function E(n){M=!0,c&&(c.terminate(),c=void 0),O(n)}function D(){if(!(M||typeof Worker>"u")){if(c)return c;try{c=new S}catch{M=!0;return}return c.addEventListener("message",n=>{const e=g.get(n.data?.id);e&&(g.delete(n.data.id),n.data.error?e.reject(new Error(n.data.error)):e.resolve(n.data.result))}),c.addEventListener("error",n=>{E(n.error??new Error(n.message||"Worker matcher failed."))}),c.addEventListener("messageerror",()=>{E(new Error("Worker matcher could not deserialize a message."))}),c}}function j(n,e){const t=D();if(!t)return;const r=C++;return new Promise((i,o)=>{g.set(r,{resolve:i,reject:o});try{t.postMessage({id:r,query:n,source:e})}catch(s){g.delete(r),o(s)}})}function N(){return b}async function U(n,e){const t=j(n,e);if(t)try{const r=await t;return b="worker",r}catch(r){E(r)}return b="sync",H(n,e)}function P(n,e,t){const r=n.textContent??"";if(!Number.isInteger(e)||!Number.isInteger(t)||e<0||e>=t||t>r.length)throw new RangeError(`Invalid range [${e}, ${t}) for text length ${r.length}`);const i=n.ownerDocument,o=i.createTreeWalker(n,i.defaultView.NodeFilter.SHOW_TEXT);let s=0,a=null,l=0,u=null,p=0;for(let d=o.nextNode();d;d=o.nextNode()){const y=s+d.data.length;if(a===null&&e>=s&&e<y&&(a=d,l=e-s),u===null&&t>s&&t<=y&&(u=d,p=t-s),a&&u)break;s=y}if(!a||!u)throw new Error("Could not map offsets to the DOM. The DOM may have changed.");const h=i.createRange();return h.setStart(a,l),h.setEnd(u,p),h}const m="website-highlighter",T=`${m}:response`,I=500;let _=0;function $(n){return new Promise(e=>globalThis.setTimeout(e,n))}function q(n,e){const t=n.ownerDocument?.defaultView??window;return t.MutationObserver?new Promise(r=>{let i;const o=new t.MutationObserver(()=>{t.clearTimeout(i),o.disconnect(),r(!0)});i=t.setTimeout(()=>{o.disconnect(),r(!1)},e),o.observe(n,{childList:!0,characterData:!0,subtree:!0})}):$(e).then(()=>!1)}function V(n,e,t){const r=Math.max(n.length,e.length);return r===0?1:(r-t)/r}function z(n){const{startContainer:e}=n;return e.nodeType===e.ELEMENT_NODE?e:e.parentElement}function A(n,e){const t=n.getClientRects()[0];if(!t){z(n)?.scrollIntoView({block:"center",inline:"nearest",behavior:"smooth"});return}const r=e.innerHeight||e.document.documentElement.clientHeight,i=t.top+e.scrollY-r/2+t.height/2;e.scrollTo({top:Math.max(0,i),behavior:"smooth"})}async function L(n,e){const t=e.textContent??"",r=e.ownerDocument?.defaultView??window;if(!r.CSS?.highlights||!r.Highlight)throw new Error("This browser does not support the CSS Custom Highlight API.");const{start:i,end:o,value:s,distance:a}=await U(n,t);return{haystack:t,range:i<o?P(e,i,o):null,value:s,view:r,score:V(n,s,a)}}function v({range:n,value:e,view:t}){if(!n)throw new Error("Could not find text to highlight.");return t.CSS.highlights.set(m,new t.Highlight(n)),A(n,t),{range:n,value:e}}async function F(n,e,t,r,i){let o=0;for(;;){const s=await L(n,e);if(s.score>=t)return v(s);for(;o<r;){const a=await q(e,i);if(o+=1,a)break}if(o>=r)throw new Error(`Could not find "${n}" with threshold ${t}. Best match was "${s.value}".`)}}function B(){return N()}async function W(n,{root:e=document.body,threshold:t=0,retries:r=6,retryInterval:i=I}={}){return t>0?F(n,e,t,r,i):v(await L(n,e))}function G(n,e,t="*"){if(!n?.contentWindow)throw new TypeError("Expected an iframe with a contentWindow");const r=`${Date.now()}-${_++}`,i=n.contentWindow;let o;function s(){i.postMessage({type:m,id:r,text:e},t)}function a(l){l.source===i&&l.data?.type===T&&l.data.id===r&&(window.clearInterval(o),window.removeEventListener("message",a))}window.addEventListener("message",a),s(),o=window.setInterval(s,I)}if(typeof window<"u"){let n=function(t){return t.origin==="null"?"*":t.origin},e=function(t){t.source?.postMessage({type:T,id:t.data.id},n(t))};window.addEventListener("message",t=>{t.data?.type===m&&(e(t),W(t.data.text,{threshold:.9}).catch(r=>console.error(r)))})}f.default=W,f.getMatcherMode=B,f.highlightInIframe=G,Object.defineProperties(f,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "website-highlighter",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Fuzzy text matching and highlighting for DOM content.",
5
5
  "type": "module",
6
6
  "main": "./dist/website-highlighter.umd.cjs",