summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js')
-rw-r--r--chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js566
1 files changed, 566 insertions, 0 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js b/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
new file mode 100644
index 00000000000..a213612f189
--- /dev/null
+++ b/chromium/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
@@ -0,0 +1,566 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview ChromeVox options page.
+ *
+ */
+
+goog.provide('cvox.OptionsPage');
+
+goog.require('cvox.BrailleBackground');
+goog.require('cvox.BrailleTable');
+goog.require('cvox.ChromeEarcons');
+goog.require('cvox.ChromeHost');
+goog.require('cvox.ChromeMsgs');
+goog.require('cvox.ChromeTts');
+goog.require('cvox.ChromeVox');
+goog.require('cvox.ChromeVoxPrefs');
+goog.require('cvox.CommandStore');
+goog.require('cvox.ExtensionBridge');
+goog.require('cvox.HostFactory');
+goog.require('cvox.KeyMap');
+goog.require('cvox.KeySequence');
+goog.require('cvox.PlatformFilter');
+goog.require('cvox.PlatformUtil');
+
+/**
+ * This object is exported by the main background page.
+ */
+window.braille;
+
+
+/**
+ * Class to manage the options page.
+ * @constructor
+ */
+cvox.OptionsPage = function() {
+};
+
+/**
+ * The ChromeVoxPrefs object.
+ * @type {cvox.ChromeVoxPrefs}
+ */
+cvox.OptionsPage.prefs;
+
+
+/**
+ * A mapping from keycodes to their human readable text equivalents.
+ * This is initialized in cvox.OptionsPage.init for internationalization.
+ * @type {Object.<string, string>}
+ */
+cvox.OptionsPage.KEYCODE_TO_TEXT = {
+};
+
+/**
+ * A mapping from human readable text to keycode values.
+ * This is initialized in cvox.OptionsPage.init for internationalization.
+ * @type {Object.<string, string>}
+ */
+cvox.OptionsPage.TEXT_TO_KEYCODE = {
+};
+
+/**
+ * Initialize the options page by setting the current value of all prefs,
+ * building the key bindings table, and adding event listeners.
+ * @suppress {missingProperties} Property prefs never defined on Window
+ */
+cvox.OptionsPage.init = function() {
+ cvox.ChromeVox.msgs = cvox.HostFactory.getMsgs();
+
+ cvox.OptionsPage.prefs = chrome.extension.getBackgroundPage().prefs;
+ cvox.OptionsPage.populateKeyMapSelect();
+ cvox.OptionsPage.addKeys();
+ cvox.OptionsPage.populateVoicesSelect();
+ cvox.BrailleTable.getAll(function(tables) {
+ /** @type {!Array.<cvox.BrailleTable.Table>} */
+ cvox.OptionsPage.brailleTables = tables;
+ cvox.OptionsPage.populateBrailleTablesSelect();
+ });
+
+ cvox.ChromeVox.msgs.addTranslatedMessagesToDom(document);
+ cvox.OptionsPage.hidePlatformSpecifics();
+
+ cvox.OptionsPage.update();
+
+ document.addEventListener('change', cvox.OptionsPage.eventListener, false);
+ document.addEventListener('click', cvox.OptionsPage.eventListener, false);
+ document.addEventListener('keydown', cvox.OptionsPage.eventListener, false);
+
+ cvox.ExtensionBridge.addMessageListener(function(message) {
+ if (message['keyBindings'] || message['prefs']) {
+ cvox.OptionsPage.update();
+ }
+ });
+
+ document.getElementById('selectKeys').addEventListener(
+ 'click', cvox.OptionsPage.reset, false);
+
+ if (cvox.PlatformUtil.matchesPlatform(cvox.PlatformFilter.WML)) {
+ document.getElementById('version').textContent =
+ chrome.app.getDetails().version;
+ }
+};
+
+/**
+ * Update the value of controls to match the current preferences.
+ * This happens if the user presses a key in a tab that changes a
+ * pref.
+ */
+cvox.OptionsPage.update = function() {
+ var prefs = cvox.OptionsPage.prefs.getPrefs();
+ for (var key in prefs) {
+ // TODO(rshearer): 'active' is a pref, but there's no place in the
+ // options page to specify whether you want ChromeVox active.
+ var elements = document.querySelectorAll('*[name="' + key + '"]');
+ for (var i = 0; i < elements.length; i++) {
+ cvox.OptionsPage.setValue(elements[i], prefs[key]);
+ }
+ }
+};
+
+/**
+ * Populate the keymap select element with stored keymaps
+ */
+cvox.OptionsPage.populateKeyMapSelect = function() {
+ var select = document.getElementById('cvox_keymaps');
+ for (var id in cvox.KeyMap.AVAILABLE_MAP_INFO) {
+ var info = cvox.KeyMap.AVAILABLE_MAP_INFO[id];
+ var option = document.createElement('option');
+ option.id = id;
+ option.className = 'i18n';
+ option.setAttribute('msgid', id);
+ if (cvox.OptionsPage.prefs.getPrefs()['currentKeyMap'] == id) {
+ option.setAttribute('selected', '');
+ }
+ select.appendChild(option);
+ }
+
+ select.addEventListener('change', cvox.OptionsPage.reset, true);
+};
+
+/**
+ * Add the input elements for the key bindings to the container element
+ * in the page. They're sorted in order of description.
+ */
+cvox.OptionsPage.addKeys = function() {
+ var container = document.getElementById('keysContainer');
+ var keyMap = cvox.OptionsPage.prefs.getKeyMap();
+
+ cvox.OptionsPage.prevTime = new Date().getTime();
+ cvox.OptionsPage.keyCount = 0;
+ container.addEventListener('keypress', goog.bind(function(evt) {
+ if (evt.target.id == 'cvoxKey') {
+ return;
+ }
+ this.keyCount++;
+ var currentTime = new Date().getTime();
+ if (currentTime - this.prevTime > 1000 || this.keyCount > 2) {
+ if (document.activeElement.id == 'toggleKeyPrefix') {
+ this.keySequence = new cvox.KeySequence(evt, false);
+ this.keySequence.keys['ctrlKey'][0] = true;
+ } else {
+ this.keySequence = new cvox.KeySequence(evt, true);
+ }
+
+ this.keyCount = 1;
+ } else {
+ this.keySequence.addKeyEvent(evt);
+ }
+
+ var keySeqStr = cvox.KeyUtil.keySequenceToString(this.keySequence, true);
+ var announce = keySeqStr.replace(/\+/g,
+ ' ' + cvox.ChromeVox.msgs.getMsg('then') + ' ');
+ announce = announce.replace(/>/g,
+ ' ' + cvox.ChromeVox.msgs.getMsg('followed_by') + ' ');
+ announce = announce.replace('Cvox',
+ ' ' + cvox.ChromeVox.msgs.getMsg('modifier_key') + ' ');
+
+ // TODO(dtseng): Only basic conflict detection; it does not speak the
+ // conflicting command. Nor does it detect prefix conflicts like Cvox+L vs
+ // Cvox+L>L.
+ if (cvox.OptionsPage.prefs.setKey(document.activeElement.id,
+ this.keySequence)) {
+ document.activeElement.value = keySeqStr;
+ } else {
+ announce = cvox.ChromeVox.msgs.getMsg('key_conflict', [announce]);
+ }
+ cvox.OptionsPage.speak(announce);
+ this.prevTime = currentTime;
+
+ evt.preventDefault();
+ evt.stopPropagation();
+ }, cvox.OptionsPage), true);
+
+ var categories = cvox.CommandStore.categories();
+ for (var i = 0; i < categories.length; i++) {
+ // Braille bindings can't be customized, so don't include them.
+ if (categories[i] == 'braille') {
+ continue;
+ }
+ var headerElement = document.createElement('h3');
+ headerElement.className = 'i18n';
+ headerElement.setAttribute('msgid', categories[i]);
+ headerElement.id = categories[i];
+ container.appendChild(headerElement);
+ var commands = cvox.CommandStore.commandsForCategory(categories[i]);
+ for (var j = 0; j < commands.length; j++) {
+ var command = commands[j];
+ // TODO: Someday we may want to have more than one key
+ // mapped to a command, so we'll need to figure out how to display
+ // that. For now, just take the first key.
+ var keySeqObj = keyMap.keyForCommand(command)[0];
+
+ // Explicitly skip toggleChromeVox in ChromeOS.
+ if (command == 'toggleChromeVox' &&
+ cvox.PlatformUtil.matchesPlatform(cvox.PlatformFilter.CHROMEOS)) {
+ continue;
+ }
+
+ var inputElement = document.createElement('input');
+ inputElement.type = 'text';
+ inputElement.className = 'key active-key';
+ inputElement.id = command;
+
+ var displayedCombo;
+ if (keySeqObj != null) {
+ displayedCombo = cvox.KeyUtil.keySequenceToString(keySeqObj, true);
+ } else {
+ displayedCombo = '';
+ }
+ inputElement.value = displayedCombo;
+
+ // Don't allow the user to change the sticky mode or stop speaking key.
+ if (command == 'toggleStickyMode' || command == 'stopSpeech') {
+ inputElement.disabled = true;
+ }
+ var message = cvox.CommandStore.messageForCommand(command);
+ if (!message) {
+ // TODO(dtseng): missing message id's.
+ message = command;
+ }
+
+ var labelElement = document.createElement('label');
+ labelElement.className = 'i18n';
+ labelElement.setAttribute('msgid', message);
+ labelElement.setAttribute('for', inputElement.id);
+
+ var divElement = document.createElement('div');
+ divElement.className = 'key-container';
+ container.appendChild(divElement);
+ divElement.appendChild(inputElement);
+ divElement.appendChild(labelElement);
+ }
+ var brElement = document.createElement('br');
+ container.appendChild(brElement);
+ }
+
+ if (document.getElementById('cvoxKey') == null) {
+ // Add the cvox key field
+ var inputElement = document.createElement('input');
+ inputElement.type = 'text';
+ inputElement.className = 'key';
+ inputElement.id = 'cvoxKey';
+
+ var labelElement = document.createElement('label');
+ labelElement.className = 'i18n';
+ labelElement.setAttribute('msgid', 'options_cvox_modifier_key');
+ labelElement.setAttribute('for', 'cvoxKey');
+
+ var modifierSectionSibling =
+ document.getElementById('modifier_keys').nextSibling;
+ var modifierSectionParent = modifierSectionSibling.parentNode;
+ modifierSectionParent.insertBefore(labelElement, modifierSectionSibling);
+ modifierSectionParent.insertBefore(inputElement, labelElement);
+ var cvoxKey = document.getElementById('cvoxKey');
+ cvoxKey.value = localStorage['cvoxKey'];
+
+ cvoxKey.addEventListener('keydown', function(evt) {
+ if (!this.modifierSeq_) {
+ this.modifierCount_ = 0;
+ this.modifierSeq_ = new cvox.KeySequence(evt, false);
+ } else {
+ this.modifierSeq_.addKeyEvent(evt);
+ }
+
+ // Never allow non-modified keys.
+ if (!this.modifierSeq_.isAnyModifierActive()) {
+ // Indicate error and instructions excluding tab.
+ if (evt.keyCode != 9) {
+ cvox.OptionsPage.speak(
+ cvox.ChromeVox.msgs.getMsg('modifier_entry_error'), 0, {});
+ }
+ this.modifierSeq_ = null;
+ } else {
+ this.modifierCount_++;
+ }
+
+ // Don't trap tab or shift.
+ if (!evt.shiftKey && evt.keyCode != 9) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+ }, true);
+
+ cvoxKey.addEventListener('keyup', function(evt) {
+ if (this.modifierSeq_) {
+ this.modifierCount_--;
+
+ if (this.modifierCount_ == 0) {
+ var modifierStr =
+ cvox.KeyUtil.keySequenceToString(this.modifierSeq_, true, true);
+ evt.target.value = modifierStr;
+ cvox.OptionsPage.speak(
+ cvox.ChromeVox.msgs.getMsg('modifier_entry_set', [modifierStr]));
+ localStorage['cvoxKey'] = modifierStr;
+ this.modifierSeq_ = null;
+ }
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+ }, true);
+ }
+};
+
+/**
+ * Populates the voices select with options.
+ */
+cvox.OptionsPage.populateVoicesSelect = function() {
+ var select = document.getElementById('voices');
+ chrome.tts.getVoices(function(voices) {
+ voices.forEach(function(voice) {
+ var option = document.createElement('option');
+ option.voiceName = voice.voiceName || '';
+ option.innerText = option.voiceName;
+ if (localStorage['voiceName'] == voice.voiceName) {
+ option.setAttribute('selected', '');
+ }
+ select.add(option);
+ });
+ });
+
+ select.addEventListener('change', function(evt) {
+ var selIndex = select.selectedIndex;
+ var sel = select.options[selIndex];
+ localStorage['voiceName'] = sel.voiceName;
+ }, true);
+};
+
+/**
+ * Populates the braille select control.
+ * @this {cvox.OptionsPage}
+ */
+cvox.OptionsPage.populateBrailleTablesSelect = function() {
+ if (!cvox.ChromeVox.isChromeOS) {
+ return;
+ }
+ var tables = cvox.OptionsPage.brailleTables;
+ var localeDict = JSON.parse(cvox.ChromeVox.msgs.getMsg('locale_dict'));
+ var populateSelect = function(node, dots) {
+ var localeCount = [];
+ var activeTable = localStorage[node.id] || localStorage['brailleTable'];
+ for (var i = 0, table; table = tables[i]; i++) {
+ if (table.dots !== dots) {
+ continue;
+ }
+ var item = document.createElement('option');
+ item.id = table.id;
+ if (!localeCount[table.locale]) {
+ localeCount[table.locale] = 0;
+ }
+ localeCount[table.locale]++;
+ var grade = table.grade;
+ if (!grade) {
+ item.textContent = localeDict[table.locale];
+ } else {
+ item.textContent = cvox.ChromeVox.msgs.getMsg(
+ 'options_braille_locale_grade',
+ [localeDict[table.locale], grade]);
+ }
+ if (table.id == activeTable) {
+ item.setAttribute('selected', '');
+ }
+ node.appendChild(item);
+ }
+ };
+ var select6 = document.getElementById('brailleTable6');
+ var select8 = document.getElementById('brailleTable8');
+ populateSelect(select6, '6');
+ populateSelect(select8, '8');
+
+ var handleBrailleSelect = function(node) {
+ return function(evt) {
+ var selIndex = node.selectedIndex;
+ var sel = node.options[selIndex];
+ localStorage['brailleTable'] = sel.id;
+ localStorage[node.id] = sel.id;
+ /** @type {cvox.BrailleBackground} */
+ var braille = chrome.extension.getBackgroundPage().braille;
+ braille.refreshTranslator();
+ };
+ };
+
+ select6.addEventListener('change', handleBrailleSelect(select6), true);
+ select8.addEventListener('change', handleBrailleSelect(select8), true);
+
+ var tableTypeButton = document.getElementById('brailleTableType');
+ var updateTableType = function(setFocus) {
+ var currentTableType = localStorage['brailleTableType'] || 'brailleTable6';
+ if (currentTableType == 'brailleTable6') {
+ select6.removeAttribute('aria-hidden');
+ select6.setAttribute('tabIndex', 0);
+ select6.style.display = 'block';
+ if (setFocus) {
+ select6.focus();
+ }
+ select8.setAttribute('aria-hidden', 'true');
+ select8.setAttribute('tabIndex', -1);
+ select8.style.display = 'none';
+ localStorage['brailleTable'] = localStorage['brailleTable6'];
+ localStorage['brailleTableType'] = 'brailleTable6';
+ tableTypeButton.textContent =
+ cvox.ChromeVox.msgs.getMsg('options_braille_table_type_6');
+ } else {
+ select6.setAttribute('aria-hidden', 'true');
+ select6.setAttribute('tabIndex', -1);
+ select6.style.display = 'none';
+ select8.removeAttribute('aria-hidden');
+ select8.setAttribute('tabIndex', 0);
+ select8.style.display = 'block';
+ if (setFocus) {
+ select8.focus();
+ }
+ localStorage['brailleTable'] = localStorage['brailleTable8'];
+ localStorage['brailleTableType'] = 'brailleTable8';
+ tableTypeButton.textContent =
+ cvox.ChromeVox.msgs.getMsg('options_braille_table_type_8');
+ }
+ var braille = chrome.extension.getBackgroundPage().braille;
+ braille.refreshTranslator();
+ };
+ updateTableType(false);
+
+ tableTypeButton.addEventListener('click', function(evt) {
+ var oldTableType = localStorage['brailleTableType'];
+ localStorage['brailleTableType'] =
+ oldTableType == 'brailleTable6' ? 'brailleTable8' : 'brailleTable6';
+ updateTableType(true);
+ }, true);
+};
+
+/**
+ * Set the html element for a preference to match the given value.
+ * @param {Element} element The HTML control.
+ * @param {string} value The new value.
+ */
+cvox.OptionsPage.setValue = function(element, value) {
+ if (element.tagName == 'INPUT' && element.type == 'checkbox') {
+ element.checked = (value == 'true');
+ } else if (element.tagName == 'INPUT' && element.type == 'radio') {
+ element.checked = (String(element.value) == value);
+ } else {
+ element.value = value;
+ }
+};
+
+/**
+ * Event listener, called when an event occurs in the page that might
+ * affect one of the preference controls.
+ * @param {Event} event The event.
+ * @return {boolean} True if the default action should occur.
+ */
+cvox.OptionsPage.eventListener = function(event) {
+ window.setTimeout(function() {
+ var target = event.target;
+ if (target.classList.contains('pref')) {
+ if (target.tagName == 'INPUT' && target.type == 'checkbox') {
+ cvox.OptionsPage.prefs.setPref(target.name, target.checked);
+ } else if (target.tagName == 'INPUT' && target.type == 'radio') {
+ var key = target.name;
+ var elements = document.querySelectorAll('*[name="' + key + '"]');
+ for (var i = 0; i < elements.length; i++) {
+ if (elements[i].checked) {
+ cvox.OptionsPage.prefs.setPref(target.name, elements[i].value);
+ }
+ }
+ }
+ } else if (target.classList.contains('key')) {
+ var keySeq = cvox.KeySequence.fromStr(target.value);
+ var success = false;
+ if (target.id == 'cvoxKey') {
+ cvox.OptionsPage.prefs.setPref(target.id, target.value);
+ cvox.OptionsPage.prefs.sendPrefsToAllTabs(true, true);
+ success = true;
+ } else {
+ success =
+ cvox.OptionsPage.prefs.setKey(target.id, keySeq);
+
+ // TODO(dtseng): Don't surface conflicts until we have a better
+ // workflow.
+ }
+ }
+ }, 0);
+ return true;
+};
+
+/**
+ * Refreshes all dynamic content on the page.
+This includes all key related information.
+ */
+cvox.OptionsPage.reset = function() {
+ var selectKeyMap = document.getElementById('cvox_keymaps');
+ var id = selectKeyMap.options[selectKeyMap.selectedIndex].id;
+
+ var msgs = cvox.ChromeVox.msgs;
+ var announce = cvox.OptionsPage.prefs.getPrefs()['currentKeyMap'] == id ?
+ msgs.getMsg('keymap_reset', [msgs.getMsg(id)]) :
+ msgs.getMsg('keymap_switch', [msgs.getMsg(id)]);
+ cvox.OptionsPage.updateStatus_(announce);
+
+ cvox.OptionsPage.prefs.switchToKeyMap(id);
+ document.getElementById('keysContainer').innerHTML = '';
+ cvox.OptionsPage.addKeys();
+ cvox.ChromeVox.msgs.addTranslatedMessagesToDom(document);
+};
+
+/**
+ * Updates the status live region.
+ * @param {string} status The new status.
+ * @private
+ */
+cvox.OptionsPage.updateStatus_ = function(status) {
+ document.getElementById('status').innerText = status;
+};
+
+
+/**
+ * Hides all elements not matching the current platform.
+ */
+cvox.OptionsPage.hidePlatformSpecifics = function() {
+ if (!cvox.ChromeVox.isChromeOS) {
+ var elements = document.body.querySelectorAll('.chromeos');
+ for (var i = 0, el; el = elements[i]; i++) {
+ el.setAttribute('aria-hidden', 'true');
+ el.style.display = 'none';
+ }
+ }
+};
+
+
+/**
+ * Calls a {@code cvox.TtsInterface.speak} method in the background page to
+ * speak an utterance. See that method for further details.
+ * @param {string} textString The string of text to be spoken.
+ * @param {number=} queueMode The queue mode to use.
+ * @param {Object=} properties Speech properties to use for this utterance.
+ */
+cvox.OptionsPage.speak = function(textString, queueMode, properties) {
+ var speak =
+ /** @type Function} */ (chrome.extension.getBackgroundPage()['speak']);
+ speak.apply(null, arguments);
+};
+
+document.addEventListener('DOMContentLoaded', function() {
+ cvox.OptionsPage.init();
+}, false);