OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 ChromeVox options page. | 6 * @fileoverview ChromeVox options page. |
7 * | 7 * |
8 */ | 8 */ |
9 | 9 |
10 goog.provide('cvox.OptionsPage'); | 10 goog.provide('cvox.OptionsPage'); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
54 }; | 54 }; |
55 | 55 |
56 /** | 56 /** |
57 * Initialize the options page by setting the current value of all prefs, | 57 * Initialize the options page by setting the current value of all prefs, |
58 * building the key bindings table, and adding event listeners. | 58 * building the key bindings table, and adding event listeners. |
59 * @suppress {missingProperties} Property prefs never defined on Window | 59 * @suppress {missingProperties} Property prefs never defined on Window |
60 */ | 60 */ |
61 cvox.OptionsPage.init = function() { | 61 cvox.OptionsPage.init = function() { |
62 cvox.OptionsPage.prefs = chrome.extension.getBackgroundPage().prefs; | 62 cvox.OptionsPage.prefs = chrome.extension.getBackgroundPage().prefs; |
63 cvox.OptionsPage.populateKeyMapSelect(); | 63 cvox.OptionsPage.populateKeyMapSelect(); |
64 cvox.OptionsPage.addKeys(); | |
65 cvox.OptionsPage.populateVoicesSelect(); | 64 cvox.OptionsPage.populateVoicesSelect(); |
66 cvox.BrailleTable.getAll(function(tables) { | 65 cvox.BrailleTable.getAll(function(tables) { |
67 /** @type {!Array<cvox.BrailleTable.Table>} */ | 66 /** @type {!Array<cvox.BrailleTable.Table>} */ |
68 cvox.OptionsPage.brailleTables = tables; | 67 cvox.OptionsPage.brailleTables = tables; |
69 cvox.OptionsPage.populateBrailleTablesSelect(); | 68 cvox.OptionsPage.populateBrailleTablesSelect(); |
70 }); | 69 }); |
71 chrome.storage.local.get({'brailleWordWrap': true}, function(items) { | 70 chrome.storage.local.get({'brailleWordWrap': true}, function(items) { |
72 $('brailleWordWrap').checked = items.brailleWordWrap; | 71 $('brailleWordWrap').checked = items.brailleWordWrap; |
73 }); | 72 }); |
74 | 73 |
(...skipping 12 matching lines...) Expand all Loading... |
87 } | 86 } |
88 }); | 87 }); |
89 | 88 |
90 $('selectKeys').addEventListener( | 89 $('selectKeys').addEventListener( |
91 'click', cvox.OptionsPage.reset, false); | 90 'click', cvox.OptionsPage.reset, false); |
92 | 91 |
93 if (cvox.PlatformUtil.matchesPlatform(cvox.PlatformFilter.WML)) { | 92 if (cvox.PlatformUtil.matchesPlatform(cvox.PlatformFilter.WML)) { |
94 $('version').textContent = | 93 $('version').textContent = |
95 chrome.app.getDetails().version; | 94 chrome.app.getDetails().version; |
96 } | 95 } |
97 | |
98 $('useNext').addEventListener('change', function(evt) { | |
99 var checked = evt.target.checked; | |
100 var background = | |
101 chrome.extension.getBackgroundPage().ChromeVoxState.instance; | |
102 background.toggleNext(checked); | |
103 }, true); | |
104 }; | 96 }; |
105 | 97 |
106 /** | 98 /** |
107 * Update the value of controls to match the current preferences. | 99 * Update the value of controls to match the current preferences. |
108 * This happens if the user presses a key in a tab that changes a | 100 * This happens if the user presses a key in a tab that changes a |
109 * pref. | 101 * pref. |
110 */ | 102 */ |
111 cvox.OptionsPage.update = function() { | 103 cvox.OptionsPage.update = function() { |
112 var prefs = cvox.OptionsPage.prefs.getPrefs(); | 104 var prefs = cvox.OptionsPage.prefs.getPrefs(); |
113 for (var key in prefs) { | 105 for (var key in prefs) { |
(...skipping 20 matching lines...) Expand all Loading... |
134 if (cvox.OptionsPage.prefs.getPrefs()['currentKeyMap'] == id) { | 126 if (cvox.OptionsPage.prefs.getPrefs()['currentKeyMap'] == id) { |
135 option.setAttribute('selected', ''); | 127 option.setAttribute('selected', ''); |
136 } | 128 } |
137 select.appendChild(option); | 129 select.appendChild(option); |
138 } | 130 } |
139 | 131 |
140 select.addEventListener('change', cvox.OptionsPage.reset, true); | 132 select.addEventListener('change', cvox.OptionsPage.reset, true); |
141 }; | 133 }; |
142 | 134 |
143 /** | 135 /** |
144 * Add the input elements for the key bindings to the container element | |
145 * in the page. They're sorted in order of description. | |
146 */ | |
147 cvox.OptionsPage.addKeys = function() { | |
148 var container = $('keysContainer'); | |
149 var keyMap = cvox.OptionsPage.prefs.getKeyMap(); | |
150 | |
151 cvox.OptionsPage.prevTime = new Date().getTime(); | |
152 cvox.OptionsPage.keyCount = 0; | |
153 container.addEventListener('keypress', goog.bind(function(evt) { | |
154 if (evt.target.id == 'cvoxKey') { | |
155 return; | |
156 } | |
157 this.keyCount++; | |
158 var currentTime = new Date().getTime(); | |
159 if (currentTime - this.prevTime > 1000 || this.keyCount > 2) { | |
160 if (document.activeElement.id == 'toggleKeyPrefix') { | |
161 this.keySequence = new cvox.KeySequence(evt, false); | |
162 this.keySequence.keys['ctrlKey'][0] = true; | |
163 } else { | |
164 this.keySequence = new cvox.KeySequence(evt, true); | |
165 } | |
166 | |
167 this.keyCount = 1; | |
168 } else { | |
169 this.keySequence.addKeyEvent(evt); | |
170 } | |
171 | |
172 var keySeqStr = cvox.KeyUtil.keySequenceToString(this.keySequence, true); | |
173 var announce = keySeqStr.replace(/\+/g, | |
174 ' ' + Msgs.getMsg('then') + ' '); | |
175 announce = announce.replace(/>/g, | |
176 ' ' + Msgs.getMsg('followed_by') + ' '); | |
177 announce = announce.replace('Cvox', | |
178 ' ' + Msgs.getMsg('modifier_key') + ' '); | |
179 | |
180 // TODO(dtseng): Only basic conflict detection; it does not speak the | |
181 // conflicting command. Nor does it detect prefix conflicts like Cvox+L vs | |
182 // Cvox+L>L. | |
183 if (cvox.OptionsPage.prefs.setKey(document.activeElement.id, | |
184 this.keySequence)) { | |
185 document.activeElement.value = keySeqStr; | |
186 } else { | |
187 announce = Msgs.getMsg('key_conflict', [announce]); | |
188 } | |
189 cvox.OptionsPage.speak(announce, cvox.QueueMode.QUEUE); | |
190 this.prevTime = currentTime; | |
191 | |
192 evt.preventDefault(); | |
193 evt.stopPropagation(); | |
194 }, cvox.OptionsPage), true); | |
195 | |
196 var categories = cvox.CommandStore.categories(); | |
197 for (var i = 0; i < categories.length; i++) { | |
198 // Braille bindings can't be customized, so don't include them. | |
199 if (categories[i] == 'braille') { | |
200 continue; | |
201 } | |
202 var headerElement = document.createElement('h3'); | |
203 headerElement.className = 'i18n'; | |
204 headerElement.setAttribute('msgid', categories[i]); | |
205 headerElement.id = categories[i]; | |
206 container.appendChild(headerElement); | |
207 var commands = cvox.CommandStore.commandsForCategory(categories[i]); | |
208 for (var j = 0; j < commands.length; j++) { | |
209 var command = commands[j]; | |
210 // TODO: Someday we may want to have more than one key | |
211 // mapped to a command, so we'll need to figure out how to display | |
212 // that. For now, just take the first key. | |
213 var keySeqObj = keyMap.keyForCommand(command)[0]; | |
214 | |
215 // Explicitly skip toggleChromeVox in ChromeOS. | |
216 if (command == 'toggleChromeVox' && | |
217 cvox.PlatformUtil.matchesPlatform(cvox.PlatformFilter.CHROMEOS)) { | |
218 continue; | |
219 } | |
220 | |
221 var inputElement = document.createElement('input'); | |
222 inputElement.type = 'text'; | |
223 inputElement.className = 'key active-key'; | |
224 inputElement.id = command; | |
225 | |
226 var displayedCombo; | |
227 if (keySeqObj != null) { | |
228 displayedCombo = cvox.KeyUtil.keySequenceToString(keySeqObj, true); | |
229 } else { | |
230 displayedCombo = ''; | |
231 } | |
232 inputElement.value = displayedCombo; | |
233 | |
234 // Don't allow the user to change the sticky mode or stop speaking key. | |
235 if (command == 'toggleStickyMode' || command == 'stopSpeech') { | |
236 inputElement.disabled = true; | |
237 } | |
238 var message = cvox.CommandStore.messageForCommand(command); | |
239 if (!message) { | |
240 // TODO(dtseng): missing message id's. | |
241 message = command; | |
242 } | |
243 | |
244 var labelElement = document.createElement('label'); | |
245 labelElement.className = 'i18n'; | |
246 labelElement.setAttribute('msgid', message); | |
247 labelElement.setAttribute('for', inputElement.id); | |
248 | |
249 var divElement = document.createElement('div'); | |
250 divElement.className = 'key-container'; | |
251 container.appendChild(divElement); | |
252 divElement.appendChild(inputElement); | |
253 divElement.appendChild(labelElement); | |
254 } | |
255 var brElement = document.createElement('br'); | |
256 container.appendChild(brElement); | |
257 } | |
258 | |
259 if ($('cvoxKey') == null) { | |
260 // Add the cvox key field | |
261 var inputElement = document.createElement('input'); | |
262 inputElement.type = 'text'; | |
263 inputElement.className = 'key'; | |
264 inputElement.id = 'cvoxKey'; | |
265 | |
266 var labelElement = document.createElement('label'); | |
267 labelElement.className = 'i18n'; | |
268 labelElement.setAttribute('msgid', 'options_cvox_modifier_key'); | |
269 labelElement.setAttribute('for', 'cvoxKey'); | |
270 | |
271 var modifierSectionSibling = | |
272 $('modifier_keys').nextSibling; | |
273 var modifierSectionParent = modifierSectionSibling.parentNode; | |
274 modifierSectionParent.insertBefore(labelElement, modifierSectionSibling); | |
275 modifierSectionParent.insertBefore(inputElement, labelElement); | |
276 var cvoxKey = $('cvoxKey'); | |
277 cvoxKey.value = localStorage['cvoxKey']; | |
278 | |
279 cvoxKey.addEventListener('keydown', function(evt) { | |
280 if (!this.modifierSeq_) { | |
281 this.modifierCount_ = 0; | |
282 this.modifierSeq_ = new cvox.KeySequence(evt, false); | |
283 } else { | |
284 this.modifierSeq_.addKeyEvent(evt); | |
285 } | |
286 | |
287 // Never allow non-modified keys. | |
288 if (!this.modifierSeq_.isAnyModifierActive()) { | |
289 // Indicate error and instructions excluding tab. | |
290 if (evt.keyCode != 9) { | |
291 cvox.OptionsPage.speak( | |
292 Msgs.getMsg('modifier_entry_error'), | |
293 cvox.QueueMode.FLUSH, {}); | |
294 } | |
295 this.modifierSeq_ = null; | |
296 } else { | |
297 this.modifierCount_++; | |
298 } | |
299 | |
300 // Don't trap tab or shift. | |
301 if (!evt.shiftKey && evt.keyCode != 9) { | |
302 evt.preventDefault(); | |
303 evt.stopPropagation(); | |
304 } | |
305 }, true); | |
306 | |
307 cvoxKey.addEventListener('keyup', function(evt) { | |
308 if (this.modifierSeq_) { | |
309 this.modifierCount_--; | |
310 | |
311 if (this.modifierCount_ == 0) { | |
312 var modifierStr = | |
313 cvox.KeyUtil.keySequenceToString(this.modifierSeq_, true, true); | |
314 evt.target.value = modifierStr; | |
315 cvox.OptionsPage.speak( | |
316 Msgs.getMsg('modifier_entry_set', [modifierStr]), | |
317 cvox.QueueMode.QUEUE); | |
318 localStorage['cvoxKey'] = modifierStr; | |
319 this.modifierSeq_ = null; | |
320 } | |
321 evt.preventDefault(); | |
322 evt.stopPropagation(); | |
323 } | |
324 }, true); | |
325 } | |
326 }; | |
327 | |
328 /** | |
329 * Populates the voices select with options. | 136 * Populates the voices select with options. |
330 */ | 137 */ |
331 cvox.OptionsPage.populateVoicesSelect = function() { | 138 cvox.OptionsPage.populateVoicesSelect = function() { |
332 var select = $('voices'); | 139 var select = $('voices'); |
333 | 140 |
334 function setVoiceList() { | 141 function setVoiceList() { |
335 var selectedVoiceName = | 142 var selectedVoiceName = |
336 chrome.extension.getBackgroundPage()['getCurrentVoice'](); | 143 chrome.extension.getBackgroundPage()['getCurrentVoice'](); |
337 chrome.tts.getVoices(function(voices) { | 144 chrome.tts.getVoices(function(voices) { |
338 select.innerHTML = ''; | 145 select.innerHTML = ''; |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
523 var selectKeyMap = $('cvox_keymaps'); | 330 var selectKeyMap = $('cvox_keymaps'); |
524 var id = selectKeyMap.options[selectKeyMap.selectedIndex].id; | 331 var id = selectKeyMap.options[selectKeyMap.selectedIndex].id; |
525 | 332 |
526 var msgs = Msgs; | 333 var msgs = Msgs; |
527 var announce = cvox.OptionsPage.prefs.getPrefs()['currentKeyMap'] == id ? | 334 var announce = cvox.OptionsPage.prefs.getPrefs()['currentKeyMap'] == id ? |
528 msgs.getMsg('keymap_reset', [msgs.getMsg(id)]) : | 335 msgs.getMsg('keymap_reset', [msgs.getMsg(id)]) : |
529 msgs.getMsg('keymap_switch', [msgs.getMsg(id)]); | 336 msgs.getMsg('keymap_switch', [msgs.getMsg(id)]); |
530 cvox.OptionsPage.updateStatus_(announce); | 337 cvox.OptionsPage.updateStatus_(announce); |
531 | 338 |
532 cvox.OptionsPage.prefs.switchToKeyMap(id); | 339 cvox.OptionsPage.prefs.switchToKeyMap(id); |
533 $('keysContainer').innerHTML = ''; | |
534 cvox.OptionsPage.addKeys(); | |
535 Msgs.addTranslatedMessagesToDom(document); | 340 Msgs.addTranslatedMessagesToDom(document); |
536 }; | 341 }; |
537 | 342 |
538 /** | 343 /** |
539 * Updates the status live region. | 344 * Updates the status live region. |
540 * @param {string} status The new status. | 345 * @param {string} status The new status. |
541 * @private | 346 * @private |
542 */ | 347 */ |
543 cvox.OptionsPage.updateStatus_ = function(status) { | 348 cvox.OptionsPage.updateStatus_ = function(status) { |
544 $('status').innerText = status; | 349 $('status').innerText = status; |
545 }; | 350 }; |
546 | 351 |
547 | 352 |
548 /** | 353 /** |
549 * Hides all elements not matching the current platform. | 354 * Hides all elements not matching the current platform. |
550 */ | 355 */ |
551 cvox.OptionsPage.hidePlatformSpecifics = function() { | 356 cvox.OptionsPage.hidePlatformSpecifics = function() { |
552 if (!cvox.ChromeVox.isChromeOS) { | 357 if (!cvox.ChromeVox.isChromeOS) { |
553 var elements = document.body.querySelectorAll('.chromeos'); | 358 var elements = document.body.querySelectorAll('.chromeos'); |
554 for (var i = 0, el; el = elements[i]; i++) { | 359 for (var i = 0, el; el = elements[i]; i++) { |
555 el.setAttribute('aria-hidden', 'true'); | 360 el.setAttribute('aria-hidden', 'true'); |
556 el.style.display = 'none'; | 361 el.style.display = 'none'; |
557 } | 362 } |
558 } | 363 } |
559 }; | 364 }; |
560 | 365 |
561 | 366 |
562 /** | 367 /** |
563 * Calls a {@code cvox.TtsInterface.speak} method in the background page to | |
564 * speak an utterance. See that method for further details. | |
565 * @param {string} textString The string of text to be spoken. | |
566 * @param {cvox.QueueMode} queueMode The queue mode to use. | |
567 * @param {Object=} properties Speech properties to use for this utterance. | |
568 */ | |
569 cvox.OptionsPage.speak = function(textString, queueMode, properties) { | |
570 var speak = | |
571 /** @type Function} */ (chrome.extension.getBackgroundPage()['speak']); | |
572 speak.apply(null, arguments); | |
573 }; | |
574 | |
575 /** | |
576 * @return {cvox.BrailleTranslatorManager} | 368 * @return {cvox.BrailleTranslatorManager} |
577 */ | 369 */ |
578 cvox.OptionsPage.getBrailleTranslatorManager = function() { | 370 cvox.OptionsPage.getBrailleTranslatorManager = function() { |
579 return chrome.extension.getBackgroundPage()['braille_translator_manager']; | 371 return chrome.extension.getBackgroundPage()['braille_translator_manager']; |
580 }; | 372 }; |
581 | 373 |
582 document.addEventListener('DOMContentLoaded', function() { | 374 document.addEventListener('DOMContentLoaded', function() { |
583 cvox.OptionsPage.init(); | 375 cvox.OptionsPage.init(); |
584 }, false); | 376 }, false); |
OLD | NEW |