OLD | NEW |
| (Empty) |
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 browserBridge; | |
6 | |
7 /** | |
8 * Class that keeps track of current burn process state. | |
9 * @param {Object} strings Localized state strings. | |
10 * @constructor | |
11 */ | |
12 function State(strings) { | |
13 this.setStrings(strings); | |
14 this.changeState(State.StatesEnum.DEVICE_NONE); | |
15 } | |
16 | |
17 /** | |
18 * State Enum object. | |
19 */ | |
20 State.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 | |
56 State.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 */ | |
155 function DeviceSelection() { | |
156 this.selectedDevice = undefined; | |
157 this.devices = []; | |
158 } | |
159 | |
160 DeviceSelection.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 */ | |
304 function 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 | |
314 BrowserBridge.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 | |
440 document.addEventListener('DOMContentLoaded', function() { | |
441 browserBridge = new BrowserBridge(); | |
442 | |
443 $('cancel-button').onclick = | |
444 browserBridge.sendCancelMessage.bind(browserBridge); | |
445 browserBridge.sendGetDevicesMessage(); | |
446 }); | |
OLD | NEW |