UNPKG

5.36 kBJavaScriptView Raw
1import {createText, createElm, getText} from '../dom';
2import {isNull} from '../types';
3import {rgxEsc} from '../string';
4import {defaultsStr} from '../settings';
5
6/**
7 * Highlight matched keywords upon filtering
8 *
9 * @export
10 * @class HighlightKeyword
11 */
12export class HighlightKeyword {
13
14 /**
15 * Creates an instance of HighlightKeyword
16 * @param {TableFilter} tf TableFilter instance
17 */
18 constructor(tf) {
19 let f = tf.config();
20
21 /**
22 * Css class for highlighted term
23 * @type {String}
24 */
25 this.highlightCssClass = defaultsStr(f.highlight_css_class, 'keyword');
26
27 /**
28 * TableFilter instance
29 * @type {TableFilter}
30 */
31 this.tf = tf;
32
33 /**
34 * TableFilter's emitter instance
35 * @type {Emitter}
36 */
37 this.emitter = tf.emitter;
38 }
39
40 /**
41 * Initializes HighlightKeyword instance
42 */
43 init() {
44 this.emitter.on(
45 ['before-filtering', 'destroy'],
46 () => this.unhighlightAll()
47 );
48 this.emitter.on(
49 ['highlight-keyword'],
50 (tf, cell, term) => this._processTerm(cell, term)
51 );
52 }
53
54 /**
55 * Highlight occurences of searched term in passed node
56 * @param {Node} node
57 * @param {String} term Searched term
58 * @param {String} cssClass Css class name
59 *
60 * TODO: refactor this method
61 */
62 highlight(node, term, cssClass) {
63 // Iterate into this nodes childNodes
64 if (node.hasChildNodes) {
65 let children = node.childNodes;
66 for (let i = 0; i < children.length; i++) {
67 this.highlight(children[i], term, cssClass);
68 }
69 }
70
71 if (node.nodeType === 3) {
72 let nodeVal = node.nodeValue.toLowerCase();
73 let termIdx = nodeVal.indexOf(term.toLowerCase());
74
75 if (termIdx !== -1) {
76 let pn = node.parentNode;
77 if (pn && pn.className !== cssClass) {
78 // term not highlighted yet
79 let nv = node.nodeValue,
80 // Create a load of replacement nodes
81 before = createText(nv.substr(0, termIdx)),
82 value = nv.substr(termIdx, term.length),
83 after = createText(nv.substr(termIdx + term.length)),
84 text = createText(value),
85 container = createElm('span');
86 container.className = cssClass;
87 container.appendChild(text);
88 pn.insertBefore(before, node);
89 pn.insertBefore(container, node);
90 pn.insertBefore(after, node);
91 pn.removeChild(node);
92 }
93 }
94 }
95 }
96
97 /**
98 * Removes highlight to nodes matching passed string
99 * @param {String} term
100 * @param {String} cssClass Css class to remove
101 */
102 unhighlight(term, cssClass) {
103 let highlightedNodes = this.tf.dom().querySelectorAll(`.${cssClass}`);
104 for (let i = 0; i < highlightedNodes.length; i++) {
105 let n = highlightedNodes[i];
106 let nodeVal = getText(n);
107
108 if (isNull(term) ||
109 nodeVal.toLowerCase().indexOf(term.toLowerCase()) !== -1) {
110 let parentNode = n.parentNode;
111 parentNode.replaceChild(createText(nodeVal), n);
112 parentNode.normalize();
113 }
114 }
115 }
116
117 /**
118 * Clear all occurrences of highlighted nodes
119 */
120 unhighlightAll() {
121 if (!this.tf.highlightKeywords) {
122 return;
123 }
124
125 this.unhighlight(null, this.highlightCssClass);
126 }
127
128 /** Remove feature */
129 destroy() {
130 this.emitter.off(
131 ['before-filtering', 'destroy'],
132 () => this.unhighlightAll()
133 );
134 this.emitter.off(
135 ['highlight-keyword'],
136 (tf, cell, term) => this._processTerm(cell, term)
137 );
138 }
139
140 /**
141 * Ensure filtering operators are handled before highlighting any match
142 * @param {any} Table cell to look searched term into
143 * @param {any} Searched termIdx
144 */
145 _processTerm(cell, term) {
146 let tf = this.tf;
147 let reLk = new RegExp(rgxEsc(tf.lkOperator));
148 let reEq = new RegExp(tf.eqOperator);
149 let reSt = new RegExp(tf.stOperator);
150 let reEn = new RegExp(tf.enOperator);
151 let reLe = new RegExp(tf.leOperator);
152 let reGe = new RegExp(tf.geOperator);
153 let reL = new RegExp(tf.lwOperator);
154 let reG = new RegExp(tf.grOperator);
155 let reD = new RegExp(tf.dfOperator);
156
157 term = term
158 .replace(reLk, '')
159 .replace(reEq, '')
160 .replace(reSt, '')
161 .replace(reEn, '');
162
163 if (reLe.test(term) || reGe.test(term) || reL.test(term) ||
164 reG.test(term) || reD.test(term)) {
165 term = getText(cell);
166 }
167
168 if (term === '') {
169 return;
170 }
171
172 this.highlight(cell, term, this.highlightCssClass);
173 }
174}