• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 localStrings;
6var browserBridge;
7
8/**
9 * Class that keeps track of current burn process state.
10 * @param {Object} strings Localized state strings.
11 * @constructor
12 */
13function State(strings) {
14  this.setStrings(strings);
15  this.changeState(State.StatesEnum.DEVICE_NONE);
16}
17
18/**
19 * State Enum object.
20 */
21State.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
57State.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 */
156function DeviceSelection() {
157  this.selectedDevice = undefined;
158  this.devices = [];
159}
160
161DeviceSelection.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 */
305function 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
315BrowserBridge.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
441document.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