@trishchuk/coolors-mcp 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.github/workflows/ci.yml +23 -20
  2. package/.github/workflows/deploy-docs.yml +6 -3
  3. package/.github/workflows/release.yml +11 -9
  4. package/README.md +123 -14
  5. package/dist/bin/server.js +997 -256
  6. package/dist/bin/server.js.map +1 -1
  7. package/dist/{chunk-P3ARRKLS.js → chunk-HOMDMKUY.js} +3 -1
  8. package/dist/{chunk-P3ARRKLS.js.map → chunk-HOMDMKUY.js.map} +1 -1
  9. package/dist/{chunk-IQ7NN26V.js → chunk-LHW2ZTOU.js} +14 -2
  10. package/dist/chunk-LHW2ZTOU.js.map +1 -0
  11. package/dist/color/index.js +1 -1
  12. package/dist/coolors-mcp.d.ts +4 -4
  13. package/dist/coolors-mcp.js +1 -1
  14. package/eslint.config.ts +13 -0
  15. package/jsr.json +1 -1
  16. package/package.json +16 -12
  17. package/src/bin/server.ts +13 -1
  18. package/src/color/__tests__/extract-colors.test.ts +20 -30
  19. package/src/color/apca.ts +105 -0
  20. package/src/color/color-blindness.ts +109 -0
  21. package/src/coolors-mcp.ts +1 -1
  22. package/src/session.ts +10 -2
  23. package/src/theme/matcher.ts +1 -1
  24. package/src/theme/refactor.ts +1 -1
  25. package/src/theme/types.ts +3 -0
  26. package/src/tools/__tests__/cohesion.test.ts +97 -0
  27. package/src/tools/__tests__/color-blindness.test.ts +45 -0
  28. package/src/tools/__tests__/color-conversion.test.ts +38 -0
  29. package/src/tools/__tests__/contrast-checker.test.ts +56 -0
  30. package/src/tools/__tests__/palette-export.test.ts +54 -0
  31. package/src/tools/adjust-color.tool.ts +80 -0
  32. package/src/tools/cohesion.tools.ts +380 -0
  33. package/src/tools/color-blindness.tool.ts +168 -0
  34. package/src/tools/color-conversion.tool.ts +1 -1
  35. package/src/tools/contrast-checker.tool.ts +53 -14
  36. package/src/tools/dislike-analyzer.tool.ts +41 -54
  37. package/src/tools/image-extraction.tools.ts +62 -115
  38. package/src/tools/index.ts +15 -2
  39. package/src/tools/palette-export.tool.ts +174 -0
  40. package/src/tools/palette-with-locks.tool.ts +8 -6
  41. package/src/types.ts +2 -3
  42. package/tsconfig.json +12 -2
  43. package/vitest.config.js +1 -3
  44. package/.claude/settings.local.json +0 -35
  45. package/.env +0 -2
  46. package/.mcp.json +0 -12
  47. package/CLAUDE.md +0 -201
  48. package/DOCUMENTATION.md +0 -274
  49. package/GEMINI.md +0 -54
  50. package/TOOLS_UK.md +0 -233
  51. package/demo/content_based_color.png +0 -0
  52. package/demo/music-player.html +0 -621
  53. package/demo/podcast-player.html +0 -903
  54. package/dist/chunk-IQ7NN26V.js.map +0 -1
  55. package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js +0 -111
  56. package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js.map +0 -7
  57. package/docs/.vitepress/cache/deps/_metadata.json +0 -127
  58. package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js +0 -12
  59. package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +0 -7
  60. package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js +0 -13614
  61. package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js.map +0 -7
  62. package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js +0 -10698
  63. package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js.map +0 -7
  64. package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js +0 -5609
  65. package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +0 -7
  66. package/docs/.vitepress/cache/deps/cytoscape.js +0 -36234
  67. package/docs/.vitepress/cache/deps/cytoscape.js.map +0 -7
  68. package/docs/.vitepress/cache/deps/dayjs.js +0 -507
  69. package/docs/.vitepress/cache/deps/dayjs.js.map +0 -7
  70. package/docs/.vitepress/cache/deps/debug.js +0 -512
  71. package/docs/.vitepress/cache/deps/debug.js.map +0 -7
  72. package/docs/.vitepress/cache/deps/package.json +0 -3
  73. package/docs/.vitepress/cache/deps/prismjs.js +0 -1638
  74. package/docs/.vitepress/cache/deps/prismjs.js.map +0 -7
  75. package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js +0 -235
  76. package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js.map +0 -7
  77. package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js +0 -173
  78. package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js.map +0 -7
  79. package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js +0 -27
  80. package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js.map +0 -7
  81. package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js +0 -72
  82. package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js.map +0 -7
  83. package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js +0 -56
  84. package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js.map +0 -7
  85. package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js +0 -107
  86. package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js.map +0 -7
  87. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +0 -5074
  88. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +0 -7
  89. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +0 -584
  90. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +0 -7
  91. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +0 -1483
  92. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +0 -7
  93. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +0 -1779
  94. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +0 -7
  95. package/docs/.vitepress/cache/deps/vitepress___minisearch.js +0 -2023
  96. package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +0 -7
  97. package/docs/.vitepress/cache/deps/vue.js +0 -344
  98. package/docs/.vitepress/cache/deps/vue.js.map +0 -7
  99. package/examples/theme-matching.md +0 -113
  100. package/mcp-config.json +0 -8
  101. package/note.md +0 -34
  102. package/research_results.md +0 -53
  103. package/src/tools/colors.ts +0 -31
  104. package/src/tools/registry.ts +0 -142
  105. package/src/tools/simple-tools.ts +0 -37
