@ansidev-oss/vitepress-theme-ansidev 1.0.6
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/LICENSE +21 -0
- package/dist/client/Donation-D9Xn0MPt.js +76 -0
- package/dist/client/GoogleAnalytics-Dp3EGn1x.js +29 -0
- package/dist/client/VPAlgoliaSearchBox-DXE-LCVf.js +126 -0
- package/dist/client/VPCarbonAds-Czmm53YE.js +31 -0
- package/dist/client/VPLocalSearchBox-ihwA4uH-.js +4086 -0
- package/dist/client/composables/date.d.ts +2 -0
- package/dist/client/composables/index.d.ts +4 -0
- package/dist/client/composables/locale.d.ts +3 -0
- package/dist/client/composables/project.d.ts +13 -0
- package/dist/client/composables/rank.d.ts +1 -0
- package/dist/client/composables/slug.d.ts +2 -0
- package/dist/client/index-BtT3qA6T.js +12663 -0
- package/dist/client/index-CCR5sUM8.js +10688 -0
- package/dist/client/index-CZCdwVUW.js +7566 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +5 -0
- package/dist/client/plugins/donation/index.d.ts +19 -0
- package/dist/client/plugins/google-analytics/index.d.ts +8 -0
- package/dist/client/plugins/i18n/index.d.ts +53 -0
- package/dist/client/styles.css +1 -0
- package/dist/client/types/index.d.ts +45 -0
- package/dist/node/composables.d.mts +30 -0
- package/dist/node/composables.mjs +51 -0
- package/dist/node/config.d.mts +46 -0
- package/dist/node/config.mjs +47 -0
- package/package.json +87 -0
- package/src/client/components/EmptyFooter.vue +3 -0
- package/src/client/components/Footer.vue +87 -0
- package/src/client/components/Link.vue +31 -0
- package/src/client/components/LocaleSwitcher.vue +59 -0
- package/src/client/components/PostDate.vue +42 -0
- package/src/client/components/PostItem.vue +33 -0
- package/src/client/components/PostList.vue +23 -0
- package/src/client/components/ProjectCard.vue +68 -0
- package/src/client/components/ProjectList.vue +23 -0
- package/src/client/components/RouterLink.vue +19 -0
- package/src/client/components/StatusBadge.vue +56 -0
- package/src/client/components/TermBadge.vue +29 -0
- package/src/client/components/TermLink.vue +25 -0
- package/src/client/composables/date.ts +12 -0
- package/src/client/composables/index.ts +4 -0
- package/src/client/composables/locale.ts +7 -0
- package/src/client/composables/project.ts +30 -0
- package/src/client/composables/rank.ts +13 -0
- package/src/client/composables/slug.ts +12 -0
- package/src/client/index.ts +43 -0
- package/src/client/layouts/Layout.vue +25 -0
- package/src/client/pages/ArchivesPage.vue +43 -0
- package/src/client/pages/CategoriesPage.vue +30 -0
- package/src/client/pages/HomePage.vue +18 -0
- package/src/client/pages/Page.vue +20 -0
- package/src/client/pages/PostsPage.vue +23 -0
- package/src/client/pages/ProjectsPage.vue +65 -0
- package/src/client/pages/TagsPage.vue +30 -0
- package/src/client/plugins/donation/components/Donation.vue +43 -0
- package/src/client/plugins/donation/components/DonationButton.vue +22 -0
- package/src/client/plugins/donation/index.ts +42 -0
- package/src/client/plugins/google-analytics/components/GoogleAnalytics.vue +52 -0
- package/src/client/plugins/google-analytics/index.ts +8 -0
- package/src/client/plugins/i18n/index.ts +13 -0
- package/src/client/plugins/i18n/locales/en.json +25 -0
- package/src/client/plugins/i18n/locales/vi.json +25 -0
- package/src/client/styles/index.css +30 -0
- package/src/client/types/index.ts +50 -0
- package/src/node/composables/index.ts +3 -0
- package/src/node/composables/markdown.ts +14 -0
- package/src/node/composables/route.ts +26 -0
- package/src/node/composables/slug.ts +40 -0
- package/src/node/composables/types.ts +7 -0
- package/src/node/config.ts +79 -0
- package/src/vite-env.d.ts +7 -0
|
@@ -0,0 +1,4086 @@
|
|
|
1
|
+
import { shallowRef as Z, watch as Le, computed as Se, toValue as He, readonly as Et, defineComponent as Tt, ref as ue, watchEffect as It, onMounted as Ne, nextTick as de, onBeforeUnmount as kt, openBlock as B, createBlock as Nt, Teleport as Ft, createElementVNode as T, withModifiers as Rt, unref as C, withDirectives as At, isRef as Ot, vModelText as Ct, createElementBlock as H, normalizeClass as qe, createCommentVNode as me, Fragment as Ge, renderList as Qe, createTextVNode as fe, toDisplayString as he, markRaw as Ye, createApp as Mt } from "vue";
|
|
2
|
+
import Lt from "@localSearchIndex";
|
|
3
|
+
import { t as Dt, u as Pt, n as zt, a as Vt, i as jt, s as Ze, b as ct, c as Xe, d as $t, e as Bt, w as Wt, o as ge, f as Kt, g as Jt, h as Ut, _ as Ht } from "./index-CZCdwVUW.js";
|
|
4
|
+
import { useRouter as qt, dataSymbol as Gt, inBrowser as Qt } from "vitepress";
|
|
5
|
+
import Yt from "@siteData";
|
|
6
|
+
var ut = ["input:not([inert]):not([inert] *)", "select:not([inert]):not([inert] *)", "textarea:not([inert]):not([inert] *)", "a[href]:not([inert]):not([inert] *)", "button:not([inert]):not([inert] *)", "[tabindex]:not(slot):not([inert]):not([inert] *)", "audio[controls]:not([inert]):not([inert] *)", "video[controls]:not([inert]):not([inert] *)", '[contenteditable]:not([contenteditable="false"]):not([inert]):not([inert] *)', "details>summary:first-of-type:not([inert]):not([inert] *)", "details:not([inert]):not([inert] *)"], _e = /* @__PURE__ */ ut.join(","), dt = typeof Element > "u", X = dt ? function() {
|
|
7
|
+
} : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector, xe = !dt && Element.prototype.getRootNode ? function(a) {
|
|
8
|
+
var e;
|
|
9
|
+
return a == null || (e = a.getRootNode) === null || e === void 0 ? void 0 : e.call(a);
|
|
10
|
+
} : function(a) {
|
|
11
|
+
return a?.ownerDocument;
|
|
12
|
+
}, Ee = function(e, t) {
|
|
13
|
+
var n;
|
|
14
|
+
t === void 0 && (t = !0);
|
|
15
|
+
var r = e == null || (n = e.getAttribute) === null || n === void 0 ? void 0 : n.call(e, "inert"), s = r === "" || r === "true", i = s || t && e && // closest does not exist on shadow roots, so we fall back to a manual
|
|
16
|
+
// lookup upward, in case it is not defined.
|
|
17
|
+
(typeof e.closest == "function" ? e.closest("[inert]") : Ee(e.parentNode));
|
|
18
|
+
return i;
|
|
19
|
+
}, Zt = function(e) {
|
|
20
|
+
var t, n = e == null || (t = e.getAttribute) === null || t === void 0 ? void 0 : t.call(e, "contenteditable");
|
|
21
|
+
return n === "" || n === "true";
|
|
22
|
+
}, ft = function(e, t, n) {
|
|
23
|
+
if (Ee(e))
|
|
24
|
+
return [];
|
|
25
|
+
var r = Array.prototype.slice.apply(e.querySelectorAll(_e));
|
|
26
|
+
return t && X.call(e, _e) && r.unshift(e), r = r.filter(n), r;
|
|
27
|
+
}, Te = function(e, t, n) {
|
|
28
|
+
for (var r = [], s = Array.from(e); s.length; ) {
|
|
29
|
+
var i = s.shift();
|
|
30
|
+
if (!Ee(i, !1))
|
|
31
|
+
if (i.tagName === "SLOT") {
|
|
32
|
+
var o = i.assignedElements(), l = o.length ? o : i.children, c = Te(l, !0, n);
|
|
33
|
+
n.flatten ? r.push.apply(r, c) : r.push({
|
|
34
|
+
scopeParent: i,
|
|
35
|
+
candidates: c
|
|
36
|
+
});
|
|
37
|
+
} else {
|
|
38
|
+
var f = X.call(i, _e);
|
|
39
|
+
f && n.filter(i) && (t || !e.includes(i)) && r.push(i);
|
|
40
|
+
var p = i.shadowRoot || // check for an undisclosed shadow
|
|
41
|
+
typeof n.getShadowRoot == "function" && n.getShadowRoot(i), v = !Ee(p, !1) && (!n.shadowRootFilter || n.shadowRootFilter(i));
|
|
42
|
+
if (p && v) {
|
|
43
|
+
var y = Te(p === !0 ? i.children : p.children, !0, n);
|
|
44
|
+
n.flatten ? r.push.apply(r, y) : r.push({
|
|
45
|
+
scopeParent: i,
|
|
46
|
+
candidates: y
|
|
47
|
+
});
|
|
48
|
+
} else
|
|
49
|
+
s.unshift.apply(s, i.children);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return r;
|
|
53
|
+
}, ht = function(e) {
|
|
54
|
+
return !isNaN(parseInt(e.getAttribute("tabindex"), 10));
|
|
55
|
+
}, Y = function(e) {
|
|
56
|
+
if (!e)
|
|
57
|
+
throw new Error("No node provided");
|
|
58
|
+
return e.tabIndex < 0 && (/^(AUDIO|VIDEO|DETAILS)$/.test(e.tagName) || Zt(e)) && !ht(e) ? 0 : e.tabIndex;
|
|
59
|
+
}, Xt = function(e, t) {
|
|
60
|
+
var n = Y(e);
|
|
61
|
+
return n < 0 && t && !ht(e) ? 0 : n;
|
|
62
|
+
}, en = function(e, t) {
|
|
63
|
+
return e.tabIndex === t.tabIndex ? e.documentOrder - t.documentOrder : e.tabIndex - t.tabIndex;
|
|
64
|
+
}, pt = function(e) {
|
|
65
|
+
return e.tagName === "INPUT";
|
|
66
|
+
}, tn = function(e) {
|
|
67
|
+
return pt(e) && e.type === "hidden";
|
|
68
|
+
}, nn = function(e) {
|
|
69
|
+
var t = e.tagName === "DETAILS" && Array.prototype.slice.apply(e.children).some(function(n) {
|
|
70
|
+
return n.tagName === "SUMMARY";
|
|
71
|
+
});
|
|
72
|
+
return t;
|
|
73
|
+
}, rn = function(e, t) {
|
|
74
|
+
for (var n = 0; n < e.length; n++)
|
|
75
|
+
if (e[n].checked && e[n].form === t)
|
|
76
|
+
return e[n];
|
|
77
|
+
}, sn = function(e) {
|
|
78
|
+
if (!e.name)
|
|
79
|
+
return !0;
|
|
80
|
+
var t = e.form || xe(e), n = function(o) {
|
|
81
|
+
return t.querySelectorAll('input[type="radio"][name="' + o + '"]');
|
|
82
|
+
}, r;
|
|
83
|
+
if (typeof window < "u" && typeof window.CSS < "u" && typeof window.CSS.escape == "function")
|
|
84
|
+
r = n(window.CSS.escape(e.name));
|
|
85
|
+
else
|
|
86
|
+
try {
|
|
87
|
+
r = n(e.name);
|
|
88
|
+
} catch (i) {
|
|
89
|
+
return console.error("Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s", i.message), !1;
|
|
90
|
+
}
|
|
91
|
+
var s = rn(r, e.form);
|
|
92
|
+
return !s || s === e;
|
|
93
|
+
}, an = function(e) {
|
|
94
|
+
return pt(e) && e.type === "radio";
|
|
95
|
+
}, on = function(e) {
|
|
96
|
+
return an(e) && !sn(e);
|
|
97
|
+
}, ln = function(e) {
|
|
98
|
+
var t, n = e && xe(e), r = (t = n) === null || t === void 0 ? void 0 : t.host, s = !1;
|
|
99
|
+
if (n && n !== e) {
|
|
100
|
+
var i, o, l;
|
|
101
|
+
for (s = !!((i = r) !== null && i !== void 0 && (o = i.ownerDocument) !== null && o !== void 0 && o.contains(r) || e != null && (l = e.ownerDocument) !== null && l !== void 0 && l.contains(e)); !s && r; ) {
|
|
102
|
+
var c, f, p;
|
|
103
|
+
n = xe(r), r = (c = n) === null || c === void 0 ? void 0 : c.host, s = !!((f = r) !== null && f !== void 0 && (p = f.ownerDocument) !== null && p !== void 0 && p.contains(r));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return s;
|
|
107
|
+
}, et = function(e) {
|
|
108
|
+
var t = e.getBoundingClientRect(), n = t.width, r = t.height;
|
|
109
|
+
return n === 0 && r === 0;
|
|
110
|
+
}, cn = function(e, t) {
|
|
111
|
+
var n = t.displayCheck, r = t.getShadowRoot;
|
|
112
|
+
if (n === "full-native" && "checkVisibility" in e) {
|
|
113
|
+
var s = e.checkVisibility({
|
|
114
|
+
// Checking opacity might be desirable for some use cases, but natively,
|
|
115
|
+
// opacity zero elements _are_ focusable and tabbable.
|
|
116
|
+
checkOpacity: !1,
|
|
117
|
+
opacityProperty: !1,
|
|
118
|
+
contentVisibilityAuto: !0,
|
|
119
|
+
visibilityProperty: !0,
|
|
120
|
+
// This is an alias for `visibilityProperty`. Contemporary browsers
|
|
121
|
+
// support both. However, this alias has wider browser support (Chrome
|
|
122
|
+
// >= 105 and Firefox >= 106, vs. Chrome >= 121 and Firefox >= 122), so
|
|
123
|
+
// we include it anyway.
|
|
124
|
+
checkVisibilityCSS: !0
|
|
125
|
+
});
|
|
126
|
+
return !s;
|
|
127
|
+
}
|
|
128
|
+
if (getComputedStyle(e).visibility === "hidden")
|
|
129
|
+
return !0;
|
|
130
|
+
var i = X.call(e, "details>summary:first-of-type"), o = i ? e.parentElement : e;
|
|
131
|
+
if (X.call(o, "details:not([open]) *"))
|
|
132
|
+
return !0;
|
|
133
|
+
if (!n || n === "full" || // full-native can run this branch when it falls through in case
|
|
134
|
+
// Element#checkVisibility is unsupported
|
|
135
|
+
n === "full-native" || n === "legacy-full") {
|
|
136
|
+
if (typeof r == "function") {
|
|
137
|
+
for (var l = e; e; ) {
|
|
138
|
+
var c = e.parentElement, f = xe(e);
|
|
139
|
+
if (c && !c.shadowRoot && r(c) === !0)
|
|
140
|
+
return et(e);
|
|
141
|
+
e.assignedSlot ? e = e.assignedSlot : !c && f !== e.ownerDocument ? e = f.host : e = c;
|
|
142
|
+
}
|
|
143
|
+
e = l;
|
|
144
|
+
}
|
|
145
|
+
if (ln(e))
|
|
146
|
+
return !e.getClientRects().length;
|
|
147
|
+
if (n !== "legacy-full")
|
|
148
|
+
return !0;
|
|
149
|
+
} else if (n === "non-zero-area")
|
|
150
|
+
return et(e);
|
|
151
|
+
return !1;
|
|
152
|
+
}, un = function(e) {
|
|
153
|
+
if (/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(e.tagName))
|
|
154
|
+
for (var t = e.parentElement; t; ) {
|
|
155
|
+
if (t.tagName === "FIELDSET" && t.disabled) {
|
|
156
|
+
for (var n = 0; n < t.children.length; n++) {
|
|
157
|
+
var r = t.children.item(n);
|
|
158
|
+
if (r.tagName === "LEGEND")
|
|
159
|
+
return X.call(t, "fieldset[disabled] *") ? !0 : !r.contains(e);
|
|
160
|
+
}
|
|
161
|
+
return !0;
|
|
162
|
+
}
|
|
163
|
+
t = t.parentElement;
|
|
164
|
+
}
|
|
165
|
+
return !1;
|
|
166
|
+
}, Ie = function(e, t) {
|
|
167
|
+
return !(t.disabled || tn(t) || cn(t, e) || // For a details element with a summary, the summary element gets the focus
|
|
168
|
+
nn(t) || un(t));
|
|
169
|
+
}, De = function(e, t) {
|
|
170
|
+
return !(on(t) || Y(t) < 0 || !Ie(e, t));
|
|
171
|
+
}, dn = function(e) {
|
|
172
|
+
var t = parseInt(e.getAttribute("tabindex"), 10);
|
|
173
|
+
return !!(isNaN(t) || t >= 0);
|
|
174
|
+
}, vt = function(e) {
|
|
175
|
+
var t = [], n = [];
|
|
176
|
+
return e.forEach(function(r, s) {
|
|
177
|
+
var i = !!r.scopeParent, o = i ? r.scopeParent : r, l = Xt(o, i), c = i ? vt(r.candidates) : o;
|
|
178
|
+
l === 0 ? i ? t.push.apply(t, c) : t.push(o) : n.push({
|
|
179
|
+
documentOrder: s,
|
|
180
|
+
tabIndex: l,
|
|
181
|
+
item: r,
|
|
182
|
+
isScope: i,
|
|
183
|
+
content: c
|
|
184
|
+
});
|
|
185
|
+
}), n.sort(en).reduce(function(r, s) {
|
|
186
|
+
return s.isScope ? r.push.apply(r, s.content) : r.push(s.content), r;
|
|
187
|
+
}, []).concat(t);
|
|
188
|
+
}, fn = function(e, t) {
|
|
189
|
+
t = t || {};
|
|
190
|
+
var n;
|
|
191
|
+
return t.getShadowRoot ? n = Te([e], t.includeContainer, {
|
|
192
|
+
filter: De.bind(null, t),
|
|
193
|
+
flatten: !1,
|
|
194
|
+
getShadowRoot: t.getShadowRoot,
|
|
195
|
+
shadowRootFilter: dn
|
|
196
|
+
}) : n = ft(e, t.includeContainer, De.bind(null, t)), vt(n);
|
|
197
|
+
}, hn = function(e, t) {
|
|
198
|
+
t = t || {};
|
|
199
|
+
var n;
|
|
200
|
+
return t.getShadowRoot ? n = Te([e], t.includeContainer, {
|
|
201
|
+
filter: Ie.bind(null, t),
|
|
202
|
+
flatten: !0,
|
|
203
|
+
getShadowRoot: t.getShadowRoot
|
|
204
|
+
}) : n = ft(e, t.includeContainer, Ie.bind(null, t)), n;
|
|
205
|
+
}, te = function(e, t) {
|
|
206
|
+
if (t = t || {}, !e)
|
|
207
|
+
throw new Error("No node provided");
|
|
208
|
+
return X.call(e, _e) === !1 ? !1 : De(t, e);
|
|
209
|
+
}, pn = /* @__PURE__ */ ut.concat("iframe:not([inert]):not([inert] *)").join(","), Fe = function(e, t) {
|
|
210
|
+
if (t = t || {}, !e)
|
|
211
|
+
throw new Error("No node provided");
|
|
212
|
+
return X.call(e, pn) === !1 ? !1 : Ie(t, e);
|
|
213
|
+
};
|
|
214
|
+
function Pe(a, e) {
|
|
215
|
+
(e == null || e > a.length) && (e = a.length);
|
|
216
|
+
for (var t = 0, n = Array(e); t < e; t++) n[t] = a[t];
|
|
217
|
+
return n;
|
|
218
|
+
}
|
|
219
|
+
function vn(a) {
|
|
220
|
+
if (Array.isArray(a)) return Pe(a);
|
|
221
|
+
}
|
|
222
|
+
function tt(a, e) {
|
|
223
|
+
var t = typeof Symbol < "u" && a[Symbol.iterator] || a["@@iterator"];
|
|
224
|
+
if (!t) {
|
|
225
|
+
if (Array.isArray(a) || (t = mt(a)) || e) {
|
|
226
|
+
t && (a = t);
|
|
227
|
+
var n = 0, r = function() {
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
s: r,
|
|
231
|
+
n: function() {
|
|
232
|
+
return n >= a.length ? {
|
|
233
|
+
done: !0
|
|
234
|
+
} : {
|
|
235
|
+
done: !1,
|
|
236
|
+
value: a[n++]
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
e: function(l) {
|
|
240
|
+
throw l;
|
|
241
|
+
},
|
|
242
|
+
f: r
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
throw new TypeError(`Invalid attempt to iterate non-iterable instance.
|
|
246
|
+
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`);
|
|
247
|
+
}
|
|
248
|
+
var s, i = !0, o = !1;
|
|
249
|
+
return {
|
|
250
|
+
s: function() {
|
|
251
|
+
t = t.call(a);
|
|
252
|
+
},
|
|
253
|
+
n: function() {
|
|
254
|
+
var l = t.next();
|
|
255
|
+
return i = l.done, l;
|
|
256
|
+
},
|
|
257
|
+
e: function(l) {
|
|
258
|
+
o = !0, s = l;
|
|
259
|
+
},
|
|
260
|
+
f: function() {
|
|
261
|
+
try {
|
|
262
|
+
i || t.return == null || t.return();
|
|
263
|
+
} finally {
|
|
264
|
+
if (o) throw s;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function mn(a, e, t) {
|
|
270
|
+
return (e = Sn(e)) in a ? Object.defineProperty(a, e, {
|
|
271
|
+
value: t,
|
|
272
|
+
enumerable: !0,
|
|
273
|
+
configurable: !0,
|
|
274
|
+
writable: !0
|
|
275
|
+
}) : a[e] = t, a;
|
|
276
|
+
}
|
|
277
|
+
function gn(a) {
|
|
278
|
+
if (typeof Symbol < "u" && a[Symbol.iterator] != null || a["@@iterator"] != null) return Array.from(a);
|
|
279
|
+
}
|
|
280
|
+
function bn() {
|
|
281
|
+
throw new TypeError(`Invalid attempt to spread non-iterable instance.
|
|
282
|
+
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`);
|
|
283
|
+
}
|
|
284
|
+
function nt(a, e) {
|
|
285
|
+
var t = Object.keys(a);
|
|
286
|
+
if (Object.getOwnPropertySymbols) {
|
|
287
|
+
var n = Object.getOwnPropertySymbols(a);
|
|
288
|
+
e && (n = n.filter(function(r) {
|
|
289
|
+
return Object.getOwnPropertyDescriptor(a, r).enumerable;
|
|
290
|
+
})), t.push.apply(t, n);
|
|
291
|
+
}
|
|
292
|
+
return t;
|
|
293
|
+
}
|
|
294
|
+
function it(a) {
|
|
295
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
296
|
+
var t = arguments[e] != null ? arguments[e] : {};
|
|
297
|
+
e % 2 ? nt(Object(t), !0).forEach(function(n) {
|
|
298
|
+
mn(a, n, t[n]);
|
|
299
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(a, Object.getOwnPropertyDescriptors(t)) : nt(Object(t)).forEach(function(n) {
|
|
300
|
+
Object.defineProperty(a, n, Object.getOwnPropertyDescriptor(t, n));
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return a;
|
|
304
|
+
}
|
|
305
|
+
function yn(a) {
|
|
306
|
+
return vn(a) || gn(a) || mt(a) || bn();
|
|
307
|
+
}
|
|
308
|
+
function wn(a, e) {
|
|
309
|
+
if (typeof a != "object" || !a) return a;
|
|
310
|
+
var t = a[Symbol.toPrimitive];
|
|
311
|
+
if (t !== void 0) {
|
|
312
|
+
var n = t.call(a, e);
|
|
313
|
+
if (typeof n != "object") return n;
|
|
314
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
315
|
+
}
|
|
316
|
+
return (e === "string" ? String : Number)(a);
|
|
317
|
+
}
|
|
318
|
+
function Sn(a) {
|
|
319
|
+
var e = wn(a, "string");
|
|
320
|
+
return typeof e == "symbol" ? e : e + "";
|
|
321
|
+
}
|
|
322
|
+
function mt(a, e) {
|
|
323
|
+
if (a) {
|
|
324
|
+
if (typeof a == "string") return Pe(a, e);
|
|
325
|
+
var t = {}.toString.call(a).slice(8, -1);
|
|
326
|
+
return t === "Object" && a.constructor && (t = a.constructor.name), t === "Map" || t === "Set" ? Array.from(a) : t === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? Pe(a, e) : void 0;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
var W = {
|
|
330
|
+
// Returns the trap from the top of the stack.
|
|
331
|
+
getActiveTrap: function(e) {
|
|
332
|
+
return e?.length > 0 ? e[e.length - 1] : null;
|
|
333
|
+
},
|
|
334
|
+
// Pauses the currently active trap, then adds a new trap to the stack.
|
|
335
|
+
activateTrap: function(e, t) {
|
|
336
|
+
var n = W.getActiveTrap(e);
|
|
337
|
+
t !== n && W.pauseTrap(e);
|
|
338
|
+
var r = e.indexOf(t);
|
|
339
|
+
r === -1 || e.splice(r, 1), e.push(t);
|
|
340
|
+
},
|
|
341
|
+
// Removes the trap from the top of the stack, then unpauses the next trap down.
|
|
342
|
+
deactivateTrap: function(e, t) {
|
|
343
|
+
var n = e.indexOf(t);
|
|
344
|
+
n !== -1 && e.splice(n, 1), W.unpauseTrap(e);
|
|
345
|
+
},
|
|
346
|
+
// Pauses the trap at the top of the stack.
|
|
347
|
+
pauseTrap: function(e) {
|
|
348
|
+
var t = W.getActiveTrap(e);
|
|
349
|
+
t?._setPausedState(!0);
|
|
350
|
+
},
|
|
351
|
+
// Unpauses the trap at the top of the stack.
|
|
352
|
+
unpauseTrap: function(e) {
|
|
353
|
+
var t = W.getActiveTrap(e);
|
|
354
|
+
t && !t._isManuallyPaused() && t._setPausedState(!1);
|
|
355
|
+
}
|
|
356
|
+
}, _n = function(e) {
|
|
357
|
+
return e.tagName && e.tagName.toLowerCase() === "input" && typeof e.select == "function";
|
|
358
|
+
}, xn = function(e) {
|
|
359
|
+
return e?.key === "Escape" || e?.key === "Esc" || e?.keyCode === 27;
|
|
360
|
+
}, ve = function(e) {
|
|
361
|
+
return e?.key === "Tab" || e?.keyCode === 9;
|
|
362
|
+
}, En = function(e) {
|
|
363
|
+
return ve(e) && !e.shiftKey;
|
|
364
|
+
}, Tn = function(e) {
|
|
365
|
+
return ve(e) && e.shiftKey;
|
|
366
|
+
}, rt = function(e) {
|
|
367
|
+
return setTimeout(e, 0);
|
|
368
|
+
}, pe = function(e) {
|
|
369
|
+
for (var t = arguments.length, n = new Array(t > 1 ? t - 1 : 0), r = 1; r < t; r++)
|
|
370
|
+
n[r - 1] = arguments[r];
|
|
371
|
+
return typeof e == "function" ? e.apply(void 0, n) : e;
|
|
372
|
+
}, be = function(e) {
|
|
373
|
+
return e.target.shadowRoot && typeof e.composedPath == "function" ? e.composedPath()[0] : e.target;
|
|
374
|
+
}, In = [], kn = function(e, t) {
|
|
375
|
+
var n = t?.document || document, r = t?.trapStack || In, s = it({
|
|
376
|
+
returnFocusOnDeactivate: !0,
|
|
377
|
+
escapeDeactivates: !0,
|
|
378
|
+
delayInitialFocus: !0,
|
|
379
|
+
isolateSubtrees: !1,
|
|
380
|
+
isKeyForward: En,
|
|
381
|
+
isKeyBackward: Tn
|
|
382
|
+
}, t), i = {
|
|
383
|
+
// containers given to createFocusTrap()
|
|
384
|
+
/** @type {Array<HTMLElement>} */
|
|
385
|
+
containers: [],
|
|
386
|
+
// list of objects identifying tabbable nodes in `containers` in the trap
|
|
387
|
+
// NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap
|
|
388
|
+
// is active, but the trap should never get to a state where there isn't at least one group
|
|
389
|
+
// with at least one tabbable node in it (that would lead to an error condition that would
|
|
390
|
+
// result in an error being thrown)
|
|
391
|
+
/** @type {Array<{
|
|
392
|
+
* container: HTMLElement,
|
|
393
|
+
* tabbableNodes: Array<HTMLElement>, // empty if none
|
|
394
|
+
* focusableNodes: Array<HTMLElement>, // empty if none
|
|
395
|
+
* posTabIndexesFound: boolean,
|
|
396
|
+
* firstTabbableNode: HTMLElement|undefined,
|
|
397
|
+
* lastTabbableNode: HTMLElement|undefined,
|
|
398
|
+
* firstDomTabbableNode: HTMLElement|undefined,
|
|
399
|
+
* lastDomTabbableNode: HTMLElement|undefined,
|
|
400
|
+
* nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
|
|
401
|
+
* }>}
|
|
402
|
+
*/
|
|
403
|
+
containerGroups: [],
|
|
404
|
+
// same order/length as `containers` list
|
|
405
|
+
// references to objects in `containerGroups`, but only those that actually have
|
|
406
|
+
// tabbable nodes in them
|
|
407
|
+
// NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
|
|
408
|
+
// the same length
|
|
409
|
+
tabbableGroups: [],
|
|
410
|
+
// references to nodes that are siblings to the ancestors of this trap's containers.
|
|
411
|
+
/** @type {Set<HTMLElement>} */
|
|
412
|
+
adjacentElements: /* @__PURE__ */ new Set(),
|
|
413
|
+
// references to nodes that were inert or aria-hidden before the trap was activated.
|
|
414
|
+
/** @type {Set<HTMLElement>} */
|
|
415
|
+
alreadySilent: /* @__PURE__ */ new Set(),
|
|
416
|
+
nodeFocusedBeforeActivation: null,
|
|
417
|
+
mostRecentlyFocusedNode: null,
|
|
418
|
+
active: !1,
|
|
419
|
+
paused: !1,
|
|
420
|
+
manuallyPaused: !1,
|
|
421
|
+
// timer ID for when delayInitialFocus is true and initial focus in this trap
|
|
422
|
+
// has been delayed during activation
|
|
423
|
+
delayInitialFocusTimer: void 0,
|
|
424
|
+
// the most recent KeyboardEvent for the configured nav key (typically [SHIFT+]TAB), if any
|
|
425
|
+
recentNavEvent: void 0
|
|
426
|
+
}, o, l = function(u, d, b) {
|
|
427
|
+
return u && u[d] !== void 0 ? u[d] : s[b || d];
|
|
428
|
+
}, c = function(u, d) {
|
|
429
|
+
var b = typeof d?.composedPath == "function" ? d.composedPath() : void 0;
|
|
430
|
+
return i.containerGroups.findIndex(function(I) {
|
|
431
|
+
var m = I.container, h = I.tabbableNodes;
|
|
432
|
+
return m.contains(u) || b?.includes(m) || h.find(function(g) {
|
|
433
|
+
return g === u;
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}, f = function(u) {
|
|
437
|
+
var d = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, b = d.hasFallback, I = b === void 0 ? !1 : b, m = d.params, h = m === void 0 ? [] : m, g = s[u];
|
|
438
|
+
if (typeof g == "function" && (g = g.apply(void 0, yn(h))), g === !0 && (g = void 0), !g) {
|
|
439
|
+
if (g === void 0 || g === !1)
|
|
440
|
+
return g;
|
|
441
|
+
throw new Error("`".concat(u, "` was specified but was not a node, or did not return a node"));
|
|
442
|
+
}
|
|
443
|
+
var E = g;
|
|
444
|
+
if (typeof g == "string") {
|
|
445
|
+
try {
|
|
446
|
+
E = n.querySelector(g);
|
|
447
|
+
} catch (k) {
|
|
448
|
+
throw new Error("`".concat(u, '` appears to be an invalid selector; error="').concat(k.message, '"'));
|
|
449
|
+
}
|
|
450
|
+
if (!E && !I)
|
|
451
|
+
throw new Error("`".concat(u, "` as selector refers to no known node"));
|
|
452
|
+
}
|
|
453
|
+
return E;
|
|
454
|
+
}, p = function() {
|
|
455
|
+
var u = f("initialFocus", {
|
|
456
|
+
hasFallback: !0
|
|
457
|
+
});
|
|
458
|
+
if (u === !1)
|
|
459
|
+
return !1;
|
|
460
|
+
if (u === void 0 || u && !Fe(u, s.tabbableOptions))
|
|
461
|
+
if (c(n.activeElement) >= 0)
|
|
462
|
+
u = n.activeElement;
|
|
463
|
+
else {
|
|
464
|
+
var d = i.tabbableGroups[0], b = d && d.firstTabbableNode;
|
|
465
|
+
u = b || f("fallbackFocus");
|
|
466
|
+
}
|
|
467
|
+
else u === null && (u = f("fallbackFocus"));
|
|
468
|
+
if (!u)
|
|
469
|
+
throw new Error("Your focus-trap needs to have at least one focusable element");
|
|
470
|
+
return u;
|
|
471
|
+
}, v = function() {
|
|
472
|
+
if (i.containerGroups = i.containers.map(function(u) {
|
|
473
|
+
var d = fn(u, s.tabbableOptions), b = hn(u, s.tabbableOptions), I = d.length > 0 ? d[0] : void 0, m = d.length > 0 ? d[d.length - 1] : void 0, h = b.find(function(k) {
|
|
474
|
+
return te(k);
|
|
475
|
+
}), g = b.slice().reverse().find(function(k) {
|
|
476
|
+
return te(k);
|
|
477
|
+
}), E = !!d.find(function(k) {
|
|
478
|
+
return Y(k) > 0;
|
|
479
|
+
});
|
|
480
|
+
return {
|
|
481
|
+
container: u,
|
|
482
|
+
tabbableNodes: d,
|
|
483
|
+
focusableNodes: b,
|
|
484
|
+
/** True if at least one node with positive `tabindex` was found in this container. */
|
|
485
|
+
posTabIndexesFound: E,
|
|
486
|
+
/** First tabbable node in container, __tabindex__ order; `undefined` if none. */
|
|
487
|
+
firstTabbableNode: I,
|
|
488
|
+
/** Last tabbable node in container, __tabindex__ order; `undefined` if none. */
|
|
489
|
+
lastTabbableNode: m,
|
|
490
|
+
// NOTE: DOM order is NOT NECESSARILY "document position" order, but figuring that out
|
|
491
|
+
// would require more than just https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
|
|
492
|
+
// because that API doesn't work with Shadow DOM as well as it should (@see
|
|
493
|
+
// https://github.com/whatwg/dom/issues/320) and since this first/last is only needed, so far,
|
|
494
|
+
// to address an edge case related to positive tabindex support, this seems like a much easier,
|
|
495
|
+
// "close enough most of the time" alternative for positive tabindexes which should generally
|
|
496
|
+
// be avoided anyway...
|
|
497
|
+
/** First tabbable node in container, __DOM__ order; `undefined` if none. */
|
|
498
|
+
firstDomTabbableNode: h,
|
|
499
|
+
/** Last tabbable node in container, __DOM__ order; `undefined` if none. */
|
|
500
|
+
lastDomTabbableNode: g,
|
|
501
|
+
/**
|
|
502
|
+
* Finds the __tabbable__ node that follows the given node in the specified direction,
|
|
503
|
+
* in this container, if any.
|
|
504
|
+
* @param {HTMLElement} node
|
|
505
|
+
* @param {boolean} [forward] True if going in forward tab order; false if going
|
|
506
|
+
* in reverse.
|
|
507
|
+
* @returns {HTMLElement|undefined} The next tabbable node, if any.
|
|
508
|
+
*/
|
|
509
|
+
nextTabbableNode: function(R) {
|
|
510
|
+
var P = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : !0, D = d.indexOf(R);
|
|
511
|
+
return D < 0 ? P ? b.slice(b.indexOf(R) + 1).find(function($) {
|
|
512
|
+
return te($);
|
|
513
|
+
}) : b.slice(0, b.indexOf(R)).reverse().find(function($) {
|
|
514
|
+
return te($);
|
|
515
|
+
}) : d[D + (P ? 1 : -1)];
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}), i.tabbableGroups = i.containerGroups.filter(function(u) {
|
|
519
|
+
return u.tabbableNodes.length > 0;
|
|
520
|
+
}), i.tabbableGroups.length <= 0 && !f("fallbackFocus"))
|
|
521
|
+
throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");
|
|
522
|
+
if (i.containerGroups.find(function(u) {
|
|
523
|
+
return u.posTabIndexesFound;
|
|
524
|
+
}) && i.containerGroups.length > 1)
|
|
525
|
+
throw new Error("At least one node with a positive tabindex was found in one of your focus-trap's multiple containers. Positive tabindexes are only supported in single-container focus-traps.");
|
|
526
|
+
}, y = function(u) {
|
|
527
|
+
var d = u.activeElement;
|
|
528
|
+
if (d)
|
|
529
|
+
return d.shadowRoot && d.shadowRoot.activeElement !== null ? y(d.shadowRoot) : d;
|
|
530
|
+
}, S = function(u) {
|
|
531
|
+
if (u !== !1 && u !== y(document)) {
|
|
532
|
+
if (!u || !u.focus) {
|
|
533
|
+
S(p());
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
u.focus({
|
|
537
|
+
preventScroll: !!s.preventScroll
|
|
538
|
+
}), i.mostRecentlyFocusedNode = u, _n(u) && u.select();
|
|
539
|
+
}
|
|
540
|
+
}, w = function(u) {
|
|
541
|
+
var d = f("setReturnFocus", {
|
|
542
|
+
params: [u]
|
|
543
|
+
});
|
|
544
|
+
return d || (d === !1 ? !1 : u);
|
|
545
|
+
}, _ = function(u) {
|
|
546
|
+
var d = u.target, b = u.event, I = u.isBackward, m = I === void 0 ? !1 : I;
|
|
547
|
+
d = d || be(b), v();
|
|
548
|
+
var h = null;
|
|
549
|
+
if (i.tabbableGroups.length > 0) {
|
|
550
|
+
var g = c(d, b), E = g >= 0 ? i.containerGroups[g] : void 0;
|
|
551
|
+
if (g < 0)
|
|
552
|
+
m ? h = i.tabbableGroups[i.tabbableGroups.length - 1].lastTabbableNode : h = i.tabbableGroups[0].firstTabbableNode;
|
|
553
|
+
else if (m) {
|
|
554
|
+
var k = i.tabbableGroups.findIndex(function(J) {
|
|
555
|
+
var U = J.firstTabbableNode;
|
|
556
|
+
return d === U;
|
|
557
|
+
});
|
|
558
|
+
if (k < 0 && (E.container === d || Fe(d, s.tabbableOptions) && !te(d, s.tabbableOptions) && !E.nextTabbableNode(d, !1)) && (k = g), k >= 0) {
|
|
559
|
+
var R = k === 0 ? i.tabbableGroups.length - 1 : k - 1, P = i.tabbableGroups[R];
|
|
560
|
+
h = Y(d) >= 0 ? P.lastTabbableNode : P.lastDomTabbableNode;
|
|
561
|
+
} else ve(b) || (h = E.nextTabbableNode(d, !1));
|
|
562
|
+
} else {
|
|
563
|
+
var D = i.tabbableGroups.findIndex(function(J) {
|
|
564
|
+
var U = J.lastTabbableNode;
|
|
565
|
+
return d === U;
|
|
566
|
+
});
|
|
567
|
+
if (D < 0 && (E.container === d || Fe(d, s.tabbableOptions) && !te(d, s.tabbableOptions) && !E.nextTabbableNode(d)) && (D = g), D >= 0) {
|
|
568
|
+
var $ = D === i.tabbableGroups.length - 1 ? 0 : D + 1, A = i.tabbableGroups[$];
|
|
569
|
+
h = Y(d) >= 0 ? A.firstTabbableNode : A.firstDomTabbableNode;
|
|
570
|
+
} else ve(b) || (h = E.nextTabbableNode(d));
|
|
571
|
+
}
|
|
572
|
+
} else
|
|
573
|
+
h = f("fallbackFocus");
|
|
574
|
+
return h;
|
|
575
|
+
}, N = function(u) {
|
|
576
|
+
var d = be(u);
|
|
577
|
+
if (!(c(d, u) >= 0)) {
|
|
578
|
+
if (pe(s.clickOutsideDeactivates, u)) {
|
|
579
|
+
o.deactivate({
|
|
580
|
+
// NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
|
|
581
|
+
// which will result in the outside click setting focus to the node
|
|
582
|
+
// that was clicked (and if not focusable, to "nothing"); by setting
|
|
583
|
+
// `returnFocus: true`, we'll attempt to re-focus the node originally-focused
|
|
584
|
+
// on activation (or the configured `setReturnFocus` node), whether the
|
|
585
|
+
// outside click was on a focusable node or not
|
|
586
|
+
returnFocus: s.returnFocusOnDeactivate
|
|
587
|
+
});
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
pe(s.allowOutsideClick, u) || u.preventDefault();
|
|
591
|
+
}
|
|
592
|
+
}, F = function(u) {
|
|
593
|
+
var d = be(u), b = c(d, u) >= 0;
|
|
594
|
+
if (b || d instanceof Document)
|
|
595
|
+
b && (i.mostRecentlyFocusedNode = d);
|
|
596
|
+
else {
|
|
597
|
+
u.stopImmediatePropagation();
|
|
598
|
+
var I, m = !0;
|
|
599
|
+
if (i.mostRecentlyFocusedNode)
|
|
600
|
+
if (Y(i.mostRecentlyFocusedNode) > 0) {
|
|
601
|
+
var h = c(i.mostRecentlyFocusedNode), g = i.containerGroups[h].tabbableNodes;
|
|
602
|
+
if (g.length > 0) {
|
|
603
|
+
var E = g.findIndex(function(k) {
|
|
604
|
+
return k === i.mostRecentlyFocusedNode;
|
|
605
|
+
});
|
|
606
|
+
E >= 0 && (s.isKeyForward(i.recentNavEvent) ? E + 1 < g.length && (I = g[E + 1], m = !1) : E - 1 >= 0 && (I = g[E - 1], m = !1));
|
|
607
|
+
}
|
|
608
|
+
} else
|
|
609
|
+
i.containerGroups.some(function(k) {
|
|
610
|
+
return k.tabbableNodes.some(function(R) {
|
|
611
|
+
return Y(R) > 0;
|
|
612
|
+
});
|
|
613
|
+
}) || (m = !1);
|
|
614
|
+
else
|
|
615
|
+
m = !1;
|
|
616
|
+
m && (I = _({
|
|
617
|
+
// move FROM the MRU node, not event-related node (which will be the node that is
|
|
618
|
+
// outside the trap causing the focus escape we're trying to fix)
|
|
619
|
+
target: i.mostRecentlyFocusedNode,
|
|
620
|
+
isBackward: s.isKeyBackward(i.recentNavEvent)
|
|
621
|
+
})), S(I || i.mostRecentlyFocusedNode || p());
|
|
622
|
+
}
|
|
623
|
+
i.recentNavEvent = void 0;
|
|
624
|
+
}, G = function(u) {
|
|
625
|
+
var d = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : !1;
|
|
626
|
+
i.recentNavEvent = u;
|
|
627
|
+
var b = _({
|
|
628
|
+
event: u,
|
|
629
|
+
isBackward: d
|
|
630
|
+
});
|
|
631
|
+
b && (ve(u) && u.preventDefault(), S(b));
|
|
632
|
+
}, z = function(u) {
|
|
633
|
+
(s.isKeyForward(u) || s.isKeyBackward(u)) && G(u, s.isKeyBackward(u));
|
|
634
|
+
}, j = function(u) {
|
|
635
|
+
xn(u) && pe(s.escapeDeactivates, u) !== !1 && (u.preventDefault(), o.deactivate());
|
|
636
|
+
}, L = function(u) {
|
|
637
|
+
var d = be(u);
|
|
638
|
+
c(d, u) >= 0 || pe(s.clickOutsideDeactivates, u) || pe(s.allowOutsideClick, u) || (u.preventDefault(), u.stopImmediatePropagation());
|
|
639
|
+
}, V = function() {
|
|
640
|
+
if (i.active)
|
|
641
|
+
return W.activateTrap(r, o), i.delayInitialFocusTimer = s.delayInitialFocus ? rt(function() {
|
|
642
|
+
S(p());
|
|
643
|
+
}) : S(p()), n.addEventListener("focusin", F, !0), n.addEventListener("mousedown", N, {
|
|
644
|
+
capture: !0,
|
|
645
|
+
passive: !1
|
|
646
|
+
}), n.addEventListener("touchstart", N, {
|
|
647
|
+
capture: !0,
|
|
648
|
+
passive: !1
|
|
649
|
+
}), n.addEventListener("click", L, {
|
|
650
|
+
capture: !0,
|
|
651
|
+
passive: !1
|
|
652
|
+
}), n.addEventListener("keydown", z, {
|
|
653
|
+
capture: !0,
|
|
654
|
+
passive: !1
|
|
655
|
+
}), n.addEventListener("keydown", j), o;
|
|
656
|
+
}, O = function(u) {
|
|
657
|
+
i.active && !i.paused && o._setSubtreeIsolation(!1), i.adjacentElements.clear(), i.alreadySilent.clear();
|
|
658
|
+
var d = /* @__PURE__ */ new Set(), b = /* @__PURE__ */ new Set(), I = tt(u), m;
|
|
659
|
+
try {
|
|
660
|
+
for (I.s(); !(m = I.n()).done; ) {
|
|
661
|
+
var h = m.value;
|
|
662
|
+
d.add(h);
|
|
663
|
+
for (var g = typeof ShadowRoot < "u" && h.getRootNode() instanceof ShadowRoot, E = h; E; ) {
|
|
664
|
+
d.add(E);
|
|
665
|
+
var k = E.parentElement, R = [];
|
|
666
|
+
k ? R = k.children : !k && g && (R = E.getRootNode().children, k = E.getRootNode().host, g = typeof ShadowRoot < "u" && k.getRootNode() instanceof ShadowRoot);
|
|
667
|
+
var P = tt(R), D;
|
|
668
|
+
try {
|
|
669
|
+
for (P.s(); !(D = P.n()).done; ) {
|
|
670
|
+
var $ = D.value;
|
|
671
|
+
b.add($);
|
|
672
|
+
}
|
|
673
|
+
} catch (A) {
|
|
674
|
+
P.e(A);
|
|
675
|
+
} finally {
|
|
676
|
+
P.f();
|
|
677
|
+
}
|
|
678
|
+
E = k;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
} catch (A) {
|
|
682
|
+
I.e(A);
|
|
683
|
+
} finally {
|
|
684
|
+
I.f();
|
|
685
|
+
}
|
|
686
|
+
d.forEach(function(A) {
|
|
687
|
+
b.delete(A);
|
|
688
|
+
}), i.adjacentElements = b;
|
|
689
|
+
}, K = function() {
|
|
690
|
+
if (i.active)
|
|
691
|
+
return n.removeEventListener("focusin", F, !0), n.removeEventListener("mousedown", N, !0), n.removeEventListener("touchstart", N, !0), n.removeEventListener("click", L, !0), n.removeEventListener("keydown", z, !0), n.removeEventListener("keydown", j), o;
|
|
692
|
+
}, se = function(u) {
|
|
693
|
+
var d = u.some(function(b) {
|
|
694
|
+
var I = Array.from(b.removedNodes);
|
|
695
|
+
return I.some(function(m) {
|
|
696
|
+
return m === i.mostRecentlyFocusedNode;
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
d && S(p());
|
|
700
|
+
}, ae = typeof window < "u" && "MutationObserver" in window ? new MutationObserver(se) : void 0, ee = function() {
|
|
701
|
+
ae && (ae.disconnect(), i.active && !i.paused && i.containers.map(function(u) {
|
|
702
|
+
ae.observe(u, {
|
|
703
|
+
subtree: !0,
|
|
704
|
+
childList: !0
|
|
705
|
+
});
|
|
706
|
+
}));
|
|
707
|
+
};
|
|
708
|
+
return o = {
|
|
709
|
+
get active() {
|
|
710
|
+
return i.active;
|
|
711
|
+
},
|
|
712
|
+
get paused() {
|
|
713
|
+
return i.paused;
|
|
714
|
+
},
|
|
715
|
+
activate: function(u) {
|
|
716
|
+
if (i.active)
|
|
717
|
+
return this;
|
|
718
|
+
var d = l(u, "onActivate"), b = l(u, "onPostActivate"), I = l(u, "checkCanFocusTrap"), m = W.getActiveTrap(r), h = !1;
|
|
719
|
+
if (m && !m.paused) {
|
|
720
|
+
var g;
|
|
721
|
+
(g = m._setSubtreeIsolation) === null || g === void 0 || g.call(m, !1), h = !0;
|
|
722
|
+
}
|
|
723
|
+
try {
|
|
724
|
+
I || v(), i.active = !0, i.paused = !1, i.nodeFocusedBeforeActivation = y(n), d?.();
|
|
725
|
+
var E = function() {
|
|
726
|
+
I && v(), V(), ee(), s.isolateSubtrees && o._setSubtreeIsolation(!0), b?.();
|
|
727
|
+
};
|
|
728
|
+
if (I)
|
|
729
|
+
return I(i.containers.concat()).then(E, E), this;
|
|
730
|
+
E();
|
|
731
|
+
} catch (R) {
|
|
732
|
+
if (m === W.getActiveTrap(r) && h) {
|
|
733
|
+
var k;
|
|
734
|
+
(k = m._setSubtreeIsolation) === null || k === void 0 || k.call(m, !0);
|
|
735
|
+
}
|
|
736
|
+
throw R;
|
|
737
|
+
}
|
|
738
|
+
return this;
|
|
739
|
+
},
|
|
740
|
+
deactivate: function(u) {
|
|
741
|
+
if (!i.active)
|
|
742
|
+
return this;
|
|
743
|
+
var d = it({
|
|
744
|
+
onDeactivate: s.onDeactivate,
|
|
745
|
+
onPostDeactivate: s.onPostDeactivate,
|
|
746
|
+
checkCanReturnFocus: s.checkCanReturnFocus
|
|
747
|
+
}, u);
|
|
748
|
+
clearTimeout(i.delayInitialFocusTimer), i.delayInitialFocusTimer = void 0, i.paused || o._setSubtreeIsolation(!1), i.alreadySilent.clear(), K(), i.active = !1, i.paused = !1, ee(), W.deactivateTrap(r, o);
|
|
749
|
+
var b = l(d, "onDeactivate"), I = l(d, "onPostDeactivate"), m = l(d, "checkCanReturnFocus"), h = l(d, "returnFocus", "returnFocusOnDeactivate");
|
|
750
|
+
b?.();
|
|
751
|
+
var g = function() {
|
|
752
|
+
rt(function() {
|
|
753
|
+
h && S(w(i.nodeFocusedBeforeActivation)), I?.();
|
|
754
|
+
});
|
|
755
|
+
};
|
|
756
|
+
return h && m ? (m(w(i.nodeFocusedBeforeActivation)).then(g, g), this) : (g(), this);
|
|
757
|
+
},
|
|
758
|
+
pause: function(u) {
|
|
759
|
+
return i.active ? (i.manuallyPaused = !0, this._setPausedState(!0, u)) : this;
|
|
760
|
+
},
|
|
761
|
+
unpause: function(u) {
|
|
762
|
+
return i.active ? (i.manuallyPaused = !1, r[r.length - 1] !== this ? this : this._setPausedState(!1, u)) : this;
|
|
763
|
+
},
|
|
764
|
+
updateContainerElements: function(u) {
|
|
765
|
+
var d = [].concat(u).filter(Boolean);
|
|
766
|
+
return i.containers = d.map(function(b) {
|
|
767
|
+
return typeof b == "string" ? n.querySelector(b) : b;
|
|
768
|
+
}), s.isolateSubtrees && O(i.containers), i.active && (v(), s.isolateSubtrees && !i.paused && o._setSubtreeIsolation(!0)), ee(), this;
|
|
769
|
+
}
|
|
770
|
+
}, Object.defineProperties(o, {
|
|
771
|
+
_isManuallyPaused: {
|
|
772
|
+
value: function() {
|
|
773
|
+
return i.manuallyPaused;
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
_setPausedState: {
|
|
777
|
+
value: function(u, d) {
|
|
778
|
+
if (i.paused === u)
|
|
779
|
+
return this;
|
|
780
|
+
if (i.paused = u, u) {
|
|
781
|
+
var b = l(d, "onPause"), I = l(d, "onPostPause");
|
|
782
|
+
b?.(), K(), ee(), o._setSubtreeIsolation(!1), I?.();
|
|
783
|
+
} else {
|
|
784
|
+
var m = l(d, "onUnpause"), h = l(d, "onPostUnpause");
|
|
785
|
+
m?.(), o._setSubtreeIsolation(!0), v(), V(), ee(), h?.();
|
|
786
|
+
}
|
|
787
|
+
return this;
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
_setSubtreeIsolation: {
|
|
791
|
+
value: function(u) {
|
|
792
|
+
s.isolateSubtrees && i.adjacentElements.forEach(function(d) {
|
|
793
|
+
var b;
|
|
794
|
+
u ? s.isolateSubtrees === "aria-hidden" ? ((d.ariaHidden === "true" || ((b = d.getAttribute("aria-hidden")) === null || b === void 0 ? void 0 : b.toLowerCase()) === "true") && i.alreadySilent.add(d), d.setAttribute("aria-hidden", "true")) : ((d.inert || d.hasAttribute("inert")) && i.alreadySilent.add(d), d.setAttribute("inert", !0)) : i.alreadySilent.has(d) || (s.isolateSubtrees === "aria-hidden" ? d.removeAttribute("aria-hidden") : d.removeAttribute("inert"));
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}), o.updateContainerElements(e), o;
|
|
799
|
+
};
|
|
800
|
+
function Nn(a, e = {}) {
|
|
801
|
+
let t;
|
|
802
|
+
const { immediate: n, ...r } = e, s = Z(!1), i = Z(!1), o = (p) => t && t.activate(p), l = (p) => t && t.deactivate(p), c = () => {
|
|
803
|
+
t && (t.pause(), i.value = !0);
|
|
804
|
+
}, f = () => {
|
|
805
|
+
t && (t.unpause(), i.value = !1);
|
|
806
|
+
};
|
|
807
|
+
return Le(Se(() => Dt(He(a)).map((p) => {
|
|
808
|
+
const v = He(p);
|
|
809
|
+
return typeof v == "string" ? v : Pt(v);
|
|
810
|
+
}).filter(zt)), (p) => {
|
|
811
|
+
if (p.length)
|
|
812
|
+
if (!t)
|
|
813
|
+
t = kn(p, {
|
|
814
|
+
...r,
|
|
815
|
+
onActivate() {
|
|
816
|
+
s.value = !0, e.onActivate && e.onActivate();
|
|
817
|
+
},
|
|
818
|
+
onDeactivate() {
|
|
819
|
+
s.value = !1, e.onDeactivate && e.onDeactivate();
|
|
820
|
+
}
|
|
821
|
+
}), n && o();
|
|
822
|
+
else {
|
|
823
|
+
const v = t?.active;
|
|
824
|
+
t?.updateContainerElements(p), !v && n && o();
|
|
825
|
+
}
|
|
826
|
+
}, { flush: "post" }), Vt(() => l()), {
|
|
827
|
+
hasFocus: s,
|
|
828
|
+
isPaused: i,
|
|
829
|
+
activate: o,
|
|
830
|
+
deactivate: l,
|
|
831
|
+
pause: c,
|
|
832
|
+
unpause: f
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
class ie {
|
|
836
|
+
/**
|
|
837
|
+
* @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
|
|
838
|
+
* element, an array of DOM elements, a NodeList or a selector
|
|
839
|
+
* @param {boolean} [iframes=true] - A boolean indicating if iframes should
|
|
840
|
+
* be handled
|
|
841
|
+
* @param {string[]} [exclude=[]] - An array containing exclusion selectors
|
|
842
|
+
* for iframes
|
|
843
|
+
* @param {number} [iframesTimeout=5000] - A number indicating the ms to
|
|
844
|
+
* wait before an iframe should be skipped, in case the load event isn't
|
|
845
|
+
* fired. This also applies if the user is offline and the resource of the
|
|
846
|
+
* iframe is online (either by the browsers "offline" mode or because
|
|
847
|
+
* there's no internet connection)
|
|
848
|
+
*/
|
|
849
|
+
constructor(e, t = !0, n = [], r = 5e3) {
|
|
850
|
+
this.ctx = e, this.iframes = t, this.exclude = n, this.iframesTimeout = r;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Checks if the specified DOM element matches the selector
|
|
854
|
+
* @param {HTMLElement} element - The DOM element
|
|
855
|
+
* @param {string|string[]} selector - The selector or an array with
|
|
856
|
+
* selectors
|
|
857
|
+
* @return {boolean}
|
|
858
|
+
* @access public
|
|
859
|
+
*/
|
|
860
|
+
static matches(e, t) {
|
|
861
|
+
const n = typeof t == "string" ? [t] : t, r = e.matches || e.matchesSelector || e.msMatchesSelector || e.mozMatchesSelector || e.oMatchesSelector || e.webkitMatchesSelector;
|
|
862
|
+
if (r) {
|
|
863
|
+
let s = !1;
|
|
864
|
+
return n.every((i) => r.call(e, i) ? (s = !0, !1) : !0), s;
|
|
865
|
+
} else
|
|
866
|
+
return !1;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Returns all contexts filtered by duplicates (even nested)
|
|
870
|
+
* @return {HTMLElement[]} - An array containing DOM contexts
|
|
871
|
+
* @access protected
|
|
872
|
+
*/
|
|
873
|
+
getContexts() {
|
|
874
|
+
let e, t = [];
|
|
875
|
+
return typeof this.ctx > "u" || !this.ctx ? e = [] : NodeList.prototype.isPrototypeOf(this.ctx) ? e = Array.prototype.slice.call(this.ctx) : Array.isArray(this.ctx) ? e = this.ctx : typeof this.ctx == "string" ? e = Array.prototype.slice.call(
|
|
876
|
+
document.querySelectorAll(this.ctx)
|
|
877
|
+
) : e = [this.ctx], e.forEach((n) => {
|
|
878
|
+
const r = t.filter((s) => s.contains(n)).length > 0;
|
|
879
|
+
t.indexOf(n) === -1 && !r && t.push(n);
|
|
880
|
+
}), t;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* @callback DOMIterator~getIframeContentsSuccessCallback
|
|
884
|
+
* @param {HTMLDocument} contents - The contentDocument of the iframe
|
|
885
|
+
*/
|
|
886
|
+
/**
|
|
887
|
+
* Calls the success callback function with the iframe document. If it can't
|
|
888
|
+
* be accessed it calls the error callback function
|
|
889
|
+
* @param {HTMLElement} ifr - The iframe DOM element
|
|
890
|
+
* @param {DOMIterator~getIframeContentsSuccessCallback} successFn
|
|
891
|
+
* @param {function} [errorFn]
|
|
892
|
+
* @access protected
|
|
893
|
+
*/
|
|
894
|
+
getIframeContents(e, t, n = () => {
|
|
895
|
+
}) {
|
|
896
|
+
let r;
|
|
897
|
+
try {
|
|
898
|
+
const s = e.contentWindow;
|
|
899
|
+
if (r = s.document, !s || !r)
|
|
900
|
+
throw new Error("iframe inaccessible");
|
|
901
|
+
} catch {
|
|
902
|
+
n();
|
|
903
|
+
}
|
|
904
|
+
r && t(r);
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Checks if an iframe is empty (if about:blank is the shown page)
|
|
908
|
+
* @param {HTMLElement} ifr - The iframe DOM element
|
|
909
|
+
* @return {boolean}
|
|
910
|
+
* @access protected
|
|
911
|
+
*/
|
|
912
|
+
isIframeBlank(e) {
|
|
913
|
+
const t = "about:blank", n = e.getAttribute("src").trim();
|
|
914
|
+
return e.contentWindow.location.href === t && n !== t && n;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Observes the onload event of an iframe and calls the success callback or
|
|
918
|
+
* the error callback if the iframe is inaccessible. If the event isn't
|
|
919
|
+
* fired within the specified {@link DOMIterator#iframesTimeout}, then it'll
|
|
920
|
+
* call the error callback too
|
|
921
|
+
* @param {HTMLElement} ifr - The iframe DOM element
|
|
922
|
+
* @param {DOMIterator~getIframeContentsSuccessCallback} successFn
|
|
923
|
+
* @param {function} errorFn
|
|
924
|
+
* @access protected
|
|
925
|
+
*/
|
|
926
|
+
observeIframeLoad(e, t, n) {
|
|
927
|
+
let r = !1, s = null;
|
|
928
|
+
const i = () => {
|
|
929
|
+
if (!r) {
|
|
930
|
+
r = !0, clearTimeout(s);
|
|
931
|
+
try {
|
|
932
|
+
this.isIframeBlank(e) || (e.removeEventListener("load", i), this.getIframeContents(e, t, n));
|
|
933
|
+
} catch {
|
|
934
|
+
n();
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
e.addEventListener("load", i), s = setTimeout(i, this.iframesTimeout);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Callback when the iframe is ready
|
|
942
|
+
* @callback DOMIterator~onIframeReadySuccessCallback
|
|
943
|
+
* @param {HTMLDocument} contents - The contentDocument of the iframe
|
|
944
|
+
*/
|
|
945
|
+
/**
|
|
946
|
+
* Callback if the iframe can't be accessed
|
|
947
|
+
* @callback DOMIterator~onIframeReadyErrorCallback
|
|
948
|
+
*/
|
|
949
|
+
/**
|
|
950
|
+
* Calls the callback if the specified iframe is ready for DOM access
|
|
951
|
+
* @param {HTMLElement} ifr - The iframe DOM element
|
|
952
|
+
* @param {DOMIterator~onIframeReadySuccessCallback} successFn - Success
|
|
953
|
+
* callback
|
|
954
|
+
* @param {DOMIterator~onIframeReadyErrorCallback} errorFn - Error callback
|
|
955
|
+
* @see {@link http://stackoverflow.com/a/36155560/3894981} for
|
|
956
|
+
* background information
|
|
957
|
+
* @access protected
|
|
958
|
+
*/
|
|
959
|
+
onIframeReady(e, t, n) {
|
|
960
|
+
try {
|
|
961
|
+
e.contentWindow.document.readyState === "complete" ? this.isIframeBlank(e) ? this.observeIframeLoad(e, t, n) : this.getIframeContents(e, t, n) : this.observeIframeLoad(e, t, n);
|
|
962
|
+
} catch {
|
|
963
|
+
n();
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Callback when all iframes are ready for DOM access
|
|
968
|
+
* @callback DOMIterator~waitForIframesDoneCallback
|
|
969
|
+
*/
|
|
970
|
+
/**
|
|
971
|
+
* Iterates over all iframes and calls the done callback when all of them
|
|
972
|
+
* are ready for DOM access (including nested ones)
|
|
973
|
+
* @param {HTMLElement} ctx - The context DOM element
|
|
974
|
+
* @param {DOMIterator~waitForIframesDoneCallback} done - Done callback
|
|
975
|
+
*/
|
|
976
|
+
waitForIframes(e, t) {
|
|
977
|
+
let n = 0;
|
|
978
|
+
this.forEachIframe(e, () => !0, (r) => {
|
|
979
|
+
n++, this.waitForIframes(r.querySelector("html"), () => {
|
|
980
|
+
--n || t();
|
|
981
|
+
});
|
|
982
|
+
}, (r) => {
|
|
983
|
+
r || t();
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Callback allowing to filter an iframe. Must return true when the element
|
|
988
|
+
* should remain, otherwise false
|
|
989
|
+
* @callback DOMIterator~forEachIframeFilterCallback
|
|
990
|
+
* @param {HTMLElement} iframe - The iframe DOM element
|
|
991
|
+
*/
|
|
992
|
+
/**
|
|
993
|
+
* Callback for each iframe content
|
|
994
|
+
* @callback DOMIterator~forEachIframeEachCallback
|
|
995
|
+
* @param {HTMLElement} content - The iframe document
|
|
996
|
+
*/
|
|
997
|
+
/**
|
|
998
|
+
* Callback if all iframes inside the context were handled
|
|
999
|
+
* @callback DOMIterator~forEachIframeEndCallback
|
|
1000
|
+
* @param {number} handled - The number of handled iframes (those who
|
|
1001
|
+
* wheren't filtered)
|
|
1002
|
+
*/
|
|
1003
|
+
/**
|
|
1004
|
+
* Iterates over all iframes inside the specified context and calls the
|
|
1005
|
+
* callbacks when they're ready. Filters iframes based on the instance
|
|
1006
|
+
* exclusion selectors
|
|
1007
|
+
* @param {HTMLElement} ctx - The context DOM element
|
|
1008
|
+
* @param {DOMIterator~forEachIframeFilterCallback} filter - Filter callback
|
|
1009
|
+
* @param {DOMIterator~forEachIframeEachCallback} each - Each callback
|
|
1010
|
+
* @param {DOMIterator~forEachIframeEndCallback} [end] - End callback
|
|
1011
|
+
* @access protected
|
|
1012
|
+
*/
|
|
1013
|
+
forEachIframe(e, t, n, r = () => {
|
|
1014
|
+
}) {
|
|
1015
|
+
let s = e.querySelectorAll("iframe"), i = s.length, o = 0;
|
|
1016
|
+
s = Array.prototype.slice.call(s);
|
|
1017
|
+
const l = () => {
|
|
1018
|
+
--i <= 0 && r(o);
|
|
1019
|
+
};
|
|
1020
|
+
i || l(), s.forEach((c) => {
|
|
1021
|
+
ie.matches(c, this.exclude) ? l() : this.onIframeReady(c, (f) => {
|
|
1022
|
+
t(c) && (o++, n(f)), l();
|
|
1023
|
+
}, l);
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Creates a NodeIterator on the specified context
|
|
1028
|
+
* @see {@link https://developer.mozilla.org/en/docs/Web/API/NodeIterator}
|
|
1029
|
+
* @param {HTMLElement} ctx - The context DOM element
|
|
1030
|
+
* @param {DOMIterator~whatToShow} whatToShow
|
|
1031
|
+
* @param {DOMIterator~filterCb} filter
|
|
1032
|
+
* @return {NodeIterator}
|
|
1033
|
+
* @access protected
|
|
1034
|
+
*/
|
|
1035
|
+
createIterator(e, t, n) {
|
|
1036
|
+
return document.createNodeIterator(e, t, n, !1);
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Creates an instance of DOMIterator in an iframe
|
|
1040
|
+
* @param {HTMLDocument} contents - Iframe document
|
|
1041
|
+
* @return {DOMIterator}
|
|
1042
|
+
* @access protected
|
|
1043
|
+
*/
|
|
1044
|
+
createInstanceOnIframe(e) {
|
|
1045
|
+
return new ie(e.querySelector("html"), this.iframes);
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Checks if an iframe occurs between two nodes, more specifically if an
|
|
1049
|
+
* iframe occurs before the specified node and after the specified prevNode
|
|
1050
|
+
* @param {HTMLElement} node - The node that should occur after the iframe
|
|
1051
|
+
* @param {HTMLElement} prevNode - The node that should occur before the
|
|
1052
|
+
* iframe
|
|
1053
|
+
* @param {HTMLElement} ifr - The iframe to check against
|
|
1054
|
+
* @return {boolean}
|
|
1055
|
+
* @access protected
|
|
1056
|
+
*/
|
|
1057
|
+
compareNodeIframe(e, t, n) {
|
|
1058
|
+
const r = e.compareDocumentPosition(n), s = Node.DOCUMENT_POSITION_PRECEDING;
|
|
1059
|
+
if (r & s)
|
|
1060
|
+
if (t !== null) {
|
|
1061
|
+
const i = t.compareDocumentPosition(n), o = Node.DOCUMENT_POSITION_FOLLOWING;
|
|
1062
|
+
if (i & o)
|
|
1063
|
+
return !0;
|
|
1064
|
+
} else
|
|
1065
|
+
return !0;
|
|
1066
|
+
return !1;
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* @typedef {DOMIterator~getIteratorNodeReturn}
|
|
1070
|
+
* @type {object.<string>}
|
|
1071
|
+
* @property {HTMLElement} prevNode - The previous node or null if there is
|
|
1072
|
+
* no
|
|
1073
|
+
* @property {HTMLElement} node - The current node
|
|
1074
|
+
*/
|
|
1075
|
+
/**
|
|
1076
|
+
* Returns the previous and current node of the specified iterator
|
|
1077
|
+
* @param {NodeIterator} itr - The iterator
|
|
1078
|
+
* @return {DOMIterator~getIteratorNodeReturn}
|
|
1079
|
+
* @access protected
|
|
1080
|
+
*/
|
|
1081
|
+
getIteratorNode(e) {
|
|
1082
|
+
const t = e.previousNode();
|
|
1083
|
+
let n;
|
|
1084
|
+
return t === null ? n = e.nextNode() : n = e.nextNode() && e.nextNode(), {
|
|
1085
|
+
prevNode: t,
|
|
1086
|
+
node: n
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* An array containing objects. The object key "val" contains an iframe
|
|
1091
|
+
* DOM element. The object key "handled" contains a boolean indicating if
|
|
1092
|
+
* the iframe was handled already.
|
|
1093
|
+
* It wouldn't be enough to save all open or all already handled iframes.
|
|
1094
|
+
* The information of open iframes is necessary because they may occur after
|
|
1095
|
+
* all other text nodes (and compareNodeIframe would never be true). The
|
|
1096
|
+
* information of already handled iframes is necessary as otherwise they may
|
|
1097
|
+
* be handled multiple times
|
|
1098
|
+
* @typedef DOMIterator~checkIframeFilterIfr
|
|
1099
|
+
* @type {object[]}
|
|
1100
|
+
*/
|
|
1101
|
+
/**
|
|
1102
|
+
* Checks if an iframe wasn't handled already and if so, calls
|
|
1103
|
+
* {@link DOMIterator#compareNodeIframe} to check if it should be handled.
|
|
1104
|
+
* Information wheter an iframe was or wasn't handled is given within the
|
|
1105
|
+
* <code>ifr</code> dictionary
|
|
1106
|
+
* @param {HTMLElement} node - The node that should occur after the iframe
|
|
1107
|
+
* @param {HTMLElement} prevNode - The node that should occur before the
|
|
1108
|
+
* iframe
|
|
1109
|
+
* @param {HTMLElement} currIfr - The iframe to check
|
|
1110
|
+
* @param {DOMIterator~checkIframeFilterIfr} ifr - The iframe dictionary.
|
|
1111
|
+
* Will be manipulated (by reference)
|
|
1112
|
+
* @return {boolean} Returns true when it should be handled, otherwise false
|
|
1113
|
+
* @access protected
|
|
1114
|
+
*/
|
|
1115
|
+
checkIframeFilter(e, t, n, r) {
|
|
1116
|
+
let s = !1, i = !1;
|
|
1117
|
+
return r.forEach((o, l) => {
|
|
1118
|
+
o.val === n && (s = l, i = o.handled);
|
|
1119
|
+
}), this.compareNodeIframe(e, t, n) ? (s === !1 && !i ? r.push({
|
|
1120
|
+
val: n,
|
|
1121
|
+
handled: !0
|
|
1122
|
+
}) : s !== !1 && !i && (r[s].handled = !0), !0) : (s === !1 && r.push({
|
|
1123
|
+
val: n,
|
|
1124
|
+
handled: !1
|
|
1125
|
+
}), !1);
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Creates an iterator on all open iframes in the specified array and calls
|
|
1129
|
+
* the end callback when finished
|
|
1130
|
+
* @param {DOMIterator~checkIframeFilterIfr} ifr
|
|
1131
|
+
* @param {DOMIterator~whatToShow} whatToShow
|
|
1132
|
+
* @param {DOMIterator~forEachNodeCallback} eCb - Each callback
|
|
1133
|
+
* @param {DOMIterator~filterCb} fCb
|
|
1134
|
+
* @access protected
|
|
1135
|
+
*/
|
|
1136
|
+
handleOpenIframes(e, t, n, r) {
|
|
1137
|
+
e.forEach((s) => {
|
|
1138
|
+
s.handled || this.getIframeContents(s.val, (i) => {
|
|
1139
|
+
this.createInstanceOnIframe(i).forEachNode(
|
|
1140
|
+
t,
|
|
1141
|
+
n,
|
|
1142
|
+
r
|
|
1143
|
+
);
|
|
1144
|
+
});
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Iterates through all nodes in the specified context and handles iframe
|
|
1149
|
+
* nodes at the correct position
|
|
1150
|
+
* @param {DOMIterator~whatToShow} whatToShow
|
|
1151
|
+
* @param {HTMLElement} ctx - The context
|
|
1152
|
+
* @param {DOMIterator~forEachNodeCallback} eachCb - Each callback
|
|
1153
|
+
* @param {DOMIterator~filterCb} filterCb - Filter callback
|
|
1154
|
+
* @param {DOMIterator~forEachNodeEndCallback} doneCb - End callback
|
|
1155
|
+
* @access protected
|
|
1156
|
+
*/
|
|
1157
|
+
iterateThroughNodes(e, t, n, r, s) {
|
|
1158
|
+
const i = this.createIterator(t, e, r);
|
|
1159
|
+
let o = [], l = [], c, f, p = () => ({
|
|
1160
|
+
prevNode: f,
|
|
1161
|
+
node: c
|
|
1162
|
+
} = this.getIteratorNode(i), c);
|
|
1163
|
+
for (; p(); )
|
|
1164
|
+
this.iframes && this.forEachIframe(t, (v) => this.checkIframeFilter(c, f, v, o), (v) => {
|
|
1165
|
+
this.createInstanceOnIframe(v).forEachNode(
|
|
1166
|
+
e,
|
|
1167
|
+
(y) => l.push(y),
|
|
1168
|
+
r
|
|
1169
|
+
);
|
|
1170
|
+
}), l.push(c);
|
|
1171
|
+
l.forEach((v) => {
|
|
1172
|
+
n(v);
|
|
1173
|
+
}), this.iframes && this.handleOpenIframes(o, e, n, r), s();
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Callback for each node
|
|
1177
|
+
* @callback DOMIterator~forEachNodeCallback
|
|
1178
|
+
* @param {HTMLElement} node - The DOM text node element
|
|
1179
|
+
*/
|
|
1180
|
+
/**
|
|
1181
|
+
* Callback if all contexts were handled
|
|
1182
|
+
* @callback DOMIterator~forEachNodeEndCallback
|
|
1183
|
+
*/
|
|
1184
|
+
/**
|
|
1185
|
+
* Iterates over all contexts and initializes
|
|
1186
|
+
* {@link DOMIterator#iterateThroughNodes iterateThroughNodes} on them
|
|
1187
|
+
* @param {DOMIterator~whatToShow} whatToShow
|
|
1188
|
+
* @param {DOMIterator~forEachNodeCallback} each - Each callback
|
|
1189
|
+
* @param {DOMIterator~filterCb} filter - Filter callback
|
|
1190
|
+
* @param {DOMIterator~forEachNodeEndCallback} done - End callback
|
|
1191
|
+
* @access public
|
|
1192
|
+
*/
|
|
1193
|
+
forEachNode(e, t, n, r = () => {
|
|
1194
|
+
}) {
|
|
1195
|
+
const s = this.getContexts();
|
|
1196
|
+
let i = s.length;
|
|
1197
|
+
i || r(), s.forEach((o) => {
|
|
1198
|
+
const l = () => {
|
|
1199
|
+
this.iterateThroughNodes(e, o, t, n, () => {
|
|
1200
|
+
--i <= 0 && r();
|
|
1201
|
+
});
|
|
1202
|
+
};
|
|
1203
|
+
this.iframes ? this.waitForIframes(o, l) : l();
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Callback to filter nodes. Can return e.g. NodeFilter.FILTER_ACCEPT or
|
|
1208
|
+
* NodeFilter.FILTER_REJECT
|
|
1209
|
+
* @see {@link http://tinyurl.com/zdczmm2}
|
|
1210
|
+
* @callback DOMIterator~filterCb
|
|
1211
|
+
* @param {HTMLElement} node - The node to filter
|
|
1212
|
+
*/
|
|
1213
|
+
/**
|
|
1214
|
+
* @typedef DOMIterator~whatToShow
|
|
1215
|
+
* @see {@link http://tinyurl.com/zfqqkx2}
|
|
1216
|
+
* @type {number}
|
|
1217
|
+
*/
|
|
1218
|
+
}
|
|
1219
|
+
let Fn = class {
|
|
1220
|
+
// eslint-disable-line no-unused-vars
|
|
1221
|
+
/**
|
|
1222
|
+
* @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
|
|
1223
|
+
* element, an array of DOM elements, a NodeList or a selector
|
|
1224
|
+
*/
|
|
1225
|
+
constructor(e) {
|
|
1226
|
+
this.ctx = e, this.ie = !1;
|
|
1227
|
+
const t = window.navigator.userAgent;
|
|
1228
|
+
(t.indexOf("MSIE") > -1 || t.indexOf("Trident") > -1) && (this.ie = !0);
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Options defined by the user. They will be initialized from one of the
|
|
1232
|
+
* public methods. See {@link Mark#mark}, {@link Mark#markRegExp},
|
|
1233
|
+
* {@link Mark#markRanges} and {@link Mark#unmark} for option properties.
|
|
1234
|
+
* @type {object}
|
|
1235
|
+
* @param {object} [val] - An object that will be merged with defaults
|
|
1236
|
+
* @access protected
|
|
1237
|
+
*/
|
|
1238
|
+
set opt(e) {
|
|
1239
|
+
this._opt = Object.assign({}, {
|
|
1240
|
+
element: "",
|
|
1241
|
+
className: "",
|
|
1242
|
+
exclude: [],
|
|
1243
|
+
iframes: !1,
|
|
1244
|
+
iframesTimeout: 5e3,
|
|
1245
|
+
separateWordSearch: !0,
|
|
1246
|
+
diacritics: !0,
|
|
1247
|
+
synonyms: {},
|
|
1248
|
+
accuracy: "partially",
|
|
1249
|
+
acrossElements: !1,
|
|
1250
|
+
caseSensitive: !1,
|
|
1251
|
+
ignoreJoiners: !1,
|
|
1252
|
+
ignoreGroups: 0,
|
|
1253
|
+
ignorePunctuation: [],
|
|
1254
|
+
wildcards: "disabled",
|
|
1255
|
+
each: () => {
|
|
1256
|
+
},
|
|
1257
|
+
noMatch: () => {
|
|
1258
|
+
},
|
|
1259
|
+
filter: () => !0,
|
|
1260
|
+
done: () => {
|
|
1261
|
+
},
|
|
1262
|
+
debug: !1,
|
|
1263
|
+
log: window.console
|
|
1264
|
+
}, e);
|
|
1265
|
+
}
|
|
1266
|
+
get opt() {
|
|
1267
|
+
return this._opt;
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* An instance of DOMIterator
|
|
1271
|
+
* @type {DOMIterator}
|
|
1272
|
+
* @access protected
|
|
1273
|
+
*/
|
|
1274
|
+
get iterator() {
|
|
1275
|
+
return new ie(
|
|
1276
|
+
this.ctx,
|
|
1277
|
+
this.opt.iframes,
|
|
1278
|
+
this.opt.exclude,
|
|
1279
|
+
this.opt.iframesTimeout
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Logs a message if log is enabled
|
|
1284
|
+
* @param {string} msg - The message to log
|
|
1285
|
+
* @param {string} [level="debug"] - The log level, e.g. <code>warn</code>
|
|
1286
|
+
* <code>error</code>, <code>debug</code>
|
|
1287
|
+
* @access protected
|
|
1288
|
+
*/
|
|
1289
|
+
log(e, t = "debug") {
|
|
1290
|
+
const n = this.opt.log;
|
|
1291
|
+
this.opt.debug && typeof n == "object" && typeof n[t] == "function" && n[t](`mark.js: ${e}`);
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Escapes a string for usage within a regular expression
|
|
1295
|
+
* @param {string} str - The string to escape
|
|
1296
|
+
* @return {string}
|
|
1297
|
+
* @access protected
|
|
1298
|
+
*/
|
|
1299
|
+
escapeStr(e) {
|
|
1300
|
+
return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Creates a regular expression string to match the specified search
|
|
1304
|
+
* term including synonyms, diacritics and accuracy if defined
|
|
1305
|
+
* @param {string} str - The search term to be used
|
|
1306
|
+
* @return {string}
|
|
1307
|
+
* @access protected
|
|
1308
|
+
*/
|
|
1309
|
+
createRegExp(e) {
|
|
1310
|
+
return this.opt.wildcards !== "disabled" && (e = this.setupWildcardsRegExp(e)), e = this.escapeStr(e), Object.keys(this.opt.synonyms).length && (e = this.createSynonymsRegExp(e)), (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) && (e = this.setupIgnoreJoinersRegExp(e)), this.opt.diacritics && (e = this.createDiacriticsRegExp(e)), e = this.createMergedBlanksRegExp(e), (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) && (e = this.createJoinersRegExp(e)), this.opt.wildcards !== "disabled" && (e = this.createWildcardsRegExp(e)), e = this.createAccuracyRegExp(e), e;
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Creates a regular expression string to match the defined synonyms
|
|
1314
|
+
* @param {string} str - The search term to be used
|
|
1315
|
+
* @return {string}
|
|
1316
|
+
* @access protected
|
|
1317
|
+
*/
|
|
1318
|
+
createSynonymsRegExp(e) {
|
|
1319
|
+
const t = this.opt.synonyms, n = this.opt.caseSensitive ? "" : "i", r = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : "";
|
|
1320
|
+
for (let s in t)
|
|
1321
|
+
if (t.hasOwnProperty(s)) {
|
|
1322
|
+
const i = t[s], o = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(s) : this.escapeStr(s), l = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(i) : this.escapeStr(i);
|
|
1323
|
+
o !== "" && l !== "" && (e = e.replace(
|
|
1324
|
+
new RegExp(
|
|
1325
|
+
`(${this.escapeStr(o)}|${this.escapeStr(l)})`,
|
|
1326
|
+
`gm${n}`
|
|
1327
|
+
),
|
|
1328
|
+
r + `(${this.processSynomyms(o)}|${this.processSynomyms(l)})` + r
|
|
1329
|
+
));
|
|
1330
|
+
}
|
|
1331
|
+
return e;
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Setup synonyms to work with ignoreJoiners and or ignorePunctuation
|
|
1335
|
+
* @param {string} str - synonym key or value to process
|
|
1336
|
+
* @return {string} - processed synonym string
|
|
1337
|
+
*/
|
|
1338
|
+
processSynomyms(e) {
|
|
1339
|
+
return (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) && (e = this.setupIgnoreJoinersRegExp(e)), e;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Sets up the regular expression string to allow later insertion of
|
|
1343
|
+
* wildcard regular expression matches
|
|
1344
|
+
* @param {string} str - The search term to be used
|
|
1345
|
+
* @return {string}
|
|
1346
|
+
* @access protected
|
|
1347
|
+
*/
|
|
1348
|
+
setupWildcardsRegExp(e) {
|
|
1349
|
+
return e = e.replace(/(?:\\)*\?/g, (t) => t.charAt(0) === "\\" ? "?" : ""), e.replace(/(?:\\)*\*/g, (t) => t.charAt(0) === "\\" ? "*" : "");
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Sets up the regular expression string to allow later insertion of
|
|
1353
|
+
* wildcard regular expression matches
|
|
1354
|
+
* @param {string} str - The search term to be used
|
|
1355
|
+
* @return {string}
|
|
1356
|
+
* @access protected
|
|
1357
|
+
*/
|
|
1358
|
+
createWildcardsRegExp(e) {
|
|
1359
|
+
let t = this.opt.wildcards === "withSpaces";
|
|
1360
|
+
return e.replace(/\u0001/g, t ? "[\\S\\s]?" : "\\S?").replace(/\u0002/g, t ? "[\\S\\s]*?" : "\\S*");
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Sets up the regular expression string to allow later insertion of
|
|
1364
|
+
* designated characters (soft hyphens & zero width characters)
|
|
1365
|
+
* @param {string} str - The search term to be used
|
|
1366
|
+
* @return {string}
|
|
1367
|
+
* @access protected
|
|
1368
|
+
*/
|
|
1369
|
+
setupIgnoreJoinersRegExp(e) {
|
|
1370
|
+
return e.replace(/[^(|)\\]/g, (t, n, r) => {
|
|
1371
|
+
let s = r.charAt(n + 1);
|
|
1372
|
+
return /[(|)\\]/.test(s) || s === "" ? t : t + "\0";
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Creates a regular expression string to allow ignoring of designated
|
|
1377
|
+
* characters (soft hyphens, zero width characters & punctuation) based on
|
|
1378
|
+
* the specified option values of <code>ignorePunctuation</code> and
|
|
1379
|
+
* <code>ignoreJoiners</code>
|
|
1380
|
+
* @param {string} str - The search term to be used
|
|
1381
|
+
* @return {string}
|
|
1382
|
+
* @access protected
|
|
1383
|
+
*/
|
|
1384
|
+
createJoinersRegExp(e) {
|
|
1385
|
+
let t = [];
|
|
1386
|
+
const n = this.opt.ignorePunctuation;
|
|
1387
|
+
return Array.isArray(n) && n.length && t.push(this.escapeStr(n.join(""))), this.opt.ignoreJoiners && t.push("\\u00ad\\u200b\\u200c\\u200d"), t.length ? e.split(/\u0000+/).join(`[${t.join("")}]*`) : e;
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Creates a regular expression string to match diacritics
|
|
1391
|
+
* @param {string} str - The search term to be used
|
|
1392
|
+
* @return {string}
|
|
1393
|
+
* @access protected
|
|
1394
|
+
*/
|
|
1395
|
+
createDiacriticsRegExp(e) {
|
|
1396
|
+
const t = this.opt.caseSensitive ? "" : "i", n = this.opt.caseSensitive ? [
|
|
1397
|
+
"aàáảãạăằắẳẵặâầấẩẫậäåāą",
|
|
1398
|
+
"AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ",
|
|
1399
|
+
"cçćč",
|
|
1400
|
+
"CÇĆČ",
|
|
1401
|
+
"dđď",
|
|
1402
|
+
"DĐĎ",
|
|
1403
|
+
"eèéẻẽẹêềếểễệëěēę",
|
|
1404
|
+
"EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ",
|
|
1405
|
+
"iìíỉĩịîïī",
|
|
1406
|
+
"IÌÍỈĨỊÎÏĪ",
|
|
1407
|
+
"lł",
|
|
1408
|
+
"LŁ",
|
|
1409
|
+
"nñňń",
|
|
1410
|
+
"NÑŇŃ",
|
|
1411
|
+
"oòóỏõọôồốổỗộơởỡớờợöøō",
|
|
1412
|
+
"OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ",
|
|
1413
|
+
"rř",
|
|
1414
|
+
"RŘ",
|
|
1415
|
+
"sšśșş",
|
|
1416
|
+
"SŠŚȘŞ",
|
|
1417
|
+
"tťțţ",
|
|
1418
|
+
"TŤȚŢ",
|
|
1419
|
+
"uùúủũụưừứửữựûüůū",
|
|
1420
|
+
"UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ",
|
|
1421
|
+
"yýỳỷỹỵÿ",
|
|
1422
|
+
"YÝỲỶỸỴŸ",
|
|
1423
|
+
"zžżź",
|
|
1424
|
+
"ZŽŻŹ"
|
|
1425
|
+
] : [
|
|
1426
|
+
"aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ",
|
|
1427
|
+
"cçćčCÇĆČ",
|
|
1428
|
+
"dđďDĐĎ",
|
|
1429
|
+
"eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ",
|
|
1430
|
+
"iìíỉĩịîïīIÌÍỈĨỊÎÏĪ",
|
|
1431
|
+
"lłLŁ",
|
|
1432
|
+
"nñňńNÑŇŃ",
|
|
1433
|
+
"oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ",
|
|
1434
|
+
"rřRŘ",
|
|
1435
|
+
"sšśșşSŠŚȘŞ",
|
|
1436
|
+
"tťțţTŤȚŢ",
|
|
1437
|
+
"uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ",
|
|
1438
|
+
"yýỳỷỹỵÿYÝỲỶỸỴŸ",
|
|
1439
|
+
"zžżźZŽŻŹ"
|
|
1440
|
+
];
|
|
1441
|
+
let r = [];
|
|
1442
|
+
return e.split("").forEach((s) => {
|
|
1443
|
+
n.every((i) => {
|
|
1444
|
+
if (i.indexOf(s) !== -1) {
|
|
1445
|
+
if (r.indexOf(i) > -1)
|
|
1446
|
+
return !1;
|
|
1447
|
+
e = e.replace(
|
|
1448
|
+
new RegExp(`[${i}]`, `gm${t}`),
|
|
1449
|
+
`[${i}]`
|
|
1450
|
+
), r.push(i);
|
|
1451
|
+
}
|
|
1452
|
+
return !0;
|
|
1453
|
+
});
|
|
1454
|
+
}), e;
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Creates a regular expression string that merges whitespace characters
|
|
1458
|
+
* including subsequent ones into a single pattern, one or multiple
|
|
1459
|
+
* whitespaces
|
|
1460
|
+
* @param {string} str - The search term to be used
|
|
1461
|
+
* @return {string}
|
|
1462
|
+
* @access protected
|
|
1463
|
+
*/
|
|
1464
|
+
createMergedBlanksRegExp(e) {
|
|
1465
|
+
return e.replace(/[\s]+/gmi, "[\\s]+");
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Creates a regular expression string to match the specified string with
|
|
1469
|
+
* the defined accuracy. As in the regular expression of "exactly" can be
|
|
1470
|
+
* a group containing a blank at the beginning, all regular expressions will
|
|
1471
|
+
* be created with two groups. The first group can be ignored (may contain
|
|
1472
|
+
* the said blank), the second contains the actual match
|
|
1473
|
+
* @param {string} str - The searm term to be used
|
|
1474
|
+
* @return {str}
|
|
1475
|
+
* @access protected
|
|
1476
|
+
*/
|
|
1477
|
+
createAccuracyRegExp(e) {
|
|
1478
|
+
const t = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿";
|
|
1479
|
+
let n = this.opt.accuracy, r = typeof n == "string" ? n : n.value, s = typeof n == "string" ? [] : n.limiters, i = "";
|
|
1480
|
+
switch (s.forEach((o) => {
|
|
1481
|
+
i += `|${this.escapeStr(o)}`;
|
|
1482
|
+
}), r) {
|
|
1483
|
+
case "partially":
|
|
1484
|
+
default:
|
|
1485
|
+
return `()(${e})`;
|
|
1486
|
+
case "complementary":
|
|
1487
|
+
return i = "\\s" + (i || this.escapeStr(t)), `()([^${i}]*${e}[^${i}]*)`;
|
|
1488
|
+
case "exactly":
|
|
1489
|
+
return `(^|\\s${i})(${e})(?=$|\\s${i})`;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* @typedef Mark~separatedKeywords
|
|
1494
|
+
* @type {object.<string>}
|
|
1495
|
+
* @property {array.<string>} keywords - The list of keywords
|
|
1496
|
+
* @property {number} length - The length
|
|
1497
|
+
*/
|
|
1498
|
+
/**
|
|
1499
|
+
* Returns a list of keywords dependent on whether separate word search
|
|
1500
|
+
* was defined. Also it filters empty keywords
|
|
1501
|
+
* @param {array} sv - The array of keywords
|
|
1502
|
+
* @return {Mark~separatedKeywords}
|
|
1503
|
+
* @access protected
|
|
1504
|
+
*/
|
|
1505
|
+
getSeparatedKeywords(e) {
|
|
1506
|
+
let t = [];
|
|
1507
|
+
return e.forEach((n) => {
|
|
1508
|
+
this.opt.separateWordSearch ? n.split(" ").forEach((r) => {
|
|
1509
|
+
r.trim() && t.indexOf(r) === -1 && t.push(r);
|
|
1510
|
+
}) : n.trim() && t.indexOf(n) === -1 && t.push(n);
|
|
1511
|
+
}), {
|
|
1512
|
+
// sort because of https://git.io/v6USg
|
|
1513
|
+
keywords: t.sort((n, r) => r.length - n.length),
|
|
1514
|
+
length: t.length
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Check if a value is a number
|
|
1519
|
+
* @param {number|string} value - the value to check;
|
|
1520
|
+
* numeric strings allowed
|
|
1521
|
+
* @return {boolean}
|
|
1522
|
+
* @access protected
|
|
1523
|
+
*/
|
|
1524
|
+
isNumeric(e) {
|
|
1525
|
+
return Number(parseFloat(e)) == e;
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* @typedef Mark~rangeObject
|
|
1529
|
+
* @type {object}
|
|
1530
|
+
* @property {number} start - The start position within the composite value
|
|
1531
|
+
* @property {number} length - The length of the string to mark within the
|
|
1532
|
+
* composite value.
|
|
1533
|
+
*/
|
|
1534
|
+
/**
|
|
1535
|
+
* @typedef Mark~setOfRanges
|
|
1536
|
+
* @type {object[]}
|
|
1537
|
+
* @property {Mark~rangeObject}
|
|
1538
|
+
*/
|
|
1539
|
+
/**
|
|
1540
|
+
* Returns a processed list of integer offset indexes that do not overlap
|
|
1541
|
+
* each other, and remove any string values or additional elements
|
|
1542
|
+
* @param {Mark~setOfRanges} array - unprocessed raw array
|
|
1543
|
+
* @return {Mark~setOfRanges} - processed array with any invalid entries
|
|
1544
|
+
* removed
|
|
1545
|
+
* @throws Will throw an error if an array of objects is not passed
|
|
1546
|
+
* @access protected
|
|
1547
|
+
*/
|
|
1548
|
+
checkRanges(e) {
|
|
1549
|
+
if (!Array.isArray(e) || Object.prototype.toString.call(e[0]) !== "[object Object]")
|
|
1550
|
+
return this.log("markRanges() will only accept an array of objects"), this.opt.noMatch(e), [];
|
|
1551
|
+
const t = [];
|
|
1552
|
+
let n = 0;
|
|
1553
|
+
return e.sort((r, s) => r.start - s.start).forEach((r) => {
|
|
1554
|
+
let { start: s, end: i, valid: o } = this.callNoMatchOnInvalidRanges(r, n);
|
|
1555
|
+
o && (r.start = s, r.length = i - s, t.push(r), n = i);
|
|
1556
|
+
}), t;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* @typedef Mark~validObject
|
|
1560
|
+
* @type {object}
|
|
1561
|
+
* @property {number} start - The start position within the composite value
|
|
1562
|
+
* @property {number} end - The calculated end position within the composite
|
|
1563
|
+
* value.
|
|
1564
|
+
* @property {boolean} valid - boolean value indicating that the start and
|
|
1565
|
+
* calculated end range is valid
|
|
1566
|
+
*/
|
|
1567
|
+
/**
|
|
1568
|
+
* Initial validation of ranges for markRanges. Preliminary checks are done
|
|
1569
|
+
* to ensure the start and length values exist and are not zero or non-
|
|
1570
|
+
* numeric
|
|
1571
|
+
* @param {Mark~rangeObject} range - the current range object
|
|
1572
|
+
* @param {number} last - last index of range
|
|
1573
|
+
* @return {Mark~validObject}
|
|
1574
|
+
* @access protected
|
|
1575
|
+
*/
|
|
1576
|
+
callNoMatchOnInvalidRanges(e, t) {
|
|
1577
|
+
let n, r, s = !1;
|
|
1578
|
+
return e && typeof e.start < "u" ? (n = parseInt(e.start, 10), r = n + parseInt(e.length, 10), this.isNumeric(e.start) && this.isNumeric(e.length) && r - t > 0 && r - n > 0 ? s = !0 : (this.log(
|
|
1579
|
+
`Ignoring invalid or overlapping range: ${JSON.stringify(e)}`
|
|
1580
|
+
), this.opt.noMatch(e))) : (this.log(`Ignoring invalid range: ${JSON.stringify(e)}`), this.opt.noMatch(e)), {
|
|
1581
|
+
start: n,
|
|
1582
|
+
end: r,
|
|
1583
|
+
valid: s
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Check valid range for markRanges. Check ranges with access to the context
|
|
1588
|
+
* string. Range values are double checked, lengths that extend the mark
|
|
1589
|
+
* beyond the string length are limitied and ranges containing only
|
|
1590
|
+
* whitespace are ignored
|
|
1591
|
+
* @param {Mark~rangeObject} range - the current range object
|
|
1592
|
+
* @param {number} originalLength - original length of the context string
|
|
1593
|
+
* @param {string} string - current content string
|
|
1594
|
+
* @return {Mark~validObject}
|
|
1595
|
+
* @access protected
|
|
1596
|
+
*/
|
|
1597
|
+
checkWhitespaceRanges(e, t, n) {
|
|
1598
|
+
let r, s = !0, i = n.length, o = t - i, l = parseInt(e.start, 10) - o;
|
|
1599
|
+
return l = l > i ? i : l, r = l + parseInt(e.length, 10), r > i && (r = i, this.log(`End range automatically set to the max value of ${i}`)), l < 0 || r - l < 0 || l > i || r > i ? (s = !1, this.log(`Invalid range: ${JSON.stringify(e)}`), this.opt.noMatch(e)) : n.substring(l, r).replace(/\s+/g, "") === "" && (s = !1, this.log("Skipping whitespace only range: " + JSON.stringify(e)), this.opt.noMatch(e)), {
|
|
1600
|
+
start: l,
|
|
1601
|
+
end: r,
|
|
1602
|
+
valid: s
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* @typedef Mark~getTextNodesDict
|
|
1607
|
+
* @type {object.<string>}
|
|
1608
|
+
* @property {string} value - The composite value of all text nodes
|
|
1609
|
+
* @property {object[]} nodes - An array of objects
|
|
1610
|
+
* @property {number} nodes.start - The start position within the composite
|
|
1611
|
+
* value
|
|
1612
|
+
* @property {number} nodes.end - The end position within the composite
|
|
1613
|
+
* value
|
|
1614
|
+
* @property {HTMLElement} nodes.node - The DOM text node element
|
|
1615
|
+
*/
|
|
1616
|
+
/**
|
|
1617
|
+
* Callback
|
|
1618
|
+
* @callback Mark~getTextNodesCallback
|
|
1619
|
+
* @param {Mark~getTextNodesDict}
|
|
1620
|
+
*/
|
|
1621
|
+
/**
|
|
1622
|
+
* Calls the callback with an object containing all text nodes (including
|
|
1623
|
+
* iframe text nodes) with start and end positions and the composite value
|
|
1624
|
+
* of them (string)
|
|
1625
|
+
* @param {Mark~getTextNodesCallback} cb - Callback
|
|
1626
|
+
* @access protected
|
|
1627
|
+
*/
|
|
1628
|
+
getTextNodes(e) {
|
|
1629
|
+
let t = "", n = [];
|
|
1630
|
+
this.iterator.forEachNode(NodeFilter.SHOW_TEXT, (r) => {
|
|
1631
|
+
n.push({
|
|
1632
|
+
start: t.length,
|
|
1633
|
+
end: (t += r.textContent).length,
|
|
1634
|
+
node: r
|
|
1635
|
+
});
|
|
1636
|
+
}, (r) => this.matchesExclude(r.parentNode) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT, () => {
|
|
1637
|
+
e({
|
|
1638
|
+
value: t,
|
|
1639
|
+
nodes: n
|
|
1640
|
+
});
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Checks if an element matches any of the specified exclude selectors. Also
|
|
1645
|
+
* it checks for elements in which no marks should be performed (e.g.
|
|
1646
|
+
* script and style tags) and optionally already marked elements
|
|
1647
|
+
* @param {HTMLElement} el - The element to check
|
|
1648
|
+
* @return {boolean}
|
|
1649
|
+
* @access protected
|
|
1650
|
+
*/
|
|
1651
|
+
matchesExclude(e) {
|
|
1652
|
+
return ie.matches(e, this.opt.exclude.concat([
|
|
1653
|
+
// ignores the elements itself, not their childrens (selector *)
|
|
1654
|
+
"script",
|
|
1655
|
+
"style",
|
|
1656
|
+
"title",
|
|
1657
|
+
"head",
|
|
1658
|
+
"html"
|
|
1659
|
+
]));
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Wraps the instance element and class around matches that fit the start
|
|
1663
|
+
* and end positions within the node
|
|
1664
|
+
* @param {HTMLElement} node - The DOM text node
|
|
1665
|
+
* @param {number} start - The position where to start wrapping
|
|
1666
|
+
* @param {number} end - The position where to end wrapping
|
|
1667
|
+
* @return {HTMLElement} Returns the splitted text node that will appear
|
|
1668
|
+
* after the wrapped text node
|
|
1669
|
+
* @access protected
|
|
1670
|
+
*/
|
|
1671
|
+
wrapRangeInTextNode(e, t, n) {
|
|
1672
|
+
const r = this.opt.element ? this.opt.element : "mark", s = e.splitText(t), i = s.splitText(n - t);
|
|
1673
|
+
let o = document.createElement(r);
|
|
1674
|
+
return o.setAttribute("data-markjs", "true"), this.opt.className && o.setAttribute("class", this.opt.className), o.textContent = s.textContent, s.parentNode.replaceChild(o, s), i;
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* @typedef Mark~wrapRangeInMappedTextNodeDict
|
|
1678
|
+
* @type {object.<string>}
|
|
1679
|
+
* @property {string} value - The composite value of all text nodes
|
|
1680
|
+
* @property {object[]} nodes - An array of objects
|
|
1681
|
+
* @property {number} nodes.start - The start position within the composite
|
|
1682
|
+
* value
|
|
1683
|
+
* @property {number} nodes.end - The end position within the composite
|
|
1684
|
+
* value
|
|
1685
|
+
* @property {HTMLElement} nodes.node - The DOM text node element
|
|
1686
|
+
*/
|
|
1687
|
+
/**
|
|
1688
|
+
* Each callback
|
|
1689
|
+
* @callback Mark~wrapMatchesEachCallback
|
|
1690
|
+
* @param {HTMLElement} node - The wrapped DOM element
|
|
1691
|
+
* @param {number} lastIndex - The last matching position within the
|
|
1692
|
+
* composite value of text nodes
|
|
1693
|
+
*/
|
|
1694
|
+
/**
|
|
1695
|
+
* Filter callback
|
|
1696
|
+
* @callback Mark~wrapMatchesFilterCallback
|
|
1697
|
+
* @param {HTMLElement} node - The matching text node DOM element
|
|
1698
|
+
*/
|
|
1699
|
+
/**
|
|
1700
|
+
* Determines matches by start and end positions using the text node
|
|
1701
|
+
* dictionary even across text nodes and calls
|
|
1702
|
+
* {@link Mark#wrapRangeInTextNode} to wrap them
|
|
1703
|
+
* @param {Mark~wrapRangeInMappedTextNodeDict} dict - The dictionary
|
|
1704
|
+
* @param {number} start - The start position of the match
|
|
1705
|
+
* @param {number} end - The end position of the match
|
|
1706
|
+
* @param {Mark~wrapMatchesFilterCallback} filterCb - Filter callback
|
|
1707
|
+
* @param {Mark~wrapMatchesEachCallback} eachCb - Each callback
|
|
1708
|
+
* @access protected
|
|
1709
|
+
*/
|
|
1710
|
+
wrapRangeInMappedTextNode(e, t, n, r, s) {
|
|
1711
|
+
e.nodes.every((i, o) => {
|
|
1712
|
+
const l = e.nodes[o + 1];
|
|
1713
|
+
if (typeof l > "u" || l.start > t) {
|
|
1714
|
+
if (!r(i.node))
|
|
1715
|
+
return !1;
|
|
1716
|
+
const c = t - i.start, f = (n > i.end ? i.end : n) - i.start, p = e.value.substr(0, i.start), v = e.value.substr(f + i.start);
|
|
1717
|
+
if (i.node = this.wrapRangeInTextNode(i.node, c, f), e.value = p + v, e.nodes.forEach((y, S) => {
|
|
1718
|
+
S >= o && (e.nodes[S].start > 0 && S !== o && (e.nodes[S].start -= f), e.nodes[S].end -= f);
|
|
1719
|
+
}), n -= f, s(i.node.previousSibling, i.start), n > i.end)
|
|
1720
|
+
t = i.end;
|
|
1721
|
+
else
|
|
1722
|
+
return !1;
|
|
1723
|
+
}
|
|
1724
|
+
return !0;
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Filter callback before each wrapping
|
|
1729
|
+
* @callback Mark~wrapMatchesFilterCallback
|
|
1730
|
+
* @param {string} match - The matching string
|
|
1731
|
+
* @param {HTMLElement} node - The text node where the match occurs
|
|
1732
|
+
*/
|
|
1733
|
+
/**
|
|
1734
|
+
* Callback for each wrapped element
|
|
1735
|
+
* @callback Mark~wrapMatchesEachCallback
|
|
1736
|
+
* @param {HTMLElement} element - The marked DOM element
|
|
1737
|
+
*/
|
|
1738
|
+
/**
|
|
1739
|
+
* Callback on end
|
|
1740
|
+
* @callback Mark~wrapMatchesEndCallback
|
|
1741
|
+
*/
|
|
1742
|
+
/**
|
|
1743
|
+
* Wraps the instance element and class around matches within single HTML
|
|
1744
|
+
* elements in all contexts
|
|
1745
|
+
* @param {RegExp} regex - The regular expression to be searched for
|
|
1746
|
+
* @param {number} ignoreGroups - A number indicating the amount of RegExp
|
|
1747
|
+
* matching groups to ignore
|
|
1748
|
+
* @param {Mark~wrapMatchesFilterCallback} filterCb
|
|
1749
|
+
* @param {Mark~wrapMatchesEachCallback} eachCb
|
|
1750
|
+
* @param {Mark~wrapMatchesEndCallback} endCb
|
|
1751
|
+
* @access protected
|
|
1752
|
+
*/
|
|
1753
|
+
wrapMatches(e, t, n, r, s) {
|
|
1754
|
+
const i = t === 0 ? 0 : t + 1;
|
|
1755
|
+
this.getTextNodes((o) => {
|
|
1756
|
+
o.nodes.forEach((l) => {
|
|
1757
|
+
l = l.node;
|
|
1758
|
+
let c;
|
|
1759
|
+
for (; (c = e.exec(l.textContent)) !== null && c[i] !== ""; ) {
|
|
1760
|
+
if (!n(c[i], l))
|
|
1761
|
+
continue;
|
|
1762
|
+
let f = c.index;
|
|
1763
|
+
if (i !== 0)
|
|
1764
|
+
for (let p = 1; p < i; p++)
|
|
1765
|
+
f += c[p].length;
|
|
1766
|
+
l = this.wrapRangeInTextNode(
|
|
1767
|
+
l,
|
|
1768
|
+
f,
|
|
1769
|
+
f + c[i].length
|
|
1770
|
+
), r(l.previousSibling), e.lastIndex = 0;
|
|
1771
|
+
}
|
|
1772
|
+
}), s();
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Callback for each wrapped element
|
|
1777
|
+
* @callback Mark~wrapMatchesAcrossElementsEachCallback
|
|
1778
|
+
* @param {HTMLElement} element - The marked DOM element
|
|
1779
|
+
*/
|
|
1780
|
+
/**
|
|
1781
|
+
* Filter callback before each wrapping
|
|
1782
|
+
* @callback Mark~wrapMatchesAcrossElementsFilterCallback
|
|
1783
|
+
* @param {string} match - The matching string
|
|
1784
|
+
* @param {HTMLElement} node - The text node where the match occurs
|
|
1785
|
+
*/
|
|
1786
|
+
/**
|
|
1787
|
+
* Callback on end
|
|
1788
|
+
* @callback Mark~wrapMatchesAcrossElementsEndCallback
|
|
1789
|
+
*/
|
|
1790
|
+
/**
|
|
1791
|
+
* Wraps the instance element and class around matches across all HTML
|
|
1792
|
+
* elements in all contexts
|
|
1793
|
+
* @param {RegExp} regex - The regular expression to be searched for
|
|
1794
|
+
* @param {number} ignoreGroups - A number indicating the amount of RegExp
|
|
1795
|
+
* matching groups to ignore
|
|
1796
|
+
* @param {Mark~wrapMatchesAcrossElementsFilterCallback} filterCb
|
|
1797
|
+
* @param {Mark~wrapMatchesAcrossElementsEachCallback} eachCb
|
|
1798
|
+
* @param {Mark~wrapMatchesAcrossElementsEndCallback} endCb
|
|
1799
|
+
* @access protected
|
|
1800
|
+
*/
|
|
1801
|
+
wrapMatchesAcrossElements(e, t, n, r, s) {
|
|
1802
|
+
const i = t === 0 ? 0 : t + 1;
|
|
1803
|
+
this.getTextNodes((o) => {
|
|
1804
|
+
let l;
|
|
1805
|
+
for (; (l = e.exec(o.value)) !== null && l[i] !== ""; ) {
|
|
1806
|
+
let c = l.index;
|
|
1807
|
+
if (i !== 0)
|
|
1808
|
+
for (let p = 1; p < i; p++)
|
|
1809
|
+
c += l[p].length;
|
|
1810
|
+
const f = c + l[i].length;
|
|
1811
|
+
this.wrapRangeInMappedTextNode(o, c, f, (p) => n(l[i], p), (p, v) => {
|
|
1812
|
+
e.lastIndex = v, r(p);
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
s();
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
/**
|
|
1819
|
+
* Callback for each wrapped element
|
|
1820
|
+
* @callback Mark~wrapRangeFromIndexEachCallback
|
|
1821
|
+
* @param {HTMLElement} element - The marked DOM element
|
|
1822
|
+
* @param {Mark~rangeObject} range - the current range object; provided
|
|
1823
|
+
* start and length values will be numeric integers modified from the
|
|
1824
|
+
* provided original ranges.
|
|
1825
|
+
*/
|
|
1826
|
+
/**
|
|
1827
|
+
* Filter callback before each wrapping
|
|
1828
|
+
* @callback Mark~wrapRangeFromIndexFilterCallback
|
|
1829
|
+
* @param {HTMLElement} node - The text node which includes the range
|
|
1830
|
+
* @param {Mark~rangeObject} range - the current range object
|
|
1831
|
+
* @param {string} match - string extracted from the matching range
|
|
1832
|
+
* @param {number} counter - A counter indicating the number of all marks
|
|
1833
|
+
*/
|
|
1834
|
+
/**
|
|
1835
|
+
* Callback on end
|
|
1836
|
+
* @callback Mark~wrapRangeFromIndexEndCallback
|
|
1837
|
+
*/
|
|
1838
|
+
/**
|
|
1839
|
+
* Wraps the indicated ranges across all HTML elements in all contexts
|
|
1840
|
+
* @param {Mark~setOfRanges} ranges
|
|
1841
|
+
* @param {Mark~wrapRangeFromIndexFilterCallback} filterCb
|
|
1842
|
+
* @param {Mark~wrapRangeFromIndexEachCallback} eachCb
|
|
1843
|
+
* @param {Mark~wrapRangeFromIndexEndCallback} endCb
|
|
1844
|
+
* @access protected
|
|
1845
|
+
*/
|
|
1846
|
+
wrapRangeFromIndex(e, t, n, r) {
|
|
1847
|
+
this.getTextNodes((s) => {
|
|
1848
|
+
const i = s.value.length;
|
|
1849
|
+
e.forEach((o, l) => {
|
|
1850
|
+
let { start: c, end: f, valid: p } = this.checkWhitespaceRanges(
|
|
1851
|
+
o,
|
|
1852
|
+
i,
|
|
1853
|
+
s.value
|
|
1854
|
+
);
|
|
1855
|
+
p && this.wrapRangeInMappedTextNode(s, c, f, (v) => t(
|
|
1856
|
+
v,
|
|
1857
|
+
o,
|
|
1858
|
+
s.value.substring(c, f),
|
|
1859
|
+
l
|
|
1860
|
+
), (v) => {
|
|
1861
|
+
n(v, o);
|
|
1862
|
+
});
|
|
1863
|
+
}), r();
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Unwraps the specified DOM node with its content (text nodes or HTML)
|
|
1868
|
+
* without destroying possibly present events (using innerHTML) and
|
|
1869
|
+
* normalizes the parent at the end (merge splitted text nodes)
|
|
1870
|
+
* @param {HTMLElement} node - The DOM node to unwrap
|
|
1871
|
+
* @access protected
|
|
1872
|
+
*/
|
|
1873
|
+
unwrapMatches(e) {
|
|
1874
|
+
const t = e.parentNode;
|
|
1875
|
+
let n = document.createDocumentFragment();
|
|
1876
|
+
for (; e.firstChild; )
|
|
1877
|
+
n.appendChild(e.removeChild(e.firstChild));
|
|
1878
|
+
t.replaceChild(n, e), this.ie ? this.normalizeTextNode(t) : t.normalize();
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Normalizes text nodes. It's a workaround for the native normalize method
|
|
1882
|
+
* that has a bug in IE (see attached link). Should only be used in IE
|
|
1883
|
+
* browsers as it's slower than the native method.
|
|
1884
|
+
* @see {@link http://tinyurl.com/z5asa8c}
|
|
1885
|
+
* @param {HTMLElement} node - The DOM node to normalize
|
|
1886
|
+
* @access protected
|
|
1887
|
+
*/
|
|
1888
|
+
normalizeTextNode(e) {
|
|
1889
|
+
if (e) {
|
|
1890
|
+
if (e.nodeType === 3)
|
|
1891
|
+
for (; e.nextSibling && e.nextSibling.nodeType === 3; )
|
|
1892
|
+
e.nodeValue += e.nextSibling.nodeValue, e.parentNode.removeChild(e.nextSibling);
|
|
1893
|
+
else
|
|
1894
|
+
this.normalizeTextNode(e.firstChild);
|
|
1895
|
+
this.normalizeTextNode(e.nextSibling);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Callback when finished
|
|
1900
|
+
* @callback Mark~commonDoneCallback
|
|
1901
|
+
* @param {number} totalMatches - The number of marked elements
|
|
1902
|
+
*/
|
|
1903
|
+
/**
|
|
1904
|
+
* @typedef Mark~commonOptions
|
|
1905
|
+
* @type {object.<string>}
|
|
1906
|
+
* @property {string} [element="mark"] - HTML element tag name
|
|
1907
|
+
* @property {string} [className] - An optional class name
|
|
1908
|
+
* @property {string[]} [exclude] - An array with exclusion selectors.
|
|
1909
|
+
* Elements matching those selectors will be ignored
|
|
1910
|
+
* @property {boolean} [iframes=false] - Whether to search inside iframes
|
|
1911
|
+
* @property {Mark~commonDoneCallback} [done]
|
|
1912
|
+
* @property {boolean} [debug=false] - Wheter to log messages
|
|
1913
|
+
* @property {object} [log=window.console] - Where to log messages (only if
|
|
1914
|
+
* debug is true)
|
|
1915
|
+
*/
|
|
1916
|
+
/**
|
|
1917
|
+
* Callback for each marked element
|
|
1918
|
+
* @callback Mark~markRegExpEachCallback
|
|
1919
|
+
* @param {HTMLElement} element - The marked DOM element
|
|
1920
|
+
*/
|
|
1921
|
+
/**
|
|
1922
|
+
* Callback if there were no matches
|
|
1923
|
+
* @callback Mark~markRegExpNoMatchCallback
|
|
1924
|
+
* @param {RegExp} regexp - The regular expression
|
|
1925
|
+
*/
|
|
1926
|
+
/**
|
|
1927
|
+
* Callback to filter matches
|
|
1928
|
+
* @callback Mark~markRegExpFilterCallback
|
|
1929
|
+
* @param {HTMLElement} textNode - The text node which includes the match
|
|
1930
|
+
* @param {string} match - The matching string for the RegExp
|
|
1931
|
+
* @param {number} counter - A counter indicating the number of all marks
|
|
1932
|
+
*/
|
|
1933
|
+
/**
|
|
1934
|
+
* These options also include the common options from
|
|
1935
|
+
* {@link Mark~commonOptions}
|
|
1936
|
+
* @typedef Mark~markRegExpOptions
|
|
1937
|
+
* @type {object.<string>}
|
|
1938
|
+
* @property {Mark~markRegExpEachCallback} [each]
|
|
1939
|
+
* @property {Mark~markRegExpNoMatchCallback} [noMatch]
|
|
1940
|
+
* @property {Mark~markRegExpFilterCallback} [filter]
|
|
1941
|
+
*/
|
|
1942
|
+
/**
|
|
1943
|
+
* Marks a custom regular expression
|
|
1944
|
+
* @param {RegExp} regexp - The regular expression
|
|
1945
|
+
* @param {Mark~markRegExpOptions} [opt] - Optional options object
|
|
1946
|
+
* @access public
|
|
1947
|
+
*/
|
|
1948
|
+
markRegExp(e, t) {
|
|
1949
|
+
this.opt = t, this.log(`Searching with expression "${e}"`);
|
|
1950
|
+
let n = 0, r = "wrapMatches";
|
|
1951
|
+
const s = (i) => {
|
|
1952
|
+
n++, this.opt.each(i);
|
|
1953
|
+
};
|
|
1954
|
+
this.opt.acrossElements && (r = "wrapMatchesAcrossElements"), this[r](e, this.opt.ignoreGroups, (i, o) => this.opt.filter(o, i, n), s, () => {
|
|
1955
|
+
n === 0 && this.opt.noMatch(e), this.opt.done(n);
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Callback for each marked element
|
|
1960
|
+
* @callback Mark~markEachCallback
|
|
1961
|
+
* @param {HTMLElement} element - The marked DOM element
|
|
1962
|
+
*/
|
|
1963
|
+
/**
|
|
1964
|
+
* Callback if there were no matches
|
|
1965
|
+
* @callback Mark~markNoMatchCallback
|
|
1966
|
+
* @param {RegExp} term - The search term that was not found
|
|
1967
|
+
*/
|
|
1968
|
+
/**
|
|
1969
|
+
* Callback to filter matches
|
|
1970
|
+
* @callback Mark~markFilterCallback
|
|
1971
|
+
* @param {HTMLElement} textNode - The text node which includes the match
|
|
1972
|
+
* @param {string} match - The matching term
|
|
1973
|
+
* @param {number} totalCounter - A counter indicating the number of all
|
|
1974
|
+
* marks
|
|
1975
|
+
* @param {number} termCounter - A counter indicating the number of marks
|
|
1976
|
+
* for the specific match
|
|
1977
|
+
*/
|
|
1978
|
+
/**
|
|
1979
|
+
* @typedef Mark~markAccuracyObject
|
|
1980
|
+
* @type {object.<string>}
|
|
1981
|
+
* @property {string} value - A accuracy string value
|
|
1982
|
+
* @property {string[]} limiters - A custom array of limiters. For example
|
|
1983
|
+
* <code>["-", ","]</code>
|
|
1984
|
+
*/
|
|
1985
|
+
/**
|
|
1986
|
+
* @typedef Mark~markAccuracySetting
|
|
1987
|
+
* @type {string}
|
|
1988
|
+
* @property {"partially"|"complementary"|"exactly"|Mark~markAccuracyObject}
|
|
1989
|
+
* [accuracy="partially"] - Either one of the following string values:
|
|
1990
|
+
* <ul>
|
|
1991
|
+
* <li><i>partially</i>: When searching for "lor" only "lor" inside
|
|
1992
|
+
* "lorem" will be marked</li>
|
|
1993
|
+
* <li><i>complementary</i>: When searching for "lor" the whole word
|
|
1994
|
+
* "lorem" will be marked</li>
|
|
1995
|
+
* <li><i>exactly</i>: When searching for "lor" only those exact words
|
|
1996
|
+
* will be marked. In this example nothing inside "lorem". This value
|
|
1997
|
+
* is equivalent to the previous option <i>wordBoundary</i></li>
|
|
1998
|
+
* </ul>
|
|
1999
|
+
* Or an object containing two properties:
|
|
2000
|
+
* <ul>
|
|
2001
|
+
* <li><i>value</i>: One of the above named string values</li>
|
|
2002
|
+
* <li><i>limiters</i>: A custom array of string limiters for accuracy
|
|
2003
|
+
* "exactly" or "complementary"</li>
|
|
2004
|
+
* </ul>
|
|
2005
|
+
*/
|
|
2006
|
+
/**
|
|
2007
|
+
* @typedef Mark~markWildcardsSetting
|
|
2008
|
+
* @type {string}
|
|
2009
|
+
* @property {"disabled"|"enabled"|"withSpaces"}
|
|
2010
|
+
* [wildcards="disabled"] - Set to any of the following string values:
|
|
2011
|
+
* <ul>
|
|
2012
|
+
* <li><i>disabled</i>: Disable wildcard usage</li>
|
|
2013
|
+
* <li><i>enabled</i>: When searching for "lor?m", the "?" will match zero
|
|
2014
|
+
* or one non-space character (e.g. "lorm", "loram", "lor3m", etc). When
|
|
2015
|
+
* searching for "lor*m", the "*" will match zero or more non-space
|
|
2016
|
+
* characters (e.g. "lorm", "loram", "lor123m", etc).</li>
|
|
2017
|
+
* <li><i>withSpaces</i>: When searching for "lor?m", the "?" will
|
|
2018
|
+
* match zero or one space or non-space character (e.g. "lor m", "loram",
|
|
2019
|
+
* etc). When searching for "lor*m", the "*" will match zero or more space
|
|
2020
|
+
* or non-space characters (e.g. "lorm", "lore et dolor ipsum", "lor: m",
|
|
2021
|
+
* etc).</li>
|
|
2022
|
+
* </ul>
|
|
2023
|
+
*/
|
|
2024
|
+
/**
|
|
2025
|
+
* @typedef Mark~markIgnorePunctuationSetting
|
|
2026
|
+
* @type {string[]}
|
|
2027
|
+
* @property {string} The strings in this setting will contain punctuation
|
|
2028
|
+
* marks that will be ignored:
|
|
2029
|
+
* <ul>
|
|
2030
|
+
* <li>These punctuation marks can be between any characters, e.g. setting
|
|
2031
|
+
* this option to <code>["'"]</code> would match "Worlds", "World's" and
|
|
2032
|
+
* "Wo'rlds"</li>
|
|
2033
|
+
* <li>One or more apostrophes between the letters would still produce a
|
|
2034
|
+
* match (e.g. "W'o''r'l'd's").</li>
|
|
2035
|
+
* <li>A typical setting for this option could be as follows:
|
|
2036
|
+
* <pre>ignorePunctuation: ":;.,-–—‒_(){}[]!'\"+=".split(""),</pre> This
|
|
2037
|
+
* setting includes common punctuation as well as a minus, en-dash,
|
|
2038
|
+
* em-dash and figure-dash
|
|
2039
|
+
* ({@link https://en.wikipedia.org/wiki/Dash#Figure_dash ref}), as well
|
|
2040
|
+
* as an underscore.</li>
|
|
2041
|
+
* </ul>
|
|
2042
|
+
*/
|
|
2043
|
+
/**
|
|
2044
|
+
* These options also include the common options from
|
|
2045
|
+
* {@link Mark~commonOptions}
|
|
2046
|
+
* @typedef Mark~markOptions
|
|
2047
|
+
* @type {object.<string>}
|
|
2048
|
+
* @property {boolean} [separateWordSearch=true] - Whether to search for
|
|
2049
|
+
* each word separated by a blank instead of the complete term
|
|
2050
|
+
* @property {boolean} [diacritics=true] - If diacritic characters should be
|
|
2051
|
+
* matched. ({@link https://en.wikipedia.org/wiki/Diacritic Diacritics})
|
|
2052
|
+
* @property {object} [synonyms] - An object with synonyms. The key will be
|
|
2053
|
+
* a synonym for the value and the value for the key
|
|
2054
|
+
* @property {Mark~markAccuracySetting} [accuracy]
|
|
2055
|
+
* @property {Mark~markWildcardsSetting} [wildcards]
|
|
2056
|
+
* @property {boolean} [acrossElements=false] - Whether to find matches
|
|
2057
|
+
* across HTML elements. By default, only matches within single HTML
|
|
2058
|
+
* elements will be found
|
|
2059
|
+
* @property {boolean} [ignoreJoiners=false] - Whether to ignore word
|
|
2060
|
+
* joiners inside of key words. These include soft-hyphens, zero-width
|
|
2061
|
+
* space, zero-width non-joiners and zero-width joiners.
|
|
2062
|
+
* @property {Mark~markIgnorePunctuationSetting} [ignorePunctuation]
|
|
2063
|
+
* @property {Mark~markEachCallback} [each]
|
|
2064
|
+
* @property {Mark~markNoMatchCallback} [noMatch]
|
|
2065
|
+
* @property {Mark~markFilterCallback} [filter]
|
|
2066
|
+
*/
|
|
2067
|
+
/**
|
|
2068
|
+
* Marks the specified search terms
|
|
2069
|
+
* @param {string|string[]} [sv] - Search value, either a search string or
|
|
2070
|
+
* an array containing multiple search strings
|
|
2071
|
+
* @param {Mark~markOptions} [opt] - Optional options object
|
|
2072
|
+
* @access public
|
|
2073
|
+
*/
|
|
2074
|
+
mark(e, t) {
|
|
2075
|
+
this.opt = t;
|
|
2076
|
+
let n = 0, r = "wrapMatches";
|
|
2077
|
+
const {
|
|
2078
|
+
keywords: s,
|
|
2079
|
+
length: i
|
|
2080
|
+
} = this.getSeparatedKeywords(typeof e == "string" ? [e] : e), o = this.opt.caseSensitive ? "" : "i", l = (c) => {
|
|
2081
|
+
let f = new RegExp(this.createRegExp(c), `gm${o}`), p = 0;
|
|
2082
|
+
this.log(`Searching with expression "${f}"`), this[r](f, 1, (v, y) => this.opt.filter(y, c, n, p), (v) => {
|
|
2083
|
+
p++, n++, this.opt.each(v);
|
|
2084
|
+
}, () => {
|
|
2085
|
+
p === 0 && this.opt.noMatch(c), s[i - 1] === c ? this.opt.done(n) : l(s[s.indexOf(c) + 1]);
|
|
2086
|
+
});
|
|
2087
|
+
};
|
|
2088
|
+
this.opt.acrossElements && (r = "wrapMatchesAcrossElements"), i === 0 ? this.opt.done(n) : l(s[0]);
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Callback for each marked element
|
|
2092
|
+
* @callback Mark~markRangesEachCallback
|
|
2093
|
+
* @param {HTMLElement} element - The marked DOM element
|
|
2094
|
+
* @param {array} range - array of range start and end points
|
|
2095
|
+
*/
|
|
2096
|
+
/**
|
|
2097
|
+
* Callback if a processed range is invalid, out-of-bounds, overlaps another
|
|
2098
|
+
* range, or only matches whitespace
|
|
2099
|
+
* @callback Mark~markRangesNoMatchCallback
|
|
2100
|
+
* @param {Mark~rangeObject} range - a range object
|
|
2101
|
+
*/
|
|
2102
|
+
/**
|
|
2103
|
+
* Callback to filter matches
|
|
2104
|
+
* @callback Mark~markRangesFilterCallback
|
|
2105
|
+
* @param {HTMLElement} node - The text node which includes the range
|
|
2106
|
+
* @param {array} range - array of range start and end points
|
|
2107
|
+
* @param {string} match - string extracted from the matching range
|
|
2108
|
+
* @param {number} counter - A counter indicating the number of all marks
|
|
2109
|
+
*/
|
|
2110
|
+
/**
|
|
2111
|
+
* These options also include the common options from
|
|
2112
|
+
* {@link Mark~commonOptions}
|
|
2113
|
+
* @typedef Mark~markRangesOptions
|
|
2114
|
+
* @type {object.<string>}
|
|
2115
|
+
* @property {Mark~markRangesEachCallback} [each]
|
|
2116
|
+
* @property {Mark~markRangesNoMatchCallback} [noMatch]
|
|
2117
|
+
* @property {Mark~markRangesFilterCallback} [filter]
|
|
2118
|
+
*/
|
|
2119
|
+
/**
|
|
2120
|
+
* Marks an array of objects containing a start with an end or length of the
|
|
2121
|
+
* string to mark
|
|
2122
|
+
* @param {Mark~setOfRanges} rawRanges - The original (preprocessed)
|
|
2123
|
+
* array of objects
|
|
2124
|
+
* @param {Mark~markRangesOptions} [opt] - Optional options object
|
|
2125
|
+
* @access public
|
|
2126
|
+
*/
|
|
2127
|
+
markRanges(e, t) {
|
|
2128
|
+
this.opt = t;
|
|
2129
|
+
let n = 0, r = this.checkRanges(e);
|
|
2130
|
+
r && r.length ? (this.log(
|
|
2131
|
+
"Starting to mark with the following ranges: " + JSON.stringify(r)
|
|
2132
|
+
), this.wrapRangeFromIndex(
|
|
2133
|
+
r,
|
|
2134
|
+
(s, i, o, l) => this.opt.filter(s, i, o, l),
|
|
2135
|
+
(s, i) => {
|
|
2136
|
+
n++, this.opt.each(s, i);
|
|
2137
|
+
},
|
|
2138
|
+
() => {
|
|
2139
|
+
this.opt.done(n);
|
|
2140
|
+
}
|
|
2141
|
+
)) : this.opt.done(n);
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Removes all marked elements inside the context with their HTML and
|
|
2145
|
+
* normalizes the parent at the end
|
|
2146
|
+
* @param {Mark~commonOptions} [opt] - Optional options object
|
|
2147
|
+
* @access public
|
|
2148
|
+
*/
|
|
2149
|
+
unmark(e) {
|
|
2150
|
+
this.opt = e;
|
|
2151
|
+
let t = this.opt.element ? this.opt.element : "*";
|
|
2152
|
+
t += "[data-markjs]", this.opt.className && (t += `.${this.opt.className}`), this.log(`Removal selector "${t}"`), this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, (n) => {
|
|
2153
|
+
this.unwrapMatches(n);
|
|
2154
|
+
}, (n) => {
|
|
2155
|
+
const r = ie.matches(n, t), s = this.matchesExclude(n);
|
|
2156
|
+
return !r || s ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
|
2157
|
+
}, this.opt.done);
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
function Rn(a) {
|
|
2161
|
+
const e = new Fn(a);
|
|
2162
|
+
return this.mark = (t, n) => (e.mark(t, n), this), this.markRegExp = (t, n) => (e.markRegExp(t, n), this), this.markRanges = (t, n) => (e.markRanges(t, n), this), this.unmark = (t) => (e.unmark(t), this), this;
|
|
2163
|
+
}
|
|
2164
|
+
const An = "ENTRIES", gt = "KEYS", bt = "VALUES", M = "";
|
|
2165
|
+
class Re {
|
|
2166
|
+
constructor(e, t) {
|
|
2167
|
+
const n = e._tree, r = Array.from(n.keys());
|
|
2168
|
+
this.set = e, this._type = t, this._path = r.length > 0 ? [{ node: n, keys: r }] : [];
|
|
2169
|
+
}
|
|
2170
|
+
next() {
|
|
2171
|
+
const e = this.dive();
|
|
2172
|
+
return this.backtrack(), e;
|
|
2173
|
+
}
|
|
2174
|
+
dive() {
|
|
2175
|
+
if (this._path.length === 0)
|
|
2176
|
+
return { done: !0, value: void 0 };
|
|
2177
|
+
const { node: e, keys: t } = ne(this._path);
|
|
2178
|
+
if (ne(t) === M)
|
|
2179
|
+
return { done: !1, value: this.result() };
|
|
2180
|
+
const n = e.get(ne(t));
|
|
2181
|
+
return this._path.push({ node: n, keys: Array.from(n.keys()) }), this.dive();
|
|
2182
|
+
}
|
|
2183
|
+
backtrack() {
|
|
2184
|
+
if (this._path.length === 0)
|
|
2185
|
+
return;
|
|
2186
|
+
const e = ne(this._path).keys;
|
|
2187
|
+
e.pop(), !(e.length > 0) && (this._path.pop(), this.backtrack());
|
|
2188
|
+
}
|
|
2189
|
+
key() {
|
|
2190
|
+
return this.set._prefix + this._path.map(({ keys: e }) => ne(e)).filter((e) => e !== M).join("");
|
|
2191
|
+
}
|
|
2192
|
+
value() {
|
|
2193
|
+
return ne(this._path).node.get(M);
|
|
2194
|
+
}
|
|
2195
|
+
result() {
|
|
2196
|
+
switch (this._type) {
|
|
2197
|
+
case bt:
|
|
2198
|
+
return this.value();
|
|
2199
|
+
case gt:
|
|
2200
|
+
return this.key();
|
|
2201
|
+
default:
|
|
2202
|
+
return [this.key(), this.value()];
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
[Symbol.iterator]() {
|
|
2206
|
+
return this;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
const ne = (a) => a[a.length - 1], On = (a, e, t) => {
|
|
2210
|
+
const n = /* @__PURE__ */ new Map();
|
|
2211
|
+
if (e === void 0)
|
|
2212
|
+
return n;
|
|
2213
|
+
const r = e.length + 1, s = r + t, i = new Uint8Array(s * r).fill(t + 1);
|
|
2214
|
+
for (let o = 0; o < r; ++o)
|
|
2215
|
+
i[o] = o;
|
|
2216
|
+
for (let o = 1; o < s; ++o)
|
|
2217
|
+
i[o * r] = o;
|
|
2218
|
+
return yt(a, e, t, n, i, 1, r, ""), n;
|
|
2219
|
+
}, yt = (a, e, t, n, r, s, i, o) => {
|
|
2220
|
+
const l = s * i;
|
|
2221
|
+
e: for (const c of a.keys())
|
|
2222
|
+
if (c === M) {
|
|
2223
|
+
const f = r[l - 1];
|
|
2224
|
+
f <= t && n.set(o, [a.get(c), f]);
|
|
2225
|
+
} else {
|
|
2226
|
+
let f = s;
|
|
2227
|
+
for (let p = 0; p < c.length; ++p, ++f) {
|
|
2228
|
+
const v = c[p], y = i * f, S = y - i;
|
|
2229
|
+
let w = r[y];
|
|
2230
|
+
const _ = Math.max(0, f - t - 1), N = Math.min(i - 1, f + t);
|
|
2231
|
+
for (let F = _; F < N; ++F) {
|
|
2232
|
+
const G = v !== e[F], z = r[S + F] + +G, j = r[S + F + 1] + 1, L = r[y + F] + 1, V = r[y + F + 1] = Math.min(z, j, L);
|
|
2233
|
+
V < w && (w = V);
|
|
2234
|
+
}
|
|
2235
|
+
if (w > t)
|
|
2236
|
+
continue e;
|
|
2237
|
+
}
|
|
2238
|
+
yt(a.get(c), e, t, n, r, f, i, o + c);
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
class q {
|
|
2242
|
+
/**
|
|
2243
|
+
* The constructor is normally called without arguments, creating an empty
|
|
2244
|
+
* map. In order to create a {@link SearchableMap} from an iterable or from an
|
|
2245
|
+
* object, check {@link SearchableMap.from} and {@link
|
|
2246
|
+
* SearchableMap.fromObject}.
|
|
2247
|
+
*
|
|
2248
|
+
* The constructor arguments are for internal use, when creating derived
|
|
2249
|
+
* mutable views of a map at a prefix.
|
|
2250
|
+
*/
|
|
2251
|
+
constructor(e = /* @__PURE__ */ new Map(), t = "") {
|
|
2252
|
+
this._size = void 0, this._tree = e, this._prefix = t;
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* Creates and returns a mutable view of this {@link SearchableMap},
|
|
2256
|
+
* containing only entries that share the given prefix.
|
|
2257
|
+
*
|
|
2258
|
+
* ### Usage:
|
|
2259
|
+
*
|
|
2260
|
+
* ```javascript
|
|
2261
|
+
* let map = new SearchableMap()
|
|
2262
|
+
* map.set("unicorn", 1)
|
|
2263
|
+
* map.set("universe", 2)
|
|
2264
|
+
* map.set("university", 3)
|
|
2265
|
+
* map.set("unique", 4)
|
|
2266
|
+
* map.set("hello", 5)
|
|
2267
|
+
*
|
|
2268
|
+
* let uni = map.atPrefix("uni")
|
|
2269
|
+
* uni.get("unique") // => 4
|
|
2270
|
+
* uni.get("unicorn") // => 1
|
|
2271
|
+
* uni.get("hello") // => undefined
|
|
2272
|
+
*
|
|
2273
|
+
* let univer = map.atPrefix("univer")
|
|
2274
|
+
* univer.get("unique") // => undefined
|
|
2275
|
+
* univer.get("universe") // => 2
|
|
2276
|
+
* univer.get("university") // => 3
|
|
2277
|
+
* ```
|
|
2278
|
+
*
|
|
2279
|
+
* @param prefix The prefix
|
|
2280
|
+
* @return A {@link SearchableMap} representing a mutable view of the original
|
|
2281
|
+
* Map at the given prefix
|
|
2282
|
+
*/
|
|
2283
|
+
atPrefix(e) {
|
|
2284
|
+
if (!e.startsWith(this._prefix))
|
|
2285
|
+
throw new Error("Mismatched prefix");
|
|
2286
|
+
const [t, n] = ke(this._tree, e.slice(this._prefix.length));
|
|
2287
|
+
if (t === void 0) {
|
|
2288
|
+
const [r, s] = $e(n);
|
|
2289
|
+
for (const i of r.keys())
|
|
2290
|
+
if (i !== M && i.startsWith(s)) {
|
|
2291
|
+
const o = /* @__PURE__ */ new Map();
|
|
2292
|
+
return o.set(i.slice(s.length), r.get(i)), new q(o, e);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
return new q(t, e);
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
|
|
2299
|
+
*/
|
|
2300
|
+
clear() {
|
|
2301
|
+
this._size = void 0, this._tree.clear();
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
|
|
2305
|
+
* @param key Key to delete
|
|
2306
|
+
*/
|
|
2307
|
+
delete(e) {
|
|
2308
|
+
return this._size = void 0, Cn(this._tree, e);
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
|
|
2312
|
+
* @return An iterator iterating through `[key, value]` entries.
|
|
2313
|
+
*/
|
|
2314
|
+
entries() {
|
|
2315
|
+
return new Re(this, An);
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2318
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
|
|
2319
|
+
* @param fn Iteration function
|
|
2320
|
+
*/
|
|
2321
|
+
forEach(e) {
|
|
2322
|
+
for (const [t, n] of this)
|
|
2323
|
+
e(t, n, this);
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Returns a Map of all the entries that have a key within the given edit
|
|
2327
|
+
* distance from the search key. The keys of the returned Map are the matching
|
|
2328
|
+
* keys, while the values are two-element arrays where the first element is
|
|
2329
|
+
* the value associated to the key, and the second is the edit distance of the
|
|
2330
|
+
* key to the search key.
|
|
2331
|
+
*
|
|
2332
|
+
* ### Usage:
|
|
2333
|
+
*
|
|
2334
|
+
* ```javascript
|
|
2335
|
+
* let map = new SearchableMap()
|
|
2336
|
+
* map.set('hello', 'world')
|
|
2337
|
+
* map.set('hell', 'yeah')
|
|
2338
|
+
* map.set('ciao', 'mondo')
|
|
2339
|
+
*
|
|
2340
|
+
* // Get all entries that match the key 'hallo' with a maximum edit distance of 2
|
|
2341
|
+
* map.fuzzyGet('hallo', 2)
|
|
2342
|
+
* // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }
|
|
2343
|
+
*
|
|
2344
|
+
* // In the example, the "hello" key has value "world" and edit distance of 1
|
|
2345
|
+
* // (change "e" to "a"), the key "hell" has value "yeah" and edit distance of 2
|
|
2346
|
+
* // (change "e" to "a", delete "o")
|
|
2347
|
+
* ```
|
|
2348
|
+
*
|
|
2349
|
+
* @param key The search key
|
|
2350
|
+
* @param maxEditDistance The maximum edit distance (Levenshtein)
|
|
2351
|
+
* @return A Map of the matching keys to their value and edit distance
|
|
2352
|
+
*/
|
|
2353
|
+
fuzzyGet(e, t) {
|
|
2354
|
+
return On(this._tree, e, t);
|
|
2355
|
+
}
|
|
2356
|
+
/**
|
|
2357
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
|
|
2358
|
+
* @param key Key to get
|
|
2359
|
+
* @return Value associated to the key, or `undefined` if the key is not
|
|
2360
|
+
* found.
|
|
2361
|
+
*/
|
|
2362
|
+
get(e) {
|
|
2363
|
+
const t = ze(this._tree, e);
|
|
2364
|
+
return t !== void 0 ? t.get(M) : void 0;
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
|
|
2368
|
+
* @param key Key
|
|
2369
|
+
* @return True if the key is in the map, false otherwise
|
|
2370
|
+
*/
|
|
2371
|
+
has(e) {
|
|
2372
|
+
const t = ze(this._tree, e);
|
|
2373
|
+
return t !== void 0 && t.has(M);
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
|
2377
|
+
* @return An `Iterable` iterating through keys
|
|
2378
|
+
*/
|
|
2379
|
+
keys() {
|
|
2380
|
+
return new Re(this, gt);
|
|
2381
|
+
}
|
|
2382
|
+
/**
|
|
2383
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
|
|
2384
|
+
* @param key Key to set
|
|
2385
|
+
* @param value Value to associate to the key
|
|
2386
|
+
* @return The {@link SearchableMap} itself, to allow chaining
|
|
2387
|
+
*/
|
|
2388
|
+
set(e, t) {
|
|
2389
|
+
if (typeof e != "string")
|
|
2390
|
+
throw new Error("key must be a string");
|
|
2391
|
+
return this._size = void 0, Ae(this._tree, e).set(M, t), this;
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
|
|
2395
|
+
*/
|
|
2396
|
+
get size() {
|
|
2397
|
+
if (this._size)
|
|
2398
|
+
return this._size;
|
|
2399
|
+
this._size = 0;
|
|
2400
|
+
const e = this.entries();
|
|
2401
|
+
for (; !e.next().done; )
|
|
2402
|
+
this._size += 1;
|
|
2403
|
+
return this._size;
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* Updates the value at the given key using the provided function. The function
|
|
2407
|
+
* is called with the current value at the key, and its return value is used as
|
|
2408
|
+
* the new value to be set.
|
|
2409
|
+
*
|
|
2410
|
+
* ### Example:
|
|
2411
|
+
*
|
|
2412
|
+
* ```javascript
|
|
2413
|
+
* // Increment the current value by one
|
|
2414
|
+
* searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)
|
|
2415
|
+
* ```
|
|
2416
|
+
*
|
|
2417
|
+
* If the value at the given key is or will be an object, it might not require
|
|
2418
|
+
* re-assignment. In that case it is better to use `fetch()`, because it is
|
|
2419
|
+
* faster.
|
|
2420
|
+
*
|
|
2421
|
+
* @param key The key to update
|
|
2422
|
+
* @param fn The function used to compute the new value from the current one
|
|
2423
|
+
* @return The {@link SearchableMap} itself, to allow chaining
|
|
2424
|
+
*/
|
|
2425
|
+
update(e, t) {
|
|
2426
|
+
if (typeof e != "string")
|
|
2427
|
+
throw new Error("key must be a string");
|
|
2428
|
+
this._size = void 0;
|
|
2429
|
+
const n = Ae(this._tree, e);
|
|
2430
|
+
return n.set(M, t(n.get(M))), this;
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* Fetches the value of the given key. If the value does not exist, calls the
|
|
2434
|
+
* given function to create a new value, which is inserted at the given key
|
|
2435
|
+
* and subsequently returned.
|
|
2436
|
+
*
|
|
2437
|
+
* ### Example:
|
|
2438
|
+
*
|
|
2439
|
+
* ```javascript
|
|
2440
|
+
* const map = searchableMap.fetch('somekey', () => new Map())
|
|
2441
|
+
* map.set('foo', 'bar')
|
|
2442
|
+
* ```
|
|
2443
|
+
*
|
|
2444
|
+
* @param key The key to update
|
|
2445
|
+
* @param initial A function that creates a new value if the key does not exist
|
|
2446
|
+
* @return The existing or new value at the given key
|
|
2447
|
+
*/
|
|
2448
|
+
fetch(e, t) {
|
|
2449
|
+
if (typeof e != "string")
|
|
2450
|
+
throw new Error("key must be a string");
|
|
2451
|
+
this._size = void 0;
|
|
2452
|
+
const n = Ae(this._tree, e);
|
|
2453
|
+
let r = n.get(M);
|
|
2454
|
+
return r === void 0 && n.set(M, r = t()), r;
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
|
|
2458
|
+
* @return An `Iterable` iterating through values.
|
|
2459
|
+
*/
|
|
2460
|
+
values() {
|
|
2461
|
+
return new Re(this, bt);
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
|
|
2465
|
+
*/
|
|
2466
|
+
[Symbol.iterator]() {
|
|
2467
|
+
return this.entries();
|
|
2468
|
+
}
|
|
2469
|
+
/**
|
|
2470
|
+
* Creates a {@link SearchableMap} from an `Iterable` of entries
|
|
2471
|
+
*
|
|
2472
|
+
* @param entries Entries to be inserted in the {@link SearchableMap}
|
|
2473
|
+
* @return A new {@link SearchableMap} with the given entries
|
|
2474
|
+
*/
|
|
2475
|
+
static from(e) {
|
|
2476
|
+
const t = new q();
|
|
2477
|
+
for (const [n, r] of e)
|
|
2478
|
+
t.set(n, r);
|
|
2479
|
+
return t;
|
|
2480
|
+
}
|
|
2481
|
+
/**
|
|
2482
|
+
* Creates a {@link SearchableMap} from the iterable properties of a JavaScript object
|
|
2483
|
+
*
|
|
2484
|
+
* @param object Object of entries for the {@link SearchableMap}
|
|
2485
|
+
* @return A new {@link SearchableMap} with the given entries
|
|
2486
|
+
*/
|
|
2487
|
+
static fromObject(e) {
|
|
2488
|
+
return q.from(Object.entries(e));
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
const ke = (a, e, t = []) => {
|
|
2492
|
+
if (e.length === 0 || a == null)
|
|
2493
|
+
return [a, t];
|
|
2494
|
+
for (const n of a.keys())
|
|
2495
|
+
if (n !== M && e.startsWith(n))
|
|
2496
|
+
return t.push([a, n]), ke(a.get(n), e.slice(n.length), t);
|
|
2497
|
+
return t.push([a, e]), ke(void 0, "", t);
|
|
2498
|
+
}, ze = (a, e) => {
|
|
2499
|
+
if (e.length === 0 || a == null)
|
|
2500
|
+
return a;
|
|
2501
|
+
for (const t of a.keys())
|
|
2502
|
+
if (t !== M && e.startsWith(t))
|
|
2503
|
+
return ze(a.get(t), e.slice(t.length));
|
|
2504
|
+
}, Ae = (a, e) => {
|
|
2505
|
+
const t = e.length;
|
|
2506
|
+
e: for (let n = 0; a && n < t; ) {
|
|
2507
|
+
for (const s of a.keys())
|
|
2508
|
+
if (s !== M && e[n] === s[0]) {
|
|
2509
|
+
const i = Math.min(t - n, s.length);
|
|
2510
|
+
let o = 1;
|
|
2511
|
+
for (; o < i && e[n + o] === s[o]; )
|
|
2512
|
+
++o;
|
|
2513
|
+
const l = a.get(s);
|
|
2514
|
+
if (o === s.length)
|
|
2515
|
+
a = l;
|
|
2516
|
+
else {
|
|
2517
|
+
const c = /* @__PURE__ */ new Map();
|
|
2518
|
+
c.set(s.slice(o), l), a.set(e.slice(n, n + o), c), a.delete(s), a = c;
|
|
2519
|
+
}
|
|
2520
|
+
n += o;
|
|
2521
|
+
continue e;
|
|
2522
|
+
}
|
|
2523
|
+
const r = /* @__PURE__ */ new Map();
|
|
2524
|
+
return a.set(e.slice(n), r), r;
|
|
2525
|
+
}
|
|
2526
|
+
return a;
|
|
2527
|
+
}, Cn = (a, e) => {
|
|
2528
|
+
const [t, n] = ke(a, e);
|
|
2529
|
+
if (t !== void 0) {
|
|
2530
|
+
if (t.delete(M), t.size === 0)
|
|
2531
|
+
wt(n);
|
|
2532
|
+
else if (t.size === 1) {
|
|
2533
|
+
const [r, s] = t.entries().next().value;
|
|
2534
|
+
St(n, r, s);
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
}, wt = (a) => {
|
|
2538
|
+
if (a.length === 0)
|
|
2539
|
+
return;
|
|
2540
|
+
const [e, t] = $e(a);
|
|
2541
|
+
if (e.delete(t), e.size === 0)
|
|
2542
|
+
wt(a.slice(0, -1));
|
|
2543
|
+
else if (e.size === 1) {
|
|
2544
|
+
const [n, r] = e.entries().next().value;
|
|
2545
|
+
n !== M && St(a.slice(0, -1), n, r);
|
|
2546
|
+
}
|
|
2547
|
+
}, St = (a, e, t) => {
|
|
2548
|
+
if (a.length === 0)
|
|
2549
|
+
return;
|
|
2550
|
+
const [n, r] = $e(a);
|
|
2551
|
+
n.set(r + e, t), n.delete(r);
|
|
2552
|
+
}, $e = (a) => a[a.length - 1], Be = "or", _t = "and", Mn = "and_not";
|
|
2553
|
+
class re {
|
|
2554
|
+
/**
|
|
2555
|
+
* @param options Configuration options
|
|
2556
|
+
*
|
|
2557
|
+
* ### Examples:
|
|
2558
|
+
*
|
|
2559
|
+
* ```javascript
|
|
2560
|
+
* // Create a search engine that indexes the 'title' and 'text' fields of your
|
|
2561
|
+
* // documents:
|
|
2562
|
+
* const miniSearch = new MiniSearch({ fields: ['title', 'text'] })
|
|
2563
|
+
* ```
|
|
2564
|
+
*
|
|
2565
|
+
* ### ID Field:
|
|
2566
|
+
*
|
|
2567
|
+
* ```javascript
|
|
2568
|
+
* // Your documents are assumed to include a unique 'id' field, but if you want
|
|
2569
|
+
* // to use a different field for document identification, you can set the
|
|
2570
|
+
* // 'idField' option:
|
|
2571
|
+
* const miniSearch = new MiniSearch({ idField: 'key', fields: ['title', 'text'] })
|
|
2572
|
+
* ```
|
|
2573
|
+
*
|
|
2574
|
+
* ### Options and defaults:
|
|
2575
|
+
*
|
|
2576
|
+
* ```javascript
|
|
2577
|
+
* // The full set of options (here with their default value) is:
|
|
2578
|
+
* const miniSearch = new MiniSearch({
|
|
2579
|
+
* // idField: field that uniquely identifies a document
|
|
2580
|
+
* idField: 'id',
|
|
2581
|
+
*
|
|
2582
|
+
* // extractField: function used to get the value of a field in a document.
|
|
2583
|
+
* // By default, it assumes the document is a flat object with field names as
|
|
2584
|
+
* // property keys and field values as string property values, but custom logic
|
|
2585
|
+
* // can be implemented by setting this option to a custom extractor function.
|
|
2586
|
+
* extractField: (document, fieldName) => document[fieldName],
|
|
2587
|
+
*
|
|
2588
|
+
* // tokenize: function used to split fields into individual terms. By
|
|
2589
|
+
* // default, it is also used to tokenize search queries, unless a specific
|
|
2590
|
+
* // `tokenize` search option is supplied. When tokenizing an indexed field,
|
|
2591
|
+
* // the field name is passed as the second argument.
|
|
2592
|
+
* tokenize: (string, _fieldName) => string.split(SPACE_OR_PUNCTUATION),
|
|
2593
|
+
*
|
|
2594
|
+
* // processTerm: function used to process each tokenized term before
|
|
2595
|
+
* // indexing. It can be used for stemming and normalization. Return a falsy
|
|
2596
|
+
* // value in order to discard a term. By default, it is also used to process
|
|
2597
|
+
* // search queries, unless a specific `processTerm` option is supplied as a
|
|
2598
|
+
* // search option. When processing a term from a indexed field, the field
|
|
2599
|
+
* // name is passed as the second argument.
|
|
2600
|
+
* processTerm: (term, _fieldName) => term.toLowerCase(),
|
|
2601
|
+
*
|
|
2602
|
+
* // searchOptions: default search options, see the `search` method for
|
|
2603
|
+
* // details
|
|
2604
|
+
* searchOptions: undefined,
|
|
2605
|
+
*
|
|
2606
|
+
* // fields: document fields to be indexed. Mandatory, but not set by default
|
|
2607
|
+
* fields: undefined
|
|
2608
|
+
*
|
|
2609
|
+
* // storeFields: document fields to be stored and returned as part of the
|
|
2610
|
+
* // search results.
|
|
2611
|
+
* storeFields: []
|
|
2612
|
+
* })
|
|
2613
|
+
* ```
|
|
2614
|
+
*/
|
|
2615
|
+
constructor(e) {
|
|
2616
|
+
if (e?.fields == null)
|
|
2617
|
+
throw new Error('MiniSearch: option "fields" must be provided');
|
|
2618
|
+
const t = e.autoVacuum == null || e.autoVacuum === !0 ? Me : e.autoVacuum;
|
|
2619
|
+
this._options = {
|
|
2620
|
+
...Ce,
|
|
2621
|
+
...e,
|
|
2622
|
+
autoVacuum: t,
|
|
2623
|
+
searchOptions: { ...st, ...e.searchOptions || {} },
|
|
2624
|
+
autoSuggestOptions: { ...Vn, ...e.autoSuggestOptions || {} }
|
|
2625
|
+
}, this._index = new q(), this._documentCount = 0, this._documentIds = /* @__PURE__ */ new Map(), this._idToShortId = /* @__PURE__ */ new Map(), this._fieldIds = {}, this._fieldLength = /* @__PURE__ */ new Map(), this._avgFieldLength = [], this._nextId = 0, this._storedFields = /* @__PURE__ */ new Map(), this._dirtCount = 0, this._currentVacuum = null, this._enqueuedVacuum = null, this._enqueuedVacuumConditions = je, this.addFields(this._options.fields);
|
|
2626
|
+
}
|
|
2627
|
+
/**
|
|
2628
|
+
* Adds a document to the index
|
|
2629
|
+
*
|
|
2630
|
+
* @param document The document to be indexed
|
|
2631
|
+
*/
|
|
2632
|
+
add(e) {
|
|
2633
|
+
const { extractField: t, stringifyField: n, tokenize: r, processTerm: s, fields: i, idField: o } = this._options, l = t(e, o);
|
|
2634
|
+
if (l == null)
|
|
2635
|
+
throw new Error(`MiniSearch: document does not have ID field "${o}"`);
|
|
2636
|
+
if (this._idToShortId.has(l))
|
|
2637
|
+
throw new Error(`MiniSearch: duplicate ID ${l}`);
|
|
2638
|
+
const c = this.addDocumentId(l);
|
|
2639
|
+
this.saveStoredFields(c, e);
|
|
2640
|
+
for (const f of i) {
|
|
2641
|
+
const p = t(e, f);
|
|
2642
|
+
if (p == null)
|
|
2643
|
+
continue;
|
|
2644
|
+
const v = r(n(p, f), f), y = this._fieldIds[f], S = new Set(v).size;
|
|
2645
|
+
this.addFieldLength(c, y, this._documentCount - 1, S);
|
|
2646
|
+
for (const w of v) {
|
|
2647
|
+
const _ = s(w, f);
|
|
2648
|
+
if (Array.isArray(_))
|
|
2649
|
+
for (const N of _)
|
|
2650
|
+
this.addTerm(y, c, N);
|
|
2651
|
+
else _ && this.addTerm(y, c, _);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
/**
|
|
2656
|
+
* Adds all the given documents to the index
|
|
2657
|
+
*
|
|
2658
|
+
* @param documents An array of documents to be indexed
|
|
2659
|
+
*/
|
|
2660
|
+
addAll(e) {
|
|
2661
|
+
for (const t of e)
|
|
2662
|
+
this.add(t);
|
|
2663
|
+
}
|
|
2664
|
+
/**
|
|
2665
|
+
* Adds all the given documents to the index asynchronously.
|
|
2666
|
+
*
|
|
2667
|
+
* Returns a promise that resolves (to `undefined`) when the indexing is done.
|
|
2668
|
+
* This method is useful when index many documents, to avoid blocking the main
|
|
2669
|
+
* thread. The indexing is performed asynchronously and in chunks.
|
|
2670
|
+
*
|
|
2671
|
+
* @param documents An array of documents to be indexed
|
|
2672
|
+
* @param options Configuration options
|
|
2673
|
+
* @return A promise resolving to `undefined` when the indexing is done
|
|
2674
|
+
*/
|
|
2675
|
+
addAllAsync(e, t = {}) {
|
|
2676
|
+
const { chunkSize: n = 10 } = t, r = { chunk: [], promise: Promise.resolve() }, { chunk: s, promise: i } = e.reduce(({ chunk: o, promise: l }, c, f) => (o.push(c), (f + 1) % n === 0 ? {
|
|
2677
|
+
chunk: [],
|
|
2678
|
+
promise: l.then(() => new Promise((p) => setTimeout(p, 0))).then(() => this.addAll(o))
|
|
2679
|
+
} : { chunk: o, promise: l }), r);
|
|
2680
|
+
return i.then(() => this.addAll(s));
|
|
2681
|
+
}
|
|
2682
|
+
/**
|
|
2683
|
+
* Removes the given document from the index.
|
|
2684
|
+
*
|
|
2685
|
+
* The document to remove must NOT have changed between indexing and removal,
|
|
2686
|
+
* otherwise the index will be corrupted.
|
|
2687
|
+
*
|
|
2688
|
+
* This method requires passing the full document to be removed (not just the
|
|
2689
|
+
* ID), and immediately removes the document from the inverted index, allowing
|
|
2690
|
+
* memory to be released. A convenient alternative is {@link
|
|
2691
|
+
* MiniSearch#discard}, which needs only the document ID, and has the same
|
|
2692
|
+
* visible effect, but delays cleaning up the index until the next vacuuming.
|
|
2693
|
+
*
|
|
2694
|
+
* @param document The document to be removed
|
|
2695
|
+
*/
|
|
2696
|
+
remove(e) {
|
|
2697
|
+
const { tokenize: t, processTerm: n, extractField: r, stringifyField: s, fields: i, idField: o } = this._options, l = r(e, o);
|
|
2698
|
+
if (l == null)
|
|
2699
|
+
throw new Error(`MiniSearch: document does not have ID field "${o}"`);
|
|
2700
|
+
const c = this._idToShortId.get(l);
|
|
2701
|
+
if (c == null)
|
|
2702
|
+
throw new Error(`MiniSearch: cannot remove document with ID ${l}: it is not in the index`);
|
|
2703
|
+
for (const f of i) {
|
|
2704
|
+
const p = r(e, f);
|
|
2705
|
+
if (p == null)
|
|
2706
|
+
continue;
|
|
2707
|
+
const v = t(s(p, f), f), y = this._fieldIds[f], S = new Set(v).size;
|
|
2708
|
+
this.removeFieldLength(c, y, this._documentCount, S);
|
|
2709
|
+
for (const w of v) {
|
|
2710
|
+
const _ = n(w, f);
|
|
2711
|
+
if (Array.isArray(_))
|
|
2712
|
+
for (const N of _)
|
|
2713
|
+
this.removeTerm(y, c, N);
|
|
2714
|
+
else _ && this.removeTerm(y, c, _);
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
this._storedFields.delete(c), this._documentIds.delete(c), this._idToShortId.delete(l), this._fieldLength.delete(c), this._documentCount -= 1;
|
|
2718
|
+
}
|
|
2719
|
+
/**
|
|
2720
|
+
* Removes all the given documents from the index. If called with no arguments,
|
|
2721
|
+
* it removes _all_ documents from the index.
|
|
2722
|
+
*
|
|
2723
|
+
* @param documents The documents to be removed. If this argument is omitted,
|
|
2724
|
+
* all documents are removed. Note that, for removing all documents, it is
|
|
2725
|
+
* more efficient to call this method with no arguments than to pass all
|
|
2726
|
+
* documents.
|
|
2727
|
+
*/
|
|
2728
|
+
removeAll(e) {
|
|
2729
|
+
if (e)
|
|
2730
|
+
for (const t of e)
|
|
2731
|
+
this.remove(t);
|
|
2732
|
+
else {
|
|
2733
|
+
if (arguments.length > 0)
|
|
2734
|
+
throw new Error("Expected documents to be present. Omit the argument to remove all documents.");
|
|
2735
|
+
this._index = new q(), this._documentCount = 0, this._documentIds = /* @__PURE__ */ new Map(), this._idToShortId = /* @__PURE__ */ new Map(), this._fieldLength = /* @__PURE__ */ new Map(), this._avgFieldLength = [], this._storedFields = /* @__PURE__ */ new Map(), this._nextId = 0;
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
/**
|
|
2739
|
+
* Discards the document with the given ID, so it won't appear in search results
|
|
2740
|
+
*
|
|
2741
|
+
* It has the same visible effect of {@link MiniSearch.remove} (both cause the
|
|
2742
|
+
* document to stop appearing in searches), but a different effect on the
|
|
2743
|
+
* internal data structures:
|
|
2744
|
+
*
|
|
2745
|
+
* - {@link MiniSearch#remove} requires passing the full document to be
|
|
2746
|
+
* removed as argument, and removes it from the inverted index immediately.
|
|
2747
|
+
*
|
|
2748
|
+
* - {@link MiniSearch#discard} instead only needs the document ID, and
|
|
2749
|
+
* works by marking the current version of the document as discarded, so it
|
|
2750
|
+
* is immediately ignored by searches. This is faster and more convenient
|
|
2751
|
+
* than {@link MiniSearch#remove}, but the index is not immediately
|
|
2752
|
+
* modified. To take care of that, vacuuming is performed after a certain
|
|
2753
|
+
* number of documents are discarded, cleaning up the index and allowing
|
|
2754
|
+
* memory to be released.
|
|
2755
|
+
*
|
|
2756
|
+
* After discarding a document, it is possible to re-add a new version, and
|
|
2757
|
+
* only the new version will appear in searches. In other words, discarding
|
|
2758
|
+
* and re-adding a document works exactly like removing and re-adding it. The
|
|
2759
|
+
* {@link MiniSearch.replace} method can also be used to replace a document
|
|
2760
|
+
* with a new version.
|
|
2761
|
+
*
|
|
2762
|
+
* #### Details about vacuuming
|
|
2763
|
+
*
|
|
2764
|
+
* Repetite calls to this method would leave obsolete document references in
|
|
2765
|
+
* the index, invisible to searches. Two mechanisms take care of cleaning up:
|
|
2766
|
+
* clean up during search, and vacuuming.
|
|
2767
|
+
*
|
|
2768
|
+
* - Upon search, whenever a discarded ID is found (and ignored for the
|
|
2769
|
+
* results), references to the discarded document are removed from the
|
|
2770
|
+
* inverted index entries for the search terms. This ensures that subsequent
|
|
2771
|
+
* searches for the same terms do not need to skip these obsolete references
|
|
2772
|
+
* again.
|
|
2773
|
+
*
|
|
2774
|
+
* - In addition, vacuuming is performed automatically by default (see the
|
|
2775
|
+
* `autoVacuum` field in {@link Options}) after a certain number of
|
|
2776
|
+
* documents are discarded. Vacuuming traverses all terms in the index,
|
|
2777
|
+
* cleaning up all references to discarded documents. Vacuuming can also be
|
|
2778
|
+
* triggered manually by calling {@link MiniSearch#vacuum}.
|
|
2779
|
+
*
|
|
2780
|
+
* @param id The ID of the document to be discarded
|
|
2781
|
+
*/
|
|
2782
|
+
discard(e) {
|
|
2783
|
+
const t = this._idToShortId.get(e);
|
|
2784
|
+
if (t == null)
|
|
2785
|
+
throw new Error(`MiniSearch: cannot discard document with ID ${e}: it is not in the index`);
|
|
2786
|
+
this._idToShortId.delete(e), this._documentIds.delete(t), this._storedFields.delete(t), (this._fieldLength.get(t) || []).forEach((n, r) => {
|
|
2787
|
+
this.removeFieldLength(t, r, this._documentCount, n);
|
|
2788
|
+
}), this._fieldLength.delete(t), this._documentCount -= 1, this._dirtCount += 1, this.maybeAutoVacuum();
|
|
2789
|
+
}
|
|
2790
|
+
maybeAutoVacuum() {
|
|
2791
|
+
if (this._options.autoVacuum === !1)
|
|
2792
|
+
return;
|
|
2793
|
+
const { minDirtFactor: e, minDirtCount: t, batchSize: n, batchWait: r } = this._options.autoVacuum;
|
|
2794
|
+
this.conditionalVacuum({ batchSize: n, batchWait: r }, { minDirtCount: t, minDirtFactor: e });
|
|
2795
|
+
}
|
|
2796
|
+
/**
|
|
2797
|
+
* Discards the documents with the given IDs, so they won't appear in search
|
|
2798
|
+
* results
|
|
2799
|
+
*
|
|
2800
|
+
* It is equivalent to calling {@link MiniSearch#discard} for all the given
|
|
2801
|
+
* IDs, but with the optimization of triggering at most one automatic
|
|
2802
|
+
* vacuuming at the end.
|
|
2803
|
+
*
|
|
2804
|
+
* Note: to remove all documents from the index, it is faster and more
|
|
2805
|
+
* convenient to call {@link MiniSearch.removeAll} with no argument, instead
|
|
2806
|
+
* of passing all IDs to this method.
|
|
2807
|
+
*/
|
|
2808
|
+
discardAll(e) {
|
|
2809
|
+
const t = this._options.autoVacuum;
|
|
2810
|
+
try {
|
|
2811
|
+
this._options.autoVacuum = !1;
|
|
2812
|
+
for (const n of e)
|
|
2813
|
+
this.discard(n);
|
|
2814
|
+
} finally {
|
|
2815
|
+
this._options.autoVacuum = t;
|
|
2816
|
+
}
|
|
2817
|
+
this.maybeAutoVacuum();
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* It replaces an existing document with the given updated version
|
|
2821
|
+
*
|
|
2822
|
+
* It works by discarding the current version and adding the updated one, so
|
|
2823
|
+
* it is functionally equivalent to calling {@link MiniSearch#discard}
|
|
2824
|
+
* followed by {@link MiniSearch#add}. The ID of the updated document should
|
|
2825
|
+
* be the same as the original one.
|
|
2826
|
+
*
|
|
2827
|
+
* Since it uses {@link MiniSearch#discard} internally, this method relies on
|
|
2828
|
+
* vacuuming to clean up obsolete document references from the index, allowing
|
|
2829
|
+
* memory to be released (see {@link MiniSearch#discard}).
|
|
2830
|
+
*
|
|
2831
|
+
* @param updatedDocument The updated document to replace the old version
|
|
2832
|
+
* with
|
|
2833
|
+
*/
|
|
2834
|
+
replace(e) {
|
|
2835
|
+
const { idField: t, extractField: n } = this._options, r = n(e, t);
|
|
2836
|
+
this.discard(r), this.add(e);
|
|
2837
|
+
}
|
|
2838
|
+
/**
|
|
2839
|
+
* Triggers a manual vacuuming, cleaning up references to discarded documents
|
|
2840
|
+
* from the inverted index
|
|
2841
|
+
*
|
|
2842
|
+
* Vacuuming is only useful for applications that use the {@link
|
|
2843
|
+
* MiniSearch#discard} or {@link MiniSearch#replace} methods.
|
|
2844
|
+
*
|
|
2845
|
+
* By default, vacuuming is performed automatically when needed (controlled by
|
|
2846
|
+
* the `autoVacuum` field in {@link Options}), so there is usually no need to
|
|
2847
|
+
* call this method, unless one wants to make sure to perform vacuuming at a
|
|
2848
|
+
* specific moment.
|
|
2849
|
+
*
|
|
2850
|
+
* Vacuuming traverses all terms in the inverted index in batches, and cleans
|
|
2851
|
+
* up references to discarded documents from the posting list, allowing memory
|
|
2852
|
+
* to be released.
|
|
2853
|
+
*
|
|
2854
|
+
* The method takes an optional object as argument with the following keys:
|
|
2855
|
+
*
|
|
2856
|
+
* - `batchSize`: the size of each batch (1000 by default)
|
|
2857
|
+
*
|
|
2858
|
+
* - `batchWait`: the number of milliseconds to wait between batches (10 by
|
|
2859
|
+
* default)
|
|
2860
|
+
*
|
|
2861
|
+
* On large indexes, vacuuming could have a non-negligible cost: batching
|
|
2862
|
+
* avoids blocking the thread for long, diluting this cost so that it is not
|
|
2863
|
+
* negatively affecting the application. Nonetheless, this method should only
|
|
2864
|
+
* be called when necessary, and relying on automatic vacuuming is usually
|
|
2865
|
+
* better.
|
|
2866
|
+
*
|
|
2867
|
+
* It returns a promise that resolves (to undefined) when the clean up is
|
|
2868
|
+
* completed. If vacuuming is already ongoing at the time this method is
|
|
2869
|
+
* called, a new one is enqueued immediately after the ongoing one, and a
|
|
2870
|
+
* corresponding promise is returned. However, no more than one vacuuming is
|
|
2871
|
+
* enqueued on top of the ongoing one, even if this method is called more
|
|
2872
|
+
* times (enqueuing multiple ones would be useless).
|
|
2873
|
+
*
|
|
2874
|
+
* @param options Configuration options for the batch size and delay. See
|
|
2875
|
+
* {@link VacuumOptions}.
|
|
2876
|
+
*/
|
|
2877
|
+
vacuum(e = {}) {
|
|
2878
|
+
return this.conditionalVacuum(e);
|
|
2879
|
+
}
|
|
2880
|
+
conditionalVacuum(e, t) {
|
|
2881
|
+
return this._currentVacuum ? (this._enqueuedVacuumConditions = this._enqueuedVacuumConditions && t, this._enqueuedVacuum != null ? this._enqueuedVacuum : (this._enqueuedVacuum = this._currentVacuum.then(() => {
|
|
2882
|
+
const n = this._enqueuedVacuumConditions;
|
|
2883
|
+
return this._enqueuedVacuumConditions = je, this.performVacuuming(e, n);
|
|
2884
|
+
}), this._enqueuedVacuum)) : this.vacuumConditionsMet(t) === !1 ? Promise.resolve() : (this._currentVacuum = this.performVacuuming(e), this._currentVacuum);
|
|
2885
|
+
}
|
|
2886
|
+
async performVacuuming(e, t) {
|
|
2887
|
+
const n = this._dirtCount;
|
|
2888
|
+
if (this.vacuumConditionsMet(t)) {
|
|
2889
|
+
const r = e.batchSize || Ve.batchSize, s = e.batchWait || Ve.batchWait;
|
|
2890
|
+
let i = 1;
|
|
2891
|
+
for (const [o, l] of this._index) {
|
|
2892
|
+
for (const [c, f] of l)
|
|
2893
|
+
for (const [p] of f)
|
|
2894
|
+
this._documentIds.has(p) || (f.size <= 1 ? l.delete(c) : f.delete(p));
|
|
2895
|
+
this._index.get(o).size === 0 && this._index.delete(o), i % r === 0 && await new Promise((c) => setTimeout(c, s)), i += 1;
|
|
2896
|
+
}
|
|
2897
|
+
this._dirtCount -= n;
|
|
2898
|
+
}
|
|
2899
|
+
await null, this._currentVacuum = this._enqueuedVacuum, this._enqueuedVacuum = null;
|
|
2900
|
+
}
|
|
2901
|
+
vacuumConditionsMet(e) {
|
|
2902
|
+
if (e == null)
|
|
2903
|
+
return !0;
|
|
2904
|
+
let { minDirtCount: t, minDirtFactor: n } = e;
|
|
2905
|
+
return t = t || Me.minDirtCount, n = n || Me.minDirtFactor, this.dirtCount >= t && this.dirtFactor >= n;
|
|
2906
|
+
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Is `true` if a vacuuming operation is ongoing, `false` otherwise
|
|
2909
|
+
*/
|
|
2910
|
+
get isVacuuming() {
|
|
2911
|
+
return this._currentVacuum != null;
|
|
2912
|
+
}
|
|
2913
|
+
/**
|
|
2914
|
+
* The number of documents discarded since the most recent vacuuming
|
|
2915
|
+
*/
|
|
2916
|
+
get dirtCount() {
|
|
2917
|
+
return this._dirtCount;
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* A number between 0 and 1 giving an indication about the proportion of
|
|
2921
|
+
* documents that are discarded, and can therefore be cleaned up by vacuuming.
|
|
2922
|
+
* A value close to 0 means that the index is relatively clean, while a higher
|
|
2923
|
+
* value means that the index is relatively dirty, and vacuuming could release
|
|
2924
|
+
* memory.
|
|
2925
|
+
*/
|
|
2926
|
+
get dirtFactor() {
|
|
2927
|
+
return this._dirtCount / (1 + this._documentCount + this._dirtCount);
|
|
2928
|
+
}
|
|
2929
|
+
/**
|
|
2930
|
+
* Returns `true` if a document with the given ID is present in the index and
|
|
2931
|
+
* available for search, `false` otherwise
|
|
2932
|
+
*
|
|
2933
|
+
* @param id The document ID
|
|
2934
|
+
*/
|
|
2935
|
+
has(e) {
|
|
2936
|
+
return this._idToShortId.has(e);
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* Returns the stored fields (as configured in the `storeFields` constructor
|
|
2940
|
+
* option) for the given document ID. Returns `undefined` if the document is
|
|
2941
|
+
* not present in the index.
|
|
2942
|
+
*
|
|
2943
|
+
* @param id The document ID
|
|
2944
|
+
*/
|
|
2945
|
+
getStoredFields(e) {
|
|
2946
|
+
const t = this._idToShortId.get(e);
|
|
2947
|
+
if (t != null)
|
|
2948
|
+
return this._storedFields.get(t);
|
|
2949
|
+
}
|
|
2950
|
+
/**
|
|
2951
|
+
* Search for documents matching the given search query.
|
|
2952
|
+
*
|
|
2953
|
+
* The result is a list of scored document IDs matching the query, sorted by
|
|
2954
|
+
* descending score, and each including data about which terms were matched and
|
|
2955
|
+
* in which fields.
|
|
2956
|
+
*
|
|
2957
|
+
* ### Basic usage:
|
|
2958
|
+
*
|
|
2959
|
+
* ```javascript
|
|
2960
|
+
* // Search for "zen art motorcycle" with default options: terms have to match
|
|
2961
|
+
* // exactly, and individual terms are joined with OR
|
|
2962
|
+
* miniSearch.search('zen art motorcycle')
|
|
2963
|
+
* // => [ { id: 2, score: 2.77258, match: { ... } }, { id: 4, score: 1.38629, match: { ... } } ]
|
|
2964
|
+
* ```
|
|
2965
|
+
*
|
|
2966
|
+
* ### Restrict search to specific fields:
|
|
2967
|
+
*
|
|
2968
|
+
* ```javascript
|
|
2969
|
+
* // Search only in the 'title' field
|
|
2970
|
+
* miniSearch.search('zen', { fields: ['title'] })
|
|
2971
|
+
* ```
|
|
2972
|
+
*
|
|
2973
|
+
* ### Field boosting:
|
|
2974
|
+
*
|
|
2975
|
+
* ```javascript
|
|
2976
|
+
* // Boost a field
|
|
2977
|
+
* miniSearch.search('zen', { boost: { title: 2 } })
|
|
2978
|
+
* ```
|
|
2979
|
+
*
|
|
2980
|
+
* ### Prefix search:
|
|
2981
|
+
*
|
|
2982
|
+
* ```javascript
|
|
2983
|
+
* // Search for "moto" with prefix search (it will match documents
|
|
2984
|
+
* // containing terms that start with "moto" or "neuro")
|
|
2985
|
+
* miniSearch.search('moto neuro', { prefix: true })
|
|
2986
|
+
* ```
|
|
2987
|
+
*
|
|
2988
|
+
* ### Fuzzy search:
|
|
2989
|
+
*
|
|
2990
|
+
* ```javascript
|
|
2991
|
+
* // Search for "ismael" with fuzzy search (it will match documents containing
|
|
2992
|
+
* // terms similar to "ismael", with a maximum edit distance of 0.2 term.length
|
|
2993
|
+
* // (rounded to nearest integer)
|
|
2994
|
+
* miniSearch.search('ismael', { fuzzy: 0.2 })
|
|
2995
|
+
* ```
|
|
2996
|
+
*
|
|
2997
|
+
* ### Combining strategies:
|
|
2998
|
+
*
|
|
2999
|
+
* ```javascript
|
|
3000
|
+
* // Mix of exact match, prefix search, and fuzzy search
|
|
3001
|
+
* miniSearch.search('ismael mob', {
|
|
3002
|
+
* prefix: true,
|
|
3003
|
+
* fuzzy: 0.2
|
|
3004
|
+
* })
|
|
3005
|
+
* ```
|
|
3006
|
+
*
|
|
3007
|
+
* ### Advanced prefix and fuzzy search:
|
|
3008
|
+
*
|
|
3009
|
+
* ```javascript
|
|
3010
|
+
* // Perform fuzzy and prefix search depending on the search term. Here
|
|
3011
|
+
* // performing prefix and fuzzy search only on terms longer than 3 characters
|
|
3012
|
+
* miniSearch.search('ismael mob', {
|
|
3013
|
+
* prefix: term => term.length > 3
|
|
3014
|
+
* fuzzy: term => term.length > 3 ? 0.2 : null
|
|
3015
|
+
* })
|
|
3016
|
+
* ```
|
|
3017
|
+
*
|
|
3018
|
+
* ### Combine with AND:
|
|
3019
|
+
*
|
|
3020
|
+
* ```javascript
|
|
3021
|
+
* // Combine search terms with AND (to match only documents that contain both
|
|
3022
|
+
* // "motorcycle" and "art")
|
|
3023
|
+
* miniSearch.search('motorcycle art', { combineWith: 'AND' })
|
|
3024
|
+
* ```
|
|
3025
|
+
*
|
|
3026
|
+
* ### Combine with AND_NOT:
|
|
3027
|
+
*
|
|
3028
|
+
* There is also an AND_NOT combinator, that finds documents that match the
|
|
3029
|
+
* first term, but do not match any of the other terms. This combinator is
|
|
3030
|
+
* rarely useful with simple queries, and is meant to be used with advanced
|
|
3031
|
+
* query combinations (see later for more details).
|
|
3032
|
+
*
|
|
3033
|
+
* ### Filtering results:
|
|
3034
|
+
*
|
|
3035
|
+
* ```javascript
|
|
3036
|
+
* // Filter only results in the 'fiction' category (assuming that 'category'
|
|
3037
|
+
* // is a stored field)
|
|
3038
|
+
* miniSearch.search('motorcycle art', {
|
|
3039
|
+
* filter: (result) => result.category === 'fiction'
|
|
3040
|
+
* })
|
|
3041
|
+
* ```
|
|
3042
|
+
*
|
|
3043
|
+
* ### Wildcard query
|
|
3044
|
+
*
|
|
3045
|
+
* Searching for an empty string (assuming the default tokenizer) returns no
|
|
3046
|
+
* results. Sometimes though, one needs to match all documents, like in a
|
|
3047
|
+
* "wildcard" search. This is possible by passing the special value
|
|
3048
|
+
* {@link MiniSearch.wildcard} as the query:
|
|
3049
|
+
*
|
|
3050
|
+
* ```javascript
|
|
3051
|
+
* // Return search results for all documents
|
|
3052
|
+
* miniSearch.search(MiniSearch.wildcard)
|
|
3053
|
+
* ```
|
|
3054
|
+
*
|
|
3055
|
+
* Note that search options such as `filter` and `boostDocument` are still
|
|
3056
|
+
* applied, influencing which results are returned, and their order:
|
|
3057
|
+
*
|
|
3058
|
+
* ```javascript
|
|
3059
|
+
* // Return search results for all documents in the 'fiction' category
|
|
3060
|
+
* miniSearch.search(MiniSearch.wildcard, {
|
|
3061
|
+
* filter: (result) => result.category === 'fiction'
|
|
3062
|
+
* })
|
|
3063
|
+
* ```
|
|
3064
|
+
*
|
|
3065
|
+
* ### Advanced combination of queries:
|
|
3066
|
+
*
|
|
3067
|
+
* It is possible to combine different subqueries with OR, AND, and AND_NOT,
|
|
3068
|
+
* and even with different search options, by passing a query expression
|
|
3069
|
+
* tree object as the first argument, instead of a string.
|
|
3070
|
+
*
|
|
3071
|
+
* ```javascript
|
|
3072
|
+
* // Search for documents that contain "zen" and ("motorcycle" or "archery")
|
|
3073
|
+
* miniSearch.search({
|
|
3074
|
+
* combineWith: 'AND',
|
|
3075
|
+
* queries: [
|
|
3076
|
+
* 'zen',
|
|
3077
|
+
* {
|
|
3078
|
+
* combineWith: 'OR',
|
|
3079
|
+
* queries: ['motorcycle', 'archery']
|
|
3080
|
+
* }
|
|
3081
|
+
* ]
|
|
3082
|
+
* })
|
|
3083
|
+
*
|
|
3084
|
+
* // Search for documents that contain ("apple" or "pear") but not "juice" and
|
|
3085
|
+
* // not "tree"
|
|
3086
|
+
* miniSearch.search({
|
|
3087
|
+
* combineWith: 'AND_NOT',
|
|
3088
|
+
* queries: [
|
|
3089
|
+
* {
|
|
3090
|
+
* combineWith: 'OR',
|
|
3091
|
+
* queries: ['apple', 'pear']
|
|
3092
|
+
* },
|
|
3093
|
+
* 'juice',
|
|
3094
|
+
* 'tree'
|
|
3095
|
+
* ]
|
|
3096
|
+
* })
|
|
3097
|
+
* ```
|
|
3098
|
+
*
|
|
3099
|
+
* Each node in the expression tree can be either a string, or an object that
|
|
3100
|
+
* supports all {@link SearchOptions} fields, plus a `queries` array field for
|
|
3101
|
+
* subqueries.
|
|
3102
|
+
*
|
|
3103
|
+
* Note that, while this can become complicated to do by hand for complex or
|
|
3104
|
+
* deeply nested queries, it provides a formalized expression tree API for
|
|
3105
|
+
* external libraries that implement a parser for custom query languages.
|
|
3106
|
+
*
|
|
3107
|
+
* @param query Search query
|
|
3108
|
+
* @param searchOptions Search options. Each option, if not given, defaults to the corresponding value of `searchOptions` given to the constructor, or to the library default.
|
|
3109
|
+
*/
|
|
3110
|
+
search(e, t = {}) {
|
|
3111
|
+
const { searchOptions: n } = this._options, r = { ...n, ...t }, s = this.executeQuery(e, t), i = [];
|
|
3112
|
+
for (const [o, { score: l, terms: c, match: f }] of s) {
|
|
3113
|
+
const p = c.length || 1, v = {
|
|
3114
|
+
id: this._documentIds.get(o),
|
|
3115
|
+
score: l * p,
|
|
3116
|
+
terms: Object.keys(f),
|
|
3117
|
+
queryTerms: c,
|
|
3118
|
+
match: f
|
|
3119
|
+
};
|
|
3120
|
+
Object.assign(v, this._storedFields.get(o)), (r.filter == null || r.filter(v)) && i.push(v);
|
|
3121
|
+
}
|
|
3122
|
+
return e === re.wildcard && r.boostDocument == null || i.sort(ot), i;
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Provide suggestions for the given search query
|
|
3126
|
+
*
|
|
3127
|
+
* The result is a list of suggested modified search queries, derived from the
|
|
3128
|
+
* given search query, each with a relevance score, sorted by descending score.
|
|
3129
|
+
*
|
|
3130
|
+
* By default, it uses the same options used for search, except that by
|
|
3131
|
+
* default it performs prefix search on the last term of the query, and
|
|
3132
|
+
* combine terms with `'AND'` (requiring all query terms to match). Custom
|
|
3133
|
+
* options can be passed as a second argument. Defaults can be changed upon
|
|
3134
|
+
* calling the {@link MiniSearch} constructor, by passing a
|
|
3135
|
+
* `autoSuggestOptions` option.
|
|
3136
|
+
*
|
|
3137
|
+
* ### Basic usage:
|
|
3138
|
+
*
|
|
3139
|
+
* ```javascript
|
|
3140
|
+
* // Get suggestions for 'neuro':
|
|
3141
|
+
* miniSearch.autoSuggest('neuro')
|
|
3142
|
+
* // => [ { suggestion: 'neuromancer', terms: [ 'neuromancer' ], score: 0.46240 } ]
|
|
3143
|
+
* ```
|
|
3144
|
+
*
|
|
3145
|
+
* ### Multiple words:
|
|
3146
|
+
*
|
|
3147
|
+
* ```javascript
|
|
3148
|
+
* // Get suggestions for 'zen ar':
|
|
3149
|
+
* miniSearch.autoSuggest('zen ar')
|
|
3150
|
+
* // => [
|
|
3151
|
+
* // { suggestion: 'zen archery art', terms: [ 'zen', 'archery', 'art' ], score: 1.73332 },
|
|
3152
|
+
* // { suggestion: 'zen art', terms: [ 'zen', 'art' ], score: 1.21313 }
|
|
3153
|
+
* // ]
|
|
3154
|
+
* ```
|
|
3155
|
+
*
|
|
3156
|
+
* ### Fuzzy suggestions:
|
|
3157
|
+
*
|
|
3158
|
+
* ```javascript
|
|
3159
|
+
* // Correct spelling mistakes using fuzzy search:
|
|
3160
|
+
* miniSearch.autoSuggest('neromancer', { fuzzy: 0.2 })
|
|
3161
|
+
* // => [ { suggestion: 'neuromancer', terms: [ 'neuromancer' ], score: 1.03998 } ]
|
|
3162
|
+
* ```
|
|
3163
|
+
*
|
|
3164
|
+
* ### Filtering:
|
|
3165
|
+
*
|
|
3166
|
+
* ```javascript
|
|
3167
|
+
* // Get suggestions for 'zen ar', but only within the 'fiction' category
|
|
3168
|
+
* // (assuming that 'category' is a stored field):
|
|
3169
|
+
* miniSearch.autoSuggest('zen ar', {
|
|
3170
|
+
* filter: (result) => result.category === 'fiction'
|
|
3171
|
+
* })
|
|
3172
|
+
* // => [
|
|
3173
|
+
* // { suggestion: 'zen archery art', terms: [ 'zen', 'archery', 'art' ], score: 1.73332 },
|
|
3174
|
+
* // { suggestion: 'zen art', terms: [ 'zen', 'art' ], score: 1.21313 }
|
|
3175
|
+
* // ]
|
|
3176
|
+
* ```
|
|
3177
|
+
*
|
|
3178
|
+
* @param queryString Query string to be expanded into suggestions
|
|
3179
|
+
* @param options Search options. The supported options and default values
|
|
3180
|
+
* are the same as for the {@link MiniSearch#search} method, except that by
|
|
3181
|
+
* default prefix search is performed on the last term in the query, and terms
|
|
3182
|
+
* are combined with `'AND'`.
|
|
3183
|
+
* @return A sorted array of suggestions sorted by relevance score.
|
|
3184
|
+
*/
|
|
3185
|
+
autoSuggest(e, t = {}) {
|
|
3186
|
+
t = { ...this._options.autoSuggestOptions, ...t };
|
|
3187
|
+
const n = /* @__PURE__ */ new Map();
|
|
3188
|
+
for (const { score: s, terms: i } of this.search(e, t)) {
|
|
3189
|
+
const o = i.join(" "), l = n.get(o);
|
|
3190
|
+
l != null ? (l.score += s, l.count += 1) : n.set(o, { score: s, terms: i, count: 1 });
|
|
3191
|
+
}
|
|
3192
|
+
const r = [];
|
|
3193
|
+
for (const [s, { score: i, terms: o, count: l }] of n)
|
|
3194
|
+
r.push({ suggestion: s, terms: o, score: i / l });
|
|
3195
|
+
return r.sort(ot), r;
|
|
3196
|
+
}
|
|
3197
|
+
/**
|
|
3198
|
+
* Total number of documents available to search
|
|
3199
|
+
*/
|
|
3200
|
+
get documentCount() {
|
|
3201
|
+
return this._documentCount;
|
|
3202
|
+
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Number of terms in the index
|
|
3205
|
+
*/
|
|
3206
|
+
get termCount() {
|
|
3207
|
+
return this._index.size;
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Deserializes a JSON index (serialized with `JSON.stringify(miniSearch)`)
|
|
3211
|
+
* and instantiates a MiniSearch instance. It should be given the same options
|
|
3212
|
+
* originally used when serializing the index.
|
|
3213
|
+
*
|
|
3214
|
+
* ### Usage:
|
|
3215
|
+
*
|
|
3216
|
+
* ```javascript
|
|
3217
|
+
* // If the index was serialized with:
|
|
3218
|
+
* let miniSearch = new MiniSearch({ fields: ['title', 'text'] })
|
|
3219
|
+
* miniSearch.addAll(documents)
|
|
3220
|
+
*
|
|
3221
|
+
* const json = JSON.stringify(miniSearch)
|
|
3222
|
+
* // It can later be deserialized like this:
|
|
3223
|
+
* miniSearch = MiniSearch.loadJSON(json, { fields: ['title', 'text'] })
|
|
3224
|
+
* ```
|
|
3225
|
+
*
|
|
3226
|
+
* @param json JSON-serialized index
|
|
3227
|
+
* @param options configuration options, same as the constructor
|
|
3228
|
+
* @return An instance of MiniSearch deserialized from the given JSON.
|
|
3229
|
+
*/
|
|
3230
|
+
static loadJSON(e, t) {
|
|
3231
|
+
if (t == null)
|
|
3232
|
+
throw new Error("MiniSearch: loadJSON should be given the same options used when serializing the index");
|
|
3233
|
+
return this.loadJS(JSON.parse(e), t);
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Async equivalent of {@link MiniSearch.loadJSON}
|
|
3237
|
+
*
|
|
3238
|
+
* This function is an alternative to {@link MiniSearch.loadJSON} that returns
|
|
3239
|
+
* a promise, and loads the index in batches, leaving pauses between them to avoid
|
|
3240
|
+
* blocking the main thread. It tends to be slower than the synchronous
|
|
3241
|
+
* version, but does not block the main thread, so it can be a better choice
|
|
3242
|
+
* when deserializing very large indexes.
|
|
3243
|
+
*
|
|
3244
|
+
* @param json JSON-serialized index
|
|
3245
|
+
* @param options configuration options, same as the constructor
|
|
3246
|
+
* @return A Promise that will resolve to an instance of MiniSearch deserialized from the given JSON.
|
|
3247
|
+
*/
|
|
3248
|
+
static async loadJSONAsync(e, t) {
|
|
3249
|
+
if (t == null)
|
|
3250
|
+
throw new Error("MiniSearch: loadJSON should be given the same options used when serializing the index");
|
|
3251
|
+
return this.loadJSAsync(JSON.parse(e), t);
|
|
3252
|
+
}
|
|
3253
|
+
/**
|
|
3254
|
+
* Returns the default value of an option. It will throw an error if no option
|
|
3255
|
+
* with the given name exists.
|
|
3256
|
+
*
|
|
3257
|
+
* @param optionName Name of the option
|
|
3258
|
+
* @return The default value of the given option
|
|
3259
|
+
*
|
|
3260
|
+
* ### Usage:
|
|
3261
|
+
*
|
|
3262
|
+
* ```javascript
|
|
3263
|
+
* // Get default tokenizer
|
|
3264
|
+
* MiniSearch.getDefault('tokenize')
|
|
3265
|
+
*
|
|
3266
|
+
* // Get default term processor
|
|
3267
|
+
* MiniSearch.getDefault('processTerm')
|
|
3268
|
+
*
|
|
3269
|
+
* // Unknown options will throw an error
|
|
3270
|
+
* MiniSearch.getDefault('notExisting')
|
|
3271
|
+
* // => throws 'MiniSearch: unknown option "notExisting"'
|
|
3272
|
+
* ```
|
|
3273
|
+
*/
|
|
3274
|
+
static getDefault(e) {
|
|
3275
|
+
if (Ce.hasOwnProperty(e))
|
|
3276
|
+
return Oe(Ce, e);
|
|
3277
|
+
throw new Error(`MiniSearch: unknown option "${e}"`);
|
|
3278
|
+
}
|
|
3279
|
+
/**
|
|
3280
|
+
* @ignore
|
|
3281
|
+
*/
|
|
3282
|
+
static loadJS(e, t) {
|
|
3283
|
+
const { index: n, documentIds: r, fieldLength: s, storedFields: i, serializationVersion: o } = e, l = this.instantiateMiniSearch(e, t);
|
|
3284
|
+
l._documentIds = ye(r), l._fieldLength = ye(s), l._storedFields = ye(i);
|
|
3285
|
+
for (const [c, f] of l._documentIds)
|
|
3286
|
+
l._idToShortId.set(f, c);
|
|
3287
|
+
for (const [c, f] of n) {
|
|
3288
|
+
const p = /* @__PURE__ */ new Map();
|
|
3289
|
+
for (const v of Object.keys(f)) {
|
|
3290
|
+
let y = f[v];
|
|
3291
|
+
o === 1 && (y = y.ds), p.set(parseInt(v, 10), ye(y));
|
|
3292
|
+
}
|
|
3293
|
+
l._index.set(c, p);
|
|
3294
|
+
}
|
|
3295
|
+
return l;
|
|
3296
|
+
}
|
|
3297
|
+
/**
|
|
3298
|
+
* @ignore
|
|
3299
|
+
*/
|
|
3300
|
+
static async loadJSAsync(e, t) {
|
|
3301
|
+
const { index: n, documentIds: r, fieldLength: s, storedFields: i, serializationVersion: o } = e, l = this.instantiateMiniSearch(e, t);
|
|
3302
|
+
l._documentIds = await we(r), l._fieldLength = await we(s), l._storedFields = await we(i);
|
|
3303
|
+
for (const [f, p] of l._documentIds)
|
|
3304
|
+
l._idToShortId.set(p, f);
|
|
3305
|
+
let c = 0;
|
|
3306
|
+
for (const [f, p] of n) {
|
|
3307
|
+
const v = /* @__PURE__ */ new Map();
|
|
3308
|
+
for (const y of Object.keys(p)) {
|
|
3309
|
+
let S = p[y];
|
|
3310
|
+
o === 1 && (S = S.ds), v.set(parseInt(y, 10), await we(S));
|
|
3311
|
+
}
|
|
3312
|
+
++c % 1e3 === 0 && await xt(0), l._index.set(f, v);
|
|
3313
|
+
}
|
|
3314
|
+
return l;
|
|
3315
|
+
}
|
|
3316
|
+
/**
|
|
3317
|
+
* @ignore
|
|
3318
|
+
*/
|
|
3319
|
+
static instantiateMiniSearch(e, t) {
|
|
3320
|
+
const { documentCount: n, nextId: r, fieldIds: s, averageFieldLength: i, dirtCount: o, serializationVersion: l } = e;
|
|
3321
|
+
if (l !== 1 && l !== 2)
|
|
3322
|
+
throw new Error("MiniSearch: cannot deserialize an index created with an incompatible version");
|
|
3323
|
+
const c = new re(t);
|
|
3324
|
+
return c._documentCount = n, c._nextId = r, c._idToShortId = /* @__PURE__ */ new Map(), c._fieldIds = s, c._avgFieldLength = i, c._dirtCount = o || 0, c._index = new q(), c;
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
3327
|
+
* @ignore
|
|
3328
|
+
*/
|
|
3329
|
+
executeQuery(e, t = {}) {
|
|
3330
|
+
if (e === re.wildcard)
|
|
3331
|
+
return this.executeWildcardQuery(t);
|
|
3332
|
+
if (typeof e != "string") {
|
|
3333
|
+
const v = { ...t, ...e, queries: void 0 }, y = e.queries.map((S) => this.executeQuery(S, v));
|
|
3334
|
+
return this.combineResults(y, v.combineWith);
|
|
3335
|
+
}
|
|
3336
|
+
const { tokenize: n, processTerm: r, searchOptions: s } = this._options, i = { tokenize: n, processTerm: r, ...s, ...t }, { tokenize: o, processTerm: l } = i, p = o(e).flatMap((v) => l(v)).filter((v) => !!v).map(zn(i)).map((v) => this.executeQuerySpec(v, i));
|
|
3337
|
+
return this.combineResults(p, i.combineWith);
|
|
3338
|
+
}
|
|
3339
|
+
/**
|
|
3340
|
+
* @ignore
|
|
3341
|
+
*/
|
|
3342
|
+
executeQuerySpec(e, t) {
|
|
3343
|
+
const n = { ...this._options.searchOptions, ...t }, r = (n.fields || this._options.fields).reduce((w, _) => ({ ...w, [_]: Oe(n.boost, _) || 1 }), {}), { boostDocument: s, weights: i, maxFuzzy: o, bm25: l } = n, { fuzzy: c, prefix: f } = { ...st.weights, ...i }, p = this._index.get(e.term), v = this.termResults(e.term, e.term, 1, e.termBoost, p, r, s, l);
|
|
3344
|
+
let y, S;
|
|
3345
|
+
if (e.prefix && (y = this._index.atPrefix(e.term)), e.fuzzy) {
|
|
3346
|
+
const w = e.fuzzy === !0 ? 0.2 : e.fuzzy, _ = w < 1 ? Math.min(o, Math.round(e.term.length * w)) : w;
|
|
3347
|
+
_ && (S = this._index.fuzzyGet(e.term, _));
|
|
3348
|
+
}
|
|
3349
|
+
if (y)
|
|
3350
|
+
for (const [w, _] of y) {
|
|
3351
|
+
const N = w.length - e.term.length;
|
|
3352
|
+
if (!N)
|
|
3353
|
+
continue;
|
|
3354
|
+
S?.delete(w);
|
|
3355
|
+
const F = f * w.length / (w.length + 0.3 * N);
|
|
3356
|
+
this.termResults(e.term, w, F, e.termBoost, _, r, s, l, v);
|
|
3357
|
+
}
|
|
3358
|
+
if (S)
|
|
3359
|
+
for (const w of S.keys()) {
|
|
3360
|
+
const [_, N] = S.get(w);
|
|
3361
|
+
if (!N)
|
|
3362
|
+
continue;
|
|
3363
|
+
const F = c * w.length / (w.length + N);
|
|
3364
|
+
this.termResults(e.term, w, F, e.termBoost, _, r, s, l, v);
|
|
3365
|
+
}
|
|
3366
|
+
return v;
|
|
3367
|
+
}
|
|
3368
|
+
/**
|
|
3369
|
+
* @ignore
|
|
3370
|
+
*/
|
|
3371
|
+
executeWildcardQuery(e) {
|
|
3372
|
+
const t = /* @__PURE__ */ new Map(), n = { ...this._options.searchOptions, ...e };
|
|
3373
|
+
for (const [r, s] of this._documentIds) {
|
|
3374
|
+
const i = n.boostDocument ? n.boostDocument(s, "", this._storedFields.get(r)) : 1;
|
|
3375
|
+
t.set(r, {
|
|
3376
|
+
score: i,
|
|
3377
|
+
terms: [],
|
|
3378
|
+
match: {}
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
return t;
|
|
3382
|
+
}
|
|
3383
|
+
/**
|
|
3384
|
+
* @ignore
|
|
3385
|
+
*/
|
|
3386
|
+
combineResults(e, t = Be) {
|
|
3387
|
+
if (e.length === 0)
|
|
3388
|
+
return /* @__PURE__ */ new Map();
|
|
3389
|
+
const n = t.toLowerCase(), r = Ln[n];
|
|
3390
|
+
if (!r)
|
|
3391
|
+
throw new Error(`Invalid combination operator: ${t}`);
|
|
3392
|
+
return e.reduce(r) || /* @__PURE__ */ new Map();
|
|
3393
|
+
}
|
|
3394
|
+
/**
|
|
3395
|
+
* Allows serialization of the index to JSON, to possibly store it and later
|
|
3396
|
+
* deserialize it with {@link MiniSearch.loadJSON}.
|
|
3397
|
+
*
|
|
3398
|
+
* Normally one does not directly call this method, but rather call the
|
|
3399
|
+
* standard JavaScript `JSON.stringify()` passing the {@link MiniSearch}
|
|
3400
|
+
* instance, and JavaScript will internally call this method. Upon
|
|
3401
|
+
* deserialization, one must pass to {@link MiniSearch.loadJSON} the same
|
|
3402
|
+
* options used to create the original instance that was serialized.
|
|
3403
|
+
*
|
|
3404
|
+
* ### Usage:
|
|
3405
|
+
*
|
|
3406
|
+
* ```javascript
|
|
3407
|
+
* // Serialize the index:
|
|
3408
|
+
* let miniSearch = new MiniSearch({ fields: ['title', 'text'] })
|
|
3409
|
+
* miniSearch.addAll(documents)
|
|
3410
|
+
* const json = JSON.stringify(miniSearch)
|
|
3411
|
+
*
|
|
3412
|
+
* // Later, to deserialize it:
|
|
3413
|
+
* miniSearch = MiniSearch.loadJSON(json, { fields: ['title', 'text'] })
|
|
3414
|
+
* ```
|
|
3415
|
+
*
|
|
3416
|
+
* @return A plain-object serializable representation of the search index.
|
|
3417
|
+
*/
|
|
3418
|
+
toJSON() {
|
|
3419
|
+
const e = [];
|
|
3420
|
+
for (const [t, n] of this._index) {
|
|
3421
|
+
const r = {};
|
|
3422
|
+
for (const [s, i] of n)
|
|
3423
|
+
r[s] = Object.fromEntries(i);
|
|
3424
|
+
e.push([t, r]);
|
|
3425
|
+
}
|
|
3426
|
+
return {
|
|
3427
|
+
documentCount: this._documentCount,
|
|
3428
|
+
nextId: this._nextId,
|
|
3429
|
+
documentIds: Object.fromEntries(this._documentIds),
|
|
3430
|
+
fieldIds: this._fieldIds,
|
|
3431
|
+
fieldLength: Object.fromEntries(this._fieldLength),
|
|
3432
|
+
averageFieldLength: this._avgFieldLength,
|
|
3433
|
+
storedFields: Object.fromEntries(this._storedFields),
|
|
3434
|
+
dirtCount: this._dirtCount,
|
|
3435
|
+
index: e,
|
|
3436
|
+
serializationVersion: 2
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3440
|
+
* @ignore
|
|
3441
|
+
*/
|
|
3442
|
+
termResults(e, t, n, r, s, i, o, l, c = /* @__PURE__ */ new Map()) {
|
|
3443
|
+
if (s == null)
|
|
3444
|
+
return c;
|
|
3445
|
+
for (const f of Object.keys(i)) {
|
|
3446
|
+
const p = i[f], v = this._fieldIds[f], y = s.get(v);
|
|
3447
|
+
if (y == null)
|
|
3448
|
+
continue;
|
|
3449
|
+
let S = y.size;
|
|
3450
|
+
const w = this._avgFieldLength[v];
|
|
3451
|
+
for (const _ of y.keys()) {
|
|
3452
|
+
if (!this._documentIds.has(_)) {
|
|
3453
|
+
this.removeTerm(v, _, t), S -= 1;
|
|
3454
|
+
continue;
|
|
3455
|
+
}
|
|
3456
|
+
const N = o ? o(this._documentIds.get(_), t, this._storedFields.get(_)) : 1;
|
|
3457
|
+
if (!N)
|
|
3458
|
+
continue;
|
|
3459
|
+
const F = y.get(_), G = this._fieldLength.get(_)[v], z = Pn(F, S, this._documentCount, G, w, l), j = n * r * p * N * z, L = c.get(_);
|
|
3460
|
+
if (L) {
|
|
3461
|
+
L.score += j, jn(L.terms, e);
|
|
3462
|
+
const V = Oe(L.match, t);
|
|
3463
|
+
V ? V.push(f) : L.match[t] = [f];
|
|
3464
|
+
} else
|
|
3465
|
+
c.set(_, {
|
|
3466
|
+
score: j,
|
|
3467
|
+
terms: [e],
|
|
3468
|
+
match: { [t]: [f] }
|
|
3469
|
+
});
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
return c;
|
|
3473
|
+
}
|
|
3474
|
+
/**
|
|
3475
|
+
* @ignore
|
|
3476
|
+
*/
|
|
3477
|
+
addTerm(e, t, n) {
|
|
3478
|
+
const r = this._index.fetch(n, lt);
|
|
3479
|
+
let s = r.get(e);
|
|
3480
|
+
if (s == null)
|
|
3481
|
+
s = /* @__PURE__ */ new Map(), s.set(t, 1), r.set(e, s);
|
|
3482
|
+
else {
|
|
3483
|
+
const i = s.get(t);
|
|
3484
|
+
s.set(t, (i || 0) + 1);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
/**
|
|
3488
|
+
* @ignore
|
|
3489
|
+
*/
|
|
3490
|
+
removeTerm(e, t, n) {
|
|
3491
|
+
if (!this._index.has(n)) {
|
|
3492
|
+
this.warnDocumentChanged(t, e, n);
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
3495
|
+
const r = this._index.fetch(n, lt), s = r.get(e);
|
|
3496
|
+
s == null || s.get(t) == null ? this.warnDocumentChanged(t, e, n) : s.get(t) <= 1 ? s.size <= 1 ? r.delete(e) : s.delete(t) : s.set(t, s.get(t) - 1), this._index.get(n).size === 0 && this._index.delete(n);
|
|
3497
|
+
}
|
|
3498
|
+
/**
|
|
3499
|
+
* @ignore
|
|
3500
|
+
*/
|
|
3501
|
+
warnDocumentChanged(e, t, n) {
|
|
3502
|
+
for (const r of Object.keys(this._fieldIds))
|
|
3503
|
+
if (this._fieldIds[r] === t) {
|
|
3504
|
+
this._options.logger("warn", `MiniSearch: document with ID ${this._documentIds.get(e)} has changed before removal: term "${n}" was not present in field "${r}". Removing a document after it has changed can corrupt the index!`, "version_conflict");
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
/**
|
|
3509
|
+
* @ignore
|
|
3510
|
+
*/
|
|
3511
|
+
addDocumentId(e) {
|
|
3512
|
+
const t = this._nextId;
|
|
3513
|
+
return this._idToShortId.set(e, t), this._documentIds.set(t, e), this._documentCount += 1, this._nextId += 1, t;
|
|
3514
|
+
}
|
|
3515
|
+
/**
|
|
3516
|
+
* @ignore
|
|
3517
|
+
*/
|
|
3518
|
+
addFields(e) {
|
|
3519
|
+
for (let t = 0; t < e.length; t++)
|
|
3520
|
+
this._fieldIds[e[t]] = t;
|
|
3521
|
+
}
|
|
3522
|
+
/**
|
|
3523
|
+
* @ignore
|
|
3524
|
+
*/
|
|
3525
|
+
addFieldLength(e, t, n, r) {
|
|
3526
|
+
let s = this._fieldLength.get(e);
|
|
3527
|
+
s == null && this._fieldLength.set(e, s = []), s[t] = r;
|
|
3528
|
+
const o = (this._avgFieldLength[t] || 0) * n + r;
|
|
3529
|
+
this._avgFieldLength[t] = o / (n + 1);
|
|
3530
|
+
}
|
|
3531
|
+
/**
|
|
3532
|
+
* @ignore
|
|
3533
|
+
*/
|
|
3534
|
+
removeFieldLength(e, t, n, r) {
|
|
3535
|
+
if (n === 1) {
|
|
3536
|
+
this._avgFieldLength[t] = 0;
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
const s = this._avgFieldLength[t] * n - r;
|
|
3540
|
+
this._avgFieldLength[t] = s / (n - 1);
|
|
3541
|
+
}
|
|
3542
|
+
/**
|
|
3543
|
+
* @ignore
|
|
3544
|
+
*/
|
|
3545
|
+
saveStoredFields(e, t) {
|
|
3546
|
+
const { storeFields: n, extractField: r } = this._options;
|
|
3547
|
+
if (n == null || n.length === 0)
|
|
3548
|
+
return;
|
|
3549
|
+
let s = this._storedFields.get(e);
|
|
3550
|
+
s == null && this._storedFields.set(e, s = {});
|
|
3551
|
+
for (const i of n) {
|
|
3552
|
+
const o = r(t, i);
|
|
3553
|
+
o !== void 0 && (s[i] = o);
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
re.wildcard = /* @__PURE__ */ Symbol("*");
|
|
3558
|
+
const Oe = (a, e) => Object.prototype.hasOwnProperty.call(a, e) ? a[e] : void 0, Ln = {
|
|
3559
|
+
[Be]: (a, e) => {
|
|
3560
|
+
for (const t of e.keys()) {
|
|
3561
|
+
const n = a.get(t);
|
|
3562
|
+
if (n == null)
|
|
3563
|
+
a.set(t, e.get(t));
|
|
3564
|
+
else {
|
|
3565
|
+
const { score: r, terms: s, match: i } = e.get(t);
|
|
3566
|
+
n.score = n.score + r, n.match = Object.assign(n.match, i), at(n.terms, s);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
return a;
|
|
3570
|
+
},
|
|
3571
|
+
[_t]: (a, e) => {
|
|
3572
|
+
const t = /* @__PURE__ */ new Map();
|
|
3573
|
+
for (const n of e.keys()) {
|
|
3574
|
+
const r = a.get(n);
|
|
3575
|
+
if (r == null)
|
|
3576
|
+
continue;
|
|
3577
|
+
const { score: s, terms: i, match: o } = e.get(n);
|
|
3578
|
+
at(r.terms, i), t.set(n, {
|
|
3579
|
+
score: r.score + s,
|
|
3580
|
+
terms: r.terms,
|
|
3581
|
+
match: Object.assign(r.match, o)
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
return t;
|
|
3585
|
+
},
|
|
3586
|
+
[Mn]: (a, e) => {
|
|
3587
|
+
for (const t of e.keys())
|
|
3588
|
+
a.delete(t);
|
|
3589
|
+
return a;
|
|
3590
|
+
}
|
|
3591
|
+
}, Dn = { k: 1.2, b: 0.7, d: 0.5 }, Pn = (a, e, t, n, r, s) => {
|
|
3592
|
+
const { k: i, b: o, d: l } = s;
|
|
3593
|
+
return Math.log(1 + (t - e + 0.5) / (e + 0.5)) * (l + a * (i + 1) / (a + i * (1 - o + o * n / r)));
|
|
3594
|
+
}, zn = (a) => (e, t, n) => {
|
|
3595
|
+
const r = typeof a.fuzzy == "function" ? a.fuzzy(e, t, n) : a.fuzzy || !1, s = typeof a.prefix == "function" ? a.prefix(e, t, n) : a.prefix === !0, i = typeof a.boostTerm == "function" ? a.boostTerm(e, t, n) : 1;
|
|
3596
|
+
return { term: e, fuzzy: r, prefix: s, termBoost: i };
|
|
3597
|
+
}, Ce = {
|
|
3598
|
+
idField: "id",
|
|
3599
|
+
extractField: (a, e) => a[e],
|
|
3600
|
+
stringifyField: (a, e) => a.toString(),
|
|
3601
|
+
tokenize: (a) => a.split($n),
|
|
3602
|
+
processTerm: (a) => a.toLowerCase(),
|
|
3603
|
+
fields: void 0,
|
|
3604
|
+
searchOptions: void 0,
|
|
3605
|
+
storeFields: [],
|
|
3606
|
+
logger: (a, e) => {
|
|
3607
|
+
typeof console?.[a] == "function" && console[a](e);
|
|
3608
|
+
},
|
|
3609
|
+
autoVacuum: !0
|
|
3610
|
+
}, st = {
|
|
3611
|
+
combineWith: Be,
|
|
3612
|
+
prefix: !1,
|
|
3613
|
+
fuzzy: !1,
|
|
3614
|
+
maxFuzzy: 6,
|
|
3615
|
+
boost: {},
|
|
3616
|
+
weights: { fuzzy: 0.45, prefix: 0.375 },
|
|
3617
|
+
bm25: Dn
|
|
3618
|
+
}, Vn = {
|
|
3619
|
+
combineWith: _t,
|
|
3620
|
+
prefix: (a, e, t) => e === t.length - 1
|
|
3621
|
+
}, Ve = { batchSize: 1e3, batchWait: 10 }, je = { minDirtFactor: 0.1, minDirtCount: 20 }, Me = { ...Ve, ...je }, jn = (a, e) => {
|
|
3622
|
+
a.includes(e) || a.push(e);
|
|
3623
|
+
}, at = (a, e) => {
|
|
3624
|
+
for (const t of e)
|
|
3625
|
+
a.includes(t) || a.push(t);
|
|
3626
|
+
}, ot = ({ score: a }, { score: e }) => e - a, lt = () => /* @__PURE__ */ new Map(), ye = (a) => {
|
|
3627
|
+
const e = /* @__PURE__ */ new Map();
|
|
3628
|
+
for (const t of Object.keys(a))
|
|
3629
|
+
e.set(parseInt(t, 10), a[t]);
|
|
3630
|
+
return e;
|
|
3631
|
+
}, we = async (a) => {
|
|
3632
|
+
const e = /* @__PURE__ */ new Map();
|
|
3633
|
+
let t = 0;
|
|
3634
|
+
for (const n of Object.keys(a))
|
|
3635
|
+
e.set(parseInt(n, 10), a[n]), ++t % 1e3 === 0 && await xt(0);
|
|
3636
|
+
return e;
|
|
3637
|
+
}, xt = (a) => new Promise((e) => setTimeout(e, a)), $n = /[\n\r\p{Z}\p{P}]+/u;
|
|
3638
|
+
Z(Et(Yt));
|
|
3639
|
+
function Bn(a) {
|
|
3640
|
+
let e = a.replace(/\.html$/, "");
|
|
3641
|
+
if (e = decodeURIComponent(e), e = e.replace(/\/$/, "/index"), jt) {
|
|
3642
|
+
e = Ze(e.slice(1).replace(/\//g, "_") || "index") + ".md";
|
|
3643
|
+
let n = __VP_HASH_MAP__[e.toLowerCase()];
|
|
3644
|
+
if (n || (e = e.endsWith("_index.md") ? e.slice(0, -9) + ".md" : e.slice(0, -3) + "_index.md", n = __VP_HASH_MAP__[e.toLowerCase()]), !n)
|
|
3645
|
+
return null;
|
|
3646
|
+
e = `/${__ASSETS_DIR__}/${e}.${n}.js`;
|
|
3647
|
+
} else
|
|
3648
|
+
e = `./${Ze(e.slice(1).replace(/\//g, "_"))}.md.js`;
|
|
3649
|
+
return e;
|
|
3650
|
+
}
|
|
3651
|
+
class Wn {
|
|
3652
|
+
max;
|
|
3653
|
+
cache;
|
|
3654
|
+
constructor(e = 10) {
|
|
3655
|
+
this.max = e, this.cache = /* @__PURE__ */ new Map();
|
|
3656
|
+
}
|
|
3657
|
+
get(e) {
|
|
3658
|
+
let t = this.cache.get(e);
|
|
3659
|
+
return t !== void 0 && (this.cache.delete(e), this.cache.set(e, t)), t;
|
|
3660
|
+
}
|
|
3661
|
+
set(e, t) {
|
|
3662
|
+
this.cache.has(e) ? this.cache.delete(e) : this.cache.size === this.max && this.cache.delete(this.first()), this.cache.set(e, t);
|
|
3663
|
+
}
|
|
3664
|
+
first() {
|
|
3665
|
+
return this.cache.keys().next().value;
|
|
3666
|
+
}
|
|
3667
|
+
clear() {
|
|
3668
|
+
this.cache.clear();
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
function Kn(a) {
|
|
3672
|
+
const { localeIndex: e, theme: t } = ct();
|
|
3673
|
+
function n(r) {
|
|
3674
|
+
const s = r.split("."), i = t.value.search?.options, o = i && typeof i == "object", l = o && i.locales?.[e.value]?.translations || null, c = o && i.translations || null;
|
|
3675
|
+
let f = l, p = c, v = a;
|
|
3676
|
+
const y = s.pop();
|
|
3677
|
+
for (const S of s) {
|
|
3678
|
+
let w = null;
|
|
3679
|
+
const _ = v?.[S];
|
|
3680
|
+
_ && (w = v = _);
|
|
3681
|
+
const N = p?.[S];
|
|
3682
|
+
N && (w = p = N);
|
|
3683
|
+
const F = f?.[S];
|
|
3684
|
+
F && (w = f = F), _ || (v = w), N || (p = w), F || (f = w);
|
|
3685
|
+
}
|
|
3686
|
+
return f?.[y] ?? p?.[y] ?? v?.[y] ?? "";
|
|
3687
|
+
}
|
|
3688
|
+
return n;
|
|
3689
|
+
}
|
|
3690
|
+
const Jn = ["aria-owns"], Un = { class: "shell" }, Hn = ["title"], qn = { class: "search-actions before" }, Gn = ["title"], Qn = ["aria-activedescendant", "aria-controls", "placeholder"], Yn = { class: "search-actions" }, Zn = ["title"], Xn = ["disabled", "title"], ei = ["id", "role", "aria-labelledby"], ti = ["id", "aria-selected"], ni = ["href", "aria-label", "onMouseenter", "onFocusin", "data-index"], ii = { class: "titles" }, ri = ["innerHTML"], si = { class: "title main" }, ai = ["innerHTML"], oi = {
|
|
3691
|
+
key: 0,
|
|
3692
|
+
class: "excerpt-wrapper"
|
|
3693
|
+
}, li = {
|
|
3694
|
+
key: 0,
|
|
3695
|
+
class: "excerpt",
|
|
3696
|
+
inert: ""
|
|
3697
|
+
}, ci = ["innerHTML"], ui = {
|
|
3698
|
+
key: 0,
|
|
3699
|
+
class: "no-results"
|
|
3700
|
+
}, di = { class: "search-keyboard-shortcuts" }, fi = ["aria-label"], hi = ["aria-label"], pi = ["aria-label"], vi = ["aria-label"], mi = /* @__PURE__ */ Tt({
|
|
3701
|
+
__name: "VPLocalSearchBox",
|
|
3702
|
+
emits: ["close"],
|
|
3703
|
+
setup(a, { emit: e }) {
|
|
3704
|
+
const t = e, n = Z(), r = Z(), s = Z(Lt), i = ct(), { activate: o } = Nn(n, {
|
|
3705
|
+
immediate: !0,
|
|
3706
|
+
allowOutsideClick: !0,
|
|
3707
|
+
clickOutsideDeactivates: !0,
|
|
3708
|
+
escapeDeactivates: !0
|
|
3709
|
+
}), { localeIndex: l, theme: c } = i, f = Xe(
|
|
3710
|
+
async () => Ye(
|
|
3711
|
+
re.loadJSON(
|
|
3712
|
+
(await s.value[l.value]?.())?.default,
|
|
3713
|
+
{
|
|
3714
|
+
fields: ["title", "titles", "text"],
|
|
3715
|
+
storeFields: ["title", "titles"],
|
|
3716
|
+
searchOptions: {
|
|
3717
|
+
fuzzy: 0.2,
|
|
3718
|
+
prefix: !0,
|
|
3719
|
+
boost: { title: 4, text: 2, titles: 1 },
|
|
3720
|
+
...c.value.search?.provider === "local" && c.value.search.options?.miniSearch?.searchOptions
|
|
3721
|
+
},
|
|
3722
|
+
...c.value.search?.provider === "local" && c.value.search.options?.miniSearch?.options
|
|
3723
|
+
}
|
|
3724
|
+
)
|
|
3725
|
+
)
|
|
3726
|
+
), v = Se(() => c.value.search?.provider === "local" && c.value.search.options?.disableQueryPersistence === !0).value ? ue("") : $t("vitepress:local-search-filter", ""), y = Bt(
|
|
3727
|
+
"vitepress:local-search-detailed-list",
|
|
3728
|
+
c.value.search?.provider === "local" && c.value.search.options?.detailedView === !0
|
|
3729
|
+
), S = Se(() => c.value.search?.provider === "local" && (c.value.search.options?.disableDetailedView === !0 || c.value.search.options?.detailedView === !1));
|
|
3730
|
+
It(() => {
|
|
3731
|
+
S.value && (y.value = !1);
|
|
3732
|
+
});
|
|
3733
|
+
const w = Z([]), _ = ue(!1);
|
|
3734
|
+
Le(v, () => {
|
|
3735
|
+
_.value = !1;
|
|
3736
|
+
});
|
|
3737
|
+
const N = Xe(async () => {
|
|
3738
|
+
if (r.value)
|
|
3739
|
+
return Ye(new Rn(r.value));
|
|
3740
|
+
}, null), F = new Wn(16);
|
|
3741
|
+
Wt(
|
|
3742
|
+
() => [f.value, v.value, y.value],
|
|
3743
|
+
async ([m, h, g], E, k) => {
|
|
3744
|
+
E?.[0] !== m && F.clear();
|
|
3745
|
+
let R = !1;
|
|
3746
|
+
if (k(() => {
|
|
3747
|
+
R = !0;
|
|
3748
|
+
}), !m) return;
|
|
3749
|
+
w.value = m.search(h).slice(0, 16), _.value = !0;
|
|
3750
|
+
const P = g ? await Promise.all(w.value.map((A) => G(A.id))) : [];
|
|
3751
|
+
if (R) return;
|
|
3752
|
+
for (const { id: A, mod: J } of P) {
|
|
3753
|
+
const U = A.slice(0, A.indexOf("#"));
|
|
3754
|
+
let oe = F.get(U);
|
|
3755
|
+
if (oe) continue;
|
|
3756
|
+
oe = /* @__PURE__ */ new Map(), F.set(U, oe);
|
|
3757
|
+
const le = J.default ?? J;
|
|
3758
|
+
if (le?.render || le?.setup) {
|
|
3759
|
+
const Q = Mt(le);
|
|
3760
|
+
Q.config.warnHandler = () => {
|
|
3761
|
+
}, Q.provide(Gt, i), Object.defineProperties(Q.config.globalProperties, {
|
|
3762
|
+
$frontmatter: {
|
|
3763
|
+
get() {
|
|
3764
|
+
return i.frontmatter.value;
|
|
3765
|
+
}
|
|
3766
|
+
},
|
|
3767
|
+
$params: {
|
|
3768
|
+
get() {
|
|
3769
|
+
return i.page.value.params;
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
});
|
|
3773
|
+
const We = document.createElement("div");
|
|
3774
|
+
Q.mount(We), We.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((ce) => {
|
|
3775
|
+
const Ke = ce.querySelector("a")?.getAttribute("href"), Je = Ke?.startsWith("#") && Ke.slice(1);
|
|
3776
|
+
if (!Je) return;
|
|
3777
|
+
let Ue = "";
|
|
3778
|
+
for (; (ce = ce.nextElementSibling) && !/^h[1-6]$/i.test(ce.tagName); )
|
|
3779
|
+
Ue += ce.outerHTML;
|
|
3780
|
+
oe.set(Je, Ue);
|
|
3781
|
+
}), Q.unmount();
|
|
3782
|
+
}
|
|
3783
|
+
if (R) return;
|
|
3784
|
+
}
|
|
3785
|
+
const D = /* @__PURE__ */ new Set();
|
|
3786
|
+
if (w.value = w.value.map((A) => {
|
|
3787
|
+
const [J, U] = A.id.split("#"), le = F.get(J)?.get(U) ?? "";
|
|
3788
|
+
for (const Q in A.match)
|
|
3789
|
+
D.add(Q);
|
|
3790
|
+
return { ...A, text: le };
|
|
3791
|
+
}), await de(), R) return;
|
|
3792
|
+
await new Promise((A) => {
|
|
3793
|
+
N.value?.unmark({
|
|
3794
|
+
done: () => {
|
|
3795
|
+
N.value?.markRegExp(b(D), { done: A });
|
|
3796
|
+
}
|
|
3797
|
+
});
|
|
3798
|
+
});
|
|
3799
|
+
const $ = n.value?.querySelectorAll(".result .excerpt") ?? [];
|
|
3800
|
+
for (const A of $)
|
|
3801
|
+
A.querySelector('mark[data-markjs="true"]')?.scrollIntoView({ block: "center" });
|
|
3802
|
+
r.value?.firstElementChild?.scrollIntoView({ block: "start" });
|
|
3803
|
+
},
|
|
3804
|
+
{ debounce: 200, immediate: !0 }
|
|
3805
|
+
);
|
|
3806
|
+
async function G(m) {
|
|
3807
|
+
const h = Bn(m.slice(0, m.indexOf("#")));
|
|
3808
|
+
try {
|
|
3809
|
+
if (!h) throw new Error(`Cannot find file for id: ${m}`);
|
|
3810
|
+
return { id: m, mod: await import(
|
|
3811
|
+
/*@vite-ignore*/
|
|
3812
|
+
h
|
|
3813
|
+
) };
|
|
3814
|
+
} catch (g) {
|
|
3815
|
+
return console.error(g), { id: m, mod: {} };
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
const z = ue(), j = Se(() => v.value?.length <= 0);
|
|
3819
|
+
function L(m = !0) {
|
|
3820
|
+
z.value?.focus(), m && z.value?.select();
|
|
3821
|
+
}
|
|
3822
|
+
Ne(() => {
|
|
3823
|
+
L();
|
|
3824
|
+
});
|
|
3825
|
+
function V(m) {
|
|
3826
|
+
m.pointerType === "mouse" && L();
|
|
3827
|
+
}
|
|
3828
|
+
const O = ue(-1), K = ue(!0);
|
|
3829
|
+
Le(w, (m) => {
|
|
3830
|
+
O.value = m.length ? 0 : -1, se();
|
|
3831
|
+
});
|
|
3832
|
+
function se() {
|
|
3833
|
+
de(() => {
|
|
3834
|
+
document.querySelector(".result.selected")?.scrollIntoView({ block: "nearest" });
|
|
3835
|
+
});
|
|
3836
|
+
}
|
|
3837
|
+
ge("ArrowUp", (m) => {
|
|
3838
|
+
m.preventDefault(), O.value--, O.value < 0 && (O.value = w.value.length - 1), K.value = !0, se();
|
|
3839
|
+
}), ge("ArrowDown", (m) => {
|
|
3840
|
+
m.preventDefault(), O.value++, O.value >= w.value.length && (O.value = 0), K.value = !0, se();
|
|
3841
|
+
});
|
|
3842
|
+
const ae = qt();
|
|
3843
|
+
ge("Enter", (m) => {
|
|
3844
|
+
if (m.isComposing || m.target instanceof HTMLButtonElement && m.target.type !== "submit")
|
|
3845
|
+
return;
|
|
3846
|
+
const h = w.value[O.value];
|
|
3847
|
+
if (m.target instanceof HTMLInputElement && !h) {
|
|
3848
|
+
m.preventDefault();
|
|
3849
|
+
return;
|
|
3850
|
+
}
|
|
3851
|
+
h && (ae.go(h.id), t("close"));
|
|
3852
|
+
}), ge("Escape", () => {
|
|
3853
|
+
t("close");
|
|
3854
|
+
});
|
|
3855
|
+
const x = Kn({
|
|
3856
|
+
button: {
|
|
3857
|
+
buttonText: "Search"
|
|
3858
|
+
},
|
|
3859
|
+
modal: {
|
|
3860
|
+
displayDetails: "Display detailed list",
|
|
3861
|
+
resetButtonTitle: "Reset search",
|
|
3862
|
+
backButtonTitle: "Close search",
|
|
3863
|
+
noResultsText: "No results for",
|
|
3864
|
+
footer: {
|
|
3865
|
+
selectText: "to select",
|
|
3866
|
+
selectKeyAriaLabel: "enter",
|
|
3867
|
+
navigateText: "to navigate",
|
|
3868
|
+
navigateUpKeyAriaLabel: "up arrow",
|
|
3869
|
+
navigateDownKeyAriaLabel: "down arrow",
|
|
3870
|
+
closeText: "to close",
|
|
3871
|
+
closeKeyAriaLabel: "escape"
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
});
|
|
3875
|
+
Ne(() => {
|
|
3876
|
+
window.history.pushState(null, "", null);
|
|
3877
|
+
}), Kt("popstate", (m) => {
|
|
3878
|
+
m.preventDefault(), t("close");
|
|
3879
|
+
});
|
|
3880
|
+
const u = Jt(Qt ? document.body : null);
|
|
3881
|
+
Ne(() => {
|
|
3882
|
+
de(() => {
|
|
3883
|
+
u.value = !0, de().then(() => o());
|
|
3884
|
+
});
|
|
3885
|
+
}), kt(() => {
|
|
3886
|
+
u.value = !1;
|
|
3887
|
+
});
|
|
3888
|
+
function d() {
|
|
3889
|
+
v.value = "", de().then(() => L(!1));
|
|
3890
|
+
}
|
|
3891
|
+
function b(m) {
|
|
3892
|
+
return new RegExp(
|
|
3893
|
+
[...m].sort((h, g) => g.length - h.length).map((h) => `(${Ut(h)})`).join("|"),
|
|
3894
|
+
"gi"
|
|
3895
|
+
);
|
|
3896
|
+
}
|
|
3897
|
+
function I(m) {
|
|
3898
|
+
if (!K.value) return;
|
|
3899
|
+
const h = m.target?.closest(".result"), g = Number.parseInt(h?.dataset.index);
|
|
3900
|
+
g >= 0 && g !== O.value && (O.value = g), K.value = !1;
|
|
3901
|
+
}
|
|
3902
|
+
return (m, h) => (B(), Nt(Ft, { to: "body" }, [
|
|
3903
|
+
T("div", {
|
|
3904
|
+
ref_key: "el",
|
|
3905
|
+
ref: n,
|
|
3906
|
+
role: "button",
|
|
3907
|
+
"aria-owns": w.value?.length ? "localsearch-list" : void 0,
|
|
3908
|
+
"aria-expanded": "true",
|
|
3909
|
+
"aria-haspopup": "listbox",
|
|
3910
|
+
"aria-labelledby": "localsearch-label",
|
|
3911
|
+
class: "VPLocalSearchBox"
|
|
3912
|
+
}, [
|
|
3913
|
+
T("div", {
|
|
3914
|
+
class: "backdrop",
|
|
3915
|
+
onClick: h[0] || (h[0] = (g) => m.$emit("close"))
|
|
3916
|
+
}),
|
|
3917
|
+
T("div", Un, [
|
|
3918
|
+
T("form", {
|
|
3919
|
+
class: "search-bar",
|
|
3920
|
+
onPointerup: h[4] || (h[4] = (g) => V(g)),
|
|
3921
|
+
onSubmit: h[5] || (h[5] = Rt(() => {
|
|
3922
|
+
}, ["prevent"]))
|
|
3923
|
+
}, [
|
|
3924
|
+
T("label", {
|
|
3925
|
+
title: C(x)("button.buttonText"),
|
|
3926
|
+
id: "localsearch-label",
|
|
3927
|
+
for: "localsearch-input"
|
|
3928
|
+
}, [...h[7] || (h[7] = [
|
|
3929
|
+
T("span", {
|
|
3930
|
+
"aria-hidden": "true",
|
|
3931
|
+
class: "vpi-search search-icon local-search-icon"
|
|
3932
|
+
}, null, -1)
|
|
3933
|
+
])], 8, Hn),
|
|
3934
|
+
T("div", qn, [
|
|
3935
|
+
T("button", {
|
|
3936
|
+
class: "back-button",
|
|
3937
|
+
title: C(x)("modal.backButtonTitle"),
|
|
3938
|
+
onClick: h[1] || (h[1] = (g) => m.$emit("close"))
|
|
3939
|
+
}, [...h[8] || (h[8] = [
|
|
3940
|
+
T("span", { class: "vpi-arrow-left local-search-icon" }, null, -1)
|
|
3941
|
+
])], 8, Gn)
|
|
3942
|
+
]),
|
|
3943
|
+
At(T("input", {
|
|
3944
|
+
ref_key: "searchInput",
|
|
3945
|
+
ref: z,
|
|
3946
|
+
"onUpdate:modelValue": h[2] || (h[2] = (g) => Ot(v) ? v.value = g : null),
|
|
3947
|
+
"aria-activedescendant": O.value > -1 ? "localsearch-item-" + O.value : void 0,
|
|
3948
|
+
"aria-autocomplete": "both",
|
|
3949
|
+
"aria-controls": w.value?.length ? "localsearch-list" : void 0,
|
|
3950
|
+
"aria-labelledby": "localsearch-label",
|
|
3951
|
+
autocapitalize: "off",
|
|
3952
|
+
autocomplete: "off",
|
|
3953
|
+
autocorrect: "off",
|
|
3954
|
+
class: "search-input",
|
|
3955
|
+
id: "localsearch-input",
|
|
3956
|
+
enterkeyhint: "go",
|
|
3957
|
+
maxlength: "64",
|
|
3958
|
+
placeholder: C(x)("button.buttonText"),
|
|
3959
|
+
spellcheck: "false",
|
|
3960
|
+
type: "search"
|
|
3961
|
+
}, null, 8, Qn), [
|
|
3962
|
+
[Ct, C(v)]
|
|
3963
|
+
]),
|
|
3964
|
+
T("div", Yn, [
|
|
3965
|
+
S.value ? me("", !0) : (B(), H("button", {
|
|
3966
|
+
key: 0,
|
|
3967
|
+
class: qe(["toggle-layout-button", { "detailed-list": C(y) }]),
|
|
3968
|
+
type: "button",
|
|
3969
|
+
title: C(x)("modal.displayDetails"),
|
|
3970
|
+
onClick: h[3] || (h[3] = (g) => O.value > -1 && (y.value = !C(y)))
|
|
3971
|
+
}, [...h[9] || (h[9] = [
|
|
3972
|
+
T("span", { class: "vpi-layout-list local-search-icon" }, null, -1)
|
|
3973
|
+
])], 10, Zn)),
|
|
3974
|
+
T("button", {
|
|
3975
|
+
class: "clear-button",
|
|
3976
|
+
type: "reset",
|
|
3977
|
+
disabled: j.value,
|
|
3978
|
+
title: C(x)("modal.resetButtonTitle"),
|
|
3979
|
+
onClick: d
|
|
3980
|
+
}, [...h[10] || (h[10] = [
|
|
3981
|
+
T("span", { class: "vpi-delete local-search-icon" }, null, -1)
|
|
3982
|
+
])], 8, Xn)
|
|
3983
|
+
])
|
|
3984
|
+
], 32),
|
|
3985
|
+
T("ul", {
|
|
3986
|
+
ref_key: "resultsEl",
|
|
3987
|
+
ref: r,
|
|
3988
|
+
id: w.value?.length ? "localsearch-list" : void 0,
|
|
3989
|
+
role: w.value?.length ? "listbox" : void 0,
|
|
3990
|
+
"aria-labelledby": w.value?.length ? "localsearch-label" : void 0,
|
|
3991
|
+
class: "results",
|
|
3992
|
+
onMousemove: I
|
|
3993
|
+
}, [
|
|
3994
|
+
(B(!0), H(Ge, null, Qe(w.value, (g, E) => (B(), H("li", {
|
|
3995
|
+
key: g.id,
|
|
3996
|
+
id: "localsearch-item-" + E,
|
|
3997
|
+
"aria-selected": O.value === E ? "true" : "false",
|
|
3998
|
+
role: "option"
|
|
3999
|
+
}, [
|
|
4000
|
+
T("a", {
|
|
4001
|
+
href: g.id,
|
|
4002
|
+
class: qe(["result", {
|
|
4003
|
+
selected: O.value === E
|
|
4004
|
+
}]),
|
|
4005
|
+
"aria-label": [...g.titles, g.title].join(" > "),
|
|
4006
|
+
onMouseenter: (k) => !K.value && (O.value = E),
|
|
4007
|
+
onFocusin: (k) => O.value = E,
|
|
4008
|
+
onClick: h[6] || (h[6] = (k) => m.$emit("close")),
|
|
4009
|
+
"data-index": E
|
|
4010
|
+
}, [
|
|
4011
|
+
T("div", null, [
|
|
4012
|
+
T("div", ii, [
|
|
4013
|
+
h[12] || (h[12] = T("span", { class: "title-icon" }, "#", -1)),
|
|
4014
|
+
(B(!0), H(Ge, null, Qe(g.titles, (k, R) => (B(), H("span", {
|
|
4015
|
+
key: R,
|
|
4016
|
+
class: "title"
|
|
4017
|
+
}, [
|
|
4018
|
+
T("span", {
|
|
4019
|
+
class: "text",
|
|
4020
|
+
innerHTML: k
|
|
4021
|
+
}, null, 8, ri),
|
|
4022
|
+
h[11] || (h[11] = T("span", { class: "vpi-chevron-right local-search-icon" }, null, -1))
|
|
4023
|
+
]))), 128)),
|
|
4024
|
+
T("span", si, [
|
|
4025
|
+
T("span", {
|
|
4026
|
+
class: "text",
|
|
4027
|
+
innerHTML: g.title
|
|
4028
|
+
}, null, 8, ai)
|
|
4029
|
+
])
|
|
4030
|
+
]),
|
|
4031
|
+
C(y) ? (B(), H("div", oi, [
|
|
4032
|
+
g.text ? (B(), H("div", li, [
|
|
4033
|
+
T("div", {
|
|
4034
|
+
class: "vp-doc",
|
|
4035
|
+
innerHTML: g.text
|
|
4036
|
+
}, null, 8, ci)
|
|
4037
|
+
])) : me("", !0),
|
|
4038
|
+
h[13] || (h[13] = T("div", { class: "excerpt-gradient-bottom" }, null, -1)),
|
|
4039
|
+
h[14] || (h[14] = T("div", { class: "excerpt-gradient-top" }, null, -1))
|
|
4040
|
+
])) : me("", !0)
|
|
4041
|
+
])
|
|
4042
|
+
], 42, ni)
|
|
4043
|
+
], 8, ti))), 128)),
|
|
4044
|
+
C(v) && !w.value.length && _.value ? (B(), H("li", ui, [
|
|
4045
|
+
fe(he(C(x)("modal.noResultsText")) + ' "', 1),
|
|
4046
|
+
T("strong", null, he(C(v)), 1),
|
|
4047
|
+
h[15] || (h[15] = fe('" ', -1))
|
|
4048
|
+
])) : me("", !0)
|
|
4049
|
+
], 40, ei),
|
|
4050
|
+
T("div", di, [
|
|
4051
|
+
T("span", null, [
|
|
4052
|
+
T("kbd", {
|
|
4053
|
+
"aria-label": C(x)("modal.footer.navigateUpKeyAriaLabel")
|
|
4054
|
+
}, [...h[16] || (h[16] = [
|
|
4055
|
+
T("span", { class: "vpi-arrow-up navigate-icon" }, null, -1)
|
|
4056
|
+
])], 8, fi),
|
|
4057
|
+
T("kbd", {
|
|
4058
|
+
"aria-label": C(x)("modal.footer.navigateDownKeyAriaLabel")
|
|
4059
|
+
}, [...h[17] || (h[17] = [
|
|
4060
|
+
T("span", { class: "vpi-arrow-down navigate-icon" }, null, -1)
|
|
4061
|
+
])], 8, hi),
|
|
4062
|
+
fe(" " + he(C(x)("modal.footer.navigateText")), 1)
|
|
4063
|
+
]),
|
|
4064
|
+
T("span", null, [
|
|
4065
|
+
T("kbd", {
|
|
4066
|
+
"aria-label": C(x)("modal.footer.selectKeyAriaLabel")
|
|
4067
|
+
}, [...h[18] || (h[18] = [
|
|
4068
|
+
T("span", { class: "vpi-corner-down-left navigate-icon" }, null, -1)
|
|
4069
|
+
])], 8, pi),
|
|
4070
|
+
fe(" " + he(C(x)("modal.footer.selectText")), 1)
|
|
4071
|
+
]),
|
|
4072
|
+
T("span", null, [
|
|
4073
|
+
T("kbd", {
|
|
4074
|
+
"aria-label": C(x)("modal.footer.closeKeyAriaLabel")
|
|
4075
|
+
}, "esc", 8, vi),
|
|
4076
|
+
fe(" " + he(C(x)("modal.footer.closeText")), 1)
|
|
4077
|
+
])
|
|
4078
|
+
])
|
|
4079
|
+
])
|
|
4080
|
+
], 8, Jn)
|
|
4081
|
+
]));
|
|
4082
|
+
}
|
|
4083
|
+
}), Ei = /* @__PURE__ */ Ht(mi, [["__scopeId", "data-v-b3462fe7"]]);
|
|
4084
|
+
export {
|
|
4085
|
+
Ei as default
|
|
4086
|
+
};
|