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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * Entry of NavigationListModel. This construtor should be called only from | 8 * Entry of NavigationListModel. This construtor should be called only from |
9 * the helper methods (NavigationModelItem.createWithPath/createWithEntry). | 9 * the helper methods (NavigationModelItem.createWithPath/createWithEntry). |
10 * | 10 * |
11 * @param {FileSystem} filesystem Fielsystem. | 11 * @param {FileSystem} filesystem Fielsystem. |
12 * @param {?string} path Path. | 12 * @param {?string} path Path. |
13 * @param {DirectoryEntry} entry Entry. Can be a fake. | 13 * @param {DirectoryEntry} entry Entry. Can be a fake. |
14 * @extends {cr.EventTarget} | 14 * @extends {cr.EventTarget} |
15 * @constructor | 15 * @constructor |
16 */ | 16 */ |
17 function NavigationModelItem(filesystem, path, entry) { | 17 function NavigationModelItem(filesystem, path, entry) { |
18 this.filesystem_ = filesystem; | 18 this.filesystem_ = filesystem; |
19 this.path_ = path; | 19 this.path_ = path; |
20 this.entry_ = entry; | 20 this.entry_ = entry; |
| 21 this.fileError_ = null; |
21 | 22 |
22 this.resolvingQueue_ = new AsyncUtil.Queue(); | 23 this.resolvingQueue_ = new AsyncUtil.Queue(); |
| 24 |
| 25 Object.seal(this); |
23 } | 26 } |
24 | 27 |
25 NavigationModelItem.prototype = { | 28 NavigationModelItem.prototype = { |
26 __proto__: cr.EventTarget.prototype, | 29 get path() { return this.path_; }, |
27 get path() { return this.path_; } | 30 get fileError() { return this.fileError_; } |
28 }; | 31 }; |
29 | 32 |
30 /** | 33 /** |
31 * Returns the cached entry of the item. This may returns NULL if the target is | 34 * Returns the cached entry of the item. This may returns NULL if the target is |
32 * not available on the filesystem, is not resolved or is under resolving the | 35 * not available on the filesystem, is not resolved or is under resolving the |
33 * entry. | 36 * entry. |
34 * | 37 * |
35 * @return {Entry} Cached entry. | 38 * @return {Entry} Cached entry. |
36 */ | 39 */ |
37 NavigationModelItem.prototype.getCachedEntry = function() { | 40 NavigationModelItem.prototype.getCachedEntry = function() { |
38 return this.entry_; | 41 return this.entry_; |
39 }; | 42 }; |
40 | 43 |
41 /** | 44 /** |
42 * @param {FileSystem} filesystem FileSystem. | 45 * @param {FileSystem} filesystem FileSystem. |
43 * @param {string} path Path. | 46 * @param {string} path Path. |
| 47 * @param {function(NavigationModelItem)} callback Called when the resolving is |
| 48 * success/failed with the created NavigationModelItem. |
44 * @return {NavigationModelItem} Created NavigationModelItem. | 49 * @return {NavigationModelItem} Created NavigationModelItem. |
45 */ | 50 */ |
46 NavigationModelItem.createWithPath = function(filesystem, path) { | 51 NavigationModelItem.createWithPath = function(filesystem, path, callback) { |
47 var modelItem = new NavigationModelItem( | 52 var modelItem = new NavigationModelItem( |
48 filesystem, | 53 filesystem, |
49 path, | 54 path, |
50 null); | 55 null); |
51 modelItem.resolveDirectoryEntry_(); | 56 modelItem.resolveDirectoryEntry_(function() { |
| 57 callback(modelItem); |
| 58 }); |
52 return modelItem; | 59 return modelItem; |
53 }; | 60 }; |
54 | 61 |
55 /** | 62 /** |
56 * @param {DirectoryEntry} entry Entry. Can be a fake. | 63 * @param {DirectoryEntry} entry Entry. Can be a fake. |
57 * @return {NavigationModelItem} Created NavigationModelItem. | 64 * @return {NavigationModelItem} Created NavigationModelItem. |
58 */ | 65 */ |
59 NavigationModelItem.createWithEntry = function(entry) { | 66 NavigationModelItem.createWithEntry = function(entry) { |
60 if (entry) { | 67 if (entry) { |
61 return new NavigationModelItem( | 68 return new NavigationModelItem( |
(...skipping 16 matching lines...) Expand all Loading... |
78 NavigationModelItem.prototype.getEntryAsync = function(callback) { | 85 NavigationModelItem.prototype.getEntryAsync = function(callback) { |
79 // If resolveDirectoryEntry_() is running, waits until it finishes. | 86 // If resolveDirectoryEntry_() is running, waits until it finishes. |
80 this.resolvingQueue_.run(function(continueCallback) { | 87 this.resolvingQueue_.run(function(continueCallback) { |
81 callback(this.entry_); | 88 callback(this.entry_); |
82 continueCallback(); | 89 continueCallback(); |
83 }.bind(this)); | 90 }.bind(this)); |
84 }; | 91 }; |
85 | 92 |
86 /** | 93 /** |
87 * Resolves an directory entry. | 94 * Resolves an directory entry. |
| 95 * @param {function()} callback Called when the resolving is success/failed. |
88 * @private | 96 * @private |
89 */ | 97 */ |
90 NavigationModelItem.prototype.resolveDirectoryEntry_ = function() { | 98 NavigationModelItem.prototype.resolveDirectoryEntry_ = function(callback) { |
91 this.resolvingQueue_.run(function(continueCallback) { | 99 this.resolvingQueue_.run(function(continueCallback) { |
92 this.filesystem_.root.getDirectory( | 100 this.filesystem_.root.getDirectory( |
93 this.path_, | 101 this.path_, |
94 {create: false}, | 102 {create: false}, |
95 function(directoryEntry) { | 103 function(directoryEntry) { |
96 this.entry_ = directoryEntry; | 104 this.entry_ = directoryEntry; |
| 105 callback(); |
97 continueCallback(); | 106 continueCallback(); |
98 }.bind(this), | 107 }.bind(this), |
99 function(error) { | 108 function(error) { |
100 this.entry_ = null; | 109 this.entry_ = null; |
| 110 this.fileError_ = error; |
101 console.error('Error on resolving path: ' + this.path_); | 111 console.error('Error on resolving path: ' + this.path_); |
| 112 callback(); |
102 continueCallback(); | 113 continueCallback(); |
103 }.bind(this)); | 114 }.bind(this)); |
104 }.bind(this)); | 115 }.bind(this)); |
105 }; | 116 }; |
106 | 117 |
107 /** | 118 /** |
108 * Returns if this item is a shortcut or a volume root. | 119 * Returns if this item is a shortcut or a volume root. |
109 * @return {boolean} True if a shortcut, false if a volume root. | 120 * @return {boolean} True if a shortcut, false if a volume root. |
110 */ | 121 */ |
111 NavigationModelItem.prototype.isShortcut = function() { | 122 NavigationModelItem.prototype.isShortcut = function() { |
(...skipping 12 matching lines...) Expand all Loading... |
124 this.volumesListModel_ = volumesList; | 135 this.volumesListModel_ = volumesList; |
125 this.shortcutListModel_ = shortcutList; | 136 this.shortcutListModel_ = shortcutList; |
126 | 137 |
127 // On default, shortcut list is disabled. It may be enabled on initializetion. | 138 // On default, shortcut list is disabled. It may be enabled on initializetion. |
128 this.showShortcuts_ = false; | 139 this.showShortcuts_ = false; |
129 | 140 |
130 var entryToModelItem = function(entry) { | 141 var entryToModelItem = function(entry) { |
131 return NavigationModelItem.createWithEntry(entry); | 142 return NavigationModelItem.createWithEntry(entry); |
132 }; | 143 }; |
133 var pathToModelItem = function(path) { | 144 var pathToModelItem = function(path) { |
134 return NavigationModelItem.createWithPath(filesystem, path); | 145 return NavigationModelItem.createWithPath(filesystem, path, |
135 }; | 146 function(modelItem) { |
| 147 if (!modelItem.getCachedEntry() && |
| 148 modelItem.fileError && |
| 149 modelItem.fileError.code === FileError.NOT_FOUND_ERR) { |
| 150 this.onItemNotFoundError(modelItem); |
| 151 } |
| 152 }.bind(this)); |
| 153 }.bind(this); |
136 | 154 |
137 /** | 155 /** |
138 * Type of updated list. | 156 * Type of updated list. |
139 * @enum {number} | 157 * @enum {number} |
140 * @const | 158 * @const |
141 */ | 159 */ |
142 var ListType = { | 160 var ListType = { |
143 VOLUME_LIST: 1, | 161 VOLUME_LIST: 1, |
144 SHORTCUT_LIST: 2 | 162 SHORTCUT_LIST: 2 |
145 }; | 163 }; |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 */ | 339 */ |
322 NavigationListModel.prototype.indexOf = function(modelItem, opt_fromIndex) { | 340 NavigationListModel.prototype.indexOf = function(modelItem, opt_fromIndex) { |
323 for (var i = opt_fromIndex || 0; i < this.length; i++) { | 341 for (var i = opt_fromIndex || 0; i < this.length; i++) { |
324 if (modelItem === this.item(i)) | 342 if (modelItem === this.item(i)) |
325 return i; | 343 return i; |
326 } | 344 } |
327 return -1; | 345 return -1; |
328 }; | 346 }; |
329 | 347 |
330 /** | 348 /** |
| 349 * Called when one od the items is not found on the filesystem. |
| 350 * @param {NavigationModelItem} modelItem The entry which is not found. |
| 351 */ |
| 352 NavigationListModel.prototype.onItemNotFoundError = function(modelItem) { |
| 353 var index = this.indexOf(modelItem); |
| 354 if (index === -1) { |
| 355 // Invalid modelItem. |
| 356 } else if (index < this.volumesList_.length) { |
| 357 // The item is in the volume list. |
| 358 // Not implemented. |
| 359 // TODO(yoshiki): Implement it when necessary. |
| 360 } else { |
| 361 // The item is in the folder shortcut list. |
| 362 if (this.isDriveMounted()) |
| 363 this.shortcutListModel_.remove(modelItem.path); |
| 364 } |
| 365 }; |
| 366 |
| 367 /** |
| 368 * Returns if the drive is mounted or not. |
| 369 * @return {boolean} True if the drive is mounted, false otherwise. |
| 370 */ |
| 371 NavigationListModel.prototype.isDriveMounted = function() { |
| 372 for (var i = 0; i < this.volumesList_.length; i++) { |
| 373 if (PathUtil.isDriveBasedPath(this.item(i).path)) |
| 374 return true; |
| 375 } |
| 376 return false; |
| 377 }; |
| 378 |
| 379 /** |
331 * A navigation list item. | 380 * A navigation list item. |
332 * @constructor | 381 * @constructor |
333 * @extends {HTMLLIElement} | 382 * @extends {HTMLLIElement} |
334 */ | 383 */ |
335 var NavigationListItem = cr.ui.define('li'); | 384 var NavigationListItem = cr.ui.define('li'); |
336 | 385 |
337 NavigationListItem.prototype = { | 386 NavigationListItem.prototype = { |
338 __proto__: HTMLLIElement.prototype, | 387 __proto__: HTMLLIElement.prototype, |
339 get modelItem() { return this.modelItem_; }, | 388 get modelItem() { return this.modelItem_; } |
340 }; | 389 }; |
341 | 390 |
342 /** | 391 /** |
343 * Decorate the item. | 392 * Decorate the item. |
344 */ | 393 */ |
345 NavigationListItem.prototype.decorate = function() { | 394 NavigationListItem.prototype.decorate = function() { |
346 // decorate() may be called twice: from the constructor and from | 395 // decorate() may be called twice: from the constructor and from |
347 // List.createItem(). This check prevents double-decorating. | 396 // List.createItem(). This check prevents double-decorating. |
348 if (this.className) | 397 if (this.className) |
349 return; | 398 return; |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 | 628 |
580 // TODO(yoshiki): Animation is temporary disabled for volumes. Add animation | 629 // TODO(yoshiki): Animation is temporary disabled for volumes. Add animation |
581 // back. crbug.com/276132. | 630 // back. crbug.com/276132. |
582 item.setFadeinAnimation(modelItem.isShortcut() && | 631 item.setFadeinAnimation(modelItem.isShortcut() && |
583 !this.measuringTemporaryItemNow_); | 632 !this.measuringTemporaryItemNow_); |
584 | 633 |
585 var handleClick = function() { | 634 var handleClick = function() { |
586 if (item.selected && | 635 if (item.selected && |
587 modelItem.path !== this.directoryModel_.getCurrentDirPath()) { | 636 modelItem.path !== this.directoryModel_.getCurrentDirPath()) { |
588 metrics.recordUserAction('FolderShortcut.Navigate'); | 637 metrics.recordUserAction('FolderShortcut.Navigate'); |
589 this.changeDirectory_(modelItem.path); | 638 this.changeDirectory_(modelItem); |
590 } | 639 } |
591 }.bind(this); | 640 }.bind(this); |
592 item.addEventListener('click', handleClick); | 641 item.addEventListener('click', handleClick); |
593 | 642 |
594 var handleEject = function() { | 643 var handleEject = function() { |
595 var unmountCommand = cr.doc.querySelector('command#unmount'); | 644 var unmountCommand = cr.doc.querySelector('command#unmount'); |
596 // Let's make sure 'canExecute' state of the command is properly set for | 645 // Let's make sure 'canExecute' state of the command is properly set for |
597 // the root before executing it. | 646 // the root before executing it. |
598 unmountCommand.canExecuteChange(item); | 647 unmountCommand.canExecuteChange(item); |
599 unmountCommand.execute(item); | 648 unmountCommand.execute(item); |
600 }; | 649 }; |
601 item.addEventListener('eject', handleEject); | 650 item.addEventListener('eject', handleEject); |
602 | 651 |
603 if (this.contextMenu_) | 652 if (this.contextMenu_) |
604 item.maybeSetContextMenu(this.contextMenu_); | 653 item.maybeSetContextMenu(this.contextMenu_); |
605 | 654 |
606 return item; | 655 return item; |
607 }; | 656 }; |
608 | 657 |
609 /** | 658 /** |
610 * Changes the current directory to the given path. | 659 * Changes the current directory to the given path. |
611 * If the given path is not found, a 'shortcut-target-not-found' event is | 660 * If the given path is not found, a 'shortcut-target-not-found' event is |
612 * fired. | 661 * fired. |
613 * | 662 * |
614 * @param {string} path Path of the directory to be chagned to. | 663 * @param {NavigationModelItem} modelItem Directory to be chagned to. |
615 * @private | 664 * @private |
616 */ | 665 */ |
617 NavigationList.prototype.changeDirectory_ = function(path) { | 666 NavigationList.prototype.changeDirectory_ = function(modelItem) { |
618 var onErrorCallback = function() { | 667 var onErrorCallback = function(error) { |
619 // TODO(yoshiki): Remove this if the teporary patch is merged to the M30. | 668 if (error.code === FileError.NOT_FOUND_ERR) |
620 // crbug.com/270436 | 669 this.dataModel.onItemNotFoundError(modelItem); |
621 }.bind(this); | 670 }.bind(this); |
622 | 671 |
623 this.directoryModel_.changeDirectory(path, onErrorCallback); | 672 this.directoryModel_.changeDirectory(modelItem.path, onErrorCallback); |
624 }; | 673 }; |
625 | 674 |
626 /** | 675 /** |
627 * Sets a context menu. Context menu is enabled only on archive and removable | 676 * Sets a context menu. Context menu is enabled only on archive and removable |
628 * volumes as of now. | 677 * volumes as of now. |
629 * | 678 * |
630 * @param {cr.ui.Menu} menu Context menu. | 679 * @param {cr.ui.Menu} menu Context menu. |
631 */ | 680 */ |
632 NavigationList.prototype.setContextMenu = function(menu) { | 681 NavigationList.prototype.setContextMenu = function(menu) { |
633 this.contextMenu_ = menu; | 682 this.contextMenu_ = menu; |
634 | 683 |
635 for (var i = 0; i < this.dataModel.length; i++) { | 684 for (var i = 0; i < this.dataModel.length; i++) { |
636 this.getListItemByIndex(i).maybeSetContextMenu(this.contextMenu_); | 685 this.getListItemByIndex(i).maybeSetContextMenu(this.contextMenu_); |
637 } | 686 } |
638 }; | 687 }; |
639 | 688 |
640 /** | 689 /** |
641 * Selects the n-th item from the list. | 690 * Selects the n-th item from the list. |
642 * | 691 * |
643 * @param {number} index Item index. | 692 * @param {number} index Item index. |
644 * @return {boolean} True for success, otherwise false. | 693 * @return {boolean} True for success, otherwise false. |
645 */ | 694 */ |
646 NavigationList.prototype.selectByIndex = function(index) { | 695 NavigationList.prototype.selectByIndex = function(index) { |
647 if (index < 0 || index > this.dataModel.length - 1) | 696 if (index < 0 || index > this.dataModel.length - 1) |
648 return false; | 697 return false; |
649 | 698 |
650 var newPath = this.dataModel.item(index).path; | 699 var newModelItem = this.dataModel.item(index); |
| 700 var newPath = newModelItem.path; |
651 if (!newPath) | 701 if (!newPath) |
652 return false; | 702 return false; |
653 | 703 |
654 // Prevents double-moving to the current directory. | 704 // Prevents double-moving to the current directory. |
655 // eg. When user clicks the item, changing directory has already been done in | 705 // eg. When user clicks the item, changing directory has already been done in |
656 // click handler. | 706 // click handler. |
657 if (this.directoryModel_.getCurrentDirEntry().fullPath == newPath) | 707 if (this.directoryModel_.getCurrentDirEntry().fullPath == newPath) |
658 return false; | 708 return false; |
659 | 709 |
660 metrics.recordUserAction('FolderShortcut.Navigate'); | 710 metrics.recordUserAction('FolderShortcut.Navigate'); |
661 this.changeDirectory_(newPath); | 711 this.changeDirectory_(newModelItem); |
662 return true; | 712 return true; |
663 }; | 713 }; |
664 | 714 |
665 /** | 715 /** |
666 * Handler before root item change. | 716 * Handler before root item change. |
667 * @param {Event} event The event. | 717 * @param {Event} event The event. |
668 * @private | 718 * @private |
669 */ | 719 */ |
670 NavigationList.prototype.onBeforeSelectionChange_ = function(event) { | 720 NavigationList.prototype.onBeforeSelectionChange_ = function(event) { |
671 if (event.changes.length == 1 && !event.changes[0].selected) | 721 if (event.changes.length == 1 && !event.changes[0].selected) |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
741 var itemPath = this.dataModel.item(i).path; | 791 var itemPath = this.dataModel.item(i).path; |
742 if (PathUtil.getRootPath(itemPath) == newRootPath) { | 792 if (PathUtil.getRootPath(itemPath) == newRootPath) { |
743 // Not to invoke the handler of this instance, sets the guard. | 793 // Not to invoke the handler of this instance, sets the guard. |
744 this.dontHandleSelectionEvent_ = true; | 794 this.dontHandleSelectionEvent_ = true; |
745 this.selectionModel.selectedIndex = i; | 795 this.selectionModel.selectedIndex = i; |
746 this.dontHandleSelectionEvent_ = false; | 796 this.dontHandleSelectionEvent_ = false; |
747 return; | 797 return; |
748 } | 798 } |
749 } | 799 } |
750 }; | 800 }; |
OLD | NEW |