1 // Copyright 2012 Google Inc. All Rights Reserved.
4 * @fileoverview Provides different rules for each type of result.
5 * @author peterxiao@google.com (Peter Xiao)
8 goog
.provide('cvox.SearchResults');
9 goog
.provide('cvox.UnknownResult');
11 goog
.require('cvox.AbstractResult');
12 goog
.require('cvox.ChromeVox');
13 goog
.require('cvox.SearchUtil');
18 cvox
.SearchResults = function() {
22 * Speaks a result based on given selectors.
23 * @param {Element} result Search result to be spoken.
24 * @param {Array} selectTexts Array of selectors or text to speak.
26 cvox
.SearchResults
.speakResultBySelectTexts = function(result
, selectTexts
) {
27 for (var j
= 0; j
< selectTexts
.length
; j
++) {
28 var selectText
= selectTexts
[j
];
29 if (selectText
.select
) {
30 var elems
= result
.querySelectorAll(selectText
.select
);
31 for (var i
= 0; i
< elems
.length
; i
++) {
32 cvox
.ChromeVox
.speakNode(elems
.item(i
), 1);
35 if (selectText
.text
) {
36 cvox
.ChromeVox
.tts
.speak(selectText
.text
, 1);
42 * Unknown Result Type. This is used if we don't know what to do.
44 * @extends {cvox.AbstractResult}
46 cvox
.UnknownResult = function() {
48 goog
.inherits(cvox
.UnknownResult
, cvox
.AbstractResult
);
50 /* Normal Result Type. */
53 * @extends {cvox.AbstractResult}
55 cvox
.NormalResult = function() {
57 goog
.inherits(cvox
.NormalResult
, cvox
.AbstractResult
);
60 * Checks the result if it is a normal result.
61 * @param {Element} result Result to be checked.
62 * @return {boolean} Whether or not the element is a normal result.
65 cvox
.NormalResult
.prototype.isType = function(result
) {
66 var NORMAL_SELECT
= '.rc';
67 return result
.querySelector(NORMAL_SELECT
) !== null;
71 * Speak a normal search result.
72 * @param {Element} result Normal result to be spoken.
73 * @return {boolean} Whether or not the result was spoken.
76 cvox
.NormalResult
.prototype.speak = function(result
) {
80 var NORMAL_TITLE_SELECT
= '.rc .r';
81 var NORMAL_URL_SELECT
= '.kv';
82 var NORMAL_DESC_SELECT
= '.rc .st';
83 var SITE_LINK_SELECT
= '.osl';
84 var MORE_RESULTS_SELECT
= '.sld';
85 var MORE_RESULTS_LINK_SELECT
= '.mrf';
87 var NORMAL_SELECTORS
= [
88 { select
: NORMAL_TITLE_SELECT
},
89 { select
: NORMAL_DESC_SELECT
},
90 { select
: NORMAL_URL_SELECT
},
91 { select
: SITE_LINK_SELECT
},
92 { select
: MORE_RESULTS_SELECT
},
93 { select
: MORE_RESULTS_LINK_SELECT
}];
94 cvox
.SearchResults
.speakResultBySelectTexts(result
, NORMAL_SELECTORS
);
96 var DISCUSS_TITLE_SELECT
= '.mas-1st-col div';
97 var DISCUSS_DATE_SELECT
= '.mas-col div';
98 var discussTitles
= result
.querySelectorAll(DISCUSS_TITLE_SELECT
);
99 var discussDates
= result
.querySelectorAll(DISCUSS_DATE_SELECT
);
100 for (var i
= 0; i
< discussTitles
.length
; i
++) {
101 cvox
.ChromeVox
.speakNode(discussTitles
.item(i
), 1);
102 cvox
.ChromeVox
.speakNode(discussDates
.item(i
), 1);
110 * @extends {cvox.AbstractResult}
112 cvox
.WeatherResult = function() {
114 goog
.inherits(cvox
.WeatherResult
, cvox
.AbstractResult
);
117 * Checks the result if it is a weather result.
118 * @param {Element} result Result to be checked.
119 * @return {boolean} Whether or not the element is a weather result.
122 cvox
.WeatherResult
.prototype.isType = function(result
) {
123 var WEATHER_SELECT
= '#wob_wc';
124 return result
.querySelector(WEATHER_SELECT
) !== null;
128 * Speak a weather forecast.
129 * @param {Element} forecast Weather forecast to be spoken.
131 cvox
.WeatherResult
.speakForecast = function(forecast
) {
135 var FORE_DAY_SELECT
= '.vk_lgy';
136 var FORE_COND_SELECT
= 'img';
137 var FORE_HIGH_SELECT
= '.vk_gy';
138 var FORE_LOW_SELECT
= '.vk_lgy';
140 var FORE_SELECTORS
= [
141 { select
: FORE_DAY_SELECT
},
142 { select
: FORE_COND_SELECT
},
143 { select
: FORE_HIGH_SELECT
},
144 { select
: FORE_LOW_SELECT
}
146 cvox
.SearchResults
.speakResultBySelectTexts(forecast
, FORE_SELECTORS
);
150 * Speak a weather search result.
151 * @param {Element} result Weather result to be spoken.
152 * @return {boolean} Whether or not the result was spoken.
155 cvox
.WeatherResult
.prototype.speak = function(result
) {
159 /* TODO(peterxiao): Internationalization? */
160 var WEATHER_INTRO
= 'The weather forcast for';
161 var WEATHER_TEMP_UNITS
= 'degrees fahrenheit';
162 var WEATHER_PREC_INTRO
= 'precipitation is';
163 var WEATHER_HUMID_INTRO
= 'humidity is';
164 var WEATHER_WIND_INTRO
= 'wind is';
165 var FORE_INTRO
= 'Forecasts for this week';
166 var WEATHER_LOC_SELECT
= '.vk_h';
167 var WEATHER_WHEN_SELECT
= '#wob_dts';
168 var WEATHER_COND_SELECT
= '#wob_dc';
169 var WEATHER_TEMP_SELECT
= '#wob_tm';
170 var WEATHER_PREC_SELECT
= '#wob_pp';
171 var WEATHER_HUMID_SELECT
= '#wob_hm';
172 var WEATHER_WIND_SELECT
= '#wob_ws';
174 var WEATHER_SELECT_TEXTS
= [
175 { text
: WEATHER_INTRO
},
176 { select
: WEATHER_LOC_SELECT
},
177 { select
: WEATHER_WHEN_SELECT
},
178 { select
: WEATHER_COND_SELECT
},
179 { select
: WEATHER_TEMP_SELECT
},
180 { text
: WEATHER_TEMP_UNITS
},
181 { text
: WEATHER_PREC_INTRO
},
182 { select
: WEATHER_PREC_SELECT
},
183 { text
: WEATHER_HUMID_INTRO
},
184 { select
: WEATHER_HUMID_SELECT
},
185 { text
: WEATHER_WIND_INTRO
},
186 { select
: WEATHER_WIND_SELECT
}
188 cvox
.SearchResults
.speakResultBySelectTexts(result
, WEATHER_SELECT_TEXTS
);
190 var WEATHER_FORCAST_CLASS
= 'wob_df';
191 var forecasts
= result
.getElementsByClassName(WEATHER_FORCAST_CLASS
);
192 cvox
.ChromeVox
.tts
.speak(FORE_INTRO
, 1);
193 for (var i
= 0; i
< forecasts
.length
; i
++) {
194 var forecast
= forecasts
.item(i
);
195 cvox
.WeatherResult
.speakForecast(forecast
);
200 /* Knowledge Panel Result */
203 * @extends {cvox.AbstractResult}
205 cvox
.KnowResult = function() {
207 goog
.inherits(cvox
.KnowResult
, cvox
.AbstractResult
);
210 * Checks the result if it is a know result.
211 * @param {Element} result Result to be checked.
212 * @return {boolean} Whether or not the element is a know result.
215 cvox
.KnowResult
.prototype.isType = function(result
) {
216 var KNOP_SELECT
= '.kno-ec';
217 return result
.querySelector(KNOP_SELECT
) !== null;
221 * Speak a knowledge panel search result.
222 * @param {Element} result Knowledge panel result to be spoken.
223 * @return {boolean} Whether or not the result was spoken.
226 cvox
.KnowResult
.prototype.speak = function(result
) {
227 cvox
.ChromeVox
.speakNode(result
, 1);
232 * Extracts the wikipedia URL from knowledge panel.
233 * @param {Element} result Result to extract from.
234 * @return {?string} URL.
237 cvox
.KnowResult
.prototype.getURL = function(result
) {
238 var LINK_SELECTOR
= '.q';
239 return cvox
.SearchUtil
.extractURL(result
.querySelector(LINK_SELECTOR
));
243 * Extracts the node to sync to in the knowledge panel.
244 * @param {Element} result Result.
245 * @return {?Node} Node to sync to.
248 cvox
.KnowResult
.prototype.getSyncNode = function(result
) {
249 var HEADER_SELECTOR
= '.kno-ecr-pt';
250 return result
.querySelector(HEADER_SELECTOR
);
253 /* Calculator Type */
256 * @extends {cvox.AbstractResult}
258 cvox
.CalcResult = function() {
260 goog
.inherits(cvox
.CalcResult
, cvox
.AbstractResult
);
263 * Checks the result if it is a calculator result.
264 * @param {Element} result Result to be checked.
265 * @return {boolean} Whether or not the element is a calculator result.
268 cvox
.CalcResult
.prototype.isType = function(result
) {
269 var CALC_SELECT
= '#cwmcwd';
270 return result
.querySelector(CALC_SELECT
) !== null;
274 * Speak a calculator search result.
275 * @param {Element} result Calculator result to be spoken.
276 * @return {boolean} Whether or not the result was spoken.
279 cvox
.CalcResult
.prototype.speak = function(result
) {
283 var CALC_QUERY_SELECT
= '#cwles';
284 var CALC_RESULT_SELECT
= '#cwos';
285 var CALC_SELECTORS
= [
286 { select
: CALC_QUERY_SELECT
},
287 { select
: CALC_RESULT_SELECT
}
289 cvox
.SearchResults
.speakResultBySelectTexts(result
, CALC_SELECTORS
);
296 * @extends {cvox.AbstractResult}
298 cvox
.GameResult = function() {
300 goog
.inherits(cvox
.GameResult
, cvox
.AbstractResult
);
303 * Checks the result if it is a game result.
304 * @param {Element} result Result to be checked.
305 * @return {boolean} Whether or not the element is a game result.
308 cvox
.GameResult
.prototype.isType = function(result
) {
309 var GAME_SELECT
= '.xpdbox';
310 return result
.querySelector(GAME_SELECT
) !== null;
316 * @extends {cvox.AbstractResult}
318 cvox
.ImageResult = function() {
320 goog
.inherits(cvox
.ImageResult
, cvox
.AbstractResult
);
323 * Checks the result if it is a image result.
324 * @param {Element} result Result to be checked.
325 * @return {boolean} Whether or not the element is a image result.
328 cvox
.ImageResult
.prototype.isType = function(result
) {
329 var IMAGE_CLASSES
= 'rg_di';
330 return result
.className
=== IMAGE_CLASSES
;
334 * Speak an image result.
335 * @param {Element} result Image result to be spoken.
336 * @return {boolean} Whether or not the result was spoken.
339 cvox
.ImageResult
.prototype.speak = function(result
) {
343 /* Grab image result metadata. */
344 var META_CLASS
= 'rg_meta';
345 var metaDiv
= result
.querySelector('.' + META_CLASS
);
346 var metaJSON
= metaDiv
.innerHTML
;
347 var metaData
= JSON
.parse(metaJSON
);
349 var imageSelectTexts
= [];
351 var filename
= metaData
['fn'];
353 imageSelectTexts
.push({ text
: filename
});
356 var rawDimensions
= metaData
['is'];
358 /* Dimensions contain HTML codes, so we convert them. */
359 var tmpDiv
= document
.createElement('div');
360 tmpDiv
.innerHTML
= rawDimensions
;
361 var dimensions
= tmpDiv
.textContent
|| tmpDiv
.innerText
;
362 imageSelectTexts
.push({ text
: dimensions
});
365 var url
= metaData
['isu'];
367 imageSelectTexts
.push({ text
: url
});
369 cvox
.SearchResults
.speakResultBySelectTexts(result
, imageSelectTexts
);
373 /* Category Result */
376 * @extends {cvox.AbstractResult}
378 cvox
.CategoryResult = function() {
380 goog
.inherits(cvox
.CategoryResult
, cvox
.AbstractResult
);
383 * Checks the result if it is a category result.
384 * @param {Element} result Result to be checked.
385 * @return {boolean} Whether or not the element is a category result.
388 cvox
.CategoryResult
.prototype.isType = function(result
) {
389 var CATEGORY_CLASSES
= 'rg_fbl nj';
390 return result
.className
=== CATEGORY_CLASSES
;
394 * Speak a category result.
395 * @param {Element} result Category result to be spoken.
396 * @return {boolean} Whether or not the result was spoken.
399 cvox
.CategoryResult
.prototype.speak = function(result
) {
403 var LABEL_SELECT
= '.rg_bb_label';
404 var label
= result
.querySelector(LABEL_SELECT
);
405 cvox
.ChromeVox
.speakNode(label
, 1);
412 * @extends {cvox.AbstractResult}
414 cvox
.AdResult = function() {
416 goog
.inherits(cvox
.AdResult
, cvox
.AbstractResult
);
419 * Checks the result if it is an ad result.
420 * @param {Element} result Result to be checked.
421 * @return {boolean} Whether or not the element is an ad result.
424 cvox
.AdResult
.prototype.isType = function(result
) {
425 var ADS_CLASS
= 'ads-ad';
426 return result
.className
=== ADS_CLASS
;
430 * Speak an ad result.
431 * @param {Element} result Ad result to be spoken.
432 * @return {boolean} Whether or not the result was spoken.
435 cvox
.AdResult
.prototype.speak = function(result
) {
439 var HEADER_SELECT
= 'h3';
440 var DESC_SELECT
= '.ads-creative';
441 var URL_SELECT
= '.ads-visurl';
443 { select
: HEADER_SELECT
},
444 { select
: DESC_SELECT
},
445 { select
: URL_SELECT
}];
446 cvox
.SearchResults
.speakResultBySelectTexts(result
, AD_SELECTS
);
451 * To add new result types, create a new object with the following properties:
452 * isType: Function to indicate if an element is the object's type.
453 * speak: Function that takes in a result and speaks the type to the user.
454 * getURL: Function that takes in a result and extracts the URL to follow.
456 cvox
.SearchResults
.RESULT_TYPES
= [