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 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