OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // If directory files changes too often, don't rescan directory more than once | 5 // If directory files changes too often, don't rescan directory more than once |
6 // per specified interval | 6 // per specified interval |
7 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; | 7 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; |
8 // Used for operations that require almost instant rescan. | 8 // Used for operations that require almost instant rescan. |
9 var SHORT_RESCAN_INTERVAL = 100; | 9 var SHORT_RESCAN_INTERVAL = 100; |
10 | 10 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 | 43 |
44 /** | 44 /** |
45 * A map root.fullPath -> currentDirectory.fullPath. | 45 * A map root.fullPath -> currentDirectory.fullPath. |
46 * @private | 46 * @private |
47 * @type {Object.<string, string>} | 47 * @type {Object.<string, string>} |
48 */ | 48 */ |
49 this.currentDirByRoot_ = {}; | 49 this.currentDirByRoot_ = {}; |
50 | 50 |
51 // The map 'name' -> callback. Callbacks are function(entry) -> boolean. | 51 // The map 'name' -> callback. Callbacks are function(entry) -> boolean. |
52 this.filters_ = {}; | 52 this.filters_ = {}; |
53 this.filterHidden = true; | 53 this.setFilterHidden(true); |
54 | 54 |
55 // Readonly status. | 55 // Readonly status. |
56 this.readonly_ = false; | 56 this.readonly_ = false; |
57 this.currentVolumeMetadata_ = {rootPath: '/'}; | 57 this.currentVolumeMetadata_ = {rootPath: '/'}; |
58 this.offline_ = false; | 58 this.offline_ = false; |
59 } | 59 } |
60 | 60 |
61 /** | 61 /** |
62 * The name of the directory containing externally | 62 * The name of the directory containing externally |
63 * mounted removable storage volumes. | 63 * mounted removable storage volumes. |
(...skipping 20 matching lines...) Expand all Loading... |
84 /** | 84 /** |
85 * The name of the downloads directory. | 85 * The name of the downloads directory. |
86 */ | 86 */ |
87 DirectoryModel.DOWNLOADS_DIRECTORY = 'Downloads'; | 87 DirectoryModel.DOWNLOADS_DIRECTORY = 'Downloads'; |
88 | 88 |
89 /** | 89 /** |
90 * The name of the gdata provider directory. | 90 * The name of the gdata provider directory. |
91 */ | 91 */ |
92 DirectoryModel.GDATA_DIRECTORY = 'gdata'; | 92 DirectoryModel.GDATA_DIRECTORY = 'gdata'; |
93 | 93 |
94 DirectoryModel.prototype = { | 94 /** |
95 __proto__: cr.EventTarget.prototype, | 95 * DirectoryModel extends cr.EventTarget. |
| 96 */ |
| 97 DirectoryModel.prototype.__proto__ = cr.EventTarget.prototype; |
96 | 98 |
97 /** | 99 /** |
98 * Files in the current directory. | 100 * @return {cr.ui.ArrayDataModel} Files in the current directory. |
99 * @type {cr.ui.ArrayDataModel} | 101 */ |
100 */ | 102 DirectoryModel.prototype.getFileList = function() { |
101 get fileList() { | 103 return this.fileList_; |
102 return this.fileList_; | 104 }; |
103 }, | |
104 | 105 |
105 /** | 106 /** |
106 * Selection in the fileList. | 107 * Sort the file list. |
107 * @type {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} | 108 * @param {string} sortField Sort field. |
108 */ | 109 * @param {string} sortDirection "asc" or "desc". |
109 get fileListSelection() { | 110 */ |
110 return this.fileListSelection_; | 111 DirectoryModel.prototype.sortFileList = function(sortField, sortDirection) { |
111 }, | 112 this.fileList_.sort(sortField, sortDirection); |
| 113 }; |
112 | 114 |
113 get rootType() { | 115 /** |
114 return DirectoryModel.getRootType(this.currentEntry.fullPath); | 116 * @return {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} Selection |
115 }, | 117 * in the fileList. |
| 118 */ |
| 119 DirectoryModel.prototype.getFileListSelection = function() { |
| 120 return this.fileListSelection_; |
| 121 }; |
116 | 122 |
117 get rootName() { | 123 /** |
118 return DirectoryModel.getRootName(this.currentEntry.fullPath); | 124 * @return {DirectoryModel.RootType} Root type of current root. |
119 }, | 125 */ |
| 126 DirectoryModel.prototype.getRootType = function() { |
| 127 return DirectoryModel.getRootType(this.currentDirEntry_.fullPath); |
| 128 }; |
120 | 129 |
121 /** | 130 /** |
122 * True if current directory is read only. | 131 * @return {string} Root name. |
123 * @type {boolean} | 132 */ |
124 */ | 133 DirectoryModel.prototype.getRootName = function() { |
125 get readonly() { | 134 return DirectoryModel.getRootName(this.currentDirEntry_.fullPath); |
126 return this.readonly_; | 135 }; |
127 }, | |
128 | 136 |
129 get offline() { | 137 /** |
130 return this.offline_; | 138 * @return {boolean} True if current directory is read only. |
131 }, | 139 */ |
132 set offline(value) { | 140 DirectoryModel.prototype.isReadOnly = function() { |
133 if (this.offline_ != value) { | 141 return this.readonly_; |
134 this.offline_ = !!value; | 142 }; |
135 this.updateReadonlyStatus_(); | |
136 } | |
137 }, | |
138 | 143 |
139 get isSystemDirectoy() { | 144 /** |
140 var path = this.currentEntry.fullPath; | 145 * @return {boolean} If offline. |
141 return path == '/' || | 146 */ |
142 path == '/' + DirectoryModel.REMOVABLE_DIRECTORY || | 147 DirectoryModel.prototype.isOffline = function() { |
143 path == '/' + DirectoryModel.ARCHIVE_DIRECTORY; | 148 return this.offline_; |
144 }, | 149 }; |
145 | 150 |
146 get filterHidden() { | 151 /** |
147 return !!this.filters_['hidden']; | 152 * @param {boolean} value New online status. |
148 }, | 153 */ |
| 154 DirectoryModel.prototype.setOffline = function(value) { |
| 155 if (this.offline_ != value) { |
| 156 this.offline_ = !!value; |
| 157 this.updateReadonlyStatus_(); |
| 158 } |
| 159 }; |
149 | 160 |
150 set filterHidden(value) { | 161 /** |
151 if (value) { | 162 * @return {boolean} If current directory is system. |
152 this.addFilter('hidden', | 163 */ |
153 function(e) {return e.name.substr(0, 1) != '.';}); | 164 DirectoryModel.prototype.isSystemDirectory = function() { |
154 } else { | 165 var path = this.currentDirEntry_.fullPath; |
155 this.removeFilter('hidden'); | 166 return path == '/' || |
156 } | 167 path == '/' + DirectoryModel.REMOVABLE_DIRECTORY || |
157 }, | 168 path == '/' + DirectoryModel.ARCHIVE_DIRECTORY; |
| 169 }; |
158 | 170 |
159 /** | 171 /** |
160 * Current directory. | 172 * @return {boolean} If the files with names starting with "." are not shown. |
161 * @type {DirectoryEntry} | 173 */ |
162 */ | 174 DirectoryModel.prototype.isFilterHiddenOn = function() { |
163 get currentEntry() { | 175 return !!this.filters_['hidden']; |
164 return this.currentDirEntry_; | 176 }; |
165 }, | |
166 | 177 |
167 set autoSelectIndex(value) { | 178 /** |
168 this.autoSelectIndex_ = value; | 179 * @param {boolean} value Whether files with leading "." are hidden. |
169 }, | 180 */ |
| 181 DirectoryModel.prototype.setFilterHidden = function(value) { |
| 182 if (value) { |
| 183 this.addFilter('hidden', |
| 184 function(e) {return e.name.substr(0, 1) != '.';}); |
| 185 } else { |
| 186 this.removeFilter('hidden'); |
| 187 } |
| 188 }; |
170 | 189 |
171 /** | 190 /** |
172 * Names of selected files. | 191 * @return {DirectoryEntry} Current directory. |
173 * @type {Array.<string>} | 192 */ |
174 */ | 193 DirectoryModel.prototype.getCurrentDirEntry = function() { |
175 get selectedNames() { | 194 return this.currentDirEntry_; |
176 var indexes = this.fileListSelection_.selectedIndexes; | 195 }; |
177 var dataModel = this.fileList_; | |
178 if (dataModel) { | |
179 return indexes.map(function(i) { | |
180 return dataModel.item(i).name; | |
181 }); | |
182 } | |
183 return []; | |
184 }, | |
185 | 196 |
186 set selectedNames(value) { | 197 /** |
187 var indexes = []; | 198 * @private |
188 var dataModel = this.fileList_; | 199 * @return {Array.<string>} Names of selected files. |
| 200 */ |
| 201 DirectoryModel.prototype.getSelectedNames_ = function() { |
| 202 var indexes = this.fileListSelection_.selectedIndexes; |
| 203 var dataModel = this.fileList_; |
| 204 if (dataModel) { |
| 205 return indexes.map(function(i) { |
| 206 return dataModel.item(i).name; |
| 207 }); |
| 208 } |
| 209 return []; |
| 210 }; |
189 | 211 |
190 function safeKey(key) { | 212 /** |
191 // The transformation must: | 213 * @private |
192 // 1. Never generate a reserved name ('__proto__') | 214 * @param {Array.<string>} value List of names of selected files. |
193 // 2. Keep different keys different. | 215 */ |
194 return '#' + key; | 216 DirectoryModel.prototype.setSelectedNames_ = function(value) { |
195 } | 217 var indexes = []; |
| 218 var dataModel = this.fileList_; |
196 | 219 |
197 var hash = {}; | 220 function safeKey(key) { |
| 221 // The transformation must: |
| 222 // 1. Never generate a reserved name ('__proto__') |
| 223 // 2. Keep different keys different. |
| 224 return '#' + key; |
| 225 } |
198 | 226 |
199 for (var i = 0; i < value.length; i++) | 227 var hash = {}; |
200 hash[safeKey(value[i])] = 1; | |
201 | 228 |
202 for (var i = 0; i < dataModel.length; i++) { | 229 for (var i = 0; i < value.length; i++) |
203 if (hash.hasOwnProperty(safeKey(dataModel.item(i).name))) | 230 hash[safeKey(value[i])] = 1; |
204 indexes.push(i); | |
205 } | |
206 this.fileListSelection_.selectedIndexes = indexes; | |
207 }, | |
208 | 231 |
209 /** | 232 for (var i = 0; i < dataModel.length; i++) { |
210 * Lead item file name. | 233 if (hash.hasOwnProperty(safeKey(dataModel.item(i).name))) |
211 * @type {string?} | 234 indexes.push(i); |
212 */ | 235 } |
213 get leadName() { | 236 this.fileListSelection_.selectedIndexes = indexes; |
214 var index = this.fileListSelection_.leadIndex; | 237 }; |
215 return index >= 0 && this.fileList_.item(index).name; | |
216 }, | |
217 | 238 |
218 set leadName(value) { | 239 /** |
219 for (var i = 0; i < this.fileList_.length; i++) { | 240 * @private |
220 if (this.fileList_.item(i).name == value) { | 241 * @return {string} Lead item file name. |
221 this.fileListSelection_.leadIndex = i; | 242 */ |
222 return; | 243 DirectoryModel.prototype.getLeadName_ = function() { |
223 } | 244 var index = this.fileListSelection_.leadIndex; |
| 245 return index >= 0 && this.fileList_.item(index).name; |
| 246 }; |
| 247 |
| 248 /** |
| 249 * @private |
| 250 * @param {string} value The name of new lead index. |
| 251 */ |
| 252 DirectoryModel.prototype.setLeadName_ = function(value) { |
| 253 for (var i = 0; i < this.fileList_.length; i++) { |
| 254 if (this.fileList_.item(i).name == value) { |
| 255 this.fileListSelection_.leadIndex = i; |
| 256 return; |
224 } | 257 } |
225 } | 258 } |
226 }; | 259 }; |
227 | 260 |
228 /** | 261 /** |
229 * @return {cr.ui.ArrayDataModel} The list of roots. | 262 * @return {cr.ui.ArrayDataModel} The list of roots. |
230 */ | 263 */ |
231 DirectoryModel.prototype.getRootsList = function() { | 264 DirectoryModel.prototype.getRootsList = function() { |
232 return this.rootsList_; | 265 return this.rootsList_; |
233 }; | 266 }; |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 this.pendingScan_ = this.createScanner_(fileList, successCallback); | 360 this.pendingScan_ = this.createScanner_(fileList, successCallback); |
328 return; | 361 return; |
329 } | 362 } |
330 | 363 |
331 this.runningScan_ = this.createScanner_(fileList, successCallback); | 364 this.runningScan_ = this.createScanner_(fileList, successCallback); |
332 this.runningScan_.run(); | 365 this.runningScan_.run(); |
333 }; | 366 }; |
334 | 367 |
335 /** | 368 /** |
336 * @private | 369 * @private |
337 * @param {Array.<Entry>|cr.ui.ArrayDataModel} list | 370 * @param {Array.<Entry>|cr.ui.ArrayDataModel} list File list. |
338 * @param {function} successCallback Callback on success. | 371 * @param {function} successCallback Callback on success. |
339 * @return {DirectoryModel.Scanner} New Scanner instance. | 372 * @return {DirectoryModel.Scanner} New Scanner instance. |
340 */ | 373 */ |
341 DirectoryModel.prototype.createScanner_ = function(list, successCallback) { | 374 DirectoryModel.prototype.createScanner_ = function(list, successCallback) { |
342 var self = this; | 375 var self = this; |
343 function onSuccess() { | 376 function onSuccess() { |
344 self.scanFailures_ = 0; | 377 self.scanFailures_ = 0; |
345 successCallback(); | 378 successCallback(); |
346 if (self.pendingScan_) { | 379 if (self.pendingScan_) { |
347 self.runningScan_ = self.pendingScan_; | 380 self.runningScan_ = self.pendingScan_; |
(...skipping 14 matching lines...) Expand all Loading... |
362 this.currentDirEntry_, | 395 this.currentDirEntry_, |
363 list, | 396 list, |
364 onSuccess, | 397 onSuccess, |
365 onFailure, | 398 onFailure, |
366 this.prefetchCacheForSorting_.bind(this), | 399 this.prefetchCacheForSorting_.bind(this), |
367 this.filters_); | 400 this.filters_); |
368 }; | 401 }; |
369 | 402 |
370 /** | 403 /** |
371 * @private | 404 * @private |
372 * @param {Array.<Entry>} entries | 405 * @param {Array.<Entry>} entries List of files. |
373 */ | 406 */ |
374 DirectoryModel.prototype.replaceFileList_ = function(entries) { | 407 DirectoryModel.prototype.replaceFileList_ = function(entries) { |
375 cr.dispatchSimpleEvent(this, 'begin-update-files'); | 408 cr.dispatchSimpleEvent(this, 'begin-update-files'); |
376 this.fileListSelection_.beginChange(); | 409 this.fileListSelection_.beginChange(); |
377 | 410 |
378 var selectedNames = this.selectedNames; | 411 var selectedNames = this.getSelectedNames_(); |
379 // Restore leadIndex in case leadName no longer exists. | 412 // Restore leadIndex in case leadName no longer exists. |
380 var leadIndex = this.fileListSelection_.leadIndex; | 413 var leadIndex = this.fileListSelection_.leadIndex; |
381 var leadName = this.leadName; | 414 var leadName = this.getLeadName_(); |
382 | 415 |
383 var spliceArgs = [].slice.call(entries); | 416 var spliceArgs = [].slice.call(entries); |
384 spliceArgs.unshift(0, this.fileList_.length); | 417 spliceArgs.unshift(0, this.fileList_.length); |
385 this.fileList_.splice.apply(this.fileList_, spliceArgs); | 418 this.fileList_.splice.apply(this.fileList_, spliceArgs); |
386 | 419 |
387 this.selectedNames = selectedNames; | 420 this.setSelectedNames_(selectedNames); |
388 this.fileListSelection_.leadIndex = leadIndex; | 421 this.fileListSelection_.leadIndex = leadIndex; |
389 this.leadName = leadName; | 422 this.setLeadName_(leadName); |
390 this.fileListSelection_.endChange(); | 423 this.fileListSelection_.endChange(); |
391 cr.dispatchSimpleEvent(this, 'end-update-files'); | 424 cr.dispatchSimpleEvent(this, 'end-update-files'); |
392 }; | 425 }; |
393 | 426 |
394 /** | 427 /** |
395 * Cancels waiting and scheduled rescans and starts new scan. | 428 * Cancels waiting and scheduled rescans and starts new scan. |
396 * | 429 * |
397 * If the scan completes successfully on the first attempt, the callback will | 430 * If the scan completes successfully on the first attempt, the callback will |
398 * be invoked and a 'scan-completed' event will be dispatched. If the scan | 431 * be invoked and a 'scan-completed' event will be dispatched. If the scan |
399 * fails for any reason, we'll periodically retry until it succeeds (and then | 432 * fails for any reason, we'll periodically retry until it succeeds (and then |
(...skipping 26 matching lines...) Expand all Loading... |
426 if (this.currentDirEntry_ == this.unmountedGDataEntry_) { | 459 if (this.currentDirEntry_ == this.unmountedGDataEntry_) { |
427 onDone(); | 460 onDone(); |
428 return; | 461 return; |
429 } | 462 } |
430 this.runningScan_ = this.createScanner_(this.fileList_, onDone); | 463 this.runningScan_ = this.createScanner_(this.fileList_, onDone); |
431 this.runningScan_.run(); | 464 this.runningScan_.run(); |
432 }; | 465 }; |
433 | 466 |
434 /** | 467 /** |
435 * @private | 468 * @private |
436 * @param {Array.<Entry>} entries | 469 * @param {Array.<Entry>} entries Files. |
437 * @param {function} callback | 470 * @param {function} callback Callback on done. |
438 */ | 471 */ |
439 DirectoryModel.prototype.prefetchCacheForSorting_ = function(entries, | 472 DirectoryModel.prototype.prefetchCacheForSorting_ = function(entries, |
440 callback) { | 473 callback) { |
441 var field = this.fileList_.sortStatus.field; | 474 var field = this.fileList_.sortStatus.field; |
442 if (field) { | 475 if (field) { |
443 this.prepareSortEntries_(entries, field, callback); | 476 this.prepareSortEntries_(entries, field, callback); |
444 } else { | 477 } else { |
445 callback(); | 478 callback(); |
446 } | 479 } |
447 }; | 480 }; |
(...skipping 25 matching lines...) Expand all Loading... |
473 | 506 |
474 util.removeFileOrDirectory( | 507 util.removeFileOrDirectory( |
475 entry, | 508 entry, |
476 onSuccess, | 509 onSuccess, |
477 util.flog('Error deleting ' + entry.fullPath, onComplete)); | 510 util.flog('Error deleting ' + entry.fullPath, onComplete)); |
478 } | 511 } |
479 onComplete(); | 512 onComplete(); |
480 }; | 513 }; |
481 | 514 |
482 /** | 515 /** |
483 * @param {string} name | 516 * @param {string} name Filename. |
484 */ | 517 */ |
485 DirectoryModel.prototype.onEntryChanged = function(name) { | 518 DirectoryModel.prototype.onEntryChanged = function(name) { |
486 var currentEntry = this.currentEntry; | 519 var currentEntry = this.currentDirEntry_; |
487 var dm = this.fileList_; | 520 var dm = this.fileList_; |
488 var self = this; | 521 var self = this; |
489 | 522 |
490 function onEntryFound(entry) { | 523 function onEntryFound(entry) { |
491 self.prefetchCacheForSorting_([entry], function() { | 524 self.prefetchCacheForSorting_([entry], function() { |
492 // Do nothing if current directory changed during async operations. | 525 // Do nothing if current directory changed during async operations. |
493 if (self.currentEntry != currentEntry) | 526 if (self.currentDirEntry_ != currentEntry) |
494 return; | 527 return; |
495 | 528 |
496 var index = self.findIndexByName_(name); | 529 var index = self.findIndexByName_(name); |
497 if (index >= 0) | 530 if (index >= 0) |
498 dm.splice(index, 1, entry); | 531 dm.splice(index, 1, entry); |
499 else | 532 else |
500 dm.splice(dm.length, 0, entry); | 533 dm.splice(dm.length, 0, entry); |
501 }); | 534 }); |
502 }; | 535 }; |
503 | 536 |
504 function onError(err) { | 537 function onError(err) { |
505 // Do nothing if current directory changed during async operations. | 538 // Do nothing if current directory changed during async operations. |
506 if (self.currentEntry != currentEntry) | 539 if (self.currentDirEntry_ != currentEntry) |
507 return; | 540 return; |
508 if (err.code != FileError.NOT_FOUND_ERR) { | 541 if (err.code != FileError.NOT_FOUND_ERR) { |
509 self.rescanLater(); | 542 self.rescanLater(); |
510 return; | 543 return; |
511 } | 544 } |
512 | 545 |
513 var index = self.findIndexByName_(name); | 546 var index = self.findIndexByName_(name); |
514 if (index >= 0) | 547 if (index >= 0) |
515 dm.splice(index, 1); | 548 dm.splice(index, 1); |
516 }; | 549 }; |
517 | 550 |
518 util.resolvePath(currentEntry, name, onEntryFound, onError); | 551 util.resolvePath(this.currentDirEntry_, name, onEntryFound, onError); |
519 }; | 552 }; |
520 | 553 |
521 /** | 554 /** |
522 * @private | 555 * @private |
523 * @param {string} name | 556 * @param {string} name Filename. |
524 * @return {number} The index in the fileList. | 557 * @return {number} The index in the fileList. |
525 */ | 558 */ |
526 DirectoryModel.prototype.findIndexByName_ = function(name) { | 559 DirectoryModel.prototype.findIndexByName_ = function(name) { |
527 var dm = this.fileList_; | 560 var dm = this.fileList_; |
528 for (var i = 0; i < dm.length; i++) | 561 for (var i = 0; i < dm.length; i++) |
529 if (dm.item(i).name == name) | 562 if (dm.item(i).name == name) |
530 return i; | 563 return i; |
531 return -1; | 564 return -1; |
532 }; | 565 }; |
533 | 566 |
534 /** | 567 /** |
535 * Rename the entry in the filesystem and update the file list. | 568 * Rename the entry in the filesystem and update the file list. |
536 * @param {Entry} entry Entry to rename. | 569 * @param {Entry} entry Entry to rename. |
537 * @param {string} newName | 570 * @param {string} newName New name. |
538 * @param {function} errorCallback Called on error. | 571 * @param {function} errorCallback Called on error. |
539 * @param {function} opt_successCallback Called on success. | 572 * @param {function} opt_successCallback Called on success. |
540 */ | 573 */ |
541 DirectoryModel.prototype.renameEntry = function(entry, newName, errorCallback, | 574 DirectoryModel.prototype.renameEntry = function(entry, newName, errorCallback, |
542 opt_successCallback) { | 575 opt_successCallback) { |
543 var self = this; | 576 var self = this; |
544 function onSuccess(newEntry) { | 577 function onSuccess(newEntry) { |
545 self.prefetchCacheForSorting_([newEntry], function() { | 578 self.prefetchCacheForSorting_([newEntry], function() { |
546 var fileList = self.fileList_; | 579 var fileList = self.fileList_; |
547 var index = fileList.indexOf(entry); | 580 var index = fileList.indexOf(entry); |
548 if (index >= 0) | 581 if (index >= 0) |
549 fileList.splice(index, 1, newEntry); | 582 fileList.splice(index, 1, newEntry); |
550 self.selectEntry(newName); | 583 self.selectEntry(newName); |
551 // If the entry doesn't exist in the list it mean that it updated from | 584 // If the entry doesn't exist in the list it mean that it updated from |
552 // outside (probably by directory rescan). | 585 // outside (probably by directory rescan). |
553 if (opt_successCallback) | 586 if (opt_successCallback) |
554 opt_successCallback(); | 587 opt_successCallback(); |
555 }); | 588 }); |
556 } | 589 } |
557 entry.moveTo(this.currentEntry, newName, | 590 entry.moveTo(this.currentDirEntry_, newName, onSuccess, errorCallback); |
558 onSuccess, errorCallback); | |
559 }; | 591 }; |
560 | 592 |
561 /** | 593 /** |
562 * Checks if current directory contains a file or directory with this name. | 594 * Checks if current directory contains a file or directory with this name. |
563 * @param {string} newName Name to check. | 595 * @param {string} newName Name to check. |
564 * @param {function(boolean, boolean?)} callback Called when the result's | 596 * @param {function(boolean, boolean?)} callback Called when the result's |
565 * available. First parameter is true if the entry exists and second | 597 * available. First parameter is true if the entry exists and second |
566 * is true if it's a file. | 598 * is true if it's a file. |
567 */ | 599 */ |
568 DirectoryModel.prototype.doesExist = function(newName, callback) { | 600 DirectoryModel.prototype.doesExist = function(newName, callback) { |
569 util.resolvePath(this.currentEntry, newName, | 601 util.resolvePath(this.currentDirEntry_, newName, |
570 function(entry) { | 602 function(entry) { |
571 callback(true, entry.isFile); | 603 callback(true, entry.isFile); |
572 }, | 604 }, |
573 callback.bind(window, false)); | 605 callback.bind(window, false)); |
574 }; | 606 }; |
575 | 607 |
576 /** | 608 /** |
577 * Creates directory and updates the file list. | 609 * Creates directory and updates the file list. |
578 * | 610 * |
579 * @param {string} name | 611 * @param {string} name Directory name. |
580 * @param {function} successCallback | 612 * @param {function} successCallback Callback on success. |
581 * @param {function} errorCallback | 613 * @param {function} errorCallback Callback on failure. |
582 */ | 614 */ |
583 DirectoryModel.prototype.createDirectory = function(name, successCallback, | 615 DirectoryModel.prototype.createDirectory = function(name, successCallback, |
584 errorCallback) { | 616 errorCallback) { |
585 var self = this; | 617 var self = this; |
586 function onSuccess(newEntry) { | 618 function onSuccess(newEntry) { |
587 self.prefetchCacheForSorting_([newEntry], function() { | 619 self.prefetchCacheForSorting_([newEntry], function() { |
588 var fileList = self.fileList_; | 620 var fileList = self.fileList_; |
589 var existing = fileList.slice().filter( | 621 var existing = fileList.slice().filter( |
590 function(e) { return e.name == name; }); | 622 function(e) { return e.name == name; }); |
591 | 623 |
592 if (existing.length) { | 624 if (existing.length) { |
593 self.selectEntry(name); | 625 self.selectEntry(name); |
594 successCallback(existing[0]); | 626 successCallback(existing[0]); |
595 } else { | 627 } else { |
596 self.fileListSelection.beginChange(); | 628 self.fileListSelection_.beginChange(); |
597 fileList.splice(0, 0, newEntry); | 629 fileList.splice(0, 0, newEntry); |
598 self.selectEntry(name); | 630 self.selectEntry(name); |
599 self.fileListSelection.endChange(); | 631 self.fileListSelection_.endChange(); |
600 successCallback(newEntry); | 632 successCallback(newEntry); |
601 } | 633 } |
602 }); | 634 }); |
603 } | 635 } |
604 | 636 |
605 this.currentEntry.getDirectory(name, {create: true, exclusive: true}, | 637 this.currentDirEntry_.getDirectory(name, {create: true, exclusive: true}, |
606 onSuccess, errorCallback); | 638 onSuccess, errorCallback); |
607 }; | 639 }; |
608 | 640 |
609 /** | 641 /** |
610 * Changes directory. Causes 'directory-change' event. | 642 * Changes directory. Causes 'directory-change' event. |
611 * | 643 * |
612 * @param {string} path New current directory path. | 644 * @param {string} path New current directory path. |
613 * @param {function} opt_OnError Called if failed. | 645 * @param {function} opt_OnError Called if failed. |
614 */ | 646 */ |
615 DirectoryModel.prototype.changeDirectory = function(path, opt_OnError) { | 647 DirectoryModel.prototype.changeDirectory = function(path, opt_OnError) { |
616 var onDirectoryResolved = this.changeDirectoryEntry_.bind(this, false); | 648 var onDirectoryResolved = this.changeDirectoryEntry_.bind(this, false); |
(...skipping 29 matching lines...) Expand all Loading... |
646 * | 678 * |
647 * Dispatches the 'directory-changed' event when the directory is successfully | 679 * Dispatches the 'directory-changed' event when the directory is successfully |
648 * changed. | 680 * changed. |
649 * | 681 * |
650 * @private | 682 * @private |
651 * @param {boolean} initial True if it comes from setupPath and | 683 * @param {boolean} initial True if it comes from setupPath and |
652 * false if caused by an user action. | 684 * false if caused by an user action. |
653 * @param {DirectoryEntry} dirEntry A new directory entry. | 685 * @param {DirectoryEntry} dirEntry A new directory entry. |
654 */ | 686 */ |
655 DirectoryModel.prototype.changeDirectoryEntry_ = function(initial, dirEntry) { | 687 DirectoryModel.prototype.changeDirectoryEntry_ = function(initial, dirEntry) { |
656 var previous = this.currentEntry; | 688 var previous = this.currentDirEntry_; |
657 this.currentDirEntry_ = dirEntry; | 689 this.currentDirEntry_ = dirEntry; |
658 function onRescanComplete() { | 690 function onRescanComplete() { |
659 // For tests that open the dialog to empty directories, everything | 691 // For tests that open the dialog to empty directories, everything |
660 // is loaded at this point. | 692 // is loaded at this point. |
661 chrome.test.sendMessage('directory-change-complete'); | 693 chrome.test.sendMessage('directory-change-complete'); |
662 } | 694 } |
663 this.updateReadonlyStatus_(); | 695 this.updateReadonlyStatus_(); |
664 this.updateVolumeMetadata_(); | 696 this.updateVolumeMetadata_(); |
665 this.updateRootsListSelection_(); | 697 this.updateRootsListSelection_(); |
666 this.scan_(onRescanComplete); | 698 this.scan_(onRescanComplete); |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
778 onLeafError.bind(this, baseDirEntry)); | 810 onLeafError.bind(this, baseDirEntry)); |
779 }.bind(this); | 811 }.bind(this); |
780 | 812 |
781 var root = this.root_; | 813 var root = this.root_; |
782 if (!baseName) | 814 if (!baseName) |
783 baseName = this.getDefaultDirectory(); | 815 baseName = this.getDefaultDirectory(); |
784 root.getDirectory(baseName, {create: false}, onBaseFound, onBaseError); | 816 root.getDirectory(baseName, {create: false}, onBaseFound, onBaseError); |
785 }; | 817 }; |
786 | 818 |
787 /** | 819 /** |
788 * @param {function} opt_callback | 820 * @param {function} opt_callback Callback on done. |
789 */ | 821 */ |
790 DirectoryModel.prototype.setupDefaultPath = function(opt_callback) { | 822 DirectoryModel.prototype.setupDefaultPath = function(opt_callback) { |
791 var overridden = false; | 823 this.setupPath(this.getDefaultDirectory(), opt_callback); |
792 function onExternalDirChange() { overridden = true } | |
793 this.addEventListener('directory-changed', onExternalDirChange); | |
794 | |
795 this.getDefaultDirectory_(function(path) { | |
796 this.removeEventListener('directory-changed', onExternalDirChange); | |
797 if (!overridden) | |
798 this.setupPath(path, opt_callback); | |
799 }.bind(this)); | |
800 }; | 824 }; |
801 | 825 |
802 /** | 826 /** |
803 * @private | 827 * @return {string} The default directory. |
804 * @param {function} callback | |
805 */ | 828 */ |
806 DirectoryModel.prototype.getDefaultDirectory = function() { | 829 DirectoryModel.prototype.getDefaultDirectory = function() { |
807 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; | 830 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; |
808 }; | 831 }; |
809 | 832 |
810 /** | 833 /** |
811 * @param {string} name | 834 * @param {string} name Filename. |
812 */ | 835 */ |
813 DirectoryModel.prototype.selectEntry = function(name) { | 836 DirectoryModel.prototype.selectEntry = function(name) { |
814 var dm = this.fileList_; | 837 var dm = this.fileList_; |
815 for (var i = 0; i < dm.length; i++) { | 838 for (var i = 0; i < dm.length; i++) { |
816 if (dm.item(i).name == name) { | 839 if (dm.item(i).name == name) { |
817 this.selectIndex(i); | 840 this.selectIndex(i); |
818 return; | 841 return; |
819 } | 842 } |
820 } | 843 } |
821 }; | 844 }; |
822 | 845 |
823 /** | 846 /** |
824 * @param {number} index | 847 * @param {number} index Index of file. |
825 */ | 848 */ |
826 DirectoryModel.prototype.selectIndex = function(index) { | 849 DirectoryModel.prototype.selectIndex = function(index) { |
827 // this.focusCurrentList_(); | 850 // this.focusCurrentList_(); |
828 if (index >= this.fileList_.length) | 851 if (index >= this.fileList_.length) |
829 return; | 852 return; |
830 | 853 |
831 // If a list bound with the model it will do scrollIndexIntoView(index). | 854 // If a list bound with the model it will do scrollIndexIntoView(index). |
832 this.fileListSelection_.selectedIndex = index; | 855 this.fileListSelection_.selectedIndex = index; |
833 }; | 856 }; |
834 | 857 |
835 /** | 858 /** |
836 * Cache necessary data before a sort happens. | 859 * Cache necessary data before a sort happens. |
837 * | 860 * |
838 * This is called by the table code before a sort happens, so that we can | 861 * This is called by the table code before a sort happens, so that we can |
839 * go fetch data for the sort field that we may not have yet. | 862 * go fetch data for the sort field that we may not have yet. |
840 * @private | 863 * @private |
841 * @param {string} field | 864 * @param {string} field Sort field. |
842 * @param {function} callback | 865 * @param {function} callback Called when done. |
843 */ | 866 */ |
844 DirectoryModel.prototype.prepareSort_ = function(field, callback) { | 867 DirectoryModel.prototype.prepareSort_ = function(field, callback) { |
845 this.prepareSortEntries_(this.fileList_.slice(), field, callback); | 868 this.prepareSortEntries_(this.fileList_.slice(), field, callback); |
846 }; | 869 }; |
847 | 870 |
848 /** | 871 /** |
849 * @private | 872 * @private |
850 * @param {Array.<Entry>} entries | 873 * @param {Array.<Entry>} entries Files. |
851 * @param {string} field | 874 * @param {string} field Sort field. |
852 * @param {function} callback | 875 * @param {function} callback Called when done. |
853 */ | 876 */ |
854 DirectoryModel.prototype.prepareSortEntries_ = function(entries, field, | 877 DirectoryModel.prototype.prepareSortEntries_ = function(entries, field, |
855 callback) { | 878 callback) { |
856 this.metadataCache_.get(entries, 'filesystem', function(properties) { | 879 this.metadataCache_.get(entries, 'filesystem', function(properties) { |
857 callback(); | 880 callback(); |
858 }); | 881 }); |
859 }; | 882 }; |
860 | 883 |
861 /** | 884 /** |
862 * Get root entries asynchronously. | 885 * Get root entries asynchronously. |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
996 */ | 1019 */ |
997 DirectoryModel.prototype.updateRootsListSelection_ = function() { | 1020 DirectoryModel.prototype.updateRootsListSelection_ = function() { |
998 var rootPath = DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); | 1021 var rootPath = DirectoryModel.getRootPath(this.currentDirEntry_.fullPath); |
999 this.rootsListSelection_.selectedIndex = this.findRootsListItem_(rootPath); | 1022 this.rootsListSelection_.selectedIndex = this.findRootsListItem_(rootPath); |
1000 }; | 1023 }; |
1001 | 1024 |
1002 /** | 1025 /** |
1003 * @private | 1026 * @private |
1004 */ | 1027 */ |
1005 DirectoryModel.prototype.updateReadonlyStatus_ = function() { | 1028 DirectoryModel.prototype.updateReadonlyStatus_ = function() { |
1006 switch (this.rootType) { | 1029 switch (this.getRootType()) { |
1007 case DirectoryModel.RootType.REMOVABLE: | 1030 case DirectoryModel.RootType.REMOVABLE: |
1008 this.readonly_ = !!this.currentVolumeMetadata_.isReadOnly; | 1031 this.readonly_ = !!this.currentVolumeMetadata_.isReadOnly; |
1009 break; | 1032 break; |
1010 case DirectoryModel.RootType.ARCHIVE: | 1033 case DirectoryModel.RootType.ARCHIVE: |
1011 this.readonly_ = true; | 1034 this.readonly_ = true; |
1012 break; | 1035 break; |
1013 case DirectoryModel.RootType.DOWNLOADS: | 1036 case DirectoryModel.RootType.DOWNLOADS: |
1014 this.readonly_ = false; | 1037 this.readonly_ = false; |
1015 break; | 1038 break; |
1016 case DirectoryModel.RootType.GDATA: | 1039 case DirectoryModel.RootType.GDATA: |
1017 this.readonly_ = this.offline; | 1040 this.readonly_ = this.offline_; |
1018 break; | 1041 break; |
1019 default: | 1042 default: |
1020 this.readonly_ = true; | 1043 this.readonly_ = true; |
1021 break; | 1044 break; |
1022 } | 1045 } |
1023 }; | 1046 }; |
1024 | 1047 |
1025 /** | 1048 /** |
1026 * @private | 1049 * @private |
1027 */ | 1050 */ |
(...skipping 13 matching lines...) Expand all Loading... |
1041 } | 1064 } |
1042 }); | 1065 }); |
1043 }); | 1066 }); |
1044 } | 1067 } |
1045 } | 1068 } |
1046 }; | 1069 }; |
1047 | 1070 |
1048 /** | 1071 /** |
1049 * Prepare the root for the unmount. | 1072 * Prepare the root for the unmount. |
1050 * | 1073 * |
1051 * @param {string} rootPath | 1074 * @param {string} rootPath The path to the root. |
1052 */ | 1075 */ |
1053 DirectoryModel.prototype.prepareUnmount = function(rootPath) { | 1076 DirectoryModel.prototype.prepareUnmount = function(rootPath) { |
1054 var index = this.findRootsListItem_(rootPath); | 1077 var index = this.findRootsListItem_(rootPath); |
1055 if (index == -1) { | 1078 if (index == -1) { |
1056 console.error('Unknown root entry', rootPath); | 1079 console.error('Unknown root entry', rootPath); |
1057 return; | 1080 return; |
1058 } | 1081 } |
1059 var entry = this.rootsList_.item(index); | 1082 var entry = this.rootsList_.item(index); |
1060 | 1083 |
1061 // We never need to remove this attribute because even if the unmount fails | 1084 // We never need to remove this attribute because even if the unmount fails |
1062 // the onMountCompleted handler calls updateRoots which creates a new entry | 1085 // the onMountCompleted handler calls updateRoots which creates a new entry |
1063 // object for this volume. | 1086 // object for this volume. |
1064 entry.unmounting = true; | 1087 entry.unmounting = true; |
1065 | 1088 |
1066 // Re-place the entry into the roots data model to force re-rendering. | 1089 // Re-place the entry into the roots data model to force re-rendering. |
1067 this.rootsList_.splice(index, 1, entry); | 1090 this.rootsList_.splice(index, 1, entry); |
1068 | 1091 |
1069 if (rootPath == this.rootPath) { | 1092 if (rootPath == this.rootPath) { |
1070 // TODO(kaznacheev): Consider changing to the most recently used root. | 1093 // TODO(kaznacheev): Consider changing to the most recently used root. |
1071 this.changeDirectory(this.getDefaultDirectory()); | 1094 this.changeDirectory(this.getDefaultDirectory()); |
1072 } | 1095 } |
1073 }; | 1096 }; |
1074 | 1097 |
1075 /** | 1098 /** |
1076 * @param {string} path | 1099 * @param {string} path Any path. |
1077 * @return {string} The root path. | 1100 * @return {string} The root path. |
1078 */ | 1101 */ |
1079 DirectoryModel.getRootPath = function(path) { | 1102 DirectoryModel.getRootPath = function(path) { |
1080 var type = DirectoryModel.getRootType(path); | 1103 var type = DirectoryModel.getRootType(path); |
1081 | 1104 |
1082 if (type == DirectoryModel.RootType.DOWNLOADS) | 1105 if (type == DirectoryModel.RootType.DOWNLOADS) |
1083 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; | 1106 return '/' + DirectoryModel.DOWNLOADS_DIRECTORY; |
1084 if (type == DirectoryModel.RootType.GDATA) | 1107 if (type == DirectoryModel.RootType.GDATA) |
1085 return '/' + DirectoryModel.GDATA_DIRECTORY; | 1108 return '/' + DirectoryModel.GDATA_DIRECTORY; |
1086 | 1109 |
1087 function subdir(dir) { | 1110 function subdir(dir) { |
1088 var end = path.indexOf('/', dir.length + 2); | 1111 var end = path.indexOf('/', dir.length + 2); |
1089 return end == -1 ? path : path.substr(0, end); | 1112 return end == -1 ? path : path.substr(0, end); |
1090 } | 1113 } |
1091 | 1114 |
1092 if (type == DirectoryModel.RootType.ARCHIVE) | 1115 if (type == DirectoryModel.RootType.ARCHIVE) |
1093 return subdir(DirectoryModel.ARCHIVE_DIRECTORY); | 1116 return subdir(DirectoryModel.ARCHIVE_DIRECTORY); |
1094 if (type == DirectoryModel.REMOVABLE_DIRECTORY) | 1117 if (type == DirectoryModel.REMOVABLE_DIRECTORY) |
1095 return subdir(DirectoryModel.REMOVABLE_DIRECTORY); | 1118 return subdir(DirectoryModel.REMOVABLE_DIRECTORY); |
1096 return '/'; | 1119 return '/'; |
1097 }; | 1120 }; |
1098 | 1121 |
1099 /** | 1122 /** |
1100 * @param {string} path | 1123 * @param {string} path Any path. |
1101 * @return {string} | 1124 * @return {string} The name of the root. |
1102 */ | 1125 */ |
1103 DirectoryModel.getRootName = function(path) { | 1126 DirectoryModel.getRootName = function(path) { |
1104 var root = DirectoryModel.getRootPath(path); | 1127 var root = DirectoryModel.getRootPath(path); |
1105 var index = root.lastIndexOf('/'); | 1128 var index = root.lastIndexOf('/'); |
1106 return index == -1 ? root : root.substring(index + 1); | 1129 return index == -1 ? root : root.substring(index + 1); |
1107 }; | 1130 }; |
1108 | 1131 |
1109 /** | 1132 /** |
1110 * @param {string} path Path. | 1133 * @param {string} path A path. |
1111 * @return {string} A root type. | 1134 * @return {string} A root type. |
1112 */ | 1135 */ |
1113 DirectoryModel.getRootType = function(path) { | 1136 DirectoryModel.getRootType = function(path) { |
1114 function isTop(dir) { | 1137 function isTop(dir) { |
1115 return path.substr(1, dir.length) == dir; | 1138 return path.substr(1, dir.length) == dir; |
1116 } | 1139 } |
1117 | 1140 |
1118 if (isTop(DirectoryModel.DOWNLOADS_DIRECTORY)) | 1141 if (isTop(DirectoryModel.DOWNLOADS_DIRECTORY)) |
1119 return DirectoryModel.RootType.DOWNLOADS; | 1142 return DirectoryModel.RootType.DOWNLOADS; |
1120 if (isTop(DirectoryModel.GDATA_DIRECTORY)) | 1143 if (isTop(DirectoryModel.GDATA_DIRECTORY)) |
1121 return DirectoryModel.RootType.GDATA; | 1144 return DirectoryModel.RootType.GDATA; |
1122 if (isTop(DirectoryModel.ARCHIVE_DIRECTORY)) | 1145 if (isTop(DirectoryModel.ARCHIVE_DIRECTORY)) |
1123 return DirectoryModel.RootType.ARCHIVE; | 1146 return DirectoryModel.RootType.ARCHIVE; |
1124 if (isTop(DirectoryModel.REMOVABLE_DIRECTORY)) | 1147 if (isTop(DirectoryModel.REMOVABLE_DIRECTORY)) |
1125 return DirectoryModel.RootType.REMOVABLE; | 1148 return DirectoryModel.RootType.REMOVABLE; |
1126 return ''; | 1149 return ''; |
1127 }; | 1150 }; |
1128 | 1151 |
1129 /** | 1152 /** |
1130 * @param {string} path | 1153 * @param {string} path A path. |
1131 * @return {boolean} | 1154 * @return {boolean} True if it is a path to the root. |
1132 */ | 1155 */ |
1133 DirectoryModel.isRootPath = function(path) { | 1156 DirectoryModel.isRootPath = function(path) { |
1134 if (path[path.length - 1] == '/') | 1157 if (path[path.length - 1] == '/') |
1135 path = path.substring(0, path.length - 1); | 1158 path = path.substring(0, path.length - 1); |
1136 return DirectoryModel.getRootPath(path) == path; | 1159 return DirectoryModel.getRootPath(path) == path; |
1137 }; | 1160 }; |
1138 | 1161 |
1139 /** | 1162 /** |
1140 * @constructor | 1163 * @constructor |
1141 * @extends cr.EventTarget | 1164 * @extends cr.EventTarget |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1186 /** | 1209 /** |
1187 * @private | 1210 * @private |
1188 */ | 1211 */ |
1189 DirectoryModel.Scanner.prototype.readNextChunk_ = function() { | 1212 DirectoryModel.Scanner.prototype.readNextChunk_ = function() { |
1190 this.reader_.readEntries(this.onChunkComplete_.bind(this), | 1213 this.reader_.readEntries(this.onChunkComplete_.bind(this), |
1191 this.errorCallback_); | 1214 this.errorCallback_); |
1192 }; | 1215 }; |
1193 | 1216 |
1194 /** | 1217 /** |
1195 * @private | 1218 * @private |
1196 * @param {Array.<Entry>} entries | 1219 * @param {Array.<Entry>} entries File list. |
1197 */ | 1220 */ |
1198 DirectoryModel.Scanner.prototype.onChunkComplete_ = function(entries) { | 1221 DirectoryModel.Scanner.prototype.onChunkComplete_ = function(entries) { |
1199 if (this.cancelled_) | 1222 if (this.cancelled_) |
1200 return; | 1223 return; |
1201 | 1224 |
1202 if (entries.length == 0) { | 1225 if (entries.length == 0) { |
1203 this.successCallback_(); | 1226 this.successCallback_(); |
1204 this.recordMetrics_(); | 1227 this.recordMetrics_(); |
1205 return; | 1228 return; |
1206 } | 1229 } |
(...skipping 19 matching lines...) Expand all Loading... |
1226 /** | 1249 /** |
1227 * @private | 1250 * @private |
1228 */ | 1251 */ |
1229 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { | 1252 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { |
1230 metrics.recordInterval('DirectoryScan'); | 1253 metrics.recordInterval('DirectoryScan'); |
1231 if (this.dir_.fullPath == | 1254 if (this.dir_.fullPath == |
1232 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { | 1255 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { |
1233 metrics.recordMediumCount('DownloadsCount', this.list_.length); | 1256 metrics.recordMediumCount('DownloadsCount', this.list_.length); |
1234 } | 1257 } |
1235 }; | 1258 }; |
OLD | NEW |