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
3 // found in the LICENSE file.
7 * @fileoverview Provides different rules for each type of result.
10 goog
.provide('cvox.SearchResults');
11 goog
.provide('cvox.UnknownResult');
13 goog
.require('cvox.AbstractResult');
14 goog
.require('cvox.ChromeVox');
15 goog
.require('cvox.SearchUtil');
20 cvox
.SearchResults = function() {
24 * Speaks a result based on given selectors.
25 * @param {Element} result Search result to be spoken.
26 * @param {Array} selectTexts Array of selectors or text to speak.
28 cvox
.SearchResults
.speakResultBySelectTexts = function(result
, selectTexts
) {
29 for (var j
= 0; j
< selectTexts
.length
; j
++) {
30 var selectText
= selectTexts
[j
];
31 if (selectText
.select
) {
32 var elems
= result
.querySelectorAll(selectText
.select
);
33 for (var i
= 0; i
< elems
.length
; i
++) {
34 cvox
.ChromeVox
.speakNode(elems
.item(i
), cvox
.QueueMode
.QUEUE
);
37 if (selectText
.text
) {
38 cvox
.ChromeVox
.tts
.speak(selectText
.text
, cvox
.QueueMode
.QUEUE
);
44 * Unknown Result Type. This is used if we don't know what to do.
46 * @extends {cvox.AbstractResult}
48 cvox
.UnknownResult = function() {
50 goog
.inherits(cvox
.UnknownResult
, cvox
.AbstractResult
);
52 /* Normal Result Type. */
55 * @extends {cvox.AbstractResult}
57 cvox
.NormalResult = function() {
59 goog
.inherits(cvox
.NormalResult
, cvox
.AbstractResult
);
62 * Checks the result if it is a normal result.
63 * @param {Element} result Result to be checked.
64 * @return {boolean} Whether or not the element is a normal result.
67 cvox
.NormalResult
.prototype.isType = function(result
) {
68 var NORMAL_SELECT
= '.rc';
69 return result
.querySelector(NORMAL_SELECT
) !== null;
73 * Speak a normal search result.
74 * @param {Element} result Normal result to be spoken.
75 * @return {boolean} Whether or not the result was spoken.
78 cvox
.NormalResult
.prototype.speak = function(result
) {
82 var NORMAL_TITLE_SELECT
= '.rc .r';
83 var NORMAL_URL_SELECT
= '.kv';
84 var NORMAL_DESC_SELECT
= '.rc .st';
85 var SITE_LINK_SELECT
= '.osl';
86 var MORE_RESULTS_SELECT
= '.sld';
87 var MORE_RESULTS_LINK_SELECT
= '.mrf';
89 var NORMAL_SELECTORS
= [
90 { select
: NORMAL_TITLE_SELECT
},
91 { select
: NORMAL_DESC_SELECT
},
92 { select
: NORMAL_URL_SELECT
},
93 { select
: SITE_LINK_SELECT
},
94 { select
: MORE_RESULTS_SELECT
},
95 { select
: MORE_RESULTS_LINK_SELECT
}];
96 cvox
.SearchResults
.speakResultBySelectTexts(result
, NORMAL_SELECTORS
);
98 var DISCUSS_TITLE_SELECT
= '.mas-1st-col div';
99 var DISCUSS_DATE_SELECT
= '.mas-col div';
100 var discussTitles
= result
.querySelectorAll(DISCUSS_TITLE_SELECT
);
101 var discussDates
= result
.querySelectorAll(DISCUSS_DATE_SELECT
);
102 for (var i
= 0; i
< discussTitles
.length
; i
++) {
103 cvox
.ChromeVox
.speakNode(discussTitles
.item(i
), cvox
.QueueMode
.QUEUE
);
104 cvox
.ChromeVox
.speakNode(discussDates
.item(i
), cvox
.QueueMode
.QUEUE
);
112 * @extends {cvox.AbstractResult}
114 cvox
.WeatherResult = function() {
116 goog
.inherits(cvox
.WeatherResult
, cvox
.AbstractResult
);
119 * Checks the result if it is a weather result.
120 * @param {Element} result Result to be checked.
121 * @return {boolean} Whether or not the element is a weather result.
124 cvox
.WeatherResult
.prototype.isType = function(result
) {
125 var WEATHER_SELECT
= '#wob_wc';
126 return result
.querySelector(WEATHER_SELECT
) !== null;
130 * Speak a weather forecast.
131 * @param {Element} forecast Weather forecast to be spoken.
133 cvox
.WeatherResult
.speakForecast = function(forecast
) {
137 var FORE_DAY_SELECT
= '.vk_lgy';
138 var FORE_COND_SELECT
= 'img';
139 var FORE_HIGH_SELECT
= '.vk_gy';
140 var FORE_LOW_SELECT
= '.vk_lgy';
142 var FORE_SELECTORS
= [
143 { select
: FORE_DAY_SELECT
},
144 { select
: FORE_COND_SELECT
},
145 { select
: FORE_HIGH_SELECT
},
146 { select
: FORE_LOW_SELECT
}
148 cvox
.SearchResults
.speakResultBySelectTexts(forecast
, FORE_SELECTORS
);
152 * Speak a weather search result.
153 * @param {Element} result Weather result to be spoken.
154 * @return {boolean} Whether or not the result was spoken.
157 cvox
.WeatherResult
.prototype.speak = function(result
) {
161 /* TODO(peterxiao): Internationalization? */
162 var WEATHER_INTRO
= 'The weather forcast for';
163 var WEATHER_TEMP_UNITS
= 'degrees fahrenheit';
164 var WEATHER_PREC_INTRO
= 'precipitation is';
165 var WEATHER_HUMID_INTRO
= 'humidity is';
166 var WEATHER_WIND_INTRO
= 'wind is';
167 var FORE_INTRO
= 'Forecasts for this week';
168 var WEATHER_LOC_SELECT
= '.vk_h';
169 var WEATHER_WHEN_SELECT
= '#wob_dts';
170 var WEATHER_COND_SELECT
= '#wob_dc';
171 var WEATHER_TEMP_SELECT
= '#wob_tm';
172 var WEATHER_PREC_SELECT
= '#wob_pp';
173 var WEATHER_HUMID_SELECT
= '#wob_hm';
174 var WEATHER_WIND_SELECT
= '#wob_ws';
176 var WEATHER_SELECT_TEXTS
= [
177 { text
: WEATHER_INTRO
},
178 { select
: WEATHER_LOC_SELECT
},
179 { select
: WEATHER_WHEN_SELECT
},
180 { select
: WEATHER_COND_SELECT
},
181 { select
: WEATHER_TEMP_SELECT
},
182 { text
: WEATHER_TEMP_UNITS
},
183 { text
: WEATHER_PREC_INTRO
},
184 { select
: WEATHER_PREC_SELECT
},
185 { text
: WEATHER_HUMID_INTRO
},
186 { select
: WEATHER_HUMID_SELECT
},
187 { text
: WEATHER_WIND_INTRO
},
188 { select
: WEATHER_WIND_SELECT
}
190 cvox
.SearchResults
.speakResultBySelectTexts(result
, WEATHER_SELECT_TEXTS
);
192 var WEATHER_FORCAST_CLASS
= 'wob_df';
193 var forecasts
= result
.getElementsByClassName(WEATHER_FORCAST_CLASS
);
194 cvox
.ChromeVox
.tts
.speak(FORE_INTRO
, cvox
.QueueMode
.QUEUE
);
195 for (var i
= 0; i
< forecasts
.length
; i
++) {
196 var forecast
= forecasts
.item(i
);
197 cvox
.WeatherResult
.speakForecast(forecast
);
202 /* Knowledge Panel Result */
205 * @extends {cvox.AbstractResult}
207 cvox
.KnowResult = function() {
209 goog
.inherits(cvox
.KnowResult
, cvox
.AbstractResult
);
212 * Checks the result if it is a know result.
213 * @param {Element} result Result to be checked.
214 * @return {boolean} Whether or not the element is a know result.
217 cvox
.KnowResult
.prototype.isType = function(result
) {
218 var KNOP_SELECT
= '.kno-ec';
219 return result
.querySelector(KNOP_SELECT
) !== null;
223 * Speak a knowledge panel search result.
224 * @param {Element} result Knowledge panel result to be spoken.
225 * @return {boolean} Whether or not the result was spoken.
228 cvox
.KnowResult
.prototype.speak = function(result
) {
229 cvox
.ChromeVox
.speakNode(result
, cvox
.QueueMode
.QUEUE
);
234 * Extracts the wikipedia URL from knowledge panel.
235 * @param {Element} result Result to extract from.
236 * @return {?string} URL.
239 cvox
.KnowResult
.prototype.getURL = function(result
) {
240 var LINK_SELECTOR
= '.q';
241 return cvox
.SearchUtil
.extractURL(result
.querySelector(LINK_SELECTOR
));
245 * Extracts the node to sync to in the knowledge panel.
246 * @param {Element} result Result.
247 * @return {?Node} Node to sync to.
250 cvox
.KnowResult
.prototype.getSyncNode = function(result
) {
251 var HEADER_SELECTOR
= '.kno-ecr-pt';
252 return result
.querySelector(HEADER_SELECTOR
);
255 /* Calculator Type */
258 * @extends {cvox.AbstractResult}
260 cvox
.CalcResult = function() {
262 goog
.inherits(cvox
.CalcResult
, cvox
.AbstractResult
);
265 * Checks the result if it is a calculator result.
266 * @param {Element} result Result to be checked.
267 * @return {boolean} Whether or not the element is a calculator result.
270 cvox
.CalcResult
.prototype.isType = function(result
) {
271 var CALC_SELECT
= '#cwmcwd';
272 return result
.querySelector(CALC_SELECT
) !== null;
276 * Speak a calculator search result.
277 * @param {Element} result Calculator result to be spoken.
278 * @return {boolean} Whether or not the result was spoken.
281 cvox
.CalcResult
.prototype.speak = function(result
) {
285 var CALC_QUERY_SELECT
= '#cwles';
286 var CALC_RESULT_SELECT
= '#cwos';
287 var CALC_SELECTORS
= [
288 { select
: CALC_QUERY_SELECT
},
289 { select
: CALC_RESULT_SELECT
}
291 cvox
.SearchResults
.speakResultBySelectTexts(result
, CALC_SELECTORS
);
298 * @extends {cvox.AbstractResult}
300 cvox
.GameResult = function() {
302 goog
.inherits(cvox
.GameResult
, cvox
.AbstractResult
);
305 * Checks the result if it is a game result.
306 * @param {Element} result Result to be checked.
307 * @return {boolean} Whether or not the element is a game result.
310 cvox
.GameResult
.prototype.isType = function(result
) {
311 var GAME_SELECT
= '.xpdbox';
312 return result
.querySelector(GAME_SELECT
) !== null;
318 * @extends {cvox.AbstractResult}
320 cvox
.ImageResult = function() {
322 goog
.inherits(cvox
.ImageResult
, cvox
.AbstractResult
);
325 * Checks the result if it is a image result.
326 * @param {Element} result Result to be checked.
327 * @return {boolean} Whether or not the element is a image result.
330 cvox
.ImageResult
.prototype.isType = function(result
) {
331 var IMAGE_CLASSES
= 'rg_di';
332 return result
.className
=== IMAGE_CLASSES
;
336 * Speak an image result.
337 * @param {Element} result Image result to be spoken.
338 * @return {boolean} Whether or not the result was spoken.
341 cvox
.ImageResult
.prototype.speak = function(result
) {
345 /* Grab image result metadata. */
346 var META_CLASS
= 'rg_meta';
347 var metaDiv
= result
.querySelector('.' + META_CLASS
);
348 var metaJSON
= metaDiv
.innerHTML
;
349 var metaData
= JSON
.parse(metaJSON
);
351 var imageSelectTexts
= [];
353 var filename
= metaData
['fn'];
355 imageSelectTexts
.push({ text
: filename
});
358 var rawDimensions
= metaData
['is'];
360 /* Dimensions contain HTML codes, so we convert them. */
361 var tmpDiv
= document
.createElement('div');
362 tmpDiv
.innerHTML
= rawDimensions
;
363 var dimensions
= tmpDiv
.textContent
|| tmpDiv
.innerText
;
364 imageSelectTexts
.push({ text
: dimensions
});
367 var url
= metaData
['isu'];
369 imageSelectTexts
.push({ text
: url
});
371 cvox
.SearchResults
.speakResultBySelectTexts(result
, imageSelectTexts
);
375 /* Category Result */
378 * @extends {cvox.AbstractResult}
380 cvox
.CategoryResult = function() {
382 goog
.inherits(cvox
.CategoryResult
, cvox
.AbstractResult
);
385 * Checks the result if it is a category result.
386 * @param {Element} result Result to be checked.
387 * @return {boolean} Whether or not the element is a category result.
390 cvox
.CategoryResult
.prototype.isType = function(result
) {
391 var CATEGORY_CLASSES
= 'rg_fbl nj';
392 return result
.className
=== CATEGORY_CLASSES
;
396 * Speak a category result.
397 * @param {Element} result Category result to be spoken.
398 * @return {boolean} Whether or not the result was spoken.
401 cvox
.CategoryResult
.prototype.speak = function(result
) {
405 var LABEL_SELECT
= '.rg_bb_label';
406 var label
= result
.querySelector(LABEL_SELECT
);
407 cvox
.ChromeVox
.speakNode(label
, cvox
.QueueMode
.QUEUE
);
414 * @extends {cvox.AbstractResult}
416 cvox
.AdResult = function() {
418 goog
.inherits(cvox
.AdResult
, cvox
.AbstractResult
);
421 * Checks the result if it is an ad result.
422 * @param {Element} result Result to be checked.
423 * @return {boolean} Whether or not the element is an ad result.
426 cvox
.AdResult
.prototype.isType = function(result
) {
427 var ADS_CLASS
= 'ads-ad';
428 return result
.className
=== ADS_CLASS
;
432 * Speak an ad result.
433 * @param {Element} result Ad result to be spoken.
434 * @return {boolean} Whether or not the result was spoken.
437 cvox
.AdResult
.prototype.speak = function(result
) {
441 var HEADER_SELECT
= 'h3';
442 var DESC_SELECT
= '.ads-creative';
443 var URL_SELECT
= '.ads-visurl';
445 { select
: HEADER_SELECT
},
446 { select
: DESC_SELECT
},
447 { select
: URL_SELECT
}];
448 cvox
.SearchResults
.speakResultBySelectTexts(result
, AD_SELECTS
);
453 * To add new result types, create a new object with the following properties:
454 * isType: Function to indicate if an element is the object's type.
455 * speak: Function that takes in a result and speaks the type to the user.
456 * getURL: Function that takes in a result and extracts the URL to follow.
458 cvox
.SearchResults
.RESULT_TYPES
= [