Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Side by Side Diff: chrome/browser/resources/file_manager/js/file_manager.js

Issue 6881031: FileManager: Add thumbnail view. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 /** 5 /**
6 * FileManager constructor. 6 * FileManager constructor.
7 * 7 *
8 * FileManager objects encapsulate the functionality of the file selector 8 * FileManager objects encapsulate the functionality of the file selector
9 * dialogs, as well as the full screen file manager application (though the 9 * dialogs, as well as the full screen file manager application (though the
10 * latter is not yet implemented). 10 * latter is not yet implemented).
(...skipping 12 matching lines...) Expand all
23 * end with a trailing slash if it represents a directory. 23 * end with a trailing slash if it represents a directory.
24 */ 24 */
25 function FileManager(dialogDom, rootEntries, params) { 25 function FileManager(dialogDom, rootEntries, params) {
26 console.log('Init FileManager: ' + dialogDom); 26 console.log('Init FileManager: ' + dialogDom);
27 27
28 this.dialogDom_ = dialogDom; 28 this.dialogDom_ = dialogDom;
29 this.rootEntries_ = rootEntries; 29 this.rootEntries_ = rootEntries;
30 this.filesystem_ = rootEntries[0].filesystem; 30 this.filesystem_ = rootEntries[0].filesystem;
31 this.params_ = params || {}; 31 this.params_ = params || {};
32 32
33 this.listType_ = null;
34
33 this.document_ = dialogDom.ownerDocument; 35 this.document_ = dialogDom.ownerDocument;
34 this.dialogType_ = 36 this.dialogType_ =
35 this.params_.type || FileManager.DialogType.FULL_PAGE; 37 this.params_.type || FileManager.DialogType.FULL_PAGE;
36 38
37 this.defaultPath_ = this.params_.defaultPath || '/'; 39 this.defaultPath_ = this.params_.defaultPath || '/';
38 40
39 // DirectoryEntry representing the current directory of the dialog. 41 // DirectoryEntry representing the current directory of the dialog.
40 this.currentDirEntry_ = null; 42 this.currentDirEntry_ = null;
41 43
42 this.addEventListener('directory-changed', 44 this.addEventListener('directory-changed',
43 this.onDirectoryChanged_.bind(this)); 45 this.onDirectoryChanged_.bind(this));
44 this.addEventListener('selection-summarized', 46 this.addEventListener('selection-summarized',
45 this.onSelectionSummarized_.bind(this)); 47 this.onSelectionSummarized_.bind(this));
46 48
47 this.initDom_(); 49 this.initDom_();
48 this.initDialogType_(); 50 this.initDialogType_();
49 51
50 this.onDetailSelectionChanged_(); 52 this.onDetailSelectionChanged_();
51 53
52 chrome.fileBrowserPrivate.onDiskChanged.addListener( 54 chrome.fileBrowserPrivate.onDiskChanged.addListener(
53 this.onDiskChanged_.bind(this)); 55 this.onDiskChanged_.bind(this));
54 56
55 this.table.querySelector('.list').focus(); 57 // TODO(rginda) Add a focus() method to the various list classes to take care
58 // of this.
59 // this.currentList_.list_.focus();
56 } 60 }
57 61
58 FileManager.prototype = { 62 FileManager.prototype = {
59 __proto__: cr.EventTarget.prototype 63 __proto__: cr.EventTarget.prototype
60 }; 64 };
61 65
62 // Anonymous "namespace". 66 // Anonymous "namespace".
63 (function() { 67 (function() {
64 68
65 // Private variables and helper functions. 69 // Private variables and helper functions.
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 } 143 }
140 144
141 /** 145 /**
142 * Get the icon type for a given Entry. 146 * Get the icon type for a given Entry.
143 * 147 *
144 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry). 148 * @param {Entry} entry An Entry subclass (FileEntry or DirectoryEntry).
145 * @return {string} One of the keys from FileManager.iconTypes, or 149 * @return {string} One of the keys from FileManager.iconTypes, or
146 * 'unknown'. 150 * 'unknown'.
147 */ 151 */
148 function getIconType(entry) { 152 function getIconType(entry) {
149 if (entry.isDirectory) 153 if (entry.cachedIconType_)
150 return 'folder'; 154 return entry.cachedIconType_;
151 155
152 for (var name in iconTypes) { 156 var rv = 'unknown';
153 var value = iconTypes[name];
154 157
155 if (value instanceof RegExp) { 158 if (entry.isDirectory) {
156 if (value.test(entry.name)) 159 rv = 'folder';
157 return name; 160 } else {
158 } else if (typeof value == 'function') { 161 for (var name in iconTypes) {
159 try { 162 var value = iconTypes[name];
160 if (value(entry)) 163
161 return name; 164 if (value instanceof RegExp) {
162 } catch (ex) { 165 if (value.test(entry.name)) {
163 console.error('Caught exception while evaluating iconType: ' + 166 rv = name;
164 name, ex); 167 break;
168 }
169 } else if (typeof value == 'function') {
170 try {
171 if (value(entry)) {
172 rv = name;
173 break;
174 }
175 } catch (ex) {
176 console.error('Caught exception while evaluating iconType: ' +
177 name, ex);
178 }
179 } else {
180 console.log('Unexpected value in iconTypes[' + name + ']: ' + value);
165 } 181 }
166 } else {
167 console.log('Unexpected value in iconTypes[' + name + ']: ' + value);
168 } 182 }
169 } 183 }
170 184
171 return 'unknown'; 185 entry.cachedIconType_ = rv;
186 return rv;
172 } 187 }
173 188
174 /** 189 /**
175 * Call an asynchronous method on dirEntry, batching multiple callers. 190 * Call an asynchronous method on dirEntry, batching multiple callers.
176 * 191 *
177 * This batches multiple callers into a single invocation, calling all 192 * This batches multiple callers into a single invocation, calling all
178 * interested parties back when the async call completes. 193 * interested parties back when the async call completes.
179 * 194 *
180 * The Entry method to be invoked should take two callbacks as parameters 195 * The Entry method to be invoked should take two callbacks as parameters
181 * (one for success and one for failure), and it should invoke those 196 * (one for success and one for failure), and it should invoke those
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 * 336 *
322 * The successCallback is always invoked synchronously, since this does not 337 * The successCallback is always invoked synchronously, since this does not
323 * actually require an async call. You should not depend on this, as it may 338 * actually require an async call. You should not depend on this, as it may
324 * change if we were to start reading magic numbers (for example). 339 * change if we were to start reading magic numbers (for example).
325 * 340 *
326 * @param {Entry} entry An HTML5 Entry object. 341 * @param {Entry} entry An HTML5 Entry object.
327 * @param {function(Entry)} successCallback The function to invoke once the 342 * @param {function(Entry)} successCallback The function to invoke once the
328 * icon type is known. 343 * icon type is known.
329 */ 344 */
330 function cacheEntryIconType(entry, successCallback) { 345 function cacheEntryIconType(entry, successCallback) {
331 entry.cachedIconType_ = getIconType(entry); 346 getIconType(entry);
332 if (successCallback) 347 if (successCallback)
333 setTimeout(function() { successCallback(entry) }, 0); 348 setTimeout(function() { successCallback(entry) }, 0);
334 } 349 }
335 350
336 // Public statics. 351 // Public statics.
337 352
338 /** 353 /**
339 * List of dialog types. 354 * List of dialog types.
340 * 355 *
341 * Keep this in sync with FileManagerDialog::GetDialogTypeAsString, except 356 * Keep this in sync with FileManagerDialog::GetDialogTypeAsString, except
342 * FULL_PAGE which is specific to this code. 357 * FULL_PAGE which is specific to this code.
343 * 358 *
344 * @enum {string} 359 * @enum {string}
345 */ 360 */
346 FileManager.DialogType = { 361 FileManager.DialogType = {
347 SELECT_FOLDER: 'folder', 362 SELECT_FOLDER: 'folder',
348 SELECT_SAVEAS_FILE: 'saveas-file', 363 SELECT_SAVEAS_FILE: 'saveas-file',
349 SELECT_OPEN_FILE: 'open-file', 364 SELECT_OPEN_FILE: 'open-file',
350 SELECT_OPEN_MULTI_FILE: 'open-multi-file', 365 SELECT_OPEN_MULTI_FILE: 'open-multi-file',
351 FULL_PAGE: 'full-page' 366 FULL_PAGE: 'full-page'
352 }; 367 };
353 368
369 FileManager.ListType = {
370 DETAIL: 'detail',
371 THUMBNAIL: 'thumb'
372 };
373
354 /** 374 /**
355 * Load translated strings. 375 * Load translated strings.
356 */ 376 */
357 FileManager.initStrings = function(callback) { 377 FileManager.initStrings = function(callback) {
358 chrome.fileBrowserPrivate.getStrings(function(strings) { 378 chrome.fileBrowserPrivate.getStrings(function(strings) {
359 localStrings = new LocalStrings(strings); 379 localStrings = new LocalStrings(strings);
360 cr.initLocale(strings); 380 cr.initLocale(strings);
361 381
362 if (callback) 382 if (callback)
363 callback(); 383 callback();
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
397 var ary = this.dialogDom_.querySelectorAll('[visibleif]'); 417 var ary = this.dialogDom_.querySelectorAll('[visibleif]');
398 for (var i = 0; i < ary.length; i++) { 418 for (var i = 0; i < ary.length; i++) {
399 var expr = ary[i].getAttribute('visibleif'); 419 var expr = ary[i].getAttribute('visibleif');
400 if (!eval(expr)) 420 if (!eval(expr))
401 ary[i].style.display = 'none'; 421 ary[i].style.display = 'none';
402 } 422 }
403 423
404 // Populate the static localized strings. 424 // Populate the static localized strings.
405 i18nTemplate.process(this.document_, localStrings.templateData); 425 i18nTemplate.process(this.document_, localStrings.templateData);
406 426
407 // Set up the detail table. 427 this.dataModel_ = new cr.ui.table.TableDataModel([]);
408 var dataModel = new cr.ui.table.TableDataModel([]); 428 this.dataModel_.sort('name');
409 dataModel.sort('name'); 429 this.dataModel_.addEventListener('sorted',
410 dataModel.addEventListener('sorted', 430 this.onDataModelSorted_.bind(this));
411 this.onDataModelSorted_.bind(this)); 431 this.dataModel_.prepareSort = this.prepareSort_.bind(this);
412 dataModel.prepareSort = this.prepareSort_.bind(this);
413 432
433 this.initTable_();
434 this.initGrid_();
435
436 this.setListType(FileManager.ListType.DETAIL);
437
438 this.onResize_();
439 this.dialogDom_.style.opacity = '1';
440 };
441
442
443 FileManager.prototype.setListType = function(type) {
444 console.log('set list: ' + type);
445
446 if (type && type == this.listType_) {
447 console.log('no change: ' + type);
448 return;
449 }
450
451 if (type == FileManager.ListType.DETAIL) {
452 this.table_.style.display = '';
453 this.grid_.style.display = 'none';
454 this.currentList_ = this.table_;
455 this.dialogDom_.querySelector('button.detail-view').disabled = true;
456 this.dialogDom_.querySelector('button.thumbnail-view').disabled = false;
457 } else if (type == FileManager.ListType.THUMBNAIL) {
458 this.grid_.style.display = '';
459 this.table_.style.display = 'none';
460 this.currentList_ = this.grid_;
461 this.dialogDom_.querySelector('button.thumbnail-view').disabled = true;
462 this.dialogDom_.querySelector('button.detail-view').disabled = false;
463 } else {
464 throw new Error('Unknown list type: ' + type);
465 }
466
467 this.listType_ = type;
468 this.onResize_();
469 this.currentList_.redraw();
470
471 console.log('type now: ' + this.listType_);
472 };
473
474 /**
475 * Initialize the file thumbnail grid.
476 */
477 FileManager.prototype.initGrid_ = function() {
478 this.grid_ = this.dialogDom_.querySelector('.thumbnail-grid');
479 cr.ui.Grid.decorate(this.grid_);
480 this.grid_.dataModel = this.dataModel_;
481
482 var self = this;
483 this.grid_.itemConstructor = function(entry) {
484 return self.renderThumbnail_(entry);
485 };
486
487 if (this.dialogType_ != FileManager.DialogType.SELECT_OPEN_MULTI_FILE) {
488 this.grid_.selectionModel = new cr.ui.ListSingleSelectionModel();
489 }
490
491 this.grid_.addEventListener(
492 'dblclick', this.onDetailDoubleClick_.bind(this));
493 this.grid_.selectionModel.addEventListener(
494 'change', this.onDetailSelectionChanged_.bind(this));
495 };
496
497 /**
498 * Initialize the file list table.
499 */
500 FileManager.prototype.initTable_ = function() {
414 var columns = [ 501 var columns = [
415 new cr.ui.table.TableColumn('cachedIconType_', '', 5.4), 502 new cr.ui.table.TableColumn('cachedIconType_', '', 5.4),
416 new cr.ui.table.TableColumn('name', str('NAME_COLUMN_LABEL'), 64), 503 new cr.ui.table.TableColumn('name', str('NAME_COLUMN_LABEL'), 64),
417 new cr.ui.table.TableColumn('cachedSize_', 504 new cr.ui.table.TableColumn('cachedSize_',
418 str('SIZE_COLUMN_LABEL'), 15.5), 505 str('SIZE_COLUMN_LABEL'), 15.5),
419 new cr.ui.table.TableColumn('cachedMtime_', 506 new cr.ui.table.TableColumn('cachedMtime_',
420 str('DATE_COLUMN_LABEL'), 21) 507 str('DATE_COLUMN_LABEL'), 21)
421 ]; 508 ];
422 509
423 columns[0].renderFunction = this.renderIconType_.bind(this); 510 columns[0].renderFunction = this.renderIconType_.bind(this);
424 columns[1].renderFunction = this.renderName_.bind(this); 511 columns[1].renderFunction = this.renderName_.bind(this);
425 columns[2].renderFunction = this.renderSize_.bind(this); 512 columns[2].renderFunction = this.renderSize_.bind(this);
426 columns[3].renderFunction = this.renderDate_.bind(this); 513 columns[3].renderFunction = this.renderDate_.bind(this);
427 514
428 this.table = this.dialogDom_.querySelector('.detail-table'); 515 this.table_ = this.dialogDom_.querySelector('.detail-table');
429 cr.ui.Table.decorate(this.table); 516 cr.ui.Table.decorate(this.table_);
430 517
431 this.table.dataModel = dataModel; 518 this.table_.dataModel = this.dataModel_;
432 this.table.columnModel = new cr.ui.table.TableColumnModel(columns); 519 this.table_.columnModel = new cr.ui.table.TableColumnModel(columns);
433 520
434 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE || 521 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE ||
435 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER || 522 this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FOLDER ||
436 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { 523 this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) {
437 this.table.selectionModel = new cr.ui.table.TableSingleSelectionModel(); 524 this.table_.selectionModel = new cr.ui.table.TableSingleSelectionModel();
438 } 525 }
439 526
440 this.table.addEventListener( 527 this.table_.addEventListener(
441 'dblclick', this.onDetailDoubleClick_.bind(this)); 528 'dblclick', this.onDetailDoubleClick_.bind(this));
442 this.table.selectionModel.addEventListener( 529 this.table_.selectionModel.addEventListener(
443 'change', this.onDetailSelectionChanged_.bind(this)); 530 'change', this.onDetailSelectionChanged_.bind(this));
444
445 this.onResize_();
446 this.dialogDom_.style.opacity = '1';
447 }; 531 };
448 532
449 FileManager.prototype.onResize_ = function() { 533 FileManager.prototype.onResize_ = function() {
450 // TODO(rginda): Remove this hack when cr.ui.List supports resizing. 534 console.log('onResize');
451 this.table.list_.style.height = 535
452 (this.table.clientHeight - this.table.header_.clientHeight) + 'px'; 536 this.table_.style.height = this.grid_.style.height =
453 this.table.redraw(); 537 this.grid_.parentNode.clientHeight + 'px';
538 this.table_.style.width = this.grid_.style.width =
539 this.grid_.parentNode.clientWidth + 'px';
540
541 this.table_.list_.style.width = this.table_.parentNode.clientWidth + 'px';
542 this.table_.list_.style.height = (this.table_.clientHeight - 1 -
543 this.table_.header_.clientHeight) + 'px';
544
545 if (this.listType_ == FileManager.ListType.THUMBNAIL) {
546 var self = this;
547 setTimeout(function () { self.grid_.columns = 0 }, 100);
548 } else {
549 this.currentList_.redraw();
550 }
454 }; 551 };
455 552
456 /** 553 /**
457 * Tweak the UI to become a particular kind of dialog, as determined by the 554 * Tweak the UI to become a particular kind of dialog, as determined by the
458 * dialog type parameter passed to the constructor. 555 * dialog type parameter passed to the constructor.
459 */ 556 */
460 FileManager.prototype.initDialogType_ = function() { 557 FileManager.prototype.initDialogType_ = function() {
461 var defaultTitle; 558 var defaultTitle;
462 var okLabel = str('OPEN_LABEL'); 559 var okLabel = str('OPEN_LABEL');
463 560
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
559 } 656 }
560 657
561 function checkCount() { 658 function checkCount() {
562 if (uncachedCount == 0) { 659 if (uncachedCount == 0) {
563 // Callback via a setTimeout so the sync/async semantics don't change 660 // Callback via a setTimeout so the sync/async semantics don't change
564 // based on whether or not the value is cached. 661 // based on whether or not the value is cached.
565 setTimeout(callback, 0); 662 setTimeout(callback, 0);
566 } 663 }
567 } 664 }
568 665
569 var dataModel = this.table.dataModel; 666 var dataModel = this.dataModel_;
570 var uncachedCount = dataModel.length; 667 var uncachedCount = dataModel.length;
571 668
572 for (var i = uncachedCount - 1; i >= 0 ; i--) { 669 for (var i = uncachedCount - 1; i >= 0 ; i--) {
573 var entry = dataModel.item(i); 670 var entry = dataModel.item(i);
574 if (field in entry) { 671 if (field in entry) {
575 uncachedCount--; 672 uncachedCount--;
576 } else { 673 } else {
577 cacheFunction(entry, function() { 674 cacheFunction(entry, function() {
578 uncachedCount--; 675 uncachedCount--;
579 checkCount(); 676 checkCount();
580 }); 677 });
581 } 678 }
582 } 679 }
583 680
584 checkCount(); 681 checkCount();
585 } 682 }
586 683
684 FileManager.prototype.renderThumbnail_ = function(entry) {
685 var li = this.document_.createElement('li');
686 li.className = 'thumbnail-item';
687
688 var img = this.document_.createElement('img');
689 this.setIconSrc(entry, img);
690 li.appendChild(img);
691
692 var div = this.document_.createElement('div');
693 div.textContent = entry.name;
694 li.appendChild(div);
695
696 cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
697 cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
698
699 return li;
700 }
701
587 /** 702 /**
588 * Render the type column of the detail table. 703 * Render the type column of the detail table.
589 * 704 *
590 * Invoked by cr.ui.Table when a file needs to be rendered. 705 * Invoked by cr.ui.Table when a file needs to be rendered.
591 * 706 *
592 * @param {Entry} entry The Entry object to render. 707 * @param {Entry} entry The Entry object to render.
593 * @param {string} columnId The id of the column to be rendered. 708 * @param {string} columnId The id of the column to be rendered.
594 * @param {cr.ui.Table} table The table doing the rendering. 709 * @param {cr.ui.Table} table The table doing the rendering.
595 */ 710 */
596 FileManager.prototype.renderIconType_ = function(entry, columnId, table) { 711 FileManager.prototype.renderIconType_ = function(entry, columnId, table) {
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
680 /** 795 /**
681 * Compute summary information about the current selection. 796 * Compute summary information about the current selection.
682 * 797 *
683 * This method dispatches the 'selection-summarized' event when it completes. 798 * This method dispatches the 'selection-summarized' event when it completes.
684 * Depending on how many of the selected files already have known sizes, the 799 * Depending on how many of the selected files already have known sizes, the
685 * dispatch may happen immediately, or after a number of async calls complete. 800 * dispatch may happen immediately, or after a number of async calls complete.
686 */ 801 */
687 FileManager.prototype.summarizeSelection_ = function() { 802 FileManager.prototype.summarizeSelection_ = function() {
688 var selection = this.selection = { 803 var selection = this.selection = {
689 entries: [], 804 entries: [],
690 uris: [], 805 urls: [],
691 leadEntry: null, 806 leadEntry: null,
692 totalCount: 0, 807 totalCount: 0,
693 fileCount: 0, 808 fileCount: 0,
694 directoryCount: 0, 809 directoryCount: 0,
695 bytes: 0, 810 bytes: 0,
696 iconType: null, 811 iconType: null,
697 }; 812 };
698 813
699 this.previewSummary_.textContent = str('COMPUTING_SELECTION'); 814 this.previewSummary_.textContent = str('COMPUTING_SELECTION');
700 this.taskButtons_.innerHTML = ''; 815 this.taskButtons_.innerHTML = '';
701 816
702 var selectedIndexes = this.table.selectionModel.selectedIndexes; 817 var selectedIndexes = this.currentList_.selectionModel.selectedIndexes;
703 if (!selectedIndexes.length) { 818 if (!selectedIndexes.length) {
704 cr.dispatchSimpleEvent(this, 'selection-summarized'); 819 cr.dispatchSimpleEvent(this, 'selection-summarized');
705 return; 820 return;
706 } 821 }
707 822
708 var fileCount = 0; 823 var fileCount = 0;
709 var byteCount = 0; 824 var byteCount = 0;
710 var pendingFiles = []; 825 var pendingFiles = [];
711 826
712 for (var i = 0; i < selectedIndexes.length; i++) { 827 for (var i = 0; i < selectedIndexes.length; i++) {
713 var entry = this.table.dataModel.item(selectedIndexes[i]); 828 var entry = this.dataModel_.item(selectedIndexes[i]);
714 829
715 selection.entries.push(entry); 830 selection.entries.push(entry);
716 selection.uris.push(entry.toURI()); 831
832 selection.urls.push(entry.toURL());
717 833
718 if (selection.iconType == null) { 834 if (selection.iconType == null) {
719 selection.iconType = getIconType(entry); 835 selection.iconType = getIconType(entry);
720 } else if (selection.iconType != 'unknown') { 836 } else if (selection.iconType != 'unknown') {
721 var iconType = getIconType(entry); 837 var iconType = getIconType(entry);
722 if (selection.iconType != iconType) 838 if (selection.iconType != iconType)
723 selection.iconType = 'unknown'; 839 selection.iconType = 'unknown';
724 } 840 }
725 841
726 selection.totalCount++; 842 selection.totalCount++;
727 843
728 if (entry.isFile) { 844 if (entry.isFile) {
729 if (!('cachedSize_' in entry)) { 845 if (!('cachedSize_' in entry)) {
730 // Any file that hasn't been rendered may be missing its cachedSize_ 846 // Any file that hasn't been rendered may be missing its cachedSize_
731 // property. For example, visit a large file list, and press ctrl-a 847 // property. For example, visit a large file list, and press ctrl-a
732 // to select all. In this case, we need to asynchronously get the 848 // to select all. In this case, we need to asynchronously get the
733 // sizes for these files before telling the world the selection has 849 // sizes for these files before telling the world the selection has
734 // been summarized. See the 'computeNextFile' logic below. 850 // been summarized. See the 'computeNextFile' logic below.
735 pendingFiles.push(entry); 851 pendingFiles.push(entry);
736 continue; 852 continue;
737 } else { 853 } else {
738 selection.bytes += entry.cachedSize_; 854 selection.bytes += entry.cachedSize_;
739 } 855 }
740 selection.fileCount += 1; 856 selection.fileCount += 1;
741 } else { 857 } else {
742 selection.directoryCount += 1; 858 selection.directoryCount += 1;
743 } 859 }
744 } 860 }
745 861
746 var leadIndex = this.table.selectionModel.leadIndex; 862 var leadIndex = this.currentList_.selectionModel.leadIndex;
747 if (leadIndex > -1) { 863 if (leadIndex > -1) {
748 selection.leadEntry = this.table.dataModel.item(leadIndex); 864 selection.leadEntry = this.dataModel_.item(leadIndex);
749 } else { 865 } else {
750 selection.leadEntry = selection.entries[0]; 866 selection.leadEntry = selection.entries[0];
751 } 867 }
752 868
753 var self = this; 869 var self = this;
754 870
755 function cacheNextFile(fileEntry) { 871 function cacheNextFile(fileEntry) {
756 if (fileEntry) { 872 if (fileEntry) {
757 // We're careful to modify the 'selection', rather than 'self.selection' 873 // We're careful to modify the 'selection', rather than 'self.selection'
758 // here, just in case the selection has changed since this summarization 874 // here, just in case the selection has changed since this summarization
759 // began. 875 // began.
760 selection.bytes += fileEntry.cachedSize_; 876 selection.bytes += fileEntry.cachedSize_;
761 } 877 }
762 878
763 if (pendingFiles.length) { 879 if (pendingFiles.length) {
764 cacheEntrySize(pendingFiles.pop(), cacheNextFile); 880 cacheEntrySize(pendingFiles.pop(), cacheNextFile);
765 } else { 881 } else {
766 self.dispatchEvent(new cr.Event('selection-summarized')); 882 self.dispatchEvent(new cr.Event('selection-summarized'));
767 } 883 }
768 }; 884 };
769 885
770 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE) { 886 if (this.dialogType_ == FileManager.DialogType.FULL_PAGE) {
771 chrome.fileBrowserPrivate.getFileTasks(selection.uris, 887 chrome.fileBrowserPrivate.getFileTasks(selection.urls,
772 this.onTasksFound_.bind(this)); 888 this.onTasksFound_.bind(this));
773 } 889 }
774 890
775 cacheNextFile(); 891 cacheNextFile();
776 }; 892 };
777 893
778 FileManager.prototype.onTasksFound_ = function(tasksList) { 894 FileManager.prototype.onTasksFound_ = function(tasksList) {
779 for (var i = 0; i < tasksList.length; i++) { 895 for (var i = 0; i < tasksList.length; i++) {
780 var task = tasksList[i]; 896 var task = tasksList[i];
781 897
782 var button = this.document_.createElement('button'); 898 var button = this.document_.createElement('button');
783 button.addEventListener('click', this.onTaskButtonClicked_.bind(this)); 899 button.addEventListener('click', this.onTaskButtonClicked_.bind(this));
784 button.className = 'task-button'; 900 button.className = 'task-button';
785 button.task = task; 901 button.task = task;
786 902
787 var img = this.document_.createElement('img'); 903 var img = this.document_.createElement('img');
788 img.src = task.iconUrl; 904 img.src = task.iconUrl;
789 905
790 button.appendChild(img); 906 button.appendChild(img);
791 button.appendChild(this.document_.createTextNode(task.title)); 907 button.appendChild(this.document_.createTextNode(task.title));
792 908
793 this.taskButtons_.appendChild(button); 909 this.taskButtons_.appendChild(button);
794 } 910 }
795 }; 911 };
796 912
797 FileManager.prototype.onTaskButtonClicked_ = function(event) { 913 FileManager.prototype.onTaskButtonClicked_ = function(event) {
798 chrome.fileBrowserPrivate.executeTask(event.srcElement.task.taskId, 914 chrome.fileBrowserPrivate.executeTask(event.srcElement.task.taskId,
799 this.selection.uris); 915 this.selection.urls);
800 } 916 }
801 917
802 /** 918 /**
803 * Update the breadcrumb display to reflect the current directory. 919 * Update the breadcrumb display to reflect the current directory.
804 */ 920 */
805 FileManager.prototype.updateBreadcrumbs_ = function() { 921 FileManager.prototype.updateBreadcrumbs_ = function() {
806 var bc = this.dialogDom_.querySelector('.breadcrumbs'); 922 var bc = this.dialogDom_.querySelector('.breadcrumbs');
807 bc.innerHTML = ''; 923 bc.innerHTML = '';
808 924
809 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, ''); 925 var fullPath = this.currentDirEntry_.fullPath.replace(/\/$/, '');
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
860 this.previewFilename_.textContent = ''; 976 this.previewFilename_.textContent = '';
861 return; 977 return;
862 } 978 }
863 979
864 var previewName = this.selection.leadEntry.name; 980 var previewName = this.selection.leadEntry.name;
865 if (this.currentDirEntry_.name == '') 981 if (this.currentDirEntry_.name == '')
866 previewName = this.getLabelForRootPath_(previewName); 982 previewName = this.getLabelForRootPath_(previewName);
867 983
868 this.previewFilename_.textContent = previewName; 984 this.previewFilename_.textContent = previewName;
869 985
870 var entry = this.selection.leadEntry; 986 var iconType = getIconType(this.selection.leadEntry);
987 if (iconType == 'image') {
988 this.previewImage_.classList.add('transparent-background');
989 if (fileManager.selection.totalCount > 1)
990 this.previewImage_.classList.add('multiple-selected');
991 }
992
993 this.setIconSrc(this.selection.leadEntry, this.previewImage_);
994
995 };
996
997 FileManager.prototype.setIconSrc = function(entry, img, callback) {
871 var iconType = getIconType(entry); 998 var iconType = getIconType(entry);
872 if (iconType != 'image') { 999 if (iconType != 'image') {
873 // Not an image, display a canned clip-art graphic. 1000 // Not an image, display a canned clip-art graphic.
874 this.previewImage_.src = previewArt[iconType]; 1001 img.src = previewArt[iconType];
875 } else { 1002 } else {
876 // File is an image, fetch the thumbnail. 1003 // File is an image, fetch the thumbnail.
877 1004
878 var fileManager = this; 1005 img.src = entry.toURL();
879
880 batchAsyncCall(entry, 'file', function(file) {
881 var reader = new FileReader();
882
883 reader.onerror = util.ferr('Error reading preview: ' + entry.fullPath);
884 reader.onloadend = function(e) {
885 fileManager.previewImage_.src = this.result;
886 fileManager.previewImage_.classList.add('transparent-background');
887 if (fileManager.selection.totalCount > 1)
888 fileManager.previewImage_.classList.add('multiple-selected');
889 };
890
891 reader.readAsDataURL(file);
892 });
893 } 1006 }
1007 if (callback)
1008 callback();
894 }; 1009 };
895 1010
896 /** 1011 /**
897 * Change the current directory. 1012 * Change the current directory.
898 * 1013 *
899 * Dispatches the 'directory-changed' event when the directory is successfully 1014 * Dispatches the 'directory-changed' event when the directory is successfully
900 * changed. 1015 * changed.
901 * 1016 *
902 * @param {string} path The absolute path to the new directory. 1017 * @param {string} path The absolute path to the new directory.
903 */ 1018 */
(...skipping 28 matching lines...) Expand all
932 } 1047 }
933 }); 1048 });
934 }; 1049 };
935 1050
936 /** 1051 /**
937 * Invoked by the table dataModel after a sort completes. 1052 * Invoked by the table dataModel after a sort completes.
938 * 1053 *
939 * We use this hook to make sure selected files stay visible after a sort. 1054 * We use this hook to make sure selected files stay visible after a sort.
940 */ 1055 */
941 FileManager.prototype.onDataModelSorted_ = function() { 1056 FileManager.prototype.onDataModelSorted_ = function() {
942 var i = this.table.selectionModel.leadIndex; 1057 var i = this.currentList_.selectionModel.leadIndex;
943 this.table.scrollIntoView(i); 1058 this.currentList_.scrollIntoView(i);
944 } 1059 }
945 1060
946 /** 1061 /**
947 * Update the selection summary UI when the selection summarization completes. 1062 * Update the selection summary UI when the selection summarization completes.
948 */ 1063 */
949 FileManager.prototype.onSelectionSummarized_ = function() { 1064 FileManager.prototype.onSelectionSummarized_ = function() {
950 if (this.selection.totalCount == 0) { 1065 if (this.selection.totalCount == 0) {
951 this.previewSummary_.textContent = str('NOTHING_SELECTED'); 1066 this.previewSummary_.textContent = str('NOTHING_SELECTED');
952 1067
953 } else if (this.selection.totalCount == 1) { 1068 } else if (this.selection.totalCount == 1) {
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1005 this.okButton_.disabled = !selectable; 1120 this.okButton_.disabled = !selectable;
1006 this.updatePreview_(); 1121 this.updatePreview_();
1007 }; 1122 };
1008 1123
1009 /** 1124 /**
1010 * Handle a double-click event on an entry in the detail list. 1125 * Handle a double-click event on an entry in the detail list.
1011 * 1126 *
1012 * @param {Event} event The click event. 1127 * @param {Event} event The click event.
1013 */ 1128 */
1014 FileManager.prototype.onDetailDoubleClick_ = function(event) { 1129 FileManager.prototype.onDetailDoubleClick_ = function(event) {
1015 var i = this.table.selectionModel.leadIndex; 1130 var i = this.currentList_.selectionModel.leadIndex;
1016 var entry = this.table.dataModel.item(i); 1131 var entry = this.dataModel_.item(i);
1017 1132
1018 if (entry.isDirectory) 1133 if (entry.isDirectory)
1019 return this.changeDirectory(entry.fullPath); 1134 return this.changeDirectory(entry.fullPath);
1020 1135
1021 if (!this.okButton_.disabled) 1136 if (!this.okButton_.disabled)
1022 this.onOk_(); 1137 this.onOk_();
1023 1138
1024 }; 1139 };
1025 1140
1026 /** 1141 /**
(...skipping 19 matching lines...) Expand all
1046 * 1161 *
1047 * @param {function()} opt_callback Optional function to invoke when the 1162 * @param {function()} opt_callback Optional function to invoke when the
1048 * rescan is complete. 1163 * rescan is complete.
1049 */ 1164 */
1050 FileManager.prototype.rescanDirectory_ = function(opt_callback) { 1165 FileManager.prototype.rescanDirectory_ = function(opt_callback) {
1051 var self = this; 1166 var self = this;
1052 var reader; 1167 var reader;
1053 1168
1054 function onReadSome(entries) { 1169 function onReadSome(entries) {
1055 if (entries.length == 0) { 1170 if (entries.length == 0) {
1056 if (self.table.dataModel.sortStatus.field != 'name') 1171 if (self.dataModel_.sortStatus.field != 'name')
1057 self.table.dataModel.updateIndex(0); 1172 self.dataModel_.updateIndex(0);
1058 1173
1059 if (opt_callback) 1174 if (opt_callback)
1060 opt_callback(); 1175 opt_callback();
1061 return; 1176 return;
1062 } 1177 }
1063 1178
1064 // Splice takes the to-be-spliced-in array as individual parameters, 1179 // Splice takes the to-be-spliced-in array as individual parameters,
1065 // rather than as an array, so we need to perform some acrobatics... 1180 // rather than as an array, so we need to perform some acrobatics...
1066 var spliceArgs = [].slice.call(entries); 1181 var spliceArgs = [].slice.call(entries);
1067 1182
1068 // Hide files that start with a dot ('.'). 1183 // Hide files that start with a dot ('.').
1069 // TODO(rginda): User should be able to override this. Support for other 1184 // TODO(rginda): User should be able to override this. Support for other
1070 // commonly hidden patterns might be nice too. 1185 // commonly hidden patterns might be nice too.
1071 spliceArgs = spliceArgs.filter(function(e) { 1186 spliceArgs = spliceArgs.filter(function(e) {
1072 return e.name.substr(0, 1) != '.'; 1187 return e.name.substr(0, 1) != '.';
1073 }); 1188 });
1074 1189
1075 spliceArgs.unshift(0, 0); // index, deleteCount 1190 spliceArgs.unshift(0, 0); // index, deleteCount
1076 self.table.dataModel.splice.apply(self.table.dataModel, spliceArgs); 1191 self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
1077 1192
1078 // Keep reading until entries.length is 0. 1193 // Keep reading until entries.length is 0.
1079 reader.readEntries(onReadSome); 1194 reader.readEntries(onReadSome);
1080 }; 1195 };
1081 1196
1082 // Clear the table first. 1197 // Clear the table first.
1083 this.table.dataModel.splice(0, this.table.dataModel.length); 1198 this.dataModel_.splice(0, this.dataModel_.length);
1084 1199
1085 this.updateBreadcrumbs_(); 1200 this.updateBreadcrumbs_();
1086 1201
1087 if (this.currentDirEntry_.fullPath != '/') { 1202 if (this.currentDirEntry_.fullPath != '/') {
1088 // If not the root directory, just read the contents. 1203 // If not the root directory, just read the contents.
1089 reader = this.currentDirEntry_.createReader(); 1204 reader = this.currentDirEntry_.createReader();
1090 reader.readEntries(onReadSome); 1205 reader.readEntries(onReadSome);
1091 return; 1206 return;
1092 } 1207 }
1093 1208
1094 // Otherwise, use the provided list of root subdirectories, since the 1209 // Otherwise, use the provided list of root subdirectories, since the
1095 // real local filesystem root directory (the one we use outside the 1210 // real local filesystem root directory (the one we use outside the
1096 // harness) can't be enumerated yet. 1211 // harness) can't be enumerated yet.
1097 var spliceArgs = [].slice.call(this.rootEntries_); 1212 var spliceArgs = [].slice.call(this.rootEntries_);
1098 spliceArgs.unshift(0, 0); // index, deleteCount 1213 spliceArgs.unshift(0, 0); // index, deleteCount
1099 self.table.dataModel.splice.apply(self.table.dataModel, spliceArgs); 1214 self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
1100 self.table.dataModel.updateIndex(0); 1215 self.dataModel_.updateIndex(0);
1101 1216
1102 if (opt_callback) 1217 if (opt_callback)
1103 opt_callback(); 1218 opt_callback();
1104 }; 1219 };
1105 1220
1106 FileManager.prototype.onFilenameInputKeyUp_ = function(event) { 1221 FileManager.prototype.onFilenameInputKeyUp_ = function(event) {
1107 this.okButton_.disabled = this.filenameInput_.value.length == 0; 1222 this.okButton_.disabled = this.filenameInput_.value.length == 0;
1108 1223
1109 if (event.keyCode == 13 /* Enter */ && !this.okButton_.disabled) 1224 if (event.keyCode == 13 /* Enter */ && !this.okButton_.disabled)
1110 this.onOk_(); 1225 this.onOk_();
(...skipping 27 matching lines...) Expand all
1138 if (name.indexOf('/') == -1) 1253 if (name.indexOf('/') == -1)
1139 break; 1254 break;
1140 1255
1141 alert(strf('ERROR_INVALID_FOLDER_CHARACTER', '/')); 1256 alert(strf('ERROR_INVALID_FOLDER_CHARACTER', '/'));
1142 } 1257 }
1143 1258
1144 var self = this; 1259 var self = this;
1145 1260
1146 function onSuccess(dirEntry) { 1261 function onSuccess(dirEntry) {
1147 self.rescanDirectory_(function () { 1262 self.rescanDirectory_(function () {
1148 for (var i = 0; i < self.table.dataModel.length; i++) { 1263 for (var i = 0; i < self.dataModel_.length; i++) {
1149 if (self.table.dataModel.item(i).name == dirEntry.name) { 1264 if (self.dataModel_.item(i).name == dirEntry.name) {
1150 self.table.selectionModel.selectedIndex = i; 1265 self.currentList_.selectionModel.selectedIndex = i;
1151 self.table.scrollIndexIntoView(i); 1266 self.currentList_.scrollIndexIntoView(i);
1152 self.table.focus(); 1267 self.currentList_.focus();
1153 return; 1268 return;
1154 } 1269 }
1155 }; 1270 };
1156 }); 1271 });
1157 } 1272 }
1158 1273
1159 function onError(err) { 1274 function onError(err) {
1160 window.alert(strf('ERROR_CREATING_FOLDER', name, 1275 window.alert(strf('ERROR_CREATING_FOLDER', name,
1161 util.getFileErrorMnemonic(err.code))); 1276 util.getFileErrorMnemonic(err.code)));
1162 } 1277 }
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
1229 var filename = this.filenameInput_.value; 1344 var filename = this.filenameInput_.value;
1230 if (!filename) 1345 if (!filename)
1231 throw new Error('Missing filename!'); 1346 throw new Error('Missing filename!');
1232 1347
1233 chrome.fileBrowserPrivate.selectFile(currentPath + filename, 0); 1348 chrome.fileBrowserPrivate.selectFile(currentPath + filename, 0);
1234 window.close(); 1349 window.close();
1235 return; 1350 return;
1236 } 1351 }
1237 1352
1238 var ary = []; 1353 var ary = [];
1239 var selectedIndexes = this.table.selectionModel.selectedIndexes; 1354 var selectedIndexes = this.currentList_.selectionModel.selectedIndexes;
1240 1355
1241 // All other dialog types require at least one selected list item. 1356 // All other dialog types require at least one selected list item.
1242 // The logic to control whether or not the ok button is enabled should 1357 // The logic to control whether or not the ok button is enabled should
1243 // prevent us from ever getting here, but we sanity check to be sure. 1358 // prevent us from ever getting here, but we sanity check to be sure.
1244 if (!selectedIndexes.length) 1359 if (!selectedIndexes.length)
1245 throw new Error('Nothing selected!'); 1360 throw new Error('Nothing selected!');
1246 1361
1247 for (var i = 0; i < selectedIndexes.length; i++) { 1362 for (var i = 0; i < selectedIndexes.length; i++) {
1248 var entry = this.table.dataModel.item(selectedIndexes[i]); 1363 var entry = this.dataModel_.item(selectedIndexes[i]);
1249 if (!entry) { 1364 if (!entry) {
1250 console.log('Error locating selected file at index: ' + i); 1365 console.log('Error locating selected file at index: ' + i);
1251 continue; 1366 continue;
1252 } 1367 }
1253 1368
1254 ary.push(currentPath + entry.name); 1369 ary.push(currentPath + entry.name);
1255 } 1370 }
1256 1371
1257 // Multi-file selection has no other restrictions. 1372 // Multi-file selection has no other restrictions.
1258 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE) { 1373 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE) {
(...skipping 12 matching lines...) Expand all
1271 } else if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE) { 1386 } else if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE) {
1272 if (!this.selection.leadEntry.isFile) 1387 if (!this.selection.leadEntry.isFile)
1273 throw new Error('Selected entry is not a file!'); 1388 throw new Error('Selected entry is not a file!');
1274 } 1389 }
1275 1390
1276 chrome.fileBrowserPrivate.selectFile(ary[0], 0); 1391 chrome.fileBrowserPrivate.selectFile(ary[0], 0);
1277 window.close(); 1392 window.close();
1278 }; 1393 };
1279 1394
1280 })(); 1395 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698