// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. var localStrings; var browserBridge; /** * Class that keeps track of current burn process state. * @param {Object} strings Localized state strings. * @constructor */ function State(strings) { this.setStrings(strings); this.changeState(State.StatesEnum.DEVICE_NONE); } /** * State Enum object. */ State.StatesEnum = { DEVICE_NONE: { cssState: 'device-detected-none', }, DEVICE_USB: { cssState: 'device-detected-usb warning', }, DEVICE_SD: { cssState: 'device-detected-sd warning', }, DEVICE_MUL: { cssState: 'device-detected-mul warning', }, ERROR_NO_NETWORK: { cssState: 'warning-no-conf', }, ERROR_DEVICE_TOO_SMALL: { cssState: 'warning-no-conf', }, PROGRESS_DOWNLOAD: { cssState: 'progress progress-canceble', }, PROGRESS_UNZIP: { cssState: 'progress progress-canceble', }, PROGRESS_BURN: { cssState: 'progress', }, FAIL: { cssState: 'error', }, SUCCESS: { cssState: 'success', }, }; State.prototype = { /** * Sets the state strings. * @param {Object} strings Localized state strings. */ setStrings: function(strings) { State.StatesEnum.DEVICE_NONE.statusText = strings.getString('statusDevicesNone'); State.StatesEnum.DEVICE_NONE.warningText = strings.getString('warningDevicesNone'); State.StatesEnum.DEVICE_USB.statusText = strings.getString('statusDeviceUSB'); State.StatesEnum.DEVICE_SD.statusText = strings.getString('statusDeviceSD'); State.StatesEnum.DEVICE_MUL.statusText = strings.getString('statusDevicesMultiple'); State.StatesEnum.ERROR_NO_NETWORK.statusText = strings.getString('statusNoConnection'); State.StatesEnum.ERROR_NO_NETWORK.warningText = strings.getString('warningNoConnection'); State.StatesEnum.ERROR_DEVICE_TOO_SMALL.statusText = strings.getString('statusNoSpace'); State.StatesEnum.PROGRESS_DOWNLOAD.statusText = strings.getString('statusDownloading'); State.StatesEnum.PROGRESS_UNZIP.statusText = strings.getString('statusUnzip'); State.StatesEnum.PROGRESS_BURN.statusText = strings.getString('statusBurn'); State.StatesEnum.FAIL.statusText = strings.getString('statusError'); State.StatesEnum.SUCCESS.statusText = strings.getString('statusSuccess'); State.StatesEnum.SUCCESS.warningText = strings.getString('warningSuccess'); }, /** * Changes the current state to new state. * @param {Object} newState Specifies the new state object. */ changeState: function(newState) { if (newState == this.state) return; this.state = newState; $('main-content').className = this.state.cssState; $('status-text').textContent = this.state.statusText; if (newState.warningText) $('warning-text').textContent = this.state.warningText; if (this.isInitialState() && this.state != State.StatesEnum.DEVICE_NONE) { $('warning-button').textContent = localStrings.getString('confirmButton'); } else if (this.state == State.StatesEnum.FAIL) { $('warning-button').textContent = localStrings.getString('retryButton'); } }, /** * Reset to initial state. * @param {Array} devices Array of device information. */ gotoInitialState: function(devices) { if (devices.length == 0) { this.changeState(State.StatesEnum.DEVICE_NONE); } else if (devices.length == 1) { // If a device type is not specified for some reason, we should // default to display a USB device. var initialState = State.StatesEnum.DEVICE_USB; if (devices[0].type == 'sd') initialState = State.StatesEnum.DEVICE_SD; this.changeState(initialState); } else { this.changeState(State.StatesEnum.DEVICE_MUL); } }, /** * Returns true if the device is in initial state. * @return {boolean} True if the device is in initial state else false. */ isInitialState: function() { return this.state == State.StatesEnum.DEVICE_NONE || this.state == State.StatesEnum.DEVICE_USB || this.state == State.StatesEnum.DEVICE_SD || this.state == State.StatesEnum.DEVICE_MUL; }, /** * Returns true if device state matches the given state name. * @param {string} stateName Given state name. * @return {boolean} True if the device state matches the given state name. */ equals: function(stateName) { return this.state == stateName; } }; /** * Class that keeps track of available devices. * @constructor */ function DeviceSelection() { this.selectedDevice = undefined; this.devices = []; } DeviceSelection.prototype = { /** * Shows the currently selected device. */ showDeviceSelection: function() { if (this.devices.length == 0) { this.selectedDevice = undefined; } else { this.selectDevice(this.devices[0].devicePath); } }, /** * Handles device selected event. * @param {string} label Device label. * @param {string} filePath File path. * @param {string} devicePath Selected device path. */ onDeviceSelected: function(label, filePath, devicePath) { $('warning-button').onclick = browserBridge.sendBurnImageMessage.bind(browserBridge, filePath, devicePath); this.selectedDevice = devicePath; $('warning-text').textContent = localStrings.getStringF('warningDevices', label); }, /** * Selects the specified device based on the specified path. * @param {string} path Device path. */ selectDevice: function(path) { var element = $('radio-' + path); element.checked = true; element.onclick.apply(element); }, /** * Creates a new device element. * @param {Object} device Specifies new device information. * @return {HTMLLIElement} New device element. */ createNewDeviceElement: function(device) { var element = document.createElement('li'); var radioButton = document.createElement('input'); radioButton.type = 'radio'; radioButton.name = 'device'; radioButton.value = device.label; radioButton.id = 'radio-' + device.devicePath; radioButton.className = 'float-start'; var deviceLabelText = document.createElement('p'); deviceLabelText.textContent = device.label; deviceLabelText.className = 'select-option float-start'; var newLine = document.createElement('div'); newLine.className = 'new-line'; element.appendChild(radioButton); element.appendChild(deviceLabelText); element.appendChild(newLine); element.id = 'select-' + device.devicePath; element.className = 'selection-element'; radioButton.onclick = this.onDeviceSelected.bind(this, device.label, device.filePath, device.devicePath); return element; }, /** * Updates the list of selected devices. * @param {Array} devices List of devices. */ devicesUpdated: function(newDevices) { this.devices = newDevices; var selectListDOM = $('device-selection'); selectListDOM.innerHTML = ''; if (this.devices.length > 0) { for (var i = 0; i < this.devices.length; i++) { var element = this.createNewDeviceElement(this.devices[i]); selectListDOM.appendChild(element); } this.selectDevice(this.devices[0].devicePath); } else { this.selectedDevice = undefined; } }, /** * Handles device added event. * @param {Object} device Device information. * @param {boolean} allowSelect True to update the selected device info. */ deviceAdded: function(device, allowSelect) { this.devices.push(device); var selectListDOM = $('device-selection'); selectListDOM.appendChild(this.createNewDeviceElement(device)); if (allowSelect && this.devices.length == 1) this.selectDevice(device.devicePath); }, /** * Handles device removed event. * @param {string} devicePath Device path to be removed. * @param {boolean} allowSelect True to update the selected device info. */ deviceRemoved: function(devicePath, allowSelect) { device = this.findDevice(devicePath); if (!device) return; this.devices.splice(this.devices.indexOf(device), 1); // Remove device selection element from DOM. var deviceSelectElement = $('select-' + devicePath); deviceSelectElement.parentNode.removeChild(deviceSelectElement); // Update selected device element. if (allowSelect) { if (this.devices.length > 0) { if (this.selectedDevice == devicePath) this.selectDevice(this.devices[0].devicePath); } else { this.selectedDevice = undefined; } } }, /** * Finds device with given device path property. * @param {string} devicePath Device path of device to find. * @return {Object} Matching device information or undefined if not found. */ findDevice: function(devicePath) { for (var i = 0; i < this.devices.length; ++i) { if (this.devices[i].devicePath == devicePath) { return this.devices[i]; } } return undefined; } }; /** * Class that handles communication with chrome. * @constructor */ function BrowserBridge() { this.currentState = new State(localStrings); this.deviceSelection = new DeviceSelection(); // We will use these often so it makes sence making them class members to // avoid frequent document.getElementById calls. this.progressElement = $('progress-div'); this.progressText = $('progress-text'); this.progressTimeLeftText = $('pending-time'); } BrowserBridge.prototype = { sendCancelMessage: function() { chrome.send('cancelBurnImage'); }, sendGetDevicesMessage: function() { chrome.send('getDevices'); }, sendWebuiInitializedMessage: function() { chrome.send('webuiInitialized'); }, /** * Sends the burn image message to c++ code. * @param {string} filePath Specifies the file path. * @param {string} devicePath Specifies the device path. */ sendBurnImageMessage: function(filePath, devicePath) { chrome.send('burnImage', [devicePath, filePath]); }, reportSuccess: function() { this.currentState.changeState(State.StatesEnum.SUCCESS); }, /** * Update the device state to report a failure and display an error message to * the user. * @param {string} errorMessage Specifies the warning text message. */ reportFail: function(errorMessage) { this.currentState.changeState(State.StatesEnum.FAIL); $('warning-text').textContent = errorMessage; $('warning-button').onclick = this.onBurnRetry.bind(this); }, /** * Handles device added event. * @param {Object} device Device information. */ deviceAdded: function(device) { var inInitialState = this.currentState.isInitialState(); this.deviceSelection.deviceAdded(device, inInitialState); if (inInitialState) this.currentState.gotoInitialState(this.deviceSelection.devices); }, /** * Handles device removed event. * @param {string} devicePath Device path to be removed. */ deviceRemoved: function(devicePath) { var inInitialState = this.currentState.isInitialState(); this.deviceSelection.deviceRemoved(devicePath, inInitialState); if (inInitialState) this.currentState.gotoInitialState(this.deviceSelection.devices); }, /** * Gets device callbacks and update the current state. * @param {Array} devices List of devices. */ getDevicesCallback: function(devices) { this.deviceSelection.devicesUpdated(devices); this.currentState.gotoInitialState(this.deviceSelection.devices); this.sendWebuiInitializedMessage(); }, /** * Updates the progress information based on the signal received. * @param {Object} updateSignal Specifies the signal information. */ updateProgress: function(updateSignal) { if (updateSignal.progressType == 'download' && !this.currentState.equals(State.StatesEnum.PROGRESS_DOWNLOAD)) { this.currentState.changeState(State.StatesEnum.PROGRESS_DOWNLOAD); } else if (updateSignal.progressType == 'unzip' && !this.currentState.equals(State.StatesEnum.PROGRESS_UNZIP)) { this.currentState.changeState(State.StatesEnum.PROGRESS_UNZIP); } else if (updateSignal.progressType == 'burn' && !this.currentState.equals(State.StatesEnum.PROGRESS_BURN)) { this.currentState.changeState(State.StatesEnum.PROGRESS_BURN); } if (!(updateSignal.amountTotal > 0)) { this.progressElement.removeAttribute('value'); } else { this.progressElement.value = updateSignal.amountFinished; this.progressElement.max = updateSignal.amountTotal; } this.progressText.textContent = updateSignal.progressText; this.progressTimeLeftText.textContent = updateSignal.timeLeftText; }, reportNoNetwork: function() { this.currentState.changeState(State.StatesEnum.ERROR_NO_NETWORK); }, reportNetworkDetected: function() { if (this.currentState.equals(State.StatesEnum.ERROR_NO_NETWORK)) { this.deviceSelection.showDeviceSelection(); this.currentState.gotoInitialState(this.deviceSelection.devices); } }, /** * Updates the current state to report device too small error. * @param {number} deviceSize Received device size. */ reportDeviceTooSmall: function(deviceSize) { this.currentState.changeState(State.StatesEnum.ERROR_DEVICE_TOO_SMALL); $('warning-text').textContent = localStrings.getStringF('warningNoSpace', deviceSize); }, /** * Processes click on 'Retry' button in FAIL state. */ onBurnRetry: function() { this.deviceSelection.showDeviceSelection(); this.currentState.gotoInitialState(this.deviceSelection.devices); } }; document.addEventListener('DOMContentLoaded', function() { localStrings = new LocalStrings(); browserBridge = new BrowserBridge(); jstProcess(new JsEvalContext(templateData), $('more-info-link')); $('cancel-button').onclick = browserBridge.sendCancelMessage.bind(browserBridge); browserBridge.sendGetDevicesMessage(); });