Welcome to mirror list, hosted at ThFree Co, Russian Federation.

highlight.js « helpers « js « assets - github.com/bep/docuapi.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b460e3b9f5bbdf0dc53bc6fe85308e7f23951062 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
function walk(el, callback) {
	callback(el);

	let node = el.firstChild;
	while (node) {
		walk(node, callback);
		node = node.nextSibling;
	}
}

export class Highlight {
	constructor(
		opts = {
			contentSelector: '.content',
			markClass: 'da-highlight-mark'
		}
	) {
		this.opts = opts;
		this.nodeStack = [];
	}

	apply(re) {
		const treeWalker = document.createTreeWalker(
			this.content(),
			NodeFilter.SHOW_TEXT,
			{
				acceptNode: (node) => {
					if (node.parentNode.classList.contains(this.opts.markClass)) {
						return NodeFilter.FILTER_REJECT;
					}
					if (/\S/.test(node.data)) {
						return NodeFilter.FILTER_ACCEPT;
					}
					return NodeFilter.FILTER_REJECT;
				}
			},
			false
		);

		// Firs pass: Collect all the text nodes matching the provided regexp.
		// TODO(bep) improve text matching.
		let matches = [];
		while (treeWalker.nextNode()) {
			let node = treeWalker.currentNode;
			if (node.data.match(re)) {
				matches.push(node);
			}
		}

		// Second pass: Replace the matches with nodes wrapped in <mark> tags.
		matches.forEach((node) => {
			// Clone the parent so we can restore it.
			let parentClone = node.parentNode.cloneNode(true);

			parentClone.childNodes.forEach((node) => {
				if (node.nodeType !== Node.TEXT_NODE) {
					return;
				}

				let match = node.data.match(re);
				if (!match) {
					return;
				}

				let pos = node.data.indexOf(match[0], match.index);
				if (pos === -1) {
					return;
				}

				let mark = document.createElement('mark');
				mark.classList.add(this.opts.markClass);

				let wordNode = node.splitText(pos);
				wordNode.splitText(match[0].length);
				let wordClone = wordNode.cloneNode(true);

				mark.appendChild(wordClone);
				parentClone.replaceChild(mark, wordNode);
			});

			if (node.parentNode && node.parentNode.parentNode) {
				this.nodeStack.push({
					old: node.parentNode,
					new: parentClone
				});
				node.parentNode.parentNode.replaceChild(parentClone, node.parentNode);
			}
		});
	}

	remove() {
		while (this.nodeStack.length) {
			let pair = this.nodeStack.pop();
			pair.new.parentNode.replaceChild(pair.old, pair.new);
		}
	}

	content() {
		return document.querySelector(this.opts.contentSelector);
	}
}