Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / translate_internals / translate_internals.js
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.
4
5 (function() {
6   'use strict';
7
8   cr.define('cr.translateInternals', function() {
9
10     var detectionLogs_ = null;
11
12     function detectionLogs() {
13       if (detectionLogs_ === null)
14         detectionLogs_ = [];
15       return detectionLogs_;
16     }
17
18     /**
19      * Initializes UI and sends a message to the browser for
20      * initialization.
21      */
22     function initialize() {
23       cr.ui.decorate('tabbox', cr.ui.TabBox);
24       chrome.send('requestInfo');
25
26       var button = $('detection-logs-dump');
27       button.addEventListener('click', onDetectionLogsDump);
28
29       var tabpanelNodeList = document.getElementsByTagName('tabpanel');
30       var tabpanels = Array.prototype.slice.call(tabpanelNodeList, 0);
31       var tabpanelIds = tabpanels.map(function(tab) {
32         return tab.id;
33       });
34
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;
43         };
44       });
45
46       var activateTabByHash = function() {
47         var hash = window.location.hash;
48
49         // Remove the first character '#'.
50         hash = hash.substring(1);
51
52         var id = 'tabpanel-' + hash;
53         if (tabpanelIds.indexOf(id) == -1)
54           return;
55
56         $(id).selected = true;
57       };
58
59       window.onhashchange = activateTabByHash;
60       activateTabByHash();
61     }
62
63     /*
64      * Creates a button to dismiss an item.
65      *
66      * @param {Function} func Callback called when the button is clicked.
67      */
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) {
73         e.preventDefault();
74         func();
75       }, false);
76       return button;
77     }
78
79     /**
80      * Creates a new LI element with a button to dismiss the item.
81      *
82      * @param {string} text The lable of the LI element.
83      * @param {Function} func Callback called when the button is clicked.
84      */
85     function createLIWithDismissingButton(text, func) {
86       var span = document.createElement('span');
87       span.textContent = text;
88
89       var li = document.createElement('li');
90       li.appendChild(span);
91       li.appendChild(createDismissingButton(func));
92       return li;
93     }
94
95     /**
96      * Formats the language name to a human-readable text. For example, if
97      * |langCode| is 'en', this may return 'en (English)'.
98      *
99      * @param {string} langCode ISO 639 language code.
100      * @return {string} The formatted string.
101      */
102     function formatLanguageCode(langCode) {
103       var key = 'language-' + langCode;
104       if (loadTimeData.valueExists(key)) {
105         var langName = loadTimeData.getString(key);
106         return langCode + ' (' + langName + ')';
107       }
108
109       return langCode;
110     }
111
112     /**
113      * Formats the error type to a human-readable text.
114      *
115      * @param {string} error Translation error type from the browser.
116      * @return {string} The formatted string.
117      */
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.
121       var errorStrs = {
122         0: 'None',
123         1: 'Network',
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',
131         9: 'Bad Origin',
132         10: 'Script Load Error',
133       };
134
135       if (error < 0 || errorStrs.length <= error) {
136         console.error('Invalid error code:', error);
137         return 'Invalid Error Code';
138       }
139       return errorStrs[error];
140     }
141
142     /**
143      * Handles the message of 'prefsUpdated' from the browser.
144      *
145      * @param {Object} detail the object which represents pref values.
146      */
147     function onPrefsUpdated(detail) {
148       var ul;
149
150       ul = document.querySelector('#prefs-blocked-languages ul');
151       ul.innerHTML = '';
152
153       if ('translate_blocked_languages' in detail) {
154         var langs = detail['translate_blocked_languages'];
155
156         langs.forEach(function(langCode) {
157           var text = formatLanguageCode(langCode);
158
159           var li = createLIWithDismissingButton(text, function() {
160             chrome.send('removePrefItem',
161                         ['blocked_languages', langCode]);
162           });
163           ul.appendChild(li);
164         });
165       }
166
167       ul = document.querySelector('#prefs-language-blacklist ul');
168       ul.innerHTML = '';
169
170       if ('translate_language_blacklist' in detail) {
171         var langs = detail['translate_language_blacklist'];
172
173         langs.forEach(function(langCode) {
174           var text = formatLanguageCode(langCode);
175
176           var li = createLIWithDismissingButton(text, function() {
177             chrome.send('removePrefItem',
178                         ['language_blacklist', langCode]);
179           });
180           ul.appendChild(li);
181         });
182       }
183
184       ul = document.querySelector('#prefs-site-blacklist ul');
185       ul.innerHTML = '';
186
187       if ('translate_site_blacklist' in detail) {
188         var sites = detail['translate_site_blacklist'];
189
190         sites.forEach(function(site) {
191           var li = createLIWithDismissingButton(site, function() {
192             chrome.send('removePrefItem', ['site_blacklist', site]);
193           });
194           ul.appendChild(li);
195         });
196       }
197
198       ul = document.querySelector('#prefs-whitelists ul');
199       ul.innerHTML = '';
200
201       if ('translate_whitelists' in detail) {
202         var pairs = detail['translate_whitelists'];
203
204         Object.keys(pairs).forEach(function(fromLangCode) {
205           var toLangCode = pairs[fromLangCode];
206           var text = formatLanguageCode(fromLangCode) + ' \u2192 ' +
207               formatLanguageCode(toLangCode);
208
209           var li = createLIWithDismissingButton(text, function() {
210             chrome.send('removePrefItem',
211                         ['whitelists', fromLangCode, toLangCode]);
212           });
213           ul.appendChild(li);
214         });
215       }
216
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'])));
222
223       p = document.querySelector('#prefs-dump p');
224       var content = JSON.stringify(detail, null, 2);
225       p.textContent = content;
226     }
227
228     /**
229      * Handles the message of 'supportedLanguagesUpdated' from the browser.
230      *
231      * @param {Object} details the object which represents the supported
232      *     languages by the Translate server.
233      */
234     function onSupportedLanguagesUpdated(details) {
235       var span =
236           $('prefs-supported-languages-last-updated').querySelector('span');
237       span.textContent = formatDate(new Date(details['last_updated']));
238
239       var ul = $('prefs-supported-languages-languages');
240       ul.innerHTML = '';
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');
245
246         var text = formatLanguageCode(language);
247         if (details['alpha_languages'].indexOf(language) != -1)
248           text += ' - alpha';
249         li.innerText = text;
250
251         ul.appendChild(li);
252       }
253     }
254
255     /**
256      * Addes '0's to |number| as a string. |width| is length of the string
257      * including '0's.
258      *
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.
262      */
263     function padWithZeros(number, width) {
264       var numberStr = number.toString();
265       var restWidth = width - numberStr.length;
266       if (restWidth <= 0)
267         return numberStr;
268
269       return Array(restWidth + 1).join('0') + numberStr;
270     }
271
272     /**
273      * Formats |date| as a Date object into a string. The format is like
274      * '2006-01-02 15:04:05'.
275      *
276      * @param {Date} date Date to be formatted.
277      * @return {string} The formatted string.
278      */
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();
286
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);
293
294       var str = yearStr + '-' + monthStr + '-' + dayStr + ' ' +
295           hourStr + ':' + minuteStr + ':' + secondStr;
296
297       return str;
298     }
299
300     /**
301      * Appends a new TD element to the specified element.
302      *
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.
306      */
307     function appendTD(parent, content, className) {
308       var td = document.createElement('td');
309       td.textContent = content;
310       td.className = className;
311       parent.appendChild(td);
312     }
313
314     /**
315      * Handles the message of 'languageDetectionInfoAdded' from the
316      * browser.
317      *
318      * @param {Object} detail The object which represents the logs.
319      */
320     function onLanguageDetectionInfoAdded(detail) {
321       cr.translateInternals.detectionLogs().push(detail);
322
323       var tr = document.createElement('tr');
324
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, formatLanguageCode(detail['html_root_language']),
333                'detection-logs-html-root-language');
334       appendTD(tr, formatLanguageCode(detail['adopted_language']),
335                'detection-logs-adopted-language');
336       appendTD(tr, formatLanguageCode(detail['content']),
337                'detection-logs-content');
338
339       // TD (and TR) can't use the CSS property 'max-height', so DIV
340       // in the content is needed.
341       var contentTD = tr.querySelector('.detection-logs-content');
342       var div = document.createElement('div');
343       div.textContent = contentTD.textContent;
344       contentTD.textContent = '';
345       contentTD.appendChild(div);
346
347       var tabpanel = $('tabpanel-detection-logs');
348       var tbody = tabpanel.getElementsByTagName('tbody')[0];
349       tbody.appendChild(tr);
350     }
351
352     /**
353      * Handles the message of 'translateErrorDetailsAdded' from the
354      * browser.
355      *
356      * @param {Object} details The object which represents the logs.
357      */
358     function onTranslateErrorDetailsAdded(details) {
359       var tr = document.createElement('tr');
360
361       appendTD(tr, formatDate(new Date(details['time'])), 'error-logs-time');
362       appendTD(tr, details['url'], 'error-logs-url');
363       appendTD(
364           tr,
365           details['error'] + ': ' + formatTranslateErrorsType(details['error']),
366           'error-logs-error');
367
368       var tabpanel = $('tabpanel-error-logs');
369       var tbody = tabpanel.getElementsByTagName('tbody')[0];
370       tbody.appendChild(tr);
371     }
372
373     /**
374      * Handles the message of 'translateEventDetailsAdded' from the browser.
375      *
376      * @param {Object} details The object which contains event information.
377      */
378     function onTranslateEventDetailsAdded(details) {
379       var tr = document.createElement('tr');
380       appendTD(tr, formatDate(new Date(details['time'])), 'event-logs-time');
381       appendTD(tr, details['filename'] + ': ' + details['line'],
382                'event-logs-place');
383       appendTD(tr, details['message'], 'event-logs-message');
384
385       var tbody = $('tabpanel-event-logs').getElementsByTagName('tbody')[0];
386       tbody.appendChild(tr);
387     }
388
389     /**
390      * The callback entry point from the browser. This function will be
391      * called by the browser.
392      *
393      * @param {string} message The name of the sent message.
394      * @param {Object} details The argument of the sent message.
395      */
396     function messageHandler(message, details) {
397       switch (message) {
398         case 'languageDetectionInfoAdded':
399           onLanguageDetectionInfoAdded(details);
400           break;
401         case 'prefsUpdated':
402           onPrefsUpdated(details);
403           break;
404         case 'supportedLanguagesUpdated':
405           onSupportedLanguagesUpdated(details);
406           break;
407         case 'translateErrorDetailsAdded':
408           onTranslateErrorDetailsAdded(details);
409           break;
410         case 'translateEventDetailsAdded':
411           onTranslateEventDetailsAdded(details);
412           break;
413         default:
414           console.error('Unknown message:', message);
415           break;
416       }
417     }
418
419     /**
420      * The callback of button#detetion-logs-dump.
421      */
422     function onDetectionLogsDump() {
423       var data = JSON.stringify(cr.translateInternals.detectionLogs());
424       var blob = new Blob([data], {'type': 'text/json'});
425       var url = URL.createObjectURL(blob);
426       var filename = 'translate_internals_detect_logs_dump.json';
427
428       var a = document.createElement('a');
429       a.setAttribute('href', url);
430       a.setAttribute('download', filename);
431
432       var event = document.createEvent('MouseEvent');
433       event.initMouseEvent('click', true, true, window, 0,
434                            0, 0, 0, 0, 0, 0, 0, 0, 0, null);
435       a.dispatchEvent(event);
436     }
437
438     return {
439       detectionLogs: detectionLogs,
440       initialize: initialize,
441       messageHandler: messageHandler,
442     };
443   });
444
445   /**
446    * The entry point of the UI.
447    */
448   function main() {
449     cr.doc.addEventListener('DOMContentLoaded',
450                             cr.translateInternals.initialize);
451   }
452
453   main();
454 })();