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