- add sources.
[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 new LI element with a button to dismiss the item.
65      *
66      * @param {string} text The lable of the LI element.
67      * @param {Function} func Callback called when the button is clicked.
68      */
69     function createLIWithDismissingButton(text, func) {
70       var span = document.createElement('span');
71       span.textContent = text;
72
73       var li = document.createElement('li');
74       li.appendChild(span);
75
76       var button = document.createElement('button');
77       button.textContent = 'X';
78       button.addEventListener('click', function(e) {
79         e.preventDefault();
80         func();
81       }, false);
82
83       li.appendChild(button);
84       return li;
85     }
86
87     /**
88      * Formats the language name to a human-readable text. For example, if
89      * |langCode| is 'en', this may return 'en (English)'.
90      *
91      * @param {string} langCode ISO 639 language code.
92      * @return {string} The formatted string.
93      */
94     function formatLanguageCode(langCode) {
95       var key = 'language-' + langCode;
96       if (key in templateData) {
97         var langName = templateData[key];
98         return langCode + ' (' + langName + ')';
99       }
100
101       return langCode;
102     }
103
104     /**
105      * Formats the error type to a human-readable text.
106      *
107      * @param {string} error Translation error type from the browser.
108      * @return {string} The formatted string.
109      */
110     function formatTranslateErrorsType(error) {
111       // This list is from chrome/common/translate/translate_errors.h.
112       // If this header file is updated, the below list also should be updated.
113       var errorStrs = {
114         0: 'None',
115         1: 'Network',
116         2: 'Initialization Error',
117         3: 'Unknown Language',
118         4: 'Unsupported Language',
119         5: 'Identical Languages',
120         6: 'Translation Error',
121         7: 'Translation Timeout',
122         8: 'Unexpected Script Error',
123         9: 'Bad Origin',
124         10: 'Script Load Error',
125       };
126
127       if (error < 0 || errorStrs.length <= error) {
128         console.error('Invalid error code:', error);
129         return 'Invalid Error Code';
130       }
131       return errorStrs[error];
132     }
133
134     /**
135      * Handles the message of 'prefsUpdated' from the browser.
136      *
137      * @param {Object} detail the object which represents pref values.
138      */
139     function onPrefsUpdated(detail) {
140       var ul;
141
142       ul = document.querySelector('#prefs-blocked-languages ul');
143       ul.innerHTML = '';
144
145       if ('translate_blocked_languages' in detail) {
146         var langs = detail['translate_blocked_languages'];
147
148         langs.forEach(function(langCode) {
149           var text = formatLanguageCode(langCode);
150
151           var li = createLIWithDismissingButton(text, function() {
152             chrome.send('removePrefItem',
153                         ['blocked_languages', langCode]);
154           });
155           ul.appendChild(li);
156         });
157       }
158
159       ul = document.querySelector('#prefs-language-blacklist ul');
160       ul.innerHTML = '';
161
162       if ('translate_language_blacklist' in detail) {
163         var langs = detail['translate_language_blacklist'];
164
165         langs.forEach(function(langCode) {
166           var text = formatLanguageCode(langCode);
167
168           var li = createLIWithDismissingButton(text, function() {
169             chrome.send('removePrefItem',
170                         ['language_blacklist', langCode]);
171           });
172           ul.appendChild(li);
173         });
174       }
175
176       ul = document.querySelector('#prefs-site-blacklist ul');
177       ul.innerHTML = '';
178
179       if ('translate_site_blacklist' in detail) {
180         var sites = detail['translate_site_blacklist'];
181
182         sites.forEach(function(site) {
183           var li = createLIWithDismissingButton(site, function() {
184             chrome.send('removePrefItem', ['site_blacklist', site]);
185           });
186           ul.appendChild(li);
187         });
188       }
189
190       ul = document.querySelector('#prefs-whitelists ul');
191       ul.innerHTML = '';
192
193       if ('translate_whitelists' in detail) {
194         var pairs = detail['translate_whitelists'];
195
196         Object.keys(pairs).forEach(function(fromLangCode) {
197           var toLangCode = pairs[fromLangCode];
198           var text = formatLanguageCode(fromLangCode) + ' \u2192 ' +
199               formatLanguageCode(toLangCode);
200
201           var li = createLIWithDismissingButton(text, function() {
202             chrome.send('removePrefItem',
203                         ['whitelists', fromLangCode, toLangCode]);
204           });
205           ul.appendChild(li);
206         });
207       }
208
209       var p = document.querySelector('#prefs-dump p');
210       var content = JSON.stringify(detail, null, 2);
211       p.textContent = content;
212     }
213
214     /**
215      * Handles the message of 'supportedLanguagesUpdated' from the browser.
216      *
217      * @param {Object} details the object which represents the supported
218      *     languages by the Translate server.
219      */
220     function onSupportedLanguagesUpdated(details) {
221       var span =
222           $('prefs-supported-languages-last-updated').querySelector('span');
223       span.textContent = formatDate(new Date(details['last_updated']));
224
225       var ul = $('prefs-supported-languages-languages');
226       ul.innerHTML = '';
227       var languages = details['languages'];
228       for (var i = 0; i < languages.length; i++) {
229         var language = languages[i];
230         var li = document.createElement('li');
231
232         var text = formatLanguageCode(language);
233         if (details['alpha_languages'].indexOf(language) != -1)
234           text += ' - alpha';
235         li.innerText = text;
236
237         ul.appendChild(li);
238       }
239     }
240
241     /**
242      * Addes '0's to |number| as a string. |width| is length of the string
243      * including '0's.
244      *
245      * @param {string} number The number to be converted into a string.
246      * @param {number} width The width of the returned string.
247      * @return {string} The formatted string.
248      */
249     function padWithZeros(number, width) {
250       var numberStr = number.toString();
251       var restWidth = width - numberStr.length;
252       if (restWidth <= 0)
253         return numberStr;
254
255       return Array(restWidth + 1).join('0') + numberStr;
256     }
257
258     /**
259      * Formats |date| as a Date object into a string. The format is like
260      * '2006-01-02 15:04:05'.
261      *
262      * @param {Date} date Date to be formatted.
263      * @return {string} The formatted string.
264      */
265     function formatDate(date) {
266       var year = date.getFullYear();
267       var month = date.getMonth() + 1;
268       var day = date.getDate();
269       var hour = date.getHours();
270       var minute = date.getMinutes();
271       var second = date.getSeconds();
272
273       var yearStr = padWithZeros(year, 4);
274       var monthStr = padWithZeros(month, 2);
275       var dayStr = padWithZeros(day, 2);
276       var hourStr = padWithZeros(hour, 2);
277       var minuteStr = padWithZeros(minute, 2);
278       var secondStr = padWithZeros(second, 2);
279
280       var str = yearStr + '-' + monthStr + '-' + dayStr + ' ' +
281           hourStr + ':' + minuteStr + ':' + secondStr;
282
283       return str;
284     }
285
286     /**
287      * Appends a new TD element to the specified element.
288      *
289      * @param {string} parent The element to which a new TD element is appended.
290      * @param {string} content The text content of the element.
291      * @param {string} className The class name of the element.
292      */
293     function appendTD(parent, content, className) {
294       var td = document.createElement('td');
295       td.textContent = content;
296       td.className = className;
297       parent.appendChild(td);
298     }
299
300     /**
301      * Handles the message of 'languageDetectionInfoAdded' from the
302      * browser.
303      *
304      * @param {Object} detail The object which represents the logs.
305      */
306     function onLanguageDetectionInfoAdded(detail) {
307       cr.translateInternals.detectionLogs().push(detail);
308
309       var tr = document.createElement('tr');
310
311       appendTD(tr, formatDate(new Date(detail['time'])), 'detection-logs-time');
312       appendTD(tr, detail['url'], 'detection-logs-url');
313       appendTD(tr, formatLanguageCode(detail['content_language']),
314                'detection-logs-content-language');
315       appendTD(tr, formatLanguageCode(detail['cld_language']),
316                'detection-logs-cld-language');
317       appendTD(tr, detail['is_cld_reliable'], 'detection-logs-is-cld-reliable');
318       appendTD(tr, formatLanguageCode(detail['html_root_language']),
319                'detection-logs-html-root-language');
320       appendTD(tr, formatLanguageCode(detail['adopted_language']),
321                'detection-logs-adopted-language');
322       appendTD(tr, formatLanguageCode(detail['content']),
323                'detection-logs-content');
324
325       // TD (and TR) can't use the CSS property 'max-height', so DIV
326       // in the content is needed.
327       var contentTD = tr.querySelector('.detection-logs-content');
328       var div = document.createElement('div');
329       div.textContent = contentTD.textContent;
330       contentTD.textContent = '';
331       contentTD.appendChild(div);
332
333       var tabpanel = $('tabpanel-detection-logs');
334       var tbody = tabpanel.getElementsByTagName('tbody')[0];
335       tbody.appendChild(tr);
336     }
337
338     /**
339      * Handles the message of 'translateErrorDetailsAdded' from the
340      * browser.
341      *
342      * @param {Object} details The object which represents the logs.
343      */
344     function onTranslateErrorDetailsAdded(details) {
345       var tr = document.createElement('tr');
346
347       appendTD(tr, formatDate(new Date(details['time'])), 'error-logs-time');
348       appendTD(tr, details['url'], 'error-logs-url');
349       appendTD(
350           tr,
351           details['error'] + ': ' + formatTranslateErrorsType(details['error']),
352           'error-logs-error');
353
354       var tabpanel = $('tabpanel-error-logs');
355       var tbody = tabpanel.getElementsByTagName('tbody')[0];
356       tbody.appendChild(tr);
357     }
358
359     /**
360      * Handles the message of 'translateEventDetailsAdded' from the browser.
361      *
362      * @param {Object} details The object which contains event information.
363      */
364     function onTranslateEventDetailsAdded(details) {
365       var tr = document.createElement('tr');
366       appendTD(tr, formatDate(new Date(details['time'])), 'event-logs-time');
367       appendTD(tr, details['filename'] + ': ' + details['line'],
368                'event-logs-place');
369       appendTD(tr, details['message'], 'event-logs-message');
370
371       var tbody = $('event-logs').getElementsByTagName('tbody')[0];
372       tbody.appendChild(tr);
373     }
374
375     /**
376      * The callback entry point from the browser. This function will be
377      * called by the browser.
378      *
379      * @param {string} message The name of the sent message.
380      * @param {Object} details The argument of the sent message.
381      */
382     function messageHandler(message, details) {
383       switch (message) {
384         case 'languageDetectionInfoAdded':
385           onLanguageDetectionInfoAdded(details);
386           break;
387         case 'prefsUpdated':
388           onPrefsUpdated(details);
389           break;
390         case 'supportedLanguagesUpdated':
391           onSupportedLanguagesUpdated(details);
392           break;
393         case 'translateErrorDetailsAdded':
394           onTranslateErrorDetailsAdded(details);
395           break;
396         case 'translateEventDetailsAdded':
397           onTranslateEventDetailsAdded(details);
398           break;
399         default:
400           console.error('Unknown message:', message);
401           break;
402       }
403     }
404
405     /**
406      * The callback of button#detetion-logs-dump.
407      */
408     function onDetectionLogsDump() {
409       var data = JSON.stringify(cr.translateInternals.detectionLogs());
410       var blob = new Blob([data], {'type': 'text/json'});
411       var url = webkitURL.createObjectURL(blob);
412       var filename = 'translate_internals_detect_logs_dump.json';
413
414       var a = document.createElement('a');
415       a.setAttribute('href', url);
416       a.setAttribute('download', filename);
417
418       var event = document.createEvent('MouseEvent');
419       event.initMouseEvent('click', true, true, window, 0,
420                            0, 0, 0, 0, 0, 0, 0, 0, 0, null);
421       a.dispatchEvent(event);
422     }
423
424     return {
425       detectionLogs: detectionLogs,
426       initialize: initialize,
427       messageHandler: messageHandler,
428     };
429   });
430
431   /**
432    * The entry point of the UI.
433    */
434   function main() {
435     cr.doc.addEventListener('DOMContentLoaded',
436                             cr.translateInternals.initialize);
437   }
438
439   main();
440 })();