index.jsView |
---|
25 | 25 … | function Searcher(container) { |
26 | 26 … | this.container = container; |
27 | 27 … | } |
28 | 28 … | |
29 | | -Searcher.prototype.highlight = function () { |
30 | | - |
31 | | - |
32 | | -}; |
33 | | - |
34 | 29 … | Searcher.prototype.setQuery = function (str) { |
35 | 30 … | if (str == this.queryStr) |
36 | 31 … | return; |
|
37 | 32 … | |
38 | 33 … | this.queryStr = str; |
39 | | - this.queryLen = str.length; |
40 | | - this.query = new RegExp(addAccents(str), "i"); |
| 34 … | + this.query = new RegExp(addAccents(str), "ig"); |
41 | 35 … | }; |
42 | 36 … | |
43 | | -function log() { |
44 | | - var str; |
45 | | - try { |
46 | | - str = [].slice.call(arguments).join(", "); |
47 | | - } catch(e) { |
48 | | - str = e.message; |
49 | | - } |
50 | | - document.body.appendChild(document.createElement("pre")) |
51 | | - .appendChild(document.createTextNode(str)); |
| 37 … | +function shouldDescendInto(node) { |
| 38 … | + return node.nodeName != "SCRIPT" && node.nodeName != "STYLE"; |
52 | 39 … | } |
53 | | -window.log = log; |
54 | 40 … | |
55 | 41 … | function getNextTextNode(node, container, wrap) { |
56 | | - do { |
57 | | - if (node.firstChild) { |
58 | | - console.log('firstchild'); |
| 42 … | + outer: do { |
| 43 … | + if (shouldDescendInto(node) && node.firstChild) { |
59 | 44 … | node = node.firstChild; |
60 | 45 … | } else if (node.nextSibling) { |
61 | | - console.log('nextsib'); |
62 | 46 … | node = node.nextSibling; |
63 | 47 … | } else { |
64 | 48 … | do { |
65 | | - if (node == container) |
66 | | - return wrap ? getFirstTextNode(container) : null; |
67 | | - console.log('parent'); |
| 49 … | + if (node == container) { |
| 50 … | + if (!wrap) |
| 51 … | + return null; |
| 52 … | + wrap = false; |
| 53 … | + continue outer; |
| 54 … | + } |
68 | 55 … | node = node.parentNode; |
69 | 56 … | } while (!node.nextSibling); |
70 | | - console.log('next sib'); |
71 | 57 … | node = node.nextSibling; |
72 | 58 … | } |
73 | 59 … | } while (node.nodeType != Node.TEXT_NODE); |
74 | 60 … | return node; |
75 | 61 … | } |
76 | 62 … | |
77 | 63 … | function getPrevTextNode(node, container, wrap) { |
78 | 64 … | do { |
79 | | - if (node.previousSibling) { |
80 | | - console.log('previous sib'); |
| 65 … | + if (node == container) { |
| 66 … | + if (!wrap) |
| 67 … | + return null; |
| 68 … | + while (shouldDescendInto(node) && node.lastChild) |
| 69 … | + node = node.lastChild; |
| 70 … | + wrap = false; |
| 71 … | + } else if (node.previousSibling) { |
81 | 72 … | node = node.previousSibling; |
82 | | - while (node.lastChild) |
| 73 … | + while (shouldDescendInto(node) && node.lastChild) |
83 | 74 … | node = node.lastChild; |
84 | | - } else if (node.parentNode != container) { |
| 75 … | + } else { |
85 | 76 … | node = node.parentNode; |
86 | | - } else { |
87 | | - return wrap ? getLastTextNode(container) : null; |
88 | 77 … | } |
89 | 78 … | } while (node.nodeType != Node.TEXT_NODE); |
90 | 79 … | return node; |
91 | 80 … | } |
92 | 81 … | |
93 | 82 … | function getFirstTextNode(container) { |
94 | | - return getNextTextNode(container, container); |
| 83 … | + return getNextTextNode(container, container, false); |
95 | 84 … | } |
96 | 85 … | |
97 | 86 … | function getLastTextNode(container) { |
98 | | - return getPrevTextNode(container, container); |
| 87 … | + return getPrevTextNode(container, container, true); |
99 | 88 … | } |
100 | 89 … | |
| 90 … | +function matchLast(re, str) { |
| 91 … | + var last; |
| 92 … | + re.lastIndex = 0; |
| 93 … | + for (var m = re.exec(str); m; m = re.exec(str)) |
| 94 … | + last = m; |
| 95 … | + return last; |
| 96 … | +} |
| 97 … | + |
101 | 98 … | Searcher.prototype.selectNext = function () { |
102 | | - if (!this.queryLen) |
| 99 … | + if (!this.queryStr) |
103 | 100 … | return; |
104 | 101 … | |
105 | 102 … | var sel = window.getSelection(); |
106 | | - var startNode = sel.focusNode || getFirstTextNode(this.container); |
107 | | - var startOffset = sel.focusOffset; |
| 103 … | + var startNode = sel.focusNode; |
| 104 … | + var startOffset = 0; |
108 | 105 … | if (!startNode) |
109 | | - return; |
110 | | - if (!startNode.data) |
111 | | - console.log('start', startNode); |
| 106 … | + startNode = getFirstTextNode(this.container); |
| 107 … | + else if (startNode.nodeType != Node.TEXT_NODE || |
| 108 … | + !this.container.contains(startNode)) |
| 109 … | + startNode = getNextTextNode(startNode, this.container, true); |
| 110 … | + else |
| 111 … | + startOffset = sel.focusOffset; |
112 | 112 … | |
113 | | - for (var node = startNode, str = node.data.substr(startOffset); |
114 | | - node; |
115 | | - node = getNextTextNode(node, this.container, true), |
116 | | - str = node.data) |
117 | | - { |
118 | | - var m = str.match(this.query); |
| 113 … | + for (var node = startNode; node; |
| 114 … | + node = getNextTextNode(node, this.container, true)) { |
| 115 … | + var str = node.data; |
| 116 … | + this.query.lastIndex = startOffset; |
| 117 … | + if (startOffset) |
| 118 … | + startOffset = 0; |
| 119 … | + var m = this.query.exec(str); |
119 | 120 … | if (m) { |
120 | | - var i = m.index; |
121 | | - console.log('next node', node, 'index', i); |
122 | | - setSelection(node, i, node, i + m[0].length); |
| 121 … | + setSelection(node, m.index, node, m.index + m[0].length); |
123 | 122 … | return; |
124 | 123 … | } |
125 | 124 … | } |
126 | 125 … | }; |
127 | 126 … | |
128 | 127 … | Searcher.prototype.selectPrev = function () { |
129 | | - if (!this.queryLen) |
| 128 … | + if (!this.queryStr) |
130 | 129 … | return; |
131 | 130 … | |
132 | 131 … | var sel = window.getSelection(); |
133 | | - var endNode = sel.anchorNode || getLastTextNode(this.container); |
134 | | - var endOffset = sel.anchorOffset; |
| 132 … | + var endNode = sel.anchorNode; |
| 133 … | + var endOffset = 0; |
135 | 134 … | if (!endNode) |
136 | | - return; |
137 | | - if (!endNode.data) |
138 | | - console.log('end', endNode); |
| 135 … | + endNode = getLastTextNode(this.container); |
| 136 … | + else if (endNode.nodeType != Node.TEXT_NODE || |
| 137 … | + !this.container.contains(endNode)) |
| 138 … | + endNode = getPrevTextNode(endNode, this.container, true); |
| 139 … | + else |
| 140 … | + endOffset = sel.anchorOffset; |
139 | 141 … | |
140 | | - for (var node = endNode, str = endNode.data.substr(0, endOffset); |
141 | | - node; |
142 | | - node = getPrevTextNode(node, this.container, true), |
143 | | - str = node.data) |
144 | | - { |
145 | | - var m = str.match(this.query); |
| 142 … | + for (var node = endNode; node; |
| 143 … | + node = getPrevTextNode(node, this.container, true)) { |
| 144 … | + var str = node.data; |
| 145 … | + if (endOffset < Infinity) { |
| 146 … | + str = node.data.substr(0, endOffset); |
| 147 … | + endOffset = Infinity; |
| 148 … | + } |
| 149 … | + var m = matchLast(this.query, str); |
146 | 150 … | if (m) { |
147 | | - var i = m.index; |
148 | | - console.log('prev node', node, 'index', i); |
149 | | - setSelection(node, i, node, i + m[0].length); |
| 151 … | + setSelection(node, m.index, node, m.index + m[0].length); |
150 | 152 … | return; |
151 | 153 … | } |
152 | 154 … | } |
153 | 155 … | }; |