// 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();
});