OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Objects related to incremental search. | 6 * @fileoverview Objects related to incremental search. |
7 */ | 7 */ |
8 | 8 |
9 goog.provide('ISearch'); | 9 goog.provide('ISearch'); |
10 goog.provide('ISearchHandler'); | 10 goog.provide('ISearchHandler'); |
(...skipping 27 matching lines...) Expand all Loading... |
38 | 38 |
39 /** | 39 /** |
40 * Called when search result node changes. | 40 * Called when search result node changes. |
41 * @param {!AutomationNode} node The new search result. | 41 * @param {!AutomationNode} node The new search result. |
42 */ | 42 */ |
43 onSearchResultChanged: function(node) {} | 43 onSearchResultChanged: function(node) {} |
44 }; | 44 }; |
45 | 45 |
46 /** | 46 /** |
47 * Controls an incremental search. | 47 * Controls an incremental search. |
48 * @param {!AutomationNode} initialNode | 48 * @param {!cursors.Cursor} cursor |
49 * @constructor | 49 * @constructor |
50 */ | 50 */ |
51 ISearch = function(initialNode) { | 51 ISearch = function(cursor) { |
| 52 if (!cursor.node) |
| 53 throw 'Incremental search started from invalid range.'; |
| 54 |
52 var leaf = AutomationUtil.findNodePre( | 55 var leaf = AutomationUtil.findNodePre( |
53 initialNode, Dir.FORWARD, AutomationPredicate.leaf) || initialNode; | 56 cursor.node, Dir.FORWARD, AutomationPredicate.leaf) || cursor.node; |
54 | 57 |
55 /** @type {!AutomationNode} @private */ | 58 /** @type {!cursors.Cursor} */ |
56 this.node_ = leaf; | 59 this.cursor = cursors.Cursor.fromNode(leaf); |
57 | 60 |
58 /** | 61 /** |
59 * This tracks the id of a search that is in progress. | 62 * This tracks the id of a search that is in progress. |
60 * @type {number} @private | 63 * @type {number} @private |
61 */ | 64 */ |
62 this.pendingSearchId_ = 0; | 65 this.pendingSearchId_ = 0; |
63 | 66 |
64 // Global exports. | 67 // Global exports. |
65 /** Exported for this background script. */ | 68 /** Exported for this background script. */ |
66 cvox.ChromeVox = chrome.extension.getBackgroundPage()['cvox']['ChromeVox']; | 69 cvox.ChromeVox = chrome.extension.getBackgroundPage()['cvox']['ChromeVox']; |
(...skipping 16 matching lines...) Expand all Loading... |
83 searchStr = searchStr.toLocaleLowerCase(); | 86 searchStr = searchStr.toLocaleLowerCase(); |
84 // Keep things responsive by chunking cursor moves up into discrete | 87 // Keep things responsive by chunking cursor moves up into discrete |
85 // blocks. We can, if needed, simulate getting interrupted by the enter key. | 88 // blocks. We can, if needed, simulate getting interrupted by the enter key. |
86 var currentSearchId = ++this.pendingSearchId_; | 89 var currentSearchId = ++this.pendingSearchId_; |
87 var move = function(curNode) { | 90 var move = function(curNode) { |
88 var cur = cursors.Cursor.fromNode(curNode); | 91 var cur = cursors.Cursor.fromNode(curNode); |
89 var prev = cur; | 92 var prev = cur; |
90 cur = | 93 cur = |
91 cur.move(cursors.Unit.NODE, cursors.Movement.DIRECTIONAL, dir); | 94 cur.move(cursors.Unit.NODE, cursors.Movement.DIRECTIONAL, dir); |
92 if (prev.equals(cur)) { | 95 if (prev.equals(cur)) { |
93 this.handler_.onSearchReachedBoundary(this.node_); | 96 this.handler_.onSearchReachedBoundary(this.cursor.node); |
94 return; | 97 return; |
95 } | 98 } |
96 | 99 |
97 if (cur.getText().toLocaleLowerCase().indexOf(searchStr) != -1) { | 100 if (cur.getText().toLocaleLowerCase().indexOf(searchStr) != -1) { |
98 this.node_ = cur.node; | 101 this.cursor = cur; |
99 this.handler_.onSearchResultChanged(this.node_); | 102 this.handler_.onSearchResultChanged(this.cursor.node); |
100 return; | 103 return; |
101 } | 104 } |
102 if (this.pendingSearchId_ == currentSearchId) | 105 if (this.pendingSearchId_ == currentSearchId) |
103 window.setTimeout(move.bind(this, cur.node), 0); | 106 window.setTimeout(move.bind(this, cur.node), 0); |
104 }; | 107 }; |
105 window.setTimeout(move.bind(this, this.node_), 0); | 108 window.setTimeout(move.bind(this, this.cursor.node), 0); |
106 } | 109 } |
107 }; | 110 }; |
108 | 111 |
109 /** | 112 /** |
110 * @param {Element} input | 113 * @param {Element} input |
111 * @constructor | 114 * @constructor |
112 * @implements {ISearchHandler} | 115 * @implements {ISearchHandler} |
113 */ | 116 */ |
114 ISearchUI = function(input) { | 117 ISearchUI = function(input) { |
115 /** @type {ChromeVoxState} @private */ | 118 /** @type {ChromeVoxState} @private */ |
116 this.background_ = | 119 this.background_ = |
117 chrome.extension.getBackgroundPage()['ChromeVoxState']['instance']; | 120 chrome.extension.getBackgroundPage()['ChromeVoxState']['instance']; |
118 this.iSearch_ = new ISearch(this.background_.currentRange.start.node); | 121 this.iSearch_ = new ISearch(this.background_.currentRange.start); |
119 this.input_ = input; | 122 this.input_ = input; |
120 this.dir_ = Dir.FORWARD; | 123 this.dir_ = Dir.FORWARD; |
121 this.iSearch_.handler = this; | 124 this.iSearch_.handler = this; |
122 | 125 |
123 this.onKeyDown = this.onKeyDown.bind(this); | 126 this.onKeyDown = this.onKeyDown.bind(this); |
124 this.onTextInput = this.onTextInput.bind(this); | 127 this.onTextInput = this.onTextInput.bind(this); |
125 | 128 |
126 input.addEventListener('keydown', this.onKeyDown, true); | 129 input.addEventListener('keydown', this.onKeyDown, true); |
127 input.addEventListener('textInput', this.onTextInput, true); | 130 input.addEventListener('textInput', this.onTextInput, true); |
128 }; | 131 }; |
129 | 132 |
130 /** | 133 /** |
131 * @param {Element} input | 134 * @param {Element} input |
132 * @return {ISearchUI} | 135 * @return {ISearchUI} |
133 */ | 136 */ |
134 ISearchUI.get = function(input) { | 137 ISearchUI.init = function(input) { |
135 if (ISearchUI.instance_) | 138 if (ISearchUI.instance_) |
136 ISearchUI.instance_.destroy(); | 139 ISearchUI.instance_.destroy(); |
| 140 |
| 141 if (!input) |
| 142 return null; |
| 143 |
137 ISearchUI.instance_ = new ISearchUI(input); | 144 ISearchUI.instance_ = new ISearchUI(input); |
138 input.focus(); | 145 input.focus(); |
| 146 input.select(); |
139 return ISearchUI.instance_; | 147 return ISearchUI.instance_; |
140 }; | 148 }; |
141 | 149 |
142 ISearchUI.prototype = { | 150 ISearchUI.prototype = { |
143 /** | 151 /** |
144 * Listens to key down events. | 152 * Listens to key down events. |
145 * @param {Event} evt | 153 * @param {Event} evt |
146 * @return {boolean} | 154 * @return {boolean} |
147 */ | 155 */ |
148 onKeyDown: function(evt) { | 156 onKeyDown: function(evt) { |
149 switch (evt.key) { | 157 switch (evt.key) { |
150 case 'ArrowUp': | 158 case 'ArrowUp': |
151 this.dir_ = Dir.BACKWARD; | 159 this.dir_ = Dir.BACKWARD; |
152 break; | 160 break; |
153 case 'ArrowDown': | 161 case 'ArrowDown': |
154 this.dir_ = Dir.FORWARD; | 162 this.dir_ = Dir.FORWARD; |
155 break; | 163 break; |
156 case 'Escape': | 164 case 'Escape': |
157 this.pendingSearchId_ = 0; | 165 this.pendingSearchId_ = 0; |
158 Panel.closeMenusAndRestoreFocus(); | 166 Panel.closeMenusAndRestoreFocus(); |
159 return false; | 167 return false; |
160 case 'Enter': | 168 case 'Enter': |
161 this.pendingSearchId_ = 0; | 169 this.pendingSearchId_ = 0; |
| 170 Panel.setPendingCallback(function() { |
| 171 var node = this.iSearch_.cursor.node; |
| 172 if (!node) |
| 173 return; |
| 174 chrome.extension.getBackgroundPage().ChromeVoxState.instance[ |
| 175 'navigateToRange']( |
| 176 cursors.Range.fromNode(node)); |
| 177 }.bind(this)); |
162 Panel.closeMenusAndRestoreFocus(); | 178 Panel.closeMenusAndRestoreFocus(); |
163 return false; | 179 return false; |
164 default: | 180 default: |
165 this.pendingSearchId_ = 0; | 181 this.pendingSearchId_ = 0; |
166 return false; | 182 return false; |
167 } | 183 } |
168 this.iSearch_.search(this.input_.value, this.dir_); | 184 this.iSearch_.search(this.input_.value, this.dir_); |
169 evt.preventDefault(); | 185 evt.preventDefault(); |
170 evt.stopPropagation(); | 186 evt.stopPropagation(); |
171 return false; | 187 return false; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 this.iSearch_.handler_ = null; | 229 this.iSearch_.handler_ = null; |
214 this.iSearch_ = null; | 230 this.iSearch_ = null; |
215 var input = this.input_; | 231 var input = this.input_; |
216 this.input_ = null; | 232 this.input_ = null; |
217 input.removeEventListener('keydown', this.onKeyDown, true); | 233 input.removeEventListener('keydown', this.onKeyDown, true); |
218 input.removeEventListener('textInput', this.onTextInput, true); | 234 input.removeEventListener('textInput', this.onTextInput, true); |
219 } | 235 } |
220 }; | 236 }; |
221 | 237 |
222 }); // goog.scope | 238 }); // goog.scope |
OLD | NEW |