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