| 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 'use strict'; | |
| 6 | |
| 7 /** | |
| 8 * Butter bar is shown on top of the file list and is used to show the copy | |
| 9 * progress and other messages. | |
| 10 * @param {HTMLElement} dialogDom FileManager top-level div. | |
| 11 * @param {FileOperationManagerWrapper} fileOperationManager The operation | |
| 12 * manager. | |
| 13 * @constructor | |
| 14 */ | |
| 15 function ButterBar(dialogDom, fileOperationManager) { | |
| 16 this.dialogDom_ = dialogDom; | |
| 17 this.butter_ = this.dialogDom_.querySelector('#butter-bar'); | |
| 18 this.document_ = this.butter_.ownerDocument; | |
| 19 this.fileOperationManager_ = fileOperationManager; | |
| 20 this.hideTimeout_ = null; | |
| 21 this.showTimeout_ = null; | |
| 22 this.lastShowTime_ = 0; | |
| 23 this.deleteTaskId_ = null; | |
| 24 this.currentMode_ = null; | |
| 25 this.totalDeleted_ = 0; | |
| 26 this.lastProgressValue_ = 0; | |
| 27 this.alert_ = new ErrorDialog(this.dialogDom_); | |
| 28 | |
| 29 this.onCopyProgressBound_ = this.onCopyProgress_.bind(this); | |
| 30 this.fileOperationManager_.addEventListener( | |
| 31 'copy-progress', this.onCopyProgressBound_); | |
| 32 this.onDeleteBound_ = this.onDelete_.bind(this); | |
| 33 this.fileOperationManager_.addEventListener('delete', this.onDeleteBound_); | |
| 34 } | |
| 35 | |
| 36 /** | |
| 37 * The default amount of milliseconds time, before a butter bar will hide after | |
| 38 * the last update. | |
| 39 * @type {number} | |
| 40 * @private | |
| 41 * @const | |
| 42 */ | |
| 43 ButterBar.HIDE_DELAY_TIME_MS_ = 2000; | |
| 44 | |
| 45 /** | |
| 46 * Name of action which should be displayed as an 'x' button instead of | |
| 47 * link with text. | |
| 48 * @const | |
| 49 */ | |
| 50 ButterBar.ACTION_X = '--action--x--'; | |
| 51 | |
| 52 /** | |
| 53 * Butter bar mode. | |
| 54 * @const | |
| 55 */ | |
| 56 ButterBar.Mode = { | |
| 57 COPY: 1, | |
| 58 DELETE: 2 | |
| 59 }; | |
| 60 | |
| 61 /** | |
| 62 * Disposes the instance. No methods should be called after this method's | |
| 63 * invocation. | |
| 64 */ | |
| 65 ButterBar.prototype.dispose = function() { | |
| 66 // Unregister listeners from FileOperationManager. | |
| 67 this.fileOperationManager_.removeEventListener( | |
| 68 'copy-progress', this.onCopyProgressBound_); | |
| 69 this.fileOperationManager_.removeEventListener('delete', this.onDeleteBound_); | |
| 70 }; | |
| 71 | |
| 72 /** | |
| 73 * @return {boolean} True if visible. | |
| 74 * @private | |
| 75 */ | |
| 76 ButterBar.prototype.isVisible_ = function() { | |
| 77 return this.butter_.classList.contains('visible'); | |
| 78 }; | |
| 79 | |
| 80 /** | |
| 81 * Show butter bar. | |
| 82 * @param {ButterBar.Mode} mode Butter bar mode. | |
| 83 * @param {string} message The message to be shown. | |
| 84 * @param {Object=} opt_options Options: 'actions', 'progress', 'timeout'. If | |
| 85 * 'timeout' is not specified, HIDE_DELAY_TIME_MS_ is used. If 'timeout' is | |
| 86 * false, the butter bar will not be hidden. | |
| 87 */ | |
| 88 ButterBar.prototype.show = function(mode, message, opt_options) { | |
| 89 this.currentMode_ = mode; | |
| 90 | |
| 91 this.clearShowTimeout_(); | |
| 92 this.clearHideTimeout_(); | |
| 93 | |
| 94 var actions = this.butter_.querySelector('.actions'); | |
| 95 actions.textContent = ''; | |
| 96 if (opt_options && 'actions' in opt_options) { | |
| 97 for (var label in opt_options.actions) { | |
| 98 var link = this.document_.createElement('a'); | |
| 99 link.addEventListener('click', function(callback) { | |
| 100 callback(); | |
| 101 return false; | |
| 102 }.bind(null, opt_options.actions[label])); | |
| 103 if (label == ButterBar.ACTION_X) { | |
| 104 link.className = 'x'; | |
| 105 } else { | |
| 106 link.textContent = label; | |
| 107 } | |
| 108 actions.appendChild(link); | |
| 109 } | |
| 110 actions.hidden = false; | |
| 111 } else { | |
| 112 actions.hidden = true; | |
| 113 } | |
| 114 | |
| 115 this.butter_.querySelector('.progress-bar').hidden = | |
| 116 !(opt_options && 'progress' in opt_options); | |
| 117 | |
| 118 this.butter_.classList.remove('error'); | |
| 119 this.butter_.classList.remove('visible'); // Will be shown in update_ | |
| 120 this.update_(message, opt_options); | |
| 121 }; | |
| 122 | |
| 123 /** | |
| 124 * Show an error message in a popup dialog. | |
| 125 * @param {string} message Message. | |
| 126 * @private | |
| 127 */ | |
| 128 ButterBar.prototype.showError_ = function(message) { | |
| 129 // Wait in case there are previous dialogs being closed. | |
| 130 setTimeout(function() { | |
| 131 this.alert_.showHtml('', // Title. | |
| 132 message); | |
| 133 this.hide_(); | |
| 134 }.bind(this), cr.ui.dialogs.BaseDialog.ANIMATE_STABLE_DURATION); | |
| 135 }; | |
| 136 | |
| 137 /** | |
| 138 * Set message and/or progress. | |
| 139 * @param {string} message Message. | |
| 140 * @param {Object=} opt_options Same as in show(). | |
| 141 * @private | |
| 142 */ | |
| 143 ButterBar.prototype.update_ = function(message, opt_options) { | |
| 144 if (!opt_options) | |
| 145 opt_options = {}; | |
| 146 | |
| 147 this.clearHideTimeout_(); | |
| 148 | |
| 149 var butterMessage = this.butter_.querySelector('.butter-message'); | |
| 150 butterMessage.textContent = message; | |
| 151 if (message && !this.isVisible_()) { | |
| 152 // The butter bar is made visible on the first non-empty message. | |
| 153 this.butter_.classList.add('visible'); | |
| 154 } | |
| 155 if (opt_options && 'progress' in opt_options) { | |
| 156 butterMessage.classList.add('single-line'); | |
| 157 var progressTrack = this.butter_.querySelector('.progress-track'); | |
| 158 // Smoothen the progress only when it goes forward. Especially do not | |
| 159 // do the transition effect if resetting to 0. | |
| 160 if (opt_options.progress > this.lastProgressValue_) | |
| 161 progressTrack.classList.add('smoothed'); | |
| 162 else | |
| 163 progressTrack.classList.remove('smoothed'); | |
| 164 progressTrack.style.width = (opt_options.progress * 100) + '%'; | |
| 165 this.lastProgressValue_ = opt_options.progress; | |
| 166 } else { | |
| 167 butterMessage.classList.remove('single-line'); | |
| 168 } | |
| 169 | |
| 170 if (opt_options.timeout !== false) | |
| 171 this.hide_(opt_options.timeout); | |
| 172 }; | |
| 173 | |
| 174 /** | |
| 175 * Hide butter bar. There might be the delay before hiding so that butter bar | |
| 176 * would be shown for no less than the minimal time. | |
| 177 * @param {number=} opt_timeout Delay time in milliseconds before hidding. If it | |
| 178 * is zero, butter bar is hidden immediatelly. If it is not specified, | |
| 179 * HIDE_DELAY_TIME_MS_ is used. | |
| 180 * @private | |
| 181 */ | |
| 182 ButterBar.prototype.hide_ = function(opt_timeout) { | |
| 183 this.clearHideTimeout_(); | |
| 184 | |
| 185 if (!this.isVisible_()) | |
| 186 return; | |
| 187 | |
| 188 var delay = typeof opt_timeout != 'undefined' ? | |
| 189 opt_timeout : ButterBar.HIDE_DELAY_TIME_MS_; | |
| 190 if (delay <= 0) { | |
| 191 this.currentMode_ = null; | |
| 192 this.butter_.classList.remove('visible'); | |
| 193 this.butter_.querySelector('.progress-bar').hidden = true; | |
| 194 } else { | |
| 195 // Reschedule hide to comply with the minimal display time. | |
| 196 this.hideTimeout_ = setTimeout(function() { | |
| 197 this.hideTimeout_ = null; | |
| 198 this.hide_(0); | |
| 199 }.bind(this), delay); | |
| 200 } | |
| 201 }; | |
| 202 | |
| 203 /** | |
| 204 * Clear the show timeout if it is set. | |
| 205 * @private | |
| 206 */ | |
| 207 ButterBar.prototype.clearShowTimeout_ = function() { | |
| 208 if (this.showTimeout_) { | |
| 209 clearTimeout(this.showTimeout_); | |
| 210 this.showTimeout_ = null; | |
| 211 } | |
| 212 }; | |
| 213 | |
| 214 /** | |
| 215 * Clear the hide timeout if it is set. | |
| 216 * @private | |
| 217 */ | |
| 218 ButterBar.prototype.clearHideTimeout_ = function() { | |
| 219 if (this.hideTimeout_) { | |
| 220 clearTimeout(this.hideTimeout_); | |
| 221 this.hideTimeout_ = null; | |
| 222 } | |
| 223 }; | |
| 224 | |
| 225 /** | |
| 226 * @return {string} The type of operation. | |
| 227 * @private | |
| 228 */ | |
| 229 ButterBar.prototype.transferType_ = function() { | |
| 230 var progress = this.progress_; | |
| 231 if (progress && progress.operationType) | |
| 232 return progress.operationType; | |
| 233 | |
| 234 return 'TRANSFER'; | |
| 235 }; | |
| 236 | |
| 237 /** | |
| 238 * Set up butter bar for showing copy progress. | |
| 239 * | |
| 240 * @param {Object} progress Copy status object created by | |
| 241 * FileOperationManager.getStatus(). | |
| 242 * @private | |
| 243 */ | |
| 244 ButterBar.prototype.showProgress_ = function(progress) { | |
| 245 this.progress_ = progress; | |
| 246 var options = { | |
| 247 progress: progress.processedBytes / progress.totalBytes, | |
| 248 actions: {}, | |
| 249 timeout: false | |
| 250 }; | |
| 251 | |
| 252 var pendingItems = progress.numRemainingItems; | |
| 253 var type = this.transferType_(); | |
| 254 var progressString = (pendingItems === 1) ? | |
| 255 strf(type + '_FILE_NAME', progress.processingEntry.name) : | |
| 256 strf(type + '_ITEMS_REMAINING', pendingItems); | |
| 257 | |
| 258 if (this.currentMode_ == ButterBar.Mode.COPY) { | |
| 259 this.update_(progressString, options); | |
| 260 } else { | |
| 261 options.actions[ButterBar.ACTION_X] = | |
| 262 this.fileOperationManager_.requestCancel.bind( | |
| 263 this.fileOperationManager_); | |
| 264 this.show(ButterBar.Mode.COPY, progressString, options); | |
| 265 } | |
| 266 }; | |
| 267 | |
| 268 /** | |
| 269 * 'copy-progress' event handler. Show progress or an appropriate message. | |
| 270 * @param {Event} event A 'copy-progress' event from FileOperationManager. | |
| 271 * @private | |
| 272 */ | |
| 273 ButterBar.prototype.onCopyProgress_ = function(event) { | |
| 274 // Delete operation has higher priority. | |
| 275 if (this.currentMode_ == ButterBar.Mode.DELETE) | |
| 276 return; | |
| 277 | |
| 278 if (event.reason != 'PROGRESS') | |
| 279 this.clearShowTimeout_(); | |
| 280 | |
| 281 switch (event.reason) { | |
| 282 case 'BEGIN': | |
| 283 this.showTimeout_ = setTimeout(function() { | |
| 284 this.showTimeout_ = null; | |
| 285 this.showProgress_(event.status); | |
| 286 }.bind(this), 500); | |
| 287 break; | |
| 288 | |
| 289 case 'PROGRESS': | |
| 290 this.showProgress_(event.status); | |
| 291 break; | |
| 292 | |
| 293 case 'SUCCESS': | |
| 294 this.hide_(); | |
| 295 break; | |
| 296 | |
| 297 case 'CANCELLED': | |
| 298 this.show(ButterBar.Mode.DELETE, | |
| 299 str(this.transferType_() + '_CANCELLED')); | |
| 300 break; | |
| 301 | |
| 302 case 'ERROR': | |
| 303 this.progress_ = event.status; | |
| 304 var error = event.error; | |
| 305 if (error.code === util.FileOperationErrorType.TARGET_EXISTS) { | |
| 306 var name = error.data.name; | |
| 307 if (error.data.isDirectory) | |
| 308 name += '/'; | |
| 309 this.showError_( | |
| 310 strf(this.transferType_() + '_TARGET_EXISTS_ERROR', name)); | |
| 311 } else if (error.code === util.FileOperationErrorType.FILESYSTEM_ERROR) { | |
| 312 if (error.data.toDrive && | |
| 313 error.data.code === FileError.QUOTA_EXCEEDED_ERR) { | |
| 314 // The alert will be shown in FileManager.onCopyProgress_. | |
| 315 this.hide_(); | |
| 316 } else { | |
| 317 this.showError_(strf(this.transferType_() + '_FILESYSTEM_ERROR', | |
| 318 util.getFileErrorString(error.data.code))); | |
| 319 } | |
| 320 } else { | |
| 321 this.showError_( | |
| 322 strf(this.transferType_() + '_UNEXPECTED_ERROR', error)); | |
| 323 } | |
| 324 break; | |
| 325 | |
| 326 default: | |
| 327 console.warn('Unknown "copy-progress" event reason: ' + event.code); | |
| 328 } | |
| 329 }; | |
| 330 | |
| 331 /** | |
| 332 * 'delete' event handler. Shows information about deleting files. | |
| 333 * @param {Event} event A 'delete' event from FileOperationManager. | |
| 334 * @private | |
| 335 */ | |
| 336 ButterBar.prototype.onDelete_ = function(event) { | |
| 337 switch (event.reason) { | |
| 338 case 'BEGIN': | |
| 339 if (this.currentMode_ != ButterBar.Mode.DELETE) | |
| 340 this.totalDeleted_ = 0; | |
| 341 | |
| 342 case 'PROGRESS': | |
| 343 this.totalDeleted_ += event.urls.length; | |
| 344 var title = strf('DELETED_MESSAGE_PLURAL', this.totalDeleted_); | |
| 345 if (this.totalDeleted_ == 1) { | |
| 346 var fullPath = util.extractFilePath(event.urls[0]); | |
| 347 var fileName = PathUtil.split(fullPath).pop(); | |
| 348 title = strf('DELETED_MESSAGE', fileName); | |
| 349 } | |
| 350 | |
| 351 if (this.currentMode_ == ButterBar.Mode.DELETE) | |
| 352 this.update_(title); | |
| 353 else | |
| 354 this.show(ButterBar.Mode.DELETE, title); | |
| 355 break; | |
| 356 | |
| 357 case 'SUCCESS': | |
| 358 break; | |
| 359 | |
| 360 case 'ERROR': | |
| 361 this.showError_(str('DELETE_ERROR')); | |
| 362 break; | |
| 363 | |
| 364 default: | |
| 365 console.warn('Unknown "delete" event reason: ' + event.reason); | |
| 366 } | |
| 367 }; | |
| OLD | NEW |