diff options
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/chromevox/extensions/searchvox/results.js')
-rw-r--r-- | chromium/chrome/browser/resources/chromeos/chromevox/extensions/searchvox/results.js | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/extensions/searchvox/results.js b/chromium/chrome/browser/resources/chromeos/chromevox/extensions/searchvox/results.js new file mode 100644 index 00000000000..367220603a4 --- /dev/null +++ b/chromium/chrome/browser/resources/chromeos/chromevox/extensions/searchvox/results.js @@ -0,0 +1,468 @@ +// 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 Provides different rules for each type of result. + */ + +goog.provide('cvox.SearchResults'); +goog.provide('cvox.UnknownResult'); + +goog.require('cvox.AbstractResult'); +goog.require('cvox.ChromeVox'); +goog.require('cvox.SearchUtil'); + +/** + * @constructor + */ +cvox.SearchResults = function() { +}; + +/** + * Speaks a result based on given selectors. + * @param {Element} result Search result to be spoken. + * @param {Array} selectTexts Array of selectors or text to speak. + */ +cvox.SearchResults.speakResultBySelectTexts = function(result, selectTexts) { + for (var j = 0; j < selectTexts.length; j++) { + var selectText = selectTexts[j]; + if (selectText.select) { + var elems = result.querySelectorAll(selectText.select); + for (var i = 0; i < elems.length; i++) { + cvox.ChromeVox.speakNode(elems.item(i), 1); + } + } + if (selectText.text) { + cvox.ChromeVox.tts.speak(selectText.text, 1); + } + } +}; + +/** + * Unknown Result Type. This is used if we don't know what to do. + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.UnknownResult = function() { +}; +goog.inherits(cvox.UnknownResult, cvox.AbstractResult); + +/* Normal Result Type. */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.NormalResult = function() { +}; +goog.inherits(cvox.NormalResult, cvox.AbstractResult); + +/** + * Checks the result if it is a normal result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is a normal result. + * @override + */ +cvox.NormalResult.prototype.isType = function(result) { + var NORMAL_SELECT = '.rc'; + return result.querySelector(NORMAL_SELECT) !== null; +}; + +/** + * Speak a normal search result. + * @param {Element} result Normal result to be spoken. + * @return {boolean} Whether or not the result was spoken. + * @override + */ +cvox.NormalResult.prototype.speak = function(result) { + if (!result) { + return false; + } + var NORMAL_TITLE_SELECT = '.rc .r'; + var NORMAL_URL_SELECT = '.kv'; + var NORMAL_DESC_SELECT = '.rc .st'; + var SITE_LINK_SELECT = '.osl'; + var MORE_RESULTS_SELECT = '.sld'; + var MORE_RESULTS_LINK_SELECT = '.mrf'; + + var NORMAL_SELECTORS = [ + { select: NORMAL_TITLE_SELECT }, + { select: NORMAL_DESC_SELECT }, + { select: NORMAL_URL_SELECT }, + { select: SITE_LINK_SELECT }, + { select: MORE_RESULTS_SELECT }, + { select: MORE_RESULTS_LINK_SELECT }]; + cvox.SearchResults.speakResultBySelectTexts(result, NORMAL_SELECTORS); + + var DISCUSS_TITLE_SELECT = '.mas-1st-col div'; + var DISCUSS_DATE_SELECT = '.mas-col div'; + var discussTitles = result.querySelectorAll(DISCUSS_TITLE_SELECT); + var discussDates = result.querySelectorAll(DISCUSS_DATE_SELECT); + for (var i = 0; i < discussTitles.length; i++) { + cvox.ChromeVox.speakNode(discussTitles.item(i), 1); + cvox.ChromeVox.speakNode(discussDates.item(i), 1); + } + return true; +}; + +/* Weather Result */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.WeatherResult = function() { +}; +goog.inherits(cvox.WeatherResult, cvox.AbstractResult); + +/** + * Checks the result if it is a weather result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is a weather result. + * @override + */ +cvox.WeatherResult.prototype.isType = function(result) { + var WEATHER_SELECT = '#wob_wc'; + return result.querySelector(WEATHER_SELECT) !== null; +}; + +/** + * Speak a weather forecast. + * @param {Element} forecast Weather forecast to be spoken. + */ +cvox.WeatherResult.speakForecast = function(forecast) { + if (!forecast) { + return; + } + var FORE_DAY_SELECT = '.vk_lgy'; + var FORE_COND_SELECT = 'img'; + var FORE_HIGH_SELECT = '.vk_gy'; + var FORE_LOW_SELECT = '.vk_lgy'; + + var FORE_SELECTORS = [ + { select: FORE_DAY_SELECT }, + { select: FORE_COND_SELECT }, + { select: FORE_HIGH_SELECT }, + { select: FORE_LOW_SELECT } + ]; + cvox.SearchResults.speakResultBySelectTexts(forecast, FORE_SELECTORS); +}; + +/** + * Speak a weather search result. + * @param {Element} result Weather result to be spoken. + * @return {boolean} Whether or not the result was spoken. + * @override + */ +cvox.WeatherResult.prototype.speak = function(result) { + if (!result) { + return false; + } + /* TODO(peterxiao): Internationalization? */ + var WEATHER_INTRO = 'The weather forcast for'; + var WEATHER_TEMP_UNITS = 'degrees fahrenheit'; + var WEATHER_PREC_INTRO = 'precipitation is'; + var WEATHER_HUMID_INTRO = 'humidity is'; + var WEATHER_WIND_INTRO = 'wind is'; + var FORE_INTRO = 'Forecasts for this week'; + var WEATHER_LOC_SELECT = '.vk_h'; + var WEATHER_WHEN_SELECT = '#wob_dts'; + var WEATHER_COND_SELECT = '#wob_dc'; + var WEATHER_TEMP_SELECT = '#wob_tm'; + var WEATHER_PREC_SELECT = '#wob_pp'; + var WEATHER_HUMID_SELECT = '#wob_hm'; + var WEATHER_WIND_SELECT = '#wob_ws'; + + var WEATHER_SELECT_TEXTS = [ + { text: WEATHER_INTRO }, + { select: WEATHER_LOC_SELECT }, + { select: WEATHER_WHEN_SELECT }, + { select: WEATHER_COND_SELECT }, + { select: WEATHER_TEMP_SELECT }, + { text: WEATHER_TEMP_UNITS }, + { text: WEATHER_PREC_INTRO }, + { select: WEATHER_PREC_SELECT }, + { text: WEATHER_HUMID_INTRO }, + { select: WEATHER_HUMID_SELECT }, + { text: WEATHER_WIND_INTRO }, + { select: WEATHER_WIND_SELECT } + ]; + cvox.SearchResults.speakResultBySelectTexts(result, WEATHER_SELECT_TEXTS); + + var WEATHER_FORCAST_CLASS = 'wob_df'; + var forecasts = result.getElementsByClassName(WEATHER_FORCAST_CLASS); + cvox.ChromeVox.tts.speak(FORE_INTRO, 1); + for (var i = 0; i < forecasts.length; i++) { + var forecast = forecasts.item(i); + cvox.WeatherResult.speakForecast(forecast); + } + return true; +}; + +/* Knowledge Panel Result */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.KnowResult = function() { +}; +goog.inherits(cvox.KnowResult, cvox.AbstractResult); + +/** + * Checks the result if it is a know result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is a know result. + * @override + */ +cvox.KnowResult.prototype.isType = function(result) { + var KNOP_SELECT = '.kno-ec'; + return result.querySelector(KNOP_SELECT) !== null; +}; + +/** + * Speak a knowledge panel search result. + * @param {Element} result Knowledge panel result to be spoken. + * @return {boolean} Whether or not the result was spoken. + * @override + */ +cvox.KnowResult.prototype.speak = function(result) { + cvox.ChromeVox.speakNode(result, 1); + return true; +}; + +/** + * Extracts the wikipedia URL from knowledge panel. + * @param {Element} result Result to extract from. + * @return {?string} URL. + * @override + */ +cvox.KnowResult.prototype.getURL = function(result) { + var LINK_SELECTOR = '.q'; + return cvox.SearchUtil.extractURL(result.querySelector(LINK_SELECTOR)); +}; + +/** + * Extracts the node to sync to in the knowledge panel. + * @param {Element} result Result. + * @return {?Node} Node to sync to. + * @override + */ +cvox.KnowResult.prototype.getSyncNode = function(result) { + var HEADER_SELECTOR = '.kno-ecr-pt'; + return result.querySelector(HEADER_SELECTOR); +}; + +/* Calculator Type */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.CalcResult = function() { +}; +goog.inherits(cvox.CalcResult, cvox.AbstractResult); + +/** + * Checks the result if it is a calculator result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is a calculator result. + * @override + */ +cvox.CalcResult.prototype.isType = function(result) { + var CALC_SELECT = '#cwmcwd'; + return result.querySelector(CALC_SELECT) !== null; +}; + +/** + * Speak a calculator search result. + * @param {Element} result Calculator result to be spoken. + * @return {boolean} Whether or not the result was spoken. + * @override + */ +cvox.CalcResult.prototype.speak = function(result) { + if (!result) { + return false; + } + var CALC_QUERY_SELECT = '#cwles'; + var CALC_RESULT_SELECT = '#cwos'; + var CALC_SELECTORS = [ + { select: CALC_QUERY_SELECT }, + { select: CALC_RESULT_SELECT } + ]; + cvox.SearchResults.speakResultBySelectTexts(result, CALC_SELECTORS); + return true; +}; + +/* Game Type */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.GameResult = function() { +}; +goog.inherits(cvox.GameResult, cvox.AbstractResult); + +/** + * Checks the result if it is a game result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is a game result. + * @override + */ +cvox.GameResult.prototype.isType = function(result) { + var GAME_SELECT = '.xpdbox'; + return result.querySelector(GAME_SELECT) !== null; +}; + +/* Image Type */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.ImageResult = function() { +}; +goog.inherits(cvox.ImageResult, cvox.AbstractResult); + +/** + * Checks the result if it is a image result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is a image result. + * @override + */ +cvox.ImageResult.prototype.isType = function(result) { + var IMAGE_CLASSES = 'rg_di'; + return result.className === IMAGE_CLASSES; +}; + +/** + * Speak an image result. + * @param {Element} result Image result to be spoken. + * @return {boolean} Whether or not the result was spoken. + * @override + */ +cvox.ImageResult.prototype.speak = function(result) { + if (!result) { + return false; + } + /* Grab image result metadata. */ + var META_CLASS = 'rg_meta'; + var metaDiv = result.querySelector('.' + META_CLASS); + var metaJSON = metaDiv.innerHTML; + var metaData = JSON.parse(metaJSON); + + var imageSelectTexts = []; + + var filename = metaData['fn']; + if (filename) { + imageSelectTexts.push({ text: filename }); + } + + var rawDimensions = metaData['is']; + if (rawDimensions) { + /* Dimensions contain HTML codes, so we convert them. */ + var tmpDiv = document.createElement('div'); + tmpDiv.innerHTML = rawDimensions; + var dimensions = tmpDiv.textContent || tmpDiv.innerText; + imageSelectTexts.push({ text: dimensions }); + } + + var url = metaData['isu']; + if (url) { + imageSelectTexts.push({ text: url}); + } + cvox.SearchResults.speakResultBySelectTexts(result, imageSelectTexts); + return true; +}; + +/* Category Result */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.CategoryResult = function() { +}; +goog.inherits(cvox.CategoryResult, cvox.AbstractResult); + +/** + * Checks the result if it is a category result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is a category result. + * @override + */ +cvox.CategoryResult.prototype.isType = function(result) { + var CATEGORY_CLASSES = 'rg_fbl nj'; + return result.className === CATEGORY_CLASSES; +}; + +/** + * Speak a category result. + * @param {Element} result Category result to be spoken. + * @return {boolean} Whether or not the result was spoken. + * @override + */ +cvox.CategoryResult.prototype.speak = function(result) { + if (!result) { + return false; + } + var LABEL_SELECT = '.rg_bb_label'; + var label = result.querySelector(LABEL_SELECT); + cvox.ChromeVox.speakNode(label, 1); + return true; +}; + +/* Ad Result */ +/** + * @constructor + * @extends {cvox.AbstractResult} + */ +cvox.AdResult = function() { +}; +goog.inherits(cvox.AdResult, cvox.AbstractResult); + +/** + * Checks the result if it is an ad result. + * @param {Element} result Result to be checked. + * @return {boolean} Whether or not the element is an ad result. + * @override + */ +cvox.AdResult.prototype.isType = function(result) { + var ADS_CLASS = 'ads-ad'; + return result.className === ADS_CLASS; +}; + +/** + * Speak an ad result. + * @param {Element} result Ad result to be spoken. + * @return {boolean} Whether or not the result was spoken. + * @override + */ +cvox.AdResult.prototype.speak = function(result) { + if (!result) { + return false; + } + var HEADER_SELECT = 'h3'; + var DESC_SELECT = '.ads-creative'; + var URL_SELECT = '.ads-visurl'; + var AD_SELECTS = [ + { select: HEADER_SELECT }, + { select: DESC_SELECT }, + { select: URL_SELECT }]; + cvox.SearchResults.speakResultBySelectTexts(result, AD_SELECTS); + return true; +}; + +/** + * To add new result types, create a new object with the following properties: + * isType: Function to indicate if an element is the object's type. + * speak: Function that takes in a result and speaks the type to the user. + * getURL: Function that takes in a result and extracts the URL to follow. + */ +cvox.SearchResults.RESULT_TYPES = [ + cvox.UnknownResult, + cvox.NormalResult, + cvox.KnowResult, + cvox.WeatherResult, + cvox.AdResult, + cvox.CalcResult, + cvox.GameResult, + cvox.ImageResult, + cvox.CategoryResult +]; |