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