@@ -1,1779 +0,0 @@
1
- import "./chunk-BUSYA2B4.js";
2
-
3
- // node_modules/mark.js/src/lib/domiterator.js
4
- var DOMIterator = class _DOMIterator {
5
- /**
6
- * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
7
- * element, an array of DOM elements, a NodeList or a selector
8
- * @param {boolean} [iframes=true] - A boolean indicating if iframes should
9
- * be handled
10
- * @param {string[]} [exclude=[]] - An array containing exclusion selectors
11
- * for iframes
12
- * @param {number} [iframesTimeout=5000] - A number indicating the ms to
13
- * wait before an iframe should be skipped, in case the load event isn't
14
- * fired. This also applies if the user is offline and the resource of the
15
- * iframe is online (either by the browsers "offline" mode or because
16
- * there's no internet connection)
17
- */
18
- constructor(ctx, iframes = true, exclude = [], iframesTimeout = 5e3) {
19
- this.ctx = ctx;
20
- this.iframes = iframes;
21
- this.exclude = exclude;
22
- this.iframesTimeout = iframesTimeout;
23
- }
24
- /**
25
- * Checks if the specified DOM element matches the selector
26
- * @param {HTMLElement} element - The DOM element
27
- * @param {string|string[]} selector - The selector or an array with
28
- * selectors
29
- * @return {boolean}
30
- * @access public
31
- */
32
- static matches(element, selector) {
33
- const selectors = typeof selector === "string" ? [selector] : selector,
34
- fn =
35
- element.matches ||
36
- element.matchesSelector ||
37
- element.msMatchesSelector ||
38
- element.mozMatchesSelector ||
39
- element.oMatchesSelector ||
40
- element.webkitMatchesSelector;
41
- if (fn) {
42
- let match = false;
43
- selectors.every((sel) => {
44
- if (fn.call(element, sel)) {
45
- match = true;
46
- return false;
47
- }
48
- return true;
49
- });
50
- return match;
51
- } else {
52
- return false;
53
- }
54
- }
55
- /**
56
- * Returns all contexts filtered by duplicates (even nested)
57
- * @return {HTMLElement[]} - An array containing DOM contexts
58
- * @access protected
59
- */
60
- getContexts() {
61
- let ctx,
62
- filteredCtx = [];
63
- if (typeof this.ctx === "undefined" || !this.ctx) {
64
- ctx = [];
65
- } else if (NodeList.prototype.isPrototypeOf(this.ctx)) {
66
- ctx = Array.prototype.slice.call(this.ctx);
67
- } else if (Array.isArray(this.ctx)) {
68
- ctx = this.ctx;
69
- } else if (typeof this.ctx === "string") {
70
- ctx = Array.prototype.slice.call(document.querySelectorAll(this.ctx));
71
- } else {
72
- ctx = [this.ctx];
73
- }
74
- ctx.forEach((ctx2) => {
75
- const isDescendant =
76
- filteredCtx.filter((contexts) => {
77
- return contexts.contains(ctx2);
78
- }).length > 0;
79
- if (filteredCtx.indexOf(ctx2) === -1 && !isDescendant) {
80
- filteredCtx.push(ctx2);
81
- }
82
- });
83
- return filteredCtx;
84
- }
85
- /**
86
- * @callback DOMIterator~getIframeContentsSuccessCallback
87
- * @param {HTMLDocument} contents - The contentDocument of the iframe
88
- */
89
- /**
90
- * Calls the success callback function with the iframe document. If it can't
91
- * be accessed it calls the error callback function
92
- * @param {HTMLElement} ifr - The iframe DOM element
93
- * @param {DOMIterator~getIframeContentsSuccessCallback} successFn
94
- * @param {function} [errorFn]
95
- * @access protected
96
- */
97
- getIframeContents(ifr, successFn, errorFn = () => {}) {
98
- let doc;
99
- try {
100
- const ifrWin = ifr.contentWindow;
101
- doc = ifrWin.document;
102
- if (!ifrWin || !doc) {
103
- throw new Error("iframe inaccessible");
104
- }
105
- } catch (e) {
106
- errorFn();
107
- }
108
- if (doc) {
109
- successFn(doc);
110
- }
111
- }
112
- /**
113
- * Checks if an iframe is empty (if about:blank is the shown page)
114
- * @param {HTMLElement} ifr - The iframe DOM element
115
- * @return {boolean}
116
- * @access protected
117
- */
118
- isIframeBlank(ifr) {
119
- const bl = "about:blank",
120
- src = ifr.getAttribute("src").trim(),
121
- href = ifr.contentWindow.location.href;
122
- return href === bl && src !== bl && src;
123
- }
124
- /**
125
- * Observes the onload event of an iframe and calls the success callback or
126
- * the error callback if the iframe is inaccessible. If the event isn't
127
- * fired within the specified {@link DOMIterator#iframesTimeout}, then it'll
128
- * call the error callback too
129
- * @param {HTMLElement} ifr - The iframe DOM element
130
- * @param {DOMIterator~getIframeContentsSuccessCallback} successFn
131
- * @param {function} errorFn
132
- * @access protected
133
- */
134
- observeIframeLoad(ifr, successFn, errorFn) {
135
- let called = false,
136
- tout = null;
137
- const listener = () => {
138
- if (called) {
139
- return;
140
- }
141
- called = true;
142
- clearTimeout(tout);
143
- try {
144
- if (!this.isIframeBlank(ifr)) {
145
- ifr.removeEventListener("load", listener);
146
- this.getIframeContents(ifr, successFn, errorFn);
147
- }
148
- } catch (e) {
149
- errorFn();
150
- }
151
- };
152
- ifr.addEventListener("load", listener);
153
- tout = setTimeout(listener, this.iframesTimeout);
154
- }
155
- /**
156
- * Callback when the iframe is ready
157
- * @callback DOMIterator~onIframeReadySuccessCallback
158
- * @param {HTMLDocument} contents - The contentDocument of the iframe
159
- */
160
- /**
161
- * Callback if the iframe can't be accessed
162
- * @callback DOMIterator~onIframeReadyErrorCallback
163
- */
164
- /**
165
- * Calls the callback if the specified iframe is ready for DOM access
166
- * @param {HTMLElement} ifr - The iframe DOM element
167
- * @param {DOMIterator~onIframeReadySuccessCallback} successFn - Success
168
- * callback
169
- * @param {DOMIterator~onIframeReadyErrorCallback} errorFn - Error callback
170
- * @see {@link http://stackoverflow.com/a/36155560/3894981} for
171
- * background information
172
- * @access protected
173
- */
174
- onIframeReady(ifr, successFn, errorFn) {
175
- try {
176
- if (ifr.contentWindow.document.readyState === "complete") {
177
- if (this.isIframeBlank(ifr)) {
178
- this.observeIframeLoad(ifr, successFn, errorFn);
179
- } else {
180
- this.getIframeContents(ifr, successFn, errorFn);
181
- }
182
- } else {
183
- this.observeIframeLoad(ifr, successFn, errorFn);
184
- }
185
- } catch (e) {
186
- errorFn();
187
- }
188
- }
189
- /**
190
- * Callback when all iframes are ready for DOM access
191
- * @callback DOMIterator~waitForIframesDoneCallback
192
- */
193
- /**
194
- * Iterates over all iframes and calls the done callback when all of them
195
- * are ready for DOM access (including nested ones)
196
- * @param {HTMLElement} ctx - The context DOM element
197
- * @param {DOMIterator~waitForIframesDoneCallback} done - Done callback
198
- */
199
- waitForIframes(ctx, done) {
200
- let eachCalled = 0;
201
- this.forEachIframe(
202
- ctx,
203
- () => true,
204
- (ifr) => {
205
- eachCalled++;
206
- this.waitForIframes(ifr.querySelector("html"), () => {
207
- if (!--eachCalled) {
208
- done();
209
- }
210
- });
211
- },
212
- (handled) => {
213
- if (!handled) {
214
- done();
215
- }
216
- },
217
- );
218
- }
219
- /**
220
- * Callback allowing to filter an iframe. Must return true when the element
221
- * should remain, otherwise false
222
- * @callback DOMIterator~forEachIframeFilterCallback
223
- * @param {HTMLElement} iframe - The iframe DOM element
224
- */
225
- /**
226
- * Callback for each iframe content
227
- * @callback DOMIterator~forEachIframeEachCallback
228
- * @param {HTMLElement} content - The iframe document
229
- */
230
- /**
231
- * Callback if all iframes inside the context were handled
232
- * @callback DOMIterator~forEachIframeEndCallback
233
- * @param {number} handled - The number of handled iframes (those who
234
- * wheren't filtered)
235
- */
236
- /**
237
- * Iterates over all iframes inside the specified context and calls the
238
- * callbacks when they're ready. Filters iframes based on the instance
239
- * exclusion selectors
240
- * @param {HTMLElement} ctx - The context DOM element
241
- * @param {DOMIterator~forEachIframeFilterCallback} filter - Filter callback
242
- * @param {DOMIterator~forEachIframeEachCallback} each - Each callback
243
- * @param {DOMIterator~forEachIframeEndCallback} [end] - End callback
244
- * @access protected
245
- */
246
- forEachIframe(ctx, filter, each, end = () => {}) {
247
- let ifr = ctx.querySelectorAll("iframe"),
248
- open = ifr.length,
249
- handled = 0;
250
- ifr = Array.prototype.slice.call(ifr);
251
- const checkEnd = () => {
252
- if (--open <= 0) {
253
- end(handled);
254
- }
255
- };
256
- if (!open) {
257
- checkEnd();
258
- }
259
- ifr.forEach((ifr2) => {
260
- if (_DOMIterator.matches(ifr2, this.exclude)) {
261
- checkEnd();
262
- } else {
263
- this.onIframeReady(
264
- ifr2,
265
- (con) => {
266
- if (filter(ifr2)) {
267
- handled++;
268
- each(con);
269
- }
270
- checkEnd();
271
- },
272
- checkEnd,
273
- );
274
- }
275
- });
276
- }
277
- /**
278
- * Creates a NodeIterator on the specified context
279
- * @see {@link https://developer.mozilla.org/en/docs/Web/API/NodeIterator}
280
- * @param {HTMLElement} ctx - The context DOM element
281
- * @param {DOMIterator~whatToShow} whatToShow
282
- * @param {DOMIterator~filterCb} filter
283
- * @return {NodeIterator}
284
- * @access protected
285
- */
286
- createIterator(ctx, whatToShow, filter) {
287
- return document.createNodeIterator(ctx, whatToShow, filter, false);
288
- }
289
- /**
290
- * Creates an instance of DOMIterator in an iframe
291
- * @param {HTMLDocument} contents - Iframe document
292
- * @return {DOMIterator}
293
- * @access protected
294
- */
295
- createInstanceOnIframe(contents) {
296
- return new _DOMIterator(contents.querySelector("html"), this.iframes);
297
- }
298
- /**
299
- * Checks if an iframe occurs between two nodes, more specifically if an
300
- * iframe occurs before the specified node and after the specified prevNode
301
- * @param {HTMLElement} node - The node that should occur after the iframe
302
- * @param {HTMLElement} prevNode - The node that should occur before the
303
- * iframe
304
- * @param {HTMLElement} ifr - The iframe to check against
305
- * @return {boolean}
306
- * @access protected
307
- */
308
- compareNodeIframe(node, prevNode, ifr) {
309
- const compCurr = node.compareDocumentPosition(ifr),
310
- prev = Node.DOCUMENT_POSITION_PRECEDING;
311
- if (compCurr & prev) {
312
- if (prevNode !== null) {
313
- const compPrev = prevNode.compareDocumentPosition(ifr),
314
- after = Node.DOCUMENT_POSITION_FOLLOWING;
315
- if (compPrev & after) {
316
- return true;
317
- }
318
- } else {
319
- return true;
320
- }
321
- }
322
- return false;
323
- }
324
- /**
325
- * @typedef {DOMIterator~getIteratorNodeReturn}
326
- * @type {object.<string>}
327
- * @property {HTMLElement} prevNode - The previous node or null if there is
328
- * no
329
- * @property {HTMLElement} node - The current node
330
- */
331
- /**
332
- * Returns the previous and current node of the specified iterator
333
- * @param {NodeIterator} itr - The iterator
334
- * @return {DOMIterator~getIteratorNodeReturn}
335
- * @access protected
336
- */
337
- getIteratorNode(itr) {
338
- const prevNode = itr.previousNode();
339
- let node;
340
- if (prevNode === null) {
341
- node = itr.nextNode();
342
- } else {
343
- node = itr.nextNode() && itr.nextNode();
344
- }
345
- return {
346
- prevNode,
347
- node,
348
- };
349
- }
350
- /**
351
- * An array containing objects. The object key "val" contains an iframe
352
- * DOM element. The object key "handled" contains a boolean indicating if
353
- * the iframe was handled already.
354
- * It wouldn't be enough to save all open or all already handled iframes.
355
- * The information of open iframes is necessary because they may occur after
356
- * all other text nodes (and compareNodeIframe would never be true). The
357
- * information of already handled iframes is necessary as otherwise they may
358
- * be handled multiple times
359
- * @typedef DOMIterator~checkIframeFilterIfr
360
- * @type {object[]}
361
- */
362
- /**
363
- * Checks if an iframe wasn't handled already and if so, calls
364
- * {@link DOMIterator#compareNodeIframe} to check if it should be handled.
365
- * Information wheter an iframe was or wasn't handled is given within the
366
- * <code>ifr</code> dictionary
367
- * @param {HTMLElement} node - The node that should occur after the iframe
368
- * @param {HTMLElement} prevNode - The node that should occur before the
369
- * iframe
370
- * @param {HTMLElement} currIfr - The iframe to check
371
- * @param {DOMIterator~checkIframeFilterIfr} ifr - The iframe dictionary.
372
- * Will be manipulated (by reference)
373
- * @return {boolean} Returns true when it should be handled, otherwise false
374
- * @access protected
375
- */
376
- checkIframeFilter(node, prevNode, currIfr, ifr) {
377
- let key = false,
378
- handled = false;
379
- ifr.forEach((ifrDict, i) => {
380
- if (ifrDict.val === currIfr) {
381
- key = i;
382
- handled = ifrDict.handled;
383
- }
384
- });
385
- if (this.compareNodeIframe(node, prevNode, currIfr)) {
386
- if (key === false && !handled) {
387
- ifr.push({
388
- val: currIfr,
389
- handled: true,
390
- });
391
- } else if (key !== false && !handled) {
392
- ifr[key].handled = true;
393
- }
394
- return true;
395
- }
396
- if (key === false) {
397
- ifr.push({
398
- val: currIfr,
399
- handled: false,
400
- });
401
- }
402
- return false;
403
- }
404
- /**
405
- * Creates an iterator on all open iframes in the specified array and calls
406
- * the end callback when finished
407
- * @param {DOMIterator~checkIframeFilterIfr} ifr
408
- * @param {DOMIterator~whatToShow} whatToShow
409
- * @param {DOMIterator~forEachNodeCallback} eCb - Each callback
410
- * @param {DOMIterator~filterCb} fCb
411
- * @access protected
412
- */
413
- handleOpenIframes(ifr, whatToShow, eCb, fCb) {
414
- ifr.forEach((ifrDict) => {
415
- if (!ifrDict.handled) {
416
- this.getIframeContents(ifrDict.val, (con) => {
417
- this.createInstanceOnIframe(con).forEachNode(whatToShow, eCb, fCb);
418
- });
419
- }
420
- });
421
- }
422
- /**
423
- * Iterates through all nodes in the specified context and handles iframe
424
- * nodes at the correct position
425
- * @param {DOMIterator~whatToShow} whatToShow
426
- * @param {HTMLElement} ctx - The context
427
- * @param {DOMIterator~forEachNodeCallback} eachCb - Each callback
428
- * @param {DOMIterator~filterCb} filterCb - Filter callback
429
- * @param {DOMIterator~forEachNodeEndCallback} doneCb - End callback
430
- * @access protected
431
- */
432
- iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) {
433
- const itr = this.createIterator(ctx, whatToShow, filterCb);
434
- let ifr = [],
435
- elements = [],
436
- node,
437
- prevNode,
438
- retrieveNodes = () => {
439
- ({ prevNode, node } = this.getIteratorNode(itr));
440
- return node;
441
- };
442
- while (retrieveNodes()) {
443
- if (this.iframes) {
444
- this.forEachIframe(
445
- ctx,
446
- (currIfr) => {
447
- return this.checkIframeFilter(node, prevNode, currIfr, ifr);
448
- },
449
- (con) => {
450
- this.createInstanceOnIframe(con).forEachNode(
451
- whatToShow,
452
- (ifrNode) => elements.push(ifrNode),
453
- filterCb,
454
- );
455
- },
456
- );
457
- }
458
- elements.push(node);
459
- }
460
- elements.forEach((node2) => {
461
- eachCb(node2);
462
- });
463
- if (this.iframes) {
464
- this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb);
465
- }
466
- doneCb();
467
- }
468
- /**
469
- * Callback for each node
470
- * @callback DOMIterator~forEachNodeCallback
471
- * @param {HTMLElement} node - The DOM text node element
472
- */
473
- /**
474
- * Callback if all contexts were handled
475
- * @callback DOMIterator~forEachNodeEndCallback
476
- */
477
- /**
478
- * Iterates over all contexts and initializes
479
- * {@link DOMIterator#iterateThroughNodes iterateThroughNodes} on them
480
- * @param {DOMIterator~whatToShow} whatToShow
481
- * @param {DOMIterator~forEachNodeCallback} each - Each callback
482
- * @param {DOMIterator~filterCb} filter - Filter callback
483
- * @param {DOMIterator~forEachNodeEndCallback} done - End callback
484
- * @access public
485
- */
486
- forEachNode(whatToShow, each, filter, done = () => {}) {
487
- const contexts = this.getContexts();
488
- let open = contexts.length;
489
- if (!open) {
490
- done();
491
- }
492
- contexts.forEach((ctx) => {
493
- const ready = () => {
494
- this.iterateThroughNodes(whatToShow, ctx, each, filter, () => {
495
- if (--open <= 0) {
496
- done();
497
- }
498
- });
499
- };
500
- if (this.iframes) {
501
- this.waitForIframes(ctx, ready);
502
- } else {
503
- ready();
504
- }
505
- });
506
- }
507
- /**
508
- * Callback to filter nodes. Can return e.g. NodeFilter.FILTER_ACCEPT or
509
- * NodeFilter.FILTER_REJECT
510
- * @see {@link http://tinyurl.com/zdczmm2}
511
- * @callback DOMIterator~filterCb
512
- * @param {HTMLElement} node - The node to filter
513
- */
514
- /**
515
- * @typedef DOMIterator~whatToShow
516
- * @see {@link http://tinyurl.com/zfqqkx2}
517
- * @type {number}
518
- */
519
- };
520
-
521
- // node_modules/mark.js/src/lib/mark.js
522
- var Mark = class {
523
- // eslint-disable-line no-unused-vars
524
- /**
525
- * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM
526
- * element, an array of DOM elements, a NodeList or a selector
527
- */
528
- constructor(ctx) {
529
- this.ctx = ctx;
530
- this.ie = false;
531
- const ua = window.navigator.userAgent;
532
- if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) {
533
- this.ie = true;
534
- }
535
- }
536
- /**
537
- * Options defined by the user. They will be initialized from one of the
538
- * public methods. See {@link Mark#mark}, {@link Mark#markRegExp},
539
- * {@link Mark#markRanges} and {@link Mark#unmark} for option properties.
540
- * @type {object}
541
- * @param {object} [val] - An object that will be merged with defaults
542
- * @access protected
543
- */
544
- set opt(val) {
545
- this._opt = Object.assign(
546
- {},
547
- {
548
- element: "",
549
- className: "",
550
- exclude: [],
551
- iframes: false,
552
- iframesTimeout: 5e3,
553
- separateWordSearch: true,
554
- diacritics: true,
555
- synonyms: {},
556
- accuracy: "partially",
557
- acrossElements: false,
558
- caseSensitive: false,
559
- ignoreJoiners: false,
560
- ignoreGroups: 0,
561
- ignorePunctuation: [],
562
- wildcards: "disabled",
563
- each: () => {},
564
- noMatch: () => {},
565
- filter: () => true,
566
- done: () => {},
567
- debug: false,
568
- log: window.console,
569
- },
570
- val,
571
- );
572
- }
573
- get opt() {
574
- return this._opt;
575
- }
576
- /**
577
- * An instance of DOMIterator
578
- * @type {DOMIterator}
579
- * @access protected
580
- */
581
- get iterator() {
582
- return new DOMIterator(
583
- this.ctx,
584
- this.opt.iframes,
585
- this.opt.exclude,
586
- this.opt.iframesTimeout,
587
- );
588
- }
589
- /**
590
- * Logs a message if log is enabled
591
- * @param {string} msg - The message to log
592
- * @param {string} [level="debug"] - The log level, e.g. <code>warn</code>
593
- * <code>error</code>, <code>debug</code>
594
- * @access protected
595
- */
596
- log(msg, level = "debug") {
597
- const log = this.opt.log;
598
- if (!this.opt.debug) {
599
- return;
600
- }
601
- if (typeof log === "object" && typeof log[level] === "function") {
602
- log[level](`mark.js: ${msg}`);
603
- }
604
- }
605
- /**
606
- * Escapes a string for usage within a regular expression
607
- * @param {string} str - The string to escape
608
- * @return {string}
609
- * @access protected
610
- */
611
- escapeStr(str) {
612
- return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
613
- }
614
- /**
615
- * Creates a regular expression string to match the specified search
616
- * term including synonyms, diacritics and accuracy if defined
617
- * @param {string} str - The search term to be used
618
- * @return {string}
619
- * @access protected
620
- */
621
- createRegExp(str) {
622
- if (this.opt.wildcards !== "disabled") {
623
- str = this.setupWildcardsRegExp(str);
624
- }
625
- str = this.escapeStr(str);
626
- if (Object.keys(this.opt.synonyms).length) {
627
- str = this.createSynonymsRegExp(str);
628
- }
629
- if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
630
- str = this.setupIgnoreJoinersRegExp(str);
631
- }
632
- if (this.opt.diacritics) {
633
- str = this.createDiacriticsRegExp(str);
634
- }
635
- str = this.createMergedBlanksRegExp(str);
636
- if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
637
- str = this.createJoinersRegExp(str);
638
- }
639
- if (this.opt.wildcards !== "disabled") {
640
- str = this.createWildcardsRegExp(str);
641
- }
642
- str = this.createAccuracyRegExp(str);
643
- return str;
644
- }
645
- /**
646
- * Creates a regular expression string to match the defined synonyms
647
- * @param {string} str - The search term to be used
648
- * @return {string}
649
- * @access protected
650
- */
651
- createSynonymsRegExp(str) {
652
- const syn = this.opt.synonyms,
653
- sens = this.opt.caseSensitive ? "" : "i",
654
- joinerPlaceholder =
655
- this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : "";
656
- for (let index in syn) {
657
- if (syn.hasOwnProperty(index)) {
658
- const value = syn[index],
659
- k1 =
660
- this.opt.wildcards !== "disabled"
661
- ? this.setupWildcardsRegExp(index)
662
- : this.escapeStr(index),
663
- k2 =
664
- this.opt.wildcards !== "disabled"
665
- ? this.setupWildcardsRegExp(value)
666
- : this.escapeStr(value);
667
- if (k1 !== "" && k2 !== "") {
668
- str = str.replace(
669
- new RegExp(
670
- `(${this.escapeStr(k1)}|${this.escapeStr(k2)})`,
671
- `gm${sens}`,
672
- ),
673
- joinerPlaceholder +
674
- `(${this.processSynomyms(k1)}|${this.processSynomyms(k2)})` +
675
- joinerPlaceholder,
676
- );
677
- }
678
- }
679
- }
680
- return str;
681
- }
682
- /**
683
- * Setup synonyms to work with ignoreJoiners and or ignorePunctuation
684
- * @param {string} str - synonym key or value to process
685
- * @return {string} - processed synonym string
686
- */
687
- processSynomyms(str) {
688
- if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
689
- str = this.setupIgnoreJoinersRegExp(str);
690
- }
691
- return str;
692
- }
693
- /**
694
- * Sets up the regular expression string to allow later insertion of
695
- * wildcard regular expression matches
696
- * @param {string} str - The search term to be used
697
- * @return {string}
698
- * @access protected
699
- */
700
- setupWildcardsRegExp(str) {
701
- str = str.replace(/(?:\\)*\?/g, (val) => {
702
- return val.charAt(0) === "\\" ? "?" : "";
703
- });
704
- return str.replace(/(?:\\)*\*/g, (val) => {
705
- return val.charAt(0) === "\\" ? "*" : "";
706
- });
707
- }
708
- /**
709
- * Sets up the regular expression string to allow later insertion of
710
- * wildcard regular expression matches
711
- * @param {string} str - The search term to be used
712
- * @return {string}
713
- * @access protected
714
- */
715
- createWildcardsRegExp(str) {
716
- let spaces = this.opt.wildcards === "withSpaces";
717
- return str
718
- .replace(/\u0001/g, spaces ? "[\\S\\s]?" : "\\S?")
719
- .replace(/\u0002/g, spaces ? "[\\S\\s]*?" : "\\S*");
720
- }
721
- /**
722
- * Sets up the regular expression string to allow later insertion of
723
- * designated characters (soft hyphens & zero width characters)
724
- * @param {string} str - The search term to be used
725
- * @return {string}
726
- * @access protected
727
- */
728
- setupIgnoreJoinersRegExp(str) {
729
- return str.replace(/[^(|)\\]/g, (val, indx, original) => {
730
- let nextChar = original.charAt(indx + 1);
731
- if (/[(|)\\]/.test(nextChar) || nextChar === "") {
732
- return val;
733
- } else {
734
- return val + "\0";
735
- }
736
- });
737
- }
738
- /**
739
- * Creates a regular expression string to allow ignoring of designated
740
- * characters (soft hyphens, zero width characters & punctuation) based on
741
- * the specified option values of <code>ignorePunctuation</code> and
742
- * <code>ignoreJoiners</code>
743
- * @param {string} str - The search term to be used
744
- * @return {string}
745
- * @access protected
746
- */
747
- createJoinersRegExp(str) {
748
- let joiner = [];
749
- const ignorePunctuation = this.opt.ignorePunctuation;
750
- if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) {
751
- joiner.push(this.escapeStr(ignorePunctuation.join("")));
752
- }
753
- if (this.opt.ignoreJoiners) {
754
- joiner.push("\\u00ad\\u200b\\u200c\\u200d");
755
- }
756
- return joiner.length
757
- ? str.split(/\u0000+/).join(`[${joiner.join("")}]*`)
758
- : str;
759
- }
760
- /**
761
- * Creates a regular expression string to match diacritics
762
- * @param {string} str - The search term to be used
763
- * @return {string}
764
- * @access protected
765
- */
766
- createDiacriticsRegExp(str) {
767
- const sens = this.opt.caseSensitive ? "" : "i",
768
- dct = this.opt.caseSensitive
769
- ? [
770
- "aàáảãạăằắẳẵặâầấẩẫậäåāą",
771
- "AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ",
772
- "cçćč",
773
- "CÇĆČ",
774
- "dđď",
775
- "DĐĎ",
776
- "eèéẻẽẹêềếểễệëěēę",
777
- "EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ",
778
- "iìíỉĩịîïī",
779
- "IÌÍỈĨỊÎÏĪ",
780
- "lł",
781
- "LŁ",
782
- "nñňń",
783
- "NÑŇŃ",
784
- "oòóỏõọôồốổỗộơởỡớờợöøō",
785
- "OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ",
786
- "rř",
787
- "RŘ",
788
- "sšśșş",
789
- "SŠŚȘŞ",
790
- "tťțţ",
791
- "TŤȚŢ",
792
- "uùúủũụưừứửữựûüůū",
793
- "UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ",
794
- "yýỳỷỹỵÿ",
795
- "YÝỲỶỸỴŸ",
796
- "zžżź",
797
- "ZŽŻŹ",
798
- ]
799
- : [
800
- "aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ",
801
- "cçćčCÇĆČ",
802
- "dđďDĐĎ",
803
- "eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ",
804
- "iìíỉĩịîïīIÌÍỈĨỊÎÏĪ",
805
- "lłLŁ",
806
- "nñňńNÑŇŃ",
807
- "oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ",
808
- "rřRŘ",
809
- "sšśșşSŠŚȘŞ",
810
- "tťțţTŤȚŢ",
811
- "uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ",
812
- "yýỳỷỹỵÿYÝỲỶỸỴŸ",
813
- "zžżźZŽŻŹ",
814
- ];
815
- let handled = [];
816
- str.split("").forEach((ch) => {
817
- dct.every((dct2) => {
818
- if (dct2.indexOf(ch) !== -1) {
819
- if (handled.indexOf(dct2) > -1) {
820
- return false;
821
- }
822
- str = str.replace(new RegExp(`[${dct2}]`, `gm${sens}`), `[${dct2}]`);
823
- handled.push(dct2);
824
- }
825
- return true;
826
- });
827
- });
828
- return str;
829
- }
830
- /**
831
- * Creates a regular expression string that merges whitespace characters
832
- * including subsequent ones into a single pattern, one or multiple
833
- * whitespaces
834
- * @param {string} str - The search term to be used
835
- * @return {string}
836
- * @access protected
837
- */
838
- createMergedBlanksRegExp(str) {
839
- return str.replace(/[\s]+/gim, "[\\s]+");
840
- }
841
- /**
842
- * Creates a regular expression string to match the specified string with
843
- * the defined accuracy. As in the regular expression of "exactly" can be
844
- * a group containing a blank at the beginning, all regular expressions will
845
- * be created with two groups. The first group can be ignored (may contain
846
- * the said blank), the second contains the actual match
847
- * @param {string} str - The searm term to be used
848
- * @return {str}
849
- * @access protected
850
- */
851
- createAccuracyRegExp(str) {
852
- const chars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿";
853
- let acc = this.opt.accuracy,
854
- val = typeof acc === "string" ? acc : acc.value,
855
- ls = typeof acc === "string" ? [] : acc.limiters,
856
- lsJoin = "";
857
- ls.forEach((limiter) => {
858
- lsJoin += `|${this.escapeStr(limiter)}`;
859
- });
860
- switch (val) {
861
- case "partially":
862
- default:
863
- return `()(${str})`;
864
- case "complementary":
865
- lsJoin = "\\s" + (lsJoin ? lsJoin : this.escapeStr(chars));
866
- return `()([^${lsJoin}]*${str}[^${lsJoin}]*)`;
867
- case "exactly":
868
- return `(^|\\s${lsJoin})(${str})(?=$|\\s${lsJoin})`;
869
- }
870
- }
871
- /**
872
- * @typedef Mark~separatedKeywords
873
- * @type {object.<string>}
874
- * @property {array.<string>} keywords - The list of keywords
875
- * @property {number} length - The length
876
- */
877
- /**
878
- * Returns a list of keywords dependent on whether separate word search
879
- * was defined. Also it filters empty keywords
880
- * @param {array} sv - The array of keywords
881
- * @return {Mark~separatedKeywords}
882
- * @access protected
883
- */
884
- getSeparatedKeywords(sv) {
885
- let stack = [];
886
- sv.forEach((kw) => {
887
- if (!this.opt.separateWordSearch) {
888
- if (kw.trim() && stack.indexOf(kw) === -1) {
889
- stack.push(kw);
890
- }
891
- } else {
892
- kw.split(" ").forEach((kwSplitted) => {
893
- if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) {
894
- stack.push(kwSplitted);
895
- }
896
- });
897
- }
898
- });
899
- return {
900
- // sort because of https://git.io/v6USg
901
- keywords: stack.sort((a, b) => {
902
- return b.length - a.length;
903
- }),
904
- length: stack.length,
905
- };
906
- }
907
- /**
908
- * Check if a value is a number
909
- * @param {number|string} value - the value to check;
910
- * numeric strings allowed
911
- * @return {boolean}
912
- * @access protected
913
- */
914
- isNumeric(value) {
915
- return Number(parseFloat(value)) == value;
916
- }
917
- /**
918
- * @typedef Mark~rangeObject
919
- * @type {object}
920
- * @property {number} start - The start position within the composite value
921
- * @property {number} length - The length of the string to mark within the
922
- * composite value.
923
- */
924
- /**
925
- * @typedef Mark~setOfRanges
926
- * @type {object[]}
927
- * @property {Mark~rangeObject}
928
- */
929
- /**
930
- * Returns a processed list of integer offset indexes that do not overlap
931
- * each other, and remove any string values or additional elements
932
- * @param {Mark~setOfRanges} array - unprocessed raw array
933
- * @return {Mark~setOfRanges} - processed array with any invalid entries
934
- * removed
935
- * @throws Will throw an error if an array of objects is not passed
936
- * @access protected
937
- */
938
- checkRanges(array) {
939
- if (
940
- !Array.isArray(array) ||
941
- Object.prototype.toString.call(array[0]) !== "[object Object]"
942
- ) {
943
- this.log("markRanges() will only accept an array of objects");
944
- this.opt.noMatch(array);
945
- return [];
946
- }
947
- const stack = [];
948
- let last = 0;
949
- array
950
- .sort((a, b) => {
951
- return a.start - b.start;
952
- })
953
- .forEach((item) => {
954
- let { start, end, valid } = this.callNoMatchOnInvalidRanges(item, last);
955
- if (valid) {
956
- item.start = start;
957
- item.length = end - start;
958
- stack.push(item);
959
- last = end;
960
- }
961
- });
962
- return stack;
963
- }
964
- /**
965
- * @typedef Mark~validObject
966
- * @type {object}
967
- * @property {number} start - The start position within the composite value
968
- * @property {number} end - The calculated end position within the composite
969
- * value.
970
- * @property {boolean} valid - boolean value indicating that the start and
971
- * calculated end range is valid
972
- */
973
- /**
974
- * Initial validation of ranges for markRanges. Preliminary checks are done
975
- * to ensure the start and length values exist and are not zero or non-
976
- * numeric
977
- * @param {Mark~rangeObject} range - the current range object
978
- * @param {number} last - last index of range
979
- * @return {Mark~validObject}
980
- * @access protected
981
- */
982
- callNoMatchOnInvalidRanges(range, last) {
983
- let start,
984
- end,
985
- valid = false;
986
- if (range && typeof range.start !== "undefined") {
987
- start = parseInt(range.start, 10);
988
- end = start + parseInt(range.length, 10);
989
- if (
990
- this.isNumeric(range.start) &&
991
- this.isNumeric(range.length) &&
992
- end - last > 0 &&
993
- end - start > 0
994
- ) {
995
- valid = true;
996
- } else {
997
- this.log(
998
- `Ignoring invalid or overlapping range: ${JSON.stringify(range)}`,
999
- );
1000
- this.opt.noMatch(range);
1001
- }
1002
- } else {
1003
- this.log(`Ignoring invalid range: ${JSON.stringify(range)}`);
1004
- this.opt.noMatch(range);
1005
- }
1006
- return {
1007
- start,
1008
- end,
1009
- valid,
1010
- };
1011
- }
1012
- /**
1013
- * Check valid range for markRanges. Check ranges with access to the context
1014
- * string. Range values are double checked, lengths that extend the mark
1015
- * beyond the string length are limitied and ranges containing only
1016
- * whitespace are ignored
1017
- * @param {Mark~rangeObject} range - the current range object
1018
- * @param {number} originalLength - original length of the context string
1019
- * @param {string} string - current content string
1020
- * @return {Mark~validObject}
1021
- * @access protected
1022
- */
1023
- checkWhitespaceRanges(range, originalLength, string) {
1024
- let end,
1025
- valid = true,
1026
- max = string.length,
1027
- offset = originalLength - max,
1028
- start = parseInt(range.start, 10) - offset;
1029
- start = start > max ? max : start;
1030
- end = start + parseInt(range.length, 10);
1031
- if (end > max) {
1032
- end = max;
1033
- this.log(`End range automatically set to the max value of ${max}`);
1034
- }
1035
- if (start < 0 || end - start < 0 || start > max || end > max) {
1036
- valid = false;
1037
- this.log(`Invalid range: ${JSON.stringify(range)}`);
1038
- this.opt.noMatch(range);
1039
- } else if (string.substring(start, end).replace(/\s+/g, "") === "") {
1040
- valid = false;
1041
- this.log("Skipping whitespace only range: " + JSON.stringify(range));
1042
- this.opt.noMatch(range);
1043
- }
1044
- return {
1045
- start,
1046
- end,
1047
- valid,
1048
- };
1049
- }
1050
- /**
1051
- * @typedef Mark~getTextNodesDict
1052
- * @type {object.<string>}
1053
- * @property {string} value - The composite value of all text nodes
1054
- * @property {object[]} nodes - An array of objects
1055
- * @property {number} nodes.start - The start position within the composite
1056
- * value
1057
- * @property {number} nodes.end - The end position within the composite
1058
- * value
1059
- * @property {HTMLElement} nodes.node - The DOM text node element
1060
- */
1061
- /**
1062
- * Callback
1063
- * @callback Mark~getTextNodesCallback
1064
- * @param {Mark~getTextNodesDict}
1065
- */
1066
- /**
1067
- * Calls the callback with an object containing all text nodes (including
1068
- * iframe text nodes) with start and end positions and the composite value
1069
- * of them (string)
1070
- * @param {Mark~getTextNodesCallback} cb - Callback
1071
- * @access protected
1072
- */
1073
- getTextNodes(cb) {
1074
- let val = "",
1075
- nodes = [];
1076
- this.iterator.forEachNode(
1077
- NodeFilter.SHOW_TEXT,
1078
- (node) => {
1079
- nodes.push({
1080
- start: val.length,
1081
- end: (val += node.textContent).length,
1082
- node,
1083
- });
1084
- },
1085
- (node) => {
1086
- if (this.matchesExclude(node.parentNode)) {
1087
- return NodeFilter.FILTER_REJECT;
1088
- } else {
1089
- return NodeFilter.FILTER_ACCEPT;
1090
- }
1091
- },
1092
- () => {
1093
- cb({
1094
- value: val,
1095
- nodes,
1096
- });
1097
- },
1098
- );
1099
- }
1100
- /**
1101
- * Checks if an element matches any of the specified exclude selectors. Also
1102
- * it checks for elements in which no marks should be performed (e.g.
1103
- * script and style tags) and optionally already marked elements
1104
- * @param {HTMLElement} el - The element to check
1105
- * @return {boolean}
1106
- * @access protected
1107
- */
1108
- matchesExclude(el) {
1109
- return DOMIterator.matches(
1110
- el,
1111
- this.opt.exclude.concat([
1112
- // ignores the elements itself, not their childrens (selector *)
1113
- "script",
1114
- "style",
1115
- "title",
1116
- "head",
1117
- "html",
1118
- ]),
1119
- );
1120
- }
1121
- /**
1122
- * Wraps the instance element and class around matches that fit the start
1123
- * and end positions within the node
1124
- * @param {HTMLElement} node - The DOM text node
1125
- * @param {number} start - The position where to start wrapping
1126
- * @param {number} end - The position where to end wrapping
1127
- * @return {HTMLElement} Returns the splitted text node that will appear
1128
- * after the wrapped text node
1129
- * @access protected
1130
- */
1131
- wrapRangeInTextNode(node, start, end) {
1132
- const hEl = !this.opt.element ? "mark" : this.opt.element,
1133
- startNode = node.splitText(start),
1134
- ret = startNode.splitText(end - start);
1135
- let repl = document.createElement(hEl);
1136
- repl.setAttribute("data-markjs", "true");
1137
- if (this.opt.className) {
1138
- repl.setAttribute("class", this.opt.className);
1139
- }
1140
- repl.textContent = startNode.textContent;
1141
- startNode.parentNode.replaceChild(repl, startNode);
1142
- return ret;
1143
- }
1144
- /**
1145
- * @typedef Mark~wrapRangeInMappedTextNodeDict
1146
- * @type {object.<string>}
1147
- * @property {string} value - The composite value of all text nodes
1148
- * @property {object[]} nodes - An array of objects
1149
- * @property {number} nodes.start - The start position within the composite
1150
- * value
1151
- * @property {number} nodes.end - The end position within the composite
1152
- * value
1153
- * @property {HTMLElement} nodes.node - The DOM text node element
1154
- */
1155
- /**
1156
- * Each callback
1157
- * @callback Mark~wrapMatchesEachCallback
1158
- * @param {HTMLElement} node - The wrapped DOM element
1159
- * @param {number} lastIndex - The last matching position within the
1160
- * composite value of text nodes
1161
- */
1162
- /**
1163
- * Filter callback
1164
- * @callback Mark~wrapMatchesFilterCallback
1165
- * @param {HTMLElement} node - The matching text node DOM element
1166
- */
1167
- /**
1168
- * Determines matches by start and end positions using the text node
1169
- * dictionary even across text nodes and calls
1170
- * {@link Mark#wrapRangeInTextNode} to wrap them
1171
- * @param {Mark~wrapRangeInMappedTextNodeDict} dict - The dictionary
1172
- * @param {number} start - The start position of the match
1173
- * @param {number} end - The end position of the match
1174
- * @param {Mark~wrapMatchesFilterCallback} filterCb - Filter callback
1175
- * @param {Mark~wrapMatchesEachCallback} eachCb - Each callback
1176
- * @access protected
1177
- */
1178
- wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) {
1179
- dict.nodes.every((n, i) => {
1180
- const sibl = dict.nodes[i + 1];
1181
- if (typeof sibl === "undefined" || sibl.start > start) {
1182
- if (!filterCb(n.node)) {
1183
- return false;
1184
- }
1185
- const s = start - n.start,
1186
- e = (end > n.end ? n.end : end) - n.start,
1187
- startStr = dict.value.substr(0, n.start),
1188
- endStr = dict.value.substr(e + n.start);
1189
- n.node = this.wrapRangeInTextNode(n.node, s, e);
1190
- dict.value = startStr + endStr;
1191
- dict.nodes.forEach((k, j) => {
1192
- if (j >= i) {
1193
- if (dict.nodes[j].start > 0 && j !== i) {
1194
- dict.nodes[j].start -= e;
1195
- }
1196
- dict.nodes[j].end -= e;
1197
- }
1198
- });
1199
- end -= e;
1200
- eachCb(n.node.previousSibling, n.start);
1201
- if (end > n.end) {
1202
- start = n.end;
1203
- } else {
1204
- return false;
1205
- }
1206
- }
1207
- return true;
1208
- });
1209
- }
1210
- /**
1211
- * Filter callback before each wrapping
1212
- * @callback Mark~wrapMatchesFilterCallback
1213
- * @param {string} match - The matching string
1214
- * @param {HTMLElement} node - The text node where the match occurs
1215
- */
1216
- /**
1217
- * Callback for each wrapped element
1218
- * @callback Mark~wrapMatchesEachCallback
1219
- * @param {HTMLElement} element - The marked DOM element
1220
- */
1221
- /**
1222
- * Callback on end
1223
- * @callback Mark~wrapMatchesEndCallback
1224
- */
1225
- /**
1226
- * Wraps the instance element and class around matches within single HTML
1227
- * elements in all contexts
1228
- * @param {RegExp} regex - The regular expression to be searched for
1229
- * @param {number} ignoreGroups - A number indicating the amount of RegExp
1230
- * matching groups to ignore
1231
- * @param {Mark~wrapMatchesFilterCallback} filterCb
1232
- * @param {Mark~wrapMatchesEachCallback} eachCb
1233
- * @param {Mark~wrapMatchesEndCallback} endCb
1234
- * @access protected
1235
- */
1236
- wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) {
1237
- const matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
1238
- this.getTextNodes((dict) => {
1239
- dict.nodes.forEach((node) => {
1240
- node = node.node;
1241
- let match;
1242
- while (
1243
- (match = regex.exec(node.textContent)) !== null &&
1244
- match[matchIdx] !== ""
1245
- ) {
1246
- if (!filterCb(match[matchIdx], node)) {
1247
- continue;
1248
- }
1249
- let pos = match.index;
1250
- if (matchIdx !== 0) {
1251
- for (let i = 1; i < matchIdx; i++) {
1252
- pos += match[i].length;
1253
- }
1254
- }
1255
- node = this.wrapRangeInTextNode(
1256
- node,
1257
- pos,
1258
- pos + match[matchIdx].length,
1259
- );
1260
- eachCb(node.previousSibling);
1261
- regex.lastIndex = 0;
1262
- }
1263
- });
1264
- endCb();
1265
- });
1266
- }
1267
- /**
1268
- * Callback for each wrapped element
1269
- * @callback Mark~wrapMatchesAcrossElementsEachCallback
1270
- * @param {HTMLElement} element - The marked DOM element
1271
- */
1272
- /**
1273
- * Filter callback before each wrapping
1274
- * @callback Mark~wrapMatchesAcrossElementsFilterCallback
1275
- * @param {string} match - The matching string
1276
- * @param {HTMLElement} node - The text node where the match occurs
1277
- */
1278
- /**
1279
- * Callback on end
1280
- * @callback Mark~wrapMatchesAcrossElementsEndCallback
1281
- */
1282
- /**
1283
- * Wraps the instance element and class around matches across all HTML
1284
- * elements in all contexts
1285
- * @param {RegExp} regex - The regular expression to be searched for
1286
- * @param {number} ignoreGroups - A number indicating the amount of RegExp
1287
- * matching groups to ignore
1288
- * @param {Mark~wrapMatchesAcrossElementsFilterCallback} filterCb
1289
- * @param {Mark~wrapMatchesAcrossElementsEachCallback} eachCb
1290
- * @param {Mark~wrapMatchesAcrossElementsEndCallback} endCb
1291
- * @access protected
1292
- */
1293
- wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) {
1294
- const matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
1295
- this.getTextNodes((dict) => {
1296
- let match;
1297
- while (
1298
- (match = regex.exec(dict.value)) !== null &&
1299
- match[matchIdx] !== ""
1300
- ) {
1301
- let start = match.index;
1302
- if (matchIdx !== 0) {
1303
- for (let i = 1; i < matchIdx; i++) {
1304
- start += match[i].length;
1305
- }
1306
- }
1307
- const end = start + match[matchIdx].length;
1308
- this.wrapRangeInMappedTextNode(
1309
- dict,
1310
- start,
1311
- end,
1312
- (node) => {
1313
- return filterCb(match[matchIdx], node);
1314
- },
1315
- (node, lastIndex) => {
1316
- regex.lastIndex = lastIndex;
1317
- eachCb(node);
1318
- },
1319
- );
1320
- }
1321
- endCb();
1322
- });
1323
- }
1324
- /**
1325
- * Callback for each wrapped element
1326
- * @callback Mark~wrapRangeFromIndexEachCallback
1327
- * @param {HTMLElement} element - The marked DOM element
1328
- * @param {Mark~rangeObject} range - the current range object; provided
1329
- * start and length values will be numeric integers modified from the
1330
- * provided original ranges.
1331
- */
1332
- /**
1333
- * Filter callback before each wrapping
1334
- * @callback Mark~wrapRangeFromIndexFilterCallback
1335
- * @param {HTMLElement} node - The text node which includes the range
1336
- * @param {Mark~rangeObject} range - the current range object
1337
- * @param {string} match - string extracted from the matching range
1338
- * @param {number} counter - A counter indicating the number of all marks
1339
- */
1340
- /**
1341
- * Callback on end
1342
- * @callback Mark~wrapRangeFromIndexEndCallback
1343
- */
1344
- /**
1345
- * Wraps the indicated ranges across all HTML elements in all contexts
1346
- * @param {Mark~setOfRanges} ranges
1347
- * @param {Mark~wrapRangeFromIndexFilterCallback} filterCb
1348
- * @param {Mark~wrapRangeFromIndexEachCallback} eachCb
1349
- * @param {Mark~wrapRangeFromIndexEndCallback} endCb
1350
- * @access protected
1351
- */
1352
- wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) {
1353
- this.getTextNodes((dict) => {
1354
- const originalLength = dict.value.length;
1355
- ranges.forEach((range, counter) => {
1356
- let { start, end, valid } = this.checkWhitespaceRanges(
1357
- range,
1358
- originalLength,
1359
- dict.value,
1360
- );
1361
- if (valid) {
1362
- this.wrapRangeInMappedTextNode(
1363
- dict,
1364
- start,
1365
- end,
1366
- (node) => {
1367
- return filterCb(
1368
- node,
1369
- range,
1370
- dict.value.substring(start, end),
1371
- counter,
1372
- );
1373
- },
1374
- (node) => {
1375
- eachCb(node, range);
1376
- },
1377
- );
1378
- }
1379
- });
1380
- endCb();
1381
- });
1382
- }
1383
- /**
1384
- * Unwraps the specified DOM node with its content (text nodes or HTML)
1385
- * without destroying possibly present events (using innerHTML) and
1386
- * normalizes the parent at the end (merge splitted text nodes)
1387
- * @param {HTMLElement} node - The DOM node to unwrap
1388
- * @access protected
1389
- */
1390
- unwrapMatches(node) {
1391
- const parent = node.parentNode;
1392
- let docFrag = document.createDocumentFragment();
1393
- while (node.firstChild) {
1394
- docFrag.appendChild(node.removeChild(node.firstChild));
1395
- }
1396
- parent.replaceChild(docFrag, node);
1397
- if (!this.ie) {
1398
- parent.normalize();
1399
- } else {
1400
- this.normalizeTextNode(parent);
1401
- }
1402
- }
1403
- /**
1404
- * Normalizes text nodes. It's a workaround for the native normalize method
1405
- * that has a bug in IE (see attached link). Should only be used in IE
1406
- * browsers as it's slower than the native method.
1407
- * @see {@link http://tinyurl.com/z5asa8c}
1408
- * @param {HTMLElement} node - The DOM node to normalize
1409
- * @access protected
1410
- */
1411
- normalizeTextNode(node) {
1412
- if (!node) {
1413
- return;
1414
- }
1415
- if (node.nodeType === 3) {
1416
- while (node.nextSibling && node.nextSibling.nodeType === 3) {
1417
- node.nodeValue += node.nextSibling.nodeValue;
1418
- node.parentNode.removeChild(node.nextSibling);
1419
- }
1420
- } else {
1421
- this.normalizeTextNode(node.firstChild);
1422
- }
1423
- this.normalizeTextNode(node.nextSibling);
1424
- }
1425
- /**
1426
- * Callback when finished
1427
- * @callback Mark~commonDoneCallback
1428
- * @param {number} totalMatches - The number of marked elements
1429
- */
1430
- /**
1431
- * @typedef Mark~commonOptions
1432
- * @type {object.<string>}
1433
- * @property {string} [element="mark"] - HTML element tag name
1434
- * @property {string} [className] - An optional class name
1435
- * @property {string[]} [exclude] - An array with exclusion selectors.
1436
- * Elements matching those selectors will be ignored
1437
- * @property {boolean} [iframes=false] - Whether to search inside iframes
1438
- * @property {Mark~commonDoneCallback} [done]
1439
- * @property {boolean} [debug=false] - Wheter to log messages
1440
- * @property {object} [log=window.console] - Where to log messages (only if
1441
- * debug is true)
1442
- */
1443
- /**
1444
- * Callback for each marked element
1445
- * @callback Mark~markRegExpEachCallback
1446
- * @param {HTMLElement} element - The marked DOM element
1447
- */
1448
- /**
1449
- * Callback if there were no matches
1450
- * @callback Mark~markRegExpNoMatchCallback
1451
- * @param {RegExp} regexp - The regular expression
1452
- */
1453
- /**
1454
- * Callback to filter matches
1455
- * @callback Mark~markRegExpFilterCallback
1456
- * @param {HTMLElement} textNode - The text node which includes the match
1457
- * @param {string} match - The matching string for the RegExp
1458
- * @param {number} counter - A counter indicating the number of all marks
1459
- */
1460
- /**
1461
- * These options also include the common options from
1462
- * {@link Mark~commonOptions}
1463
- * @typedef Mark~markRegExpOptions
1464
- * @type {object.<string>}
1465
- * @property {Mark~markRegExpEachCallback} [each]
1466
- * @property {Mark~markRegExpNoMatchCallback} [noMatch]
1467
- * @property {Mark~markRegExpFilterCallback} [filter]
1468
- */
1469
- /**
1470
- * Marks a custom regular expression
1471
- * @param {RegExp} regexp - The regular expression
1472
- * @param {Mark~markRegExpOptions} [opt] - Optional options object
1473
- * @access public
1474
- */
1475
- markRegExp(regexp, opt) {
1476
- this.opt = opt;
1477
- this.log(`Searching with expression "${regexp}"`);
1478
- let totalMatches = 0,
1479
- fn = "wrapMatches";
1480
- const eachCb = (element) => {
1481
- totalMatches++;
1482
- this.opt.each(element);
1483
- };
1484
- if (this.opt.acrossElements) {
1485
- fn = "wrapMatchesAcrossElements";
1486
- }
1487
- this[fn](
1488
- regexp,
1489
- this.opt.ignoreGroups,
1490
- (match, node) => {
1491
- return this.opt.filter(node, match, totalMatches);
1492
- },
1493
- eachCb,
1494
- () => {
1495
- if (totalMatches === 0) {
1496
- this.opt.noMatch(regexp);
1497
- }
1498
- this.opt.done(totalMatches);
1499
- },
1500
- );
1501
- }
1502
- /**
1503
- * Callback for each marked element
1504
- * @callback Mark~markEachCallback
1505
- * @param {HTMLElement} element - The marked DOM element
1506
- */
1507
- /**
1508
- * Callback if there were no matches
1509
- * @callback Mark~markNoMatchCallback
1510
- * @param {RegExp} term - The search term that was not found
1511
- */
1512
- /**
1513
- * Callback to filter matches
1514
- * @callback Mark~markFilterCallback
1515
- * @param {HTMLElement} textNode - The text node which includes the match
1516
- * @param {string} match - The matching term
1517
- * @param {number} totalCounter - A counter indicating the number of all
1518
- * marks
1519
- * @param {number} termCounter - A counter indicating the number of marks
1520
- * for the specific match
1521
- */
1522
- /**
1523
- * @typedef Mark~markAccuracyObject
1524
- * @type {object.<string>}
1525
- * @property {string} value - A accuracy string value
1526
- * @property {string[]} limiters - A custom array of limiters. For example
1527
- * <code>["-", ","]</code>
1528
- */
1529
- /**
1530
- * @typedef Mark~markAccuracySetting
1531
- * @type {string}
1532
- * @property {"partially"|"complementary"|"exactly"|Mark~markAccuracyObject}
1533
- * [accuracy="partially"] - Either one of the following string values:
1534
- * <ul>
1535
- * <li><i>partially</i>: When searching for "lor" only "lor" inside
1536
- * "lorem" will be marked</li>
1537
- * <li><i>complementary</i>: When searching for "lor" the whole word
1538
- * "lorem" will be marked</li>
1539
- * <li><i>exactly</i>: When searching for "lor" only those exact words
1540
- * will be marked. In this example nothing inside "lorem". This value
1541
- * is equivalent to the previous option <i>wordBoundary</i></li>
1542
- * </ul>
1543
- * Or an object containing two properties:
1544
- * <ul>
1545
- * <li><i>value</i>: One of the above named string values</li>
1546
- * <li><i>limiters</i>: A custom array of string limiters for accuracy
1547
- * "exactly" or "complementary"</li>
1548
- * </ul>
1549
- */
1550
- /**
1551
- * @typedef Mark~markWildcardsSetting
1552
- * @type {string}
1553
- * @property {"disabled"|"enabled"|"withSpaces"}
1554
- * [wildcards="disabled"] - Set to any of the following string values:
1555
- * <ul>
1556
- * <li><i>disabled</i>: Disable wildcard usage</li>
1557
- * <li><i>enabled</i>: When searching for "lor?m", the "?" will match zero
1558
- * or one non-space character (e.g. "lorm", "loram", "lor3m", etc). When
1559
- * searching for "lor*m", the "*" will match zero or more non-space
1560
- * characters (e.g. "lorm", "loram", "lor123m", etc).</li>
1561
- * <li><i>withSpaces</i>: When searching for "lor?m", the "?" will
1562
- * match zero or one space or non-space character (e.g. "lor m", "loram",
1563
- * etc). When searching for "lor*m", the "*" will match zero or more space
1564
- * or non-space characters (e.g. "lorm", "lore et dolor ipsum", "lor: m",
1565
- * etc).</li>
1566
- * </ul>
1567
- */
1568
- /**
1569
- * @typedef Mark~markIgnorePunctuationSetting
1570
- * @type {string[]}
1571
- * @property {string} The strings in this setting will contain punctuation
1572
- * marks that will be ignored:
1573
- * <ul>
1574
- * <li>These punctuation marks can be between any characters, e.g. setting
1575
- * this option to <code>["'"]</code> would match "Worlds", "World's" and
1576
- * "Wo'rlds"</li>
1577
- * <li>One or more apostrophes between the letters would still produce a
1578
- * match (e.g. "W'o''r'l'd's").</li>
1579
- * <li>A typical setting for this option could be as follows:
1580
- * <pre>ignorePunctuation: ":;.,-–—‒_(){}[]!'\"+=".split(""),</pre> This
1581
- * setting includes common punctuation as well as a minus, en-dash,
1582
- * em-dash and figure-dash
1583
- * ({@link https://en.wikipedia.org/wiki/Dash#Figure_dash ref}), as well
1584
- * as an underscore.</li>
1585
- * </ul>
1586
- */
1587
- /**
1588
- * These options also include the common options from
1589
- * {@link Mark~commonOptions}
1590
- * @typedef Mark~markOptions
1591
- * @type {object.<string>}
1592
- * @property {boolean} [separateWordSearch=true] - Whether to search for
1593
- * each word separated by a blank instead of the complete term
1594
- * @property {boolean} [diacritics=true] - If diacritic characters should be
1595
- * matched. ({@link https://en.wikipedia.org/wiki/Diacritic Diacritics})
1596
- * @property {object} [synonyms] - An object with synonyms. The key will be
1597
- * a synonym for the value and the value for the key
1598
- * @property {Mark~markAccuracySetting} [accuracy]
1599
- * @property {Mark~markWildcardsSetting} [wildcards]
1600
- * @property {boolean} [acrossElements=false] - Whether to find matches
1601
- * across HTML elements. By default, only matches within single HTML
1602
- * elements will be found
1603
- * @property {boolean} [ignoreJoiners=false] - Whether to ignore word
1604
- * joiners inside of key words. These include soft-hyphens, zero-width
1605
- * space, zero-width non-joiners and zero-width joiners.
1606
- * @property {Mark~markIgnorePunctuationSetting} [ignorePunctuation]
1607
- * @property {Mark~markEachCallback} [each]
1608
- * @property {Mark~markNoMatchCallback} [noMatch]
1609
- * @property {Mark~markFilterCallback} [filter]
1610
- */
1611
- /**
1612
- * Marks the specified search terms
1613
- * @param {string|string[]} [sv] - Search value, either a search string or
1614
- * an array containing multiple search strings
1615
- * @param {Mark~markOptions} [opt] - Optional options object
1616
- * @access public
1617
- */
1618
- mark(sv, opt) {
1619
- this.opt = opt;
1620
- let totalMatches = 0,
1621
- fn = "wrapMatches";
1622
- const { keywords: kwArr, length: kwArrLen } = this.getSeparatedKeywords(
1623
- typeof sv === "string" ? [sv] : sv,
1624
- ),
1625
- sens = this.opt.caseSensitive ? "" : "i",
1626
- handler = (kw) => {
1627
- let regex = new RegExp(this.createRegExp(kw), `gm${sens}`),
1628
- matches = 0;
1629
- this.log(`Searching with expression "${regex}"`);
1630
- this[fn](
1631
- regex,
1632
- 1,
1633
- (term, node) => {
1634
- return this.opt.filter(node, kw, totalMatches, matches);
1635
- },
1636
- (element) => {
1637
- matches++;
1638
- totalMatches++;
1639
- this.opt.each(element);
1640
- },
1641
- () => {
1642
- if (matches === 0) {
1643
- this.opt.noMatch(kw);
1644
- }
1645
- if (kwArr[kwArrLen - 1] === kw) {
1646
- this.opt.done(totalMatches);
1647
- } else {
1648
- handler(kwArr[kwArr.indexOf(kw) + 1]);
1649
- }
1650
- },
1651
- );
1652
- };
1653
- if (this.opt.acrossElements) {
1654
- fn = "wrapMatchesAcrossElements";
1655
- }
1656
- if (kwArrLen === 0) {
1657
- this.opt.done(totalMatches);
1658
- } else {
1659
- handler(kwArr[0]);
1660
- }
1661
- }
1662
- /**
1663
- * Callback for each marked element
1664
- * @callback Mark~markRangesEachCallback
1665
- * @param {HTMLElement} element - The marked DOM element
1666
- * @param {array} range - array of range start and end points
1667
- */
1668
- /**
1669
- * Callback if a processed range is invalid, out-of-bounds, overlaps another
1670
- * range, or only matches whitespace
1671
- * @callback Mark~markRangesNoMatchCallback
1672
- * @param {Mark~rangeObject} range - a range object
1673
- */
1674
- /**
1675
- * Callback to filter matches
1676
- * @callback Mark~markRangesFilterCallback
1677
- * @param {HTMLElement} node - The text node which includes the range
1678
- * @param {array} range - array of range start and end points
1679
- * @param {string} match - string extracted from the matching range
1680
- * @param {number} counter - A counter indicating the number of all marks
1681
- */
1682
- /**
1683
- * These options also include the common options from
1684
- * {@link Mark~commonOptions}
1685
- * @typedef Mark~markRangesOptions
1686
- * @type {object.<string>}
1687
- * @property {Mark~markRangesEachCallback} [each]
1688
- * @property {Mark~markRangesNoMatchCallback} [noMatch]
1689
- * @property {Mark~markRangesFilterCallback} [filter]
1690
- */
1691
- /**
1692
- * Marks an array of objects containing a start with an end or length of the
1693
- * string to mark
1694
- * @param {Mark~setOfRanges} rawRanges - The original (preprocessed)
1695
- * array of objects
1696
- * @param {Mark~markRangesOptions} [opt] - Optional options object
1697
- * @access public
1698
- */
1699
- markRanges(rawRanges, opt) {
1700
- this.opt = opt;
1701
- let totalMatches = 0,
1702
- ranges = this.checkRanges(rawRanges);
1703
- if (ranges && ranges.length) {
1704
- this.log(
1705
- "Starting to mark with the following ranges: " + JSON.stringify(ranges),
1706
- );
1707
- this.wrapRangeFromIndex(
1708
- ranges,
1709
- (node, range, match, counter) => {
1710
- return this.opt.filter(node, range, match, counter);
1711
- },
1712
- (element, range) => {
1713
- totalMatches++;
1714
- this.opt.each(element, range);
1715
- },
1716
- () => {
1717
- this.opt.done(totalMatches);
1718
- },
1719
- );
1720
- } else {
1721
- this.opt.done(totalMatches);
1722
- }
1723
- }
1724
- /**
1725
- * Removes all marked elements inside the context with their HTML and
1726
- * normalizes the parent at the end
1727
- * @param {Mark~commonOptions} [opt] - Optional options object
1728
- * @access public
1729
- */
1730
- unmark(opt) {
1731
- this.opt = opt;
1732
- let sel = this.opt.element ? this.opt.element : "*";
1733
- sel += "[data-markjs]";
1734
- if (this.opt.className) {
1735
- sel += `.${this.opt.className}`;
1736
- }
1737
- this.log(`Removal selector "${sel}"`);
1738
- this.iterator.forEachNode(
1739
- NodeFilter.SHOW_ELEMENT,
1740
- (node) => {
1741
- this.unwrapMatches(node);
1742
- },
1743
- (node) => {
1744
- const matchesSel = DOMIterator.matches(node, sel),
1745
- matchesExclude = this.matchesExclude(node);
1746
- if (!matchesSel || matchesExclude) {
1747
- return NodeFilter.FILTER_REJECT;
1748
- } else {
1749
- return NodeFilter.FILTER_ACCEPT;
1750
- }
1751
- },
1752
- this.opt.done,
1753
- );
1754
- }
1755
- };
1756
-
1757
- // node_modules/mark.js/src/vanilla.js
1758
- function Mark2(ctx) {
1759
- const instance = new Mark(ctx);
1760
- this.mark = (sv, opt) => {
1761
- instance.mark(sv, opt);
1762
- return this;
1763
- };
1764
- this.markRegExp = (sv, opt) => {
1765
- instance.markRegExp(sv, opt);
1766
- return this;
1767
- };
1768
- this.markRanges = (sv, opt) => {
1769
- instance.markRanges(sv, opt);
1770
- return this;
1771
- };
1772
- this.unmark = (opt) => {
1773
- instance.unmark(opt);
1774
- return this;
1775
- };
1776
- return this;
1777
- }
1778
- export { Mark2 as default };
1779
- //# sourceMappingURL=vitepress___mark__js_src_vanilla__js.js.map