1 // Copyright 2013 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.
8 cr.define('cr.translateInternals', function() {
10 var detectionLogs_ = null;
12 function detectionLogs() {
13 if (detectionLogs_ === null)
15 return detectionLogs_;
19 * Initializes UI and sends a message to the browser for
22 function initialize() {
23 cr.ui.decorate('tabbox', cr.ui.TabBox);
24 chrome.send('requestInfo');
26 var button = $('detection-logs-dump');
27 button.addEventListener('click', onDetectionLogsDump);
29 var tabpanelNodeList = document.getElementsByTagName('tabpanel');
30 var tabpanels = Array.prototype.slice.call(tabpanelNodeList, 0);
31 var tabpanelIds = tabpanels.map(function(tab) {
35 var tabNodeList = document.getElementsByTagName('tab');
36 var tabs = Array.prototype.slice.call(tabNodeList, 0);
37 tabs.forEach(function(tab) {
38 tab.onclick = function(e) {
39 var tabbox = document.querySelector('tabbox');
40 var tabpanel = tabpanels[tabbox.selectedIndex];
41 var hash = tabpanel.id.match(/(?:^tabpanel-)(.+)/)[1];
42 window.location.hash = hash;
46 var activateTabByHash = function() {
47 var hash = window.location.hash;
49 // Remove the first character '#'.
50 hash = hash.substring(1);
52 var id = 'tabpanel-' + hash;
53 if (tabpanelIds.indexOf(id) == -1)
56 $(id).selected = true;
59 window.onhashchange = activateTabByHash;
64 * Creates a button to dismiss an item.
66 * @param {Function} func Callback called when the button is clicked.
68 function createDismissingButton(func) {
69 var button = document.createElement('button');
70 button.textContent = 'X';
71 button.classList.add('dismissing');
72 button.addEventListener('click', function(e) {
80 * Creates a new LI element with a button to dismiss the item.
82 * @param {string} text The lable of the LI element.
83 * @param {Function} func Callback called when the button is clicked.
85 function createLIWithDismissingButton(text, func) {
86 var span = document.createElement('span');
87 span.textContent = text;
89 var li = document.createElement('li');
91 li.appendChild(createDismissingButton(func));
96 * Formats the language name to a human-readable text. For example, if
97 * |langCode| is 'en', this may return 'en (English)'.
99 * @param {string} langCode ISO 639 language code.
100 * @return {string} The formatted string.
102 function formatLanguageCode(langCode) {
103 var key = 'language-' + langCode;
104 if (loadTimeData.valueExists(key)) {
105 var langName = loadTimeData.getString(key);
106 return langCode + ' (' + langName + ')';
113 * Formats the error type to a human-readable text.
115 * @param {string} error Translation error type from the browser.
116 * @return {string} The formatted string.
118 function formatTranslateErrorsType(error) {
119 // This list is from chrome/common/translate/translate_errors.h.
120 // If this header file is updated, the below list also should be updated.
124 2: 'Initialization Error',
125 3: 'Unknown Language',
126 4: 'Unsupported Language',
127 5: 'Identical Languages',
128 6: 'Translation Error',
129 7: 'Translation Timeout',
130 8: 'Unexpected Script Error',
132 10: 'Script Load Error',
135 if (error < 0 || errorStrs.length <= error) {
136 console.error('Invalid error code:', error);
137 return 'Invalid Error Code';
139 return errorStrs[error];
143 * Handles the message of 'prefsUpdated' from the browser.
145 * @param {Object} detail the object which represents pref values.
147 function onPrefsUpdated(detail) {
150 ul = document.querySelector('#prefs-blocked-languages ul');
153 if ('translate_blocked_languages' in detail) {
154 var langs = detail['translate_blocked_languages'];
156 langs.forEach(function(langCode) {
157 var text = formatLanguageCode(langCode);
159 var li = createLIWithDismissingButton(text, function() {
160 chrome.send('removePrefItem',
161 ['blocked_languages', langCode]);
167 ul = document.querySelector('#prefs-language-blacklist ul');
170 if ('translate_language_blacklist' in detail) {
171 var langs = detail['translate_language_blacklist'];
173 langs.forEach(function(langCode) {
174 var text = formatLanguageCode(langCode);
176 var li = createLIWithDismissingButton(text, function() {
177 chrome.send('removePrefItem',
178 ['language_blacklist', langCode]);
184 ul = document.querySelector('#prefs-site-blacklist ul');
187 if ('translate_site_blacklist' in detail) {
188 var sites = detail['translate_site_blacklist'];
190 sites.forEach(function(site) {
191 var li = createLIWithDismissingButton(site, function() {
192 chrome.send('removePrefItem', ['site_blacklist', site]);
198 ul = document.querySelector('#prefs-whitelists ul');
201 if ('translate_whitelists' in detail) {
202 var pairs = detail['translate_whitelists'];
204 Object.keys(pairs).forEach(function(fromLangCode) {
205 var toLangCode = pairs[fromLangCode];
206 var text = formatLanguageCode(fromLangCode) + ' \u2192 ' +
207 formatLanguageCode(toLangCode);
209 var li = createLIWithDismissingButton(text, function() {
210 chrome.send('removePrefItem',
211 ['whitelists', fromLangCode, toLangCode]);
217 var p = $('prefs-too-often-denied');
218 p.classList.toggle('prefs-setting-disabled',
219 !detail['translate_too_often_denied']);
220 p.appendChild(createDismissingButton(
221 chrome.send.bind(null, 'removePrefItem', ['too_often_denied'])));
223 p = document.querySelector('#prefs-dump p');
224 var content = JSON.stringify(detail, null, 2);
225 p.textContent = content;
229 * Handles the message of 'supportedLanguagesUpdated' from the browser.
231 * @param {Object} details the object which represents the supported
232 * languages by the Translate server.
234 function onSupportedLanguagesUpdated(details) {
236 $('prefs-supported-languages-last-updated').querySelector('span');
237 span.textContent = formatDate(new Date(details['last_updated']));
239 var ul = $('prefs-supported-languages-languages');
241 var languages = details['languages'];
242 for (var i = 0; i < languages.length; i++) {
243 var language = languages[i];
244 var li = document.createElement('li');
246 var text = formatLanguageCode(language);
247 if (details['alpha_languages'].indexOf(language) != -1)
256 * Addes '0's to |number| as a string. |width| is length of the string
259 * @param {string} number The number to be converted into a string.
260 * @param {number} width The width of the returned string.
261 * @return {string} The formatted string.
263 function padWithZeros(number, width) {
264 var numberStr = number.toString();
265 var restWidth = width - numberStr.length;
269 return Array(restWidth + 1).join('0') + numberStr;
273 * Formats |date| as a Date object into a string. The format is like
274 * '2006-01-02 15:04:05'.
276 * @param {Date} date Date to be formatted.
277 * @return {string} The formatted string.
279 function formatDate(date) {
280 var year = date.getFullYear();
281 var month = date.getMonth() + 1;
282 var day = date.getDate();
283 var hour = date.getHours();
284 var minute = date.getMinutes();
285 var second = date.getSeconds();
287 var yearStr = padWithZeros(year, 4);
288 var monthStr = padWithZeros(month, 2);
289 var dayStr = padWithZeros(day, 2);
290 var hourStr = padWithZeros(hour, 2);
291 var minuteStr = padWithZeros(minute, 2);
292 var secondStr = padWithZeros(second, 2);
294 var str = yearStr + '-' + monthStr + '-' + dayStr + ' ' +
295 hourStr + ':' + minuteStr + ':' + secondStr;
301 * Appends a new TD element to the specified element.
303 * @param {string} parent The element to which a new TD element is appended.
304 * @param {string} content The text content of the element.
305 * @param {string} className The class name of the element.
307 function appendTD(parent, content, className) {
308 var td = document.createElement('td');
309 td.textContent = content;
310 td.className = className;
311 parent.appendChild(td);
315 * Handles the message of 'languageDetectionInfoAdded' from the
318 * @param {Object} detail The object which represents the logs.
320 function onLanguageDetectionInfoAdded(detail) {
321 cr.translateInternals.detectionLogs().push(detail);
323 var tr = document.createElement('tr');
325 appendTD(tr, formatDate(new Date(detail['time'])), 'detection-logs-time');
326 appendTD(tr, detail['url'], 'detection-logs-url');
327 appendTD(tr, formatLanguageCode(detail['content_language']),
328 'detection-logs-content-language');
329 appendTD(tr, formatLanguageCode(detail['cld_language']),
330 'detection-logs-cld-language');
331 appendTD(tr, detail['is_cld_reliable'], 'detection-logs-is-cld-reliable');
332 appendTD(tr, detail['has_notranslate'], 'detection-logs-has-notranslate');
333 appendTD(tr, formatLanguageCode(detail['html_root_language']),
334 'detection-logs-html-root-language');
335 appendTD(tr, formatLanguageCode(detail['adopted_language']),
336 'detection-logs-adopted-language');
337 appendTD(tr, formatLanguageCode(detail['content']),
338 'detection-logs-content');
340 // TD (and TR) can't use the CSS property 'max-height', so DIV
341 // in the content is needed.
342 var contentTD = tr.querySelector('.detection-logs-content');
343 var div = document.createElement('div');
344 div.textContent = contentTD.textContent;
345 contentTD.textContent = '';
346 contentTD.appendChild(div);
348 var tabpanel = $('tabpanel-detection-logs');
349 var tbody = tabpanel.getElementsByTagName('tbody')[0];
350 tbody.appendChild(tr);
354 * Handles the message of 'translateErrorDetailsAdded' from the
357 * @param {Object} details The object which represents the logs.
359 function onTranslateErrorDetailsAdded(details) {
360 var tr = document.createElement('tr');
362 appendTD(tr, formatDate(new Date(details['time'])), 'error-logs-time');
363 appendTD(tr, details['url'], 'error-logs-url');
366 details['error'] + ': ' + formatTranslateErrorsType(details['error']),
369 var tabpanel = $('tabpanel-error-logs');
370 var tbody = tabpanel.getElementsByTagName('tbody')[0];
371 tbody.appendChild(tr);
375 * Handles the message of 'translateEventDetailsAdded' from the browser.
377 * @param {Object} details The object which contains event information.
379 function onTranslateEventDetailsAdded(details) {
380 var tr = document.createElement('tr');
381 appendTD(tr, formatDate(new Date(details['time'])), 'event-logs-time');
382 appendTD(tr, details['filename'] + ': ' + details['line'],
384 appendTD(tr, details['message'], 'event-logs-message');
386 var tbody = $('tabpanel-event-logs').getElementsByTagName('tbody')[0];
387 tbody.appendChild(tr);
391 * The callback entry point from the browser. This function will be
392 * called by the browser.
394 * @param {string} message The name of the sent message.
395 * @param {Object} details The argument of the sent message.
397 function messageHandler(message, details) {
399 case 'languageDetectionInfoAdded':
400 onLanguageDetectionInfoAdded(details);
403 onPrefsUpdated(details);
405 case 'supportedLanguagesUpdated':
406 onSupportedLanguagesUpdated(details);
408 case 'translateErrorDetailsAdded':
409 onTranslateErrorDetailsAdded(details);
411 case 'translateEventDetailsAdded':
412 onTranslateEventDetailsAdded(details);
415 console.error('Unknown message:', message);
421 * The callback of button#detetion-logs-dump.
423 function onDetectionLogsDump() {
424 var data = JSON.stringify(cr.translateInternals.detectionLogs());
425 var blob = new Blob([data], {'type': 'text/json'});
426 var url = URL.createObjectURL(blob);
427 var filename = 'translate_internals_detect_logs_dump.json';
429 var a = document.createElement('a');
430 a.setAttribute('href', url);
431 a.setAttribute('download', filename);
433 var event = document.createEvent('MouseEvent');
434 event.initMouseEvent('click', true, true, window, 0,
435 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
436 a.dispatchEvent(event);
440 detectionLogs: detectionLogs,
441 initialize: initialize,
442 messageHandler: messageHandler,
447 * The entry point of the UI.
450 cr.doc.addEventListener('DOMContentLoaded',
451 cr.translateInternals.initialize);