Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2012 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 var localStrings;
      6 var browserBridge;
      7 
      8 /**
      9  * Class that keeps track of current burn process state.
     10  * @param {Object} strings Localized state strings.
     11  * @constructor
     12  */
     13 function State(strings) {
     14   this.setStrings(strings);
     15   this.changeState(State.StatesEnum.DEVICE_NONE);
     16 }
     17 
     18 /**
     19  * State Enum object.
     20  */
     21 State.StatesEnum = {
     22   DEVICE_NONE: {
     23     cssState: 'device-detected-none',
     24   },
     25   DEVICE_USB: {
     26     cssState: 'device-detected-usb warning',
     27   },
     28   DEVICE_SD: {
     29     cssState: 'device-detected-sd warning',
     30   },
     31   DEVICE_MUL: {
     32     cssState: 'device-detected-mul warning',
     33   },
     34   ERROR_NO_NETWORK: {
     35     cssState: 'warning-no-conf',
     36   },
     37   ERROR_DEVICE_TOO_SMALL: {
     38     cssState: 'warning-no-conf',
     39   },
     40   PROGRESS_DOWNLOAD: {
     41     cssState: 'progress progress-canceble',
     42   },
     43   PROGRESS_UNZIP: {
     44     cssState: 'progress progress-canceble',
     45   },
     46   PROGRESS_BURN: {
     47     cssState: 'progress',
     48   },
     49   FAIL: {
     50     cssState: 'error',
     51   },
     52   SUCCESS: {
     53     cssState: 'success',
     54   },
     55 };
     56 
     57 State.prototype = {
     58   /**
     59    * Sets the state strings.
     60    * @param {Object} strings Localized state strings.
     61    */
     62   setStrings: function(strings) {
     63     State.StatesEnum.DEVICE_NONE.statusText =
     64         strings.getString('statusDevicesNone');
     65     State.StatesEnum.DEVICE_NONE.warningText =
     66         strings.getString('warningDevicesNone');
     67     State.StatesEnum.DEVICE_USB.statusText =
     68       strings.getString('statusDeviceUSB');
     69     State.StatesEnum.DEVICE_SD.statusText = strings.getString('statusDeviceSD');
     70     State.StatesEnum.DEVICE_MUL.statusText =
     71         strings.getString('statusDevicesMultiple');
     72     State.StatesEnum.ERROR_NO_NETWORK.statusText =
     73         strings.getString('statusNoConnection');
     74     State.StatesEnum.ERROR_NO_NETWORK.warningText =
     75         strings.getString('warningNoConnection');
     76     State.StatesEnum.ERROR_DEVICE_TOO_SMALL.statusText =
     77         strings.getString('statusNoSpace');
     78     State.StatesEnum.PROGRESS_DOWNLOAD.statusText =
     79         strings.getString('statusDownloading');
     80     State.StatesEnum.PROGRESS_UNZIP.statusText =
     81         strings.getString('statusUnzip');
     82     State.StatesEnum.PROGRESS_BURN.statusText = strings.getString('statusBurn');
     83     State.StatesEnum.FAIL.statusText = strings.getString('statusError');
     84     State.StatesEnum.SUCCESS.statusText = strings.getString('statusSuccess');
     85     State.StatesEnum.SUCCESS.warningText = strings.getString('warningSuccess');
     86   },
     87 
     88   /**
     89    * Changes the current state to new state.
     90    * @param {Object} newState Specifies the new state object.
     91    */
     92   changeState: function(newState) {
     93     if (newState == this.state)
     94       return;
     95     this.state = newState;
     96 
     97     $('main-content').className = this.state.cssState;
     98 
     99     $('status-text').textContent = this.state.statusText;
    100 
    101     if (newState.warningText)
    102       $('warning-text').textContent = this.state.warningText;
    103 
    104     if (this.isInitialState() && this.state != State.StatesEnum.DEVICE_NONE) {
    105       $('warning-button').textContent = localStrings.getString('confirmButton');
    106     } else if (this.state == State.StatesEnum.FAIL) {
    107       $('warning-button').textContent =
    108           localStrings.getString('retryButton');
    109     }
    110   },
    111 
    112   /**
    113    * Reset to initial state.
    114    * @param {Array} devices Array of device information.
    115    */
    116   gotoInitialState: function(devices) {
    117     if (devices.length == 0) {
    118       this.changeState(State.StatesEnum.DEVICE_NONE);
    119     } else if (devices.length == 1) {
    120       // If a device type is not specified for some reason, we should
    121       // default to display a USB device.
    122       var initialState = State.StatesEnum.DEVICE_USB;
    123       if (devices[0].type == 'sd')
    124         initialState = State.StatesEnum.DEVICE_SD;
    125       this.changeState(initialState);
    126     } else {
    127       this.changeState(State.StatesEnum.DEVICE_MUL);
    128     }
    129   },
    130 
    131   /**
    132    * Returns true if the device is in initial state.
    133    * @return {boolean} True if the device is in initial state else false.
    134    */
    135   isInitialState: function() {
    136     return this.state == State.StatesEnum.DEVICE_NONE ||
    137            this.state == State.StatesEnum.DEVICE_USB ||
    138            this.state == State.StatesEnum.DEVICE_SD ||
    139            this.state == State.StatesEnum.DEVICE_MUL;
    140   },
    141 
    142   /**
    143    * Returns true if device state matches the given state name.
    144    * @param {string} stateName Given state name.
    145    * @return {boolean} True if the device state matches the given state name.
    146    */
    147   equals: function(stateName) {
    148     return this.state == stateName;
    149   }
    150 };
    151 
    152 /**
    153  * Class that keeps track of available devices.
    154  * @constructor
    155  */
    156 function DeviceSelection() {
    157   this.selectedDevice = undefined;
    158   this.devices = [];
    159 }
    160 
    161 DeviceSelection.prototype = {
    162   /**
    163    * Shows the currently selected device.
    164    */
    165   showDeviceSelection: function() {
    166     if (this.devices.length == 0) {
    167       this.selectedDevice = undefined;
    168     } else {
    169       this.selectDevice(this.devices[0].devicePath);
    170     }
    171   },
    172 
    173   /**
    174    * Handles device selected event.
    175    * @param {string} label Device label.
    176    * @param {string} filePath File path.
    177    * @param {string} devicePath Selected device path.
    178    */
    179   onDeviceSelected: function(label, filePath, devicePath) {
    180     $('warning-button').onclick =
    181         browserBridge.sendBurnImageMessage.bind(browserBridge, filePath,
    182             devicePath);
    183 
    184     this.selectedDevice = devicePath;
    185 
    186     $('warning-text').textContent =
    187         localStrings.getStringF('warningDevices', label);
    188   },
    189 
    190   /**
    191    * Selects the specified device based on the specified path.
    192    * @param {string} path Device path.
    193    */
    194   selectDevice: function(path) {
    195     var element = $('radio-' + path);
    196     element.checked = true;
    197     element.onclick.apply(element);
    198   },
    199 
    200   /**
    201    * Creates a new device element.
    202    * @param {Object} device Specifies new device information.
    203    * @return {HTMLLIElement} New device element.
    204    */
    205   createNewDeviceElement: function(device) {
    206     var element = document.createElement('li');
    207     var radioButton = document.createElement('input');
    208     radioButton.type = 'radio';
    209     radioButton.name = 'device';
    210     radioButton.value = device.label;
    211     radioButton.id = 'radio-' + device.devicePath;
    212     radioButton.className = 'float-start';
    213     var deviceLabelText = document.createElement('p');
    214     deviceLabelText.textContent = device.label;
    215     deviceLabelText.className = 'select-option float-start';
    216     var newLine = document.createElement('div');
    217     newLine.className = 'new-line';
    218     element.appendChild(radioButton);
    219     element.appendChild(deviceLabelText);
    220     element.appendChild(newLine);
    221     element.id = 'select-' + device.devicePath;
    222     element.className = 'selection-element';
    223     radioButton.onclick = this.onDeviceSelected.bind(this,
    224         device.label, device.filePath, device.devicePath);
    225     return element;
    226   },
    227 
    228   /**
    229    * Updates the list of selected devices.
    230    * @param {Array} devices List of devices.
    231    */
    232   devicesUpdated: function(newDevices) {
    233     this.devices = newDevices;
    234     var selectListDOM = $('device-selection');
    235     selectListDOM.innerHTML = '';
    236     if (this.devices.length > 0) {
    237       for (var i = 0; i < this.devices.length; i++) {
    238         var element = this.createNewDeviceElement(this.devices[i]);
    239         selectListDOM.appendChild(element);
    240       }
    241       this.selectDevice(this.devices[0].devicePath);
    242     } else {
    243       this.selectedDevice = undefined;
    244     }
    245   },
    246 
    247   /**
    248    * Handles device added event.
    249    * @param {Object} device Device information.
    250    * @param {boolean} allowSelect True to update the selected device info.
    251    */
    252   deviceAdded: function(device, allowSelect) {
    253     this.devices.push(device);
    254     var selectListDOM = $('device-selection');
    255     selectListDOM.appendChild(this.createNewDeviceElement(device));
    256     if (allowSelect && this.devices.length == 1)
    257       this.selectDevice(device.devicePath);
    258   },
    259 
    260   /**
    261    * Handles device removed event.
    262    * @param {string} devicePath Device path to be removed.
    263    * @param {boolean} allowSelect True to update the selected device info.
    264    */
    265   deviceRemoved: function(devicePath, allowSelect) {
    266     device = this.findDevice(devicePath);
    267     if (!device)
    268       return;
    269     this.devices.splice(this.devices.indexOf(device), 1);
    270 
    271     // Remove device selection element from DOM.
    272     var deviceSelectElement = $('select-' + devicePath);
    273     deviceSelectElement.parentNode.removeChild(deviceSelectElement);
    274 
    275     // Update selected device element.
    276     if (allowSelect) {
    277       if (this.devices.length > 0) {
    278         if (this.selectedDevice == devicePath)
    279           this.selectDevice(this.devices[0].devicePath);
    280       } else {
    281         this.selectedDevice = undefined;
    282       }
    283     }
    284   },
    285 
    286   /**
    287    * Finds device with given device path property.
    288    * @param {string} devicePath Device path of device to find.
    289    * @return {Object} Matching device information or undefined if not found.
    290    */
    291   findDevice: function(devicePath) {
    292     for (var i = 0; i < this.devices.length; ++i) {
    293       if (this.devices[i].devicePath == devicePath) {
    294         return this.devices[i];
    295       }
    296     }
    297     return undefined;
    298   }
    299 };
    300 
    301 /**
    302  * Class that handles communication with chrome.
    303  * @constructor
    304  */
    305 function BrowserBridge() {
    306   this.currentState = new State(localStrings);
    307   this.deviceSelection = new DeviceSelection();
    308   // We will use these often so it makes sence making them class members to
    309   // avoid frequent document.getElementById calls.
    310   this.progressElement = $('progress-div');
    311   this.progressText = $('progress-text');
    312   this.progressTimeLeftText = $('pending-time');
    313 }
    314 
    315 BrowserBridge.prototype = {
    316   sendCancelMessage: function() {
    317     chrome.send('cancelBurnImage');
    318   },
    319 
    320   sendGetDevicesMessage: function() {
    321     chrome.send('getDevices');
    322   },
    323 
    324   sendWebuiInitializedMessage: function() {
    325     chrome.send('webuiInitialized');
    326   },
    327 
    328   /**
    329    * Sends the burn image message to c++ code.
    330    * @param {string} filePath Specifies the file path.
    331    * @param {string} devicePath Specifies the device path.
    332    */
    333   sendBurnImageMessage: function(filePath, devicePath) {
    334     chrome.send('burnImage', [devicePath, filePath]);
    335   },
    336 
    337   reportSuccess: function() {
    338     this.currentState.changeState(State.StatesEnum.SUCCESS);
    339   },
    340 
    341   /**
    342    * Update the device state to report a failure and display an error message to
    343    * the user.
    344    * @param {string} errorMessage Specifies the warning text message.
    345    */
    346   reportFail: function(errorMessage) {
    347     this.currentState.changeState(State.StatesEnum.FAIL);
    348     $('warning-text').textContent = errorMessage;
    349     $('warning-button').onclick = this.onBurnRetry.bind(this);
    350   },
    351 
    352   /**
    353    * Handles device added event.
    354    * @param {Object} device Device information.
    355    */
    356   deviceAdded: function(device) {
    357     var inInitialState = this.currentState.isInitialState();
    358     this.deviceSelection.deviceAdded(device, inInitialState);
    359     if (inInitialState)
    360       this.currentState.gotoInitialState(this.deviceSelection.devices);
    361   },
    362 
    363   /**
    364    * Handles device removed event.
    365    * @param {string} devicePath Device path to be removed.
    366    */
    367   deviceRemoved: function(devicePath) {
    368     var inInitialState = this.currentState.isInitialState();
    369     this.deviceSelection.deviceRemoved(devicePath, inInitialState);
    370     if (inInitialState)
    371       this.currentState.gotoInitialState(this.deviceSelection.devices);
    372   },
    373 
    374   /**
    375    * Gets device callbacks and update the current state.
    376    * @param {Array} devices List of devices.
    377    */
    378   getDevicesCallback: function(devices) {
    379     this.deviceSelection.devicesUpdated(devices);
    380     this.currentState.gotoInitialState(this.deviceSelection.devices);
    381     this.sendWebuiInitializedMessage();
    382   },
    383 
    384   /**
    385    *  Updates the progress information based on the signal received.
    386    *  @param {Object} updateSignal Specifies the signal information.
    387    */
    388   updateProgress: function(updateSignal) {
    389     if (updateSignal.progressType == 'download' &&
    390         !this.currentState.equals(State.StatesEnum.PROGRESS_DOWNLOAD)) {
    391       this.currentState.changeState(State.StatesEnum.PROGRESS_DOWNLOAD);
    392     } else if (updateSignal.progressType == 'unzip' &&
    393         !this.currentState.equals(State.StatesEnum.PROGRESS_UNZIP)) {
    394       this.currentState.changeState(State.StatesEnum.PROGRESS_UNZIP);
    395     } else if (updateSignal.progressType == 'burn' &&
    396         !this.currentState.equals(State.StatesEnum.PROGRESS_BURN)) {
    397       this.currentState.changeState(State.StatesEnum.PROGRESS_BURN);
    398     }
    399 
    400     if (!(updateSignal.amountTotal > 0)) {
    401       this.progressElement.removeAttribute('value');
    402     } else {
    403       this.progressElement.value = updateSignal.amountFinished;
    404       this.progressElement.max = updateSignal.amountTotal;
    405     }
    406 
    407     this.progressText.textContent = updateSignal.progressText;
    408     this.progressTimeLeftText.textContent = updateSignal.timeLeftText;
    409   },
    410 
    411   reportNoNetwork: function() {
    412     this.currentState.changeState(State.StatesEnum.ERROR_NO_NETWORK);
    413   },
    414 
    415   reportNetworkDetected: function() {
    416     if (this.currentState.equals(State.StatesEnum.ERROR_NO_NETWORK)) {
    417       this.deviceSelection.showDeviceSelection();
    418       this.currentState.gotoInitialState(this.deviceSelection.devices);
    419     }
    420   },
    421 
    422   /**
    423    *  Updates the current state to report device too small error.
    424    *  @param {number} deviceSize Received device size.
    425    */
    426   reportDeviceTooSmall: function(deviceSize) {
    427     this.currentState.changeState(State.StatesEnum.ERROR_DEVICE_TOO_SMALL);
    428     $('warning-text').textContent =
    429         localStrings.getStringF('warningNoSpace', deviceSize);
    430   },
    431 
    432   /**
    433    * Processes click on 'Retry' button in FAIL state.
    434    */
    435   onBurnRetry: function() {
    436     this.deviceSelection.showDeviceSelection();
    437     this.currentState.gotoInitialState(this.deviceSelection.devices);
    438   }
    439 };
    440 
    441 document.addEventListener('DOMContentLoaded', function() {
    442   localStrings = new LocalStrings();
    443   browserBridge = new BrowserBridge();
    444 
    445   jstProcess(new JsEvalContext(templateData), $('more-info-link'));
    446 
    447   $('cancel-button').onclick =
    448       browserBridge.sendCancelMessage.bind(browserBridge);
    449   browserBridge.sendGetDevicesMessage();
    450 });
    451