• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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})();
441