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