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

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

Issue 247123002: Move Files.app files to ui/file_manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix the test failure on non-chromeos Created 6 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
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /**
8 * Namespace for utility functions.
9 */
10 var filelist = {};
11
12 /**
13 * Custom column model for advanced auto-resizing.
14 *
15 * @param {Array.<cr.ui.table.TableColumn>} tableColumns Table columns.
16 * @extends {cr.ui.table.TableColumnModel}
17 * @constructor
18 */
19 function FileTableColumnModel(tableColumns) {
20 cr.ui.table.TableColumnModel.call(this, tableColumns);
21 }
22
23 /**
24 * The columns whose index is less than the constant are resizable.
25 * @const
26 * @type {number}
27 * @private
28 */
29 FileTableColumnModel.RESIZABLE_LENGTH_ = 4;
30
31 /**
32 * Inherits from cr.ui.TableColumnModel.
33 */
34 FileTableColumnModel.prototype.__proto__ =
35 cr.ui.table.TableColumnModel.prototype;
36
37 /**
38 * Minimum width of column.
39 * @const
40 * @type {number}
41 * @private
42 */
43 FileTableColumnModel.MIN_WIDTH_ = 10;
44
45 /**
46 * Sets column width so that the column dividers move to the specified position.
47 * This function also check the width of each column and keep the width larger
48 * than MIN_WIDTH_.
49 *
50 * @private
51 * @param {Array.<number>} newPos Positions of each column dividers.
52 */
53 FileTableColumnModel.prototype.applyColumnPositions_ = function(newPos) {
54 // Check the minimum width and adjust the positions.
55 for (var i = 0; i < newPos.length - 2; i++) {
56 if (newPos[i + 1] - newPos[i] < FileTableColumnModel.MIN_WIDTH_) {
57 newPos[i + 1] = newPos[i] + FileTableColumnModel.MIN_WIDTH_;
58 }
59 }
60 for (var i = newPos.length - 1; i >= 2; i--) {
61 if (newPos[i] - newPos[i - 1] < FileTableColumnModel.MIN_WIDTH_) {
62 newPos[i - 1] = newPos[i] - FileTableColumnModel.MIN_WIDTH_;
63 }
64 }
65 // Set the new width of columns
66 for (var i = 0; i < FileTableColumnModel.RESIZABLE_LENGTH_; i++) {
67 this.columns_[i].width = newPos[i + 1] - newPos[i];
68 }
69 };
70
71 /**
72 * Normalizes widths to make their sum 100% if possible. Uses the proportional
73 * approach with some additional constraints.
74 *
75 * @param {number} contentWidth Target width.
76 * @override
77 */
78 FileTableColumnModel.prototype.normalizeWidths = function(contentWidth) {
79 var totalWidth = 0;
80 var fixedWidth = 0;
81 // Some columns have fixed width.
82 for (var i = 0; i < this.columns_.length; i++) {
83 if (i < FileTableColumnModel.RESIZABLE_LENGTH_)
84 totalWidth += this.columns_[i].width;
85 else
86 fixedWidth += this.columns_[i].width;
87 }
88 var newTotalWidth = Math.max(contentWidth - fixedWidth, 0);
89 var positions = [0];
90 var sum = 0;
91 for (var i = 0; i < FileTableColumnModel.RESIZABLE_LENGTH_; i++) {
92 var column = this.columns_[i];
93 sum += column.width;
94 // Faster alternative to Math.floor for non-negative numbers.
95 positions[i + 1] = ~~(newTotalWidth * sum / totalWidth);
96 }
97 this.applyColumnPositions_(positions);
98 };
99
100 /**
101 * Handles to the start of column resizing by splitters.
102 */
103 FileTableColumnModel.prototype.handleSplitterDragStart = function() {
104 this.columnPos_ = [0];
105 for (var i = 0; i < this.columns_.length; i++) {
106 this.columnPos_[i + 1] = this.columns_[i].width + this.columnPos_[i];
107 }
108 };
109
110 /**
111 * Handles to the end of column resizing by splitters.
112 */
113 FileTableColumnModel.prototype.handleSplitterDragEnd = function() {
114 this.columnPos_ = null;
115 };
116
117 /**
118 * Sets the width of column with keeping the total width of table.
119 * @param {number} columnIndex Index of column that is resized.
120 * @param {number} columnWidth New width of the column.
121 */
122 FileTableColumnModel.prototype.setWidthAndKeepTotal = function(
123 columnIndex, columnWidth) {
124 // Skip to resize 'selection' column
125 if (columnIndex < 0 ||
126 columnIndex >= FileTableColumnModel.RESIZABLE_LENGTH_ ||
127 !this.columnPos_) {
128 return;
129 }
130
131 // Calculate new positions of column splitters.
132 var newPosStart =
133 this.columnPos_[columnIndex] + Math.max(columnWidth,
134 FileTableColumnModel.MIN_WIDTH_);
135 var newPos = [];
136 var posEnd = this.columnPos_[FileTableColumnModel.RESIZABLE_LENGTH_];
137 for (var i = 0; i < columnIndex + 1; i++) {
138 newPos[i] = this.columnPos_[i];
139 }
140 for (var i = columnIndex + 1;
141 i < FileTableColumnModel.RESIZABLE_LENGTH_;
142 i++) {
143 var posStart = this.columnPos_[columnIndex + 1];
144 newPos[i] = (posEnd - newPosStart) *
145 (this.columnPos_[i] - posStart) /
146 (posEnd - posStart) +
147 newPosStart;
148 // Faster alternative to Math.floor for non-negative numbers.
149 newPos[i] = ~~newPos[i];
150 }
151 newPos[columnIndex] = this.columnPos_[columnIndex];
152 newPos[FileTableColumnModel.RESIZABLE_LENGTH_] = posEnd;
153 this.applyColumnPositions_(newPos);
154
155 // Notifiy about resizing
156 cr.dispatchSimpleEvent(this, 'resize');
157 };
158
159 /**
160 * Custom splitter that resizes column with retaining the sum of all the column
161 * width.
162 */
163 var FileTableSplitter = cr.ui.define('div');
164
165 /**
166 * Inherits from cr.ui.TableSplitter.
167 */
168 FileTableSplitter.prototype.__proto__ = cr.ui.TableSplitter.prototype;
169
170 /**
171 * Handles the drag start event.
172 */
173 FileTableSplitter.prototype.handleSplitterDragStart = function() {
174 cr.ui.TableSplitter.prototype.handleSplitterDragStart.call(this);
175 this.table_.columnModel.handleSplitterDragStart();
176 };
177
178 /**
179 * Handles the drag move event.
180 * @param {number} deltaX Horizontal mouse move offset.
181 */
182 FileTableSplitter.prototype.handleSplitterDragMove = function(deltaX) {
183 this.table_.columnModel.setWidthAndKeepTotal(this.columnIndex,
184 this.columnWidth_ + deltaX,
185 true);
186 };
187
188 /**
189 * Handles the drag end event.
190 */
191 FileTableSplitter.prototype.handleSplitterDragEnd = function() {
192 cr.ui.TableSplitter.prototype.handleSplitterDragEnd.call(this);
193 this.table_.columnModel.handleSplitterDragEnd();
194 };
195
196 /**
197 * File list Table View.
198 * @constructor
199 */
200 function FileTable() {
201 throw new Error('Designed to decorate elements');
202 }
203
204 /**
205 * Inherits from cr.ui.Table.
206 */
207 FileTable.prototype.__proto__ = cr.ui.Table.prototype;
208
209 /**
210 * Decorates the element.
211 * @param {HTMLElement} self Table to decorate.
212 * @param {MetadataCache} metadataCache To retrieve metadata.
213 * @param {boolean} fullPage True if it's full page File Manager,
214 * False if a file open/save dialog.
215 */
216 FileTable.decorate = function(self, metadataCache, fullPage) {
217 cr.ui.Table.decorate(self);
218 self.__proto__ = FileTable.prototype;
219 self.metadataCache_ = metadataCache;
220 self.collator_ = Intl.Collator([], {numeric: true, sensitivity: 'base'});
221
222 var columns = [
223 new cr.ui.table.TableColumn('name', str('NAME_COLUMN_LABEL'),
224 fullPage ? 386 : 324),
225 new cr.ui.table.TableColumn('size', str('SIZE_COLUMN_LABEL'),
226 110, true),
227 new cr.ui.table.TableColumn('type', str('TYPE_COLUMN_LABEL'),
228 fullPage ? 110 : 110),
229 new cr.ui.table.TableColumn('modificationTime',
230 str('DATE_COLUMN_LABEL'),
231 fullPage ? 150 : 210)
232 ];
233
234 columns[0].renderFunction = self.renderName_.bind(self);
235 columns[1].renderFunction = self.renderSize_.bind(self);
236 columns[1].defaultOrder = 'desc';
237 columns[2].renderFunction = self.renderType_.bind(self);
238 columns[3].renderFunction = self.renderDate_.bind(self);
239 columns[3].defaultOrder = 'desc';
240
241 var tableColumnModelClass;
242 tableColumnModelClass = FileTableColumnModel;
243
244 var columnModel = Object.create(tableColumnModelClass.prototype, {
245 /**
246 * The number of columns.
247 * @type {number}
248 */
249 size: {
250 /**
251 * @this {FileTableColumnModel}
252 * @return {number} Number of columns.
253 */
254 get: function() {
255 return this.totalSize;
256 }
257 },
258
259 /**
260 * The number of columns.
261 * @type {number}
262 */
263 totalSize: {
264 /**
265 * @this {FileTableColumnModel}
266 * @return {number} Number of columns.
267 */
268 get: function() {
269 return columns.length;
270 }
271 },
272
273 /**
274 * Obtains a column by the specified horizontal position.
275 */
276 getHitColumn: {
277 /**
278 * @this {FileTableColumnModel}
279 * @param {number} x Horizontal position.
280 * @return {object} The object that contains column index, column width,
281 * and hitPosition where the horizontal position is hit in the column.
282 */
283 value: function(x) {
284 for (var i = 0; x >= this.columns_[i].width; i++) {
285 x -= this.columns_[i].width;
286 }
287 if (i >= this.columns_.length)
288 return null;
289 return {index: i, hitPosition: x, width: this.columns_[i].width};
290 }
291 }
292 });
293
294 tableColumnModelClass.call(columnModel, columns);
295 self.columnModel = columnModel;
296 self.setDateTimeFormat(true);
297 self.setRenderFunction(self.renderTableRow_.bind(self,
298 self.getRenderFunction()));
299
300 self.scrollBar_ = MainPanelScrollBar();
301 self.scrollBar_.initialize(self, self.list);
302 // Keep focus on the file list when clicking on the header.
303 self.header.addEventListener('mousedown', function(e) {
304 self.list.focus();
305 e.preventDefault();
306 });
307
308 self.relayoutAggregation_ =
309 new AsyncUtil.Aggregation(self.relayoutImmediately_.bind(self));
310
311 // Override header#redraw to use FileTableSplitter.
312 self.header_.redraw = function() {
313 this.__proto__.redraw.call(this);
314 // Extend table splitters
315 var splitters = this.querySelectorAll('.table-header-splitter');
316 for (var i = 0; i < splitters.length; i++) {
317 if (splitters[i] instanceof FileTableSplitter)
318 continue;
319 FileTableSplitter.decorate(splitters[i]);
320 }
321 };
322
323 // Save the last selection. This is used by shouldStartDragSelection.
324 self.list.addEventListener('mousedown', function(e) {
325 this.lastSelection_ = this.selectionModel.selectedIndexes;
326 }.bind(self), true);
327 self.list.shouldStartDragSelection =
328 self.shouldStartDragSelection_.bind(self);
329
330 /**
331 * Obtains the index list of elements that are hit by the point or the
332 * rectangle.
333 *
334 * @param {number} x X coordinate value.
335 * @param {number} y Y coordinate value.
336 * @param {=number} opt_width Width of the coordinate.
337 * @param {=number} opt_height Height of the coordinate.
338 * @return {Array.<number>} Index list of hit elements.
339 */
340 self.list.getHitElements = function(x, y, opt_width, opt_height) {
341 var currentSelection = [];
342 var bottom = y + (opt_height || 0);
343 for (var i = 0; i < this.selectionModel_.length; i++) {
344 var itemMetrics = this.getHeightsForIndex_(i);
345 if (itemMetrics.top < bottom && itemMetrics.top + itemMetrics.height >= y)
346 currentSelection.push(i);
347 }
348 return currentSelection;
349 };
350 };
351
352 /**
353 * Sets date and time format.
354 * @param {boolean} use12hourClock True if 12 hours clock, False if 24 hours.
355 */
356 FileTable.prototype.setDateTimeFormat = function(use12hourClock) {
357 this.timeFormatter_ = Intl.DateTimeFormat(
358 [] /* default locale */,
359 {hour: 'numeric', minute: 'numeric',
360 hour12: use12hourClock});
361 this.dateFormatter_ = Intl.DateTimeFormat(
362 [] /* default locale */,
363 {year: 'numeric', month: 'short', day: 'numeric',
364 hour: 'numeric', minute: 'numeric',
365 hour12: use12hourClock});
366 };
367
368 /**
369 * Obtains if the drag selection should be start or not by referring the mouse
370 * event.
371 * @param {MouseEvent} event Drag start event.
372 * @return {boolean} True if the mouse is hit to the background of the list.
373 * @private
374 */
375 FileTable.prototype.shouldStartDragSelection_ = function(event) {
376 // If the shift key is pressed, it should starts drag selection.
377 if (event.shiftKey)
378 return true;
379
380 // If the position values are negative, it points the out of list.
381 // It should start the drag selection.
382 var pos = DragSelector.getScrolledPosition(this.list, event);
383 if (!pos)
384 return false;
385 if (pos.x < 0 || pos.y < 0)
386 return true;
387
388 // If the item index is out of range, it should start the drag selection.
389 var itemHeight = this.list.measureItem().height;
390 // Faster alternative to Math.floor for non-negative numbers.
391 var itemIndex = ~~(pos.y / itemHeight);
392 if (itemIndex >= this.list.dataModel.length)
393 return true;
394
395 // If the pointed item is already selected, it should not start the drag
396 // selection.
397 if (this.lastSelection_.indexOf(itemIndex) !== -1)
398 return false;
399
400 // If the horizontal value is not hit to column, it should start the drag
401 // selection.
402 var hitColumn = this.columnModel.getHitColumn(pos.x);
403 if (!hitColumn)
404 return true;
405
406 // Check if the point is on the column contents or not.
407 var item = this.list.getListItemByIndex(itemIndex);
408 switch (this.columnModel.columns_[hitColumn.index].id) {
409 case 'name':
410 var spanElement = item.querySelector('.filename-label span');
411 var spanRect = spanElement.getBoundingClientRect();
412 // The this.list.cachedBounds_ object is set by
413 // DragSelector.getScrolledPosition.
414 if (!this.list.cachedBounds)
415 return true;
416 var textRight =
417 spanRect.left - this.list.cachedBounds.left + spanRect.width;
418 return textRight <= hitColumn.hitPosition;
419 default:
420 return true;
421 }
422 };
423
424 /**
425 * Prepares the data model to be sorted by columns.
426 * @param {cr.ui.ArrayDataModel} dataModel Data model to prepare.
427 */
428 FileTable.prototype.setupCompareFunctions = function(dataModel) {
429 dataModel.setCompareFunction('name',
430 this.compareName_.bind(this));
431 dataModel.setCompareFunction('modificationTime',
432 this.compareMtime_.bind(this));
433 dataModel.setCompareFunction('size',
434 this.compareSize_.bind(this));
435 dataModel.setCompareFunction('type',
436 this.compareType_.bind(this));
437 };
438
439 /**
440 * Render the Name column of the detail table.
441 *
442 * Invoked by cr.ui.Table when a file needs to be rendered.
443 *
444 * @param {Entry} entry The Entry object to render.
445 * @param {string} columnId The id of the column to be rendered.
446 * @param {cr.ui.Table} table The table doing the rendering.
447 * @return {HTMLDivElement} Created element.
448 * @private
449 */
450 FileTable.prototype.renderName_ = function(entry, columnId, table) {
451 var label = this.ownerDocument.createElement('div');
452 label.appendChild(this.renderIconType_(entry, columnId, table));
453 label.entry = entry;
454 label.className = 'detail-name';
455 label.appendChild(filelist.renderFileNameLabel(this.ownerDocument, entry));
456 return label;
457 };
458
459 /**
460 * Render the Size column of the detail table.
461 *
462 * @param {Entry} entry The Entry object to render.
463 * @param {string} columnId The id of the column to be rendered.
464 * @param {cr.ui.Table} table The table doing the rendering.
465 * @return {HTMLDivElement} Created element.
466 * @private
467 */
468 FileTable.prototype.renderSize_ = function(entry, columnId, table) {
469 var div = this.ownerDocument.createElement('div');
470 div.className = 'size';
471 this.updateSize_(
472 div, entry, this.metadataCache_.getCached(entry, 'filesystem'));
473
474 return div;
475 };
476
477 /**
478 * Sets up or updates the size cell.
479 *
480 * @param {HTMLDivElement} div The table cell.
481 * @param {Entry} entry The corresponding entry.
482 * @param {Object} filesystemProps Metadata.
483 * @private
484 */
485 FileTable.prototype.updateSize_ = function(div, entry, filesystemProps) {
486 if (!filesystemProps) {
487 div.textContent = '...';
488 } else if (filesystemProps.size === -1) {
489 div.textContent = '--';
490 } else if (filesystemProps.size === 0 &&
491 FileType.isHosted(entry)) {
492 div.textContent = '--';
493 } else {
494 div.textContent = util.bytesToString(filesystemProps.size);
495 }
496 };
497
498 /**
499 * Render the Type column of the detail table.
500 *
501 * @param {Entry} entry The Entry object to render.
502 * @param {string} columnId The id of the column to be rendered.
503 * @param {cr.ui.Table} table The table doing the rendering.
504 * @return {HTMLDivElement} Created element.
505 * @private
506 */
507 FileTable.prototype.renderType_ = function(entry, columnId, table) {
508 var div = this.ownerDocument.createElement('div');
509 div.className = 'type';
510 div.textContent = FileType.typeToString(FileType.getType(entry));
511 return div;
512 };
513
514 /**
515 * Render the Date column of the detail table.
516 *
517 * @param {Entry} entry The Entry object to render.
518 * @param {string} columnId The id of the column to be rendered.
519 * @param {cr.ui.Table} table The table doing the rendering.
520 * @return {HTMLDivElement} Created element.
521 * @private
522 */
523 FileTable.prototype.renderDate_ = function(entry, columnId, table) {
524 var div = this.ownerDocument.createElement('div');
525 div.className = 'date';
526
527 this.updateDate_(div,
528 this.metadataCache_.getCached(entry, 'filesystem'));
529 return div;
530 };
531
532 /**
533 * Sets up or updates the date cell.
534 *
535 * @param {HTMLDivElement} div The table cell.
536 * @param {Object} filesystemProps Metadata.
537 * @private
538 */
539 FileTable.prototype.updateDate_ = function(div, filesystemProps) {
540 if (!filesystemProps) {
541 div.textContent = '...';
542 return;
543 }
544
545 var modTime = filesystemProps.modificationTime;
546 var today = new Date();
547 today.setHours(0);
548 today.setMinutes(0);
549 today.setSeconds(0);
550 today.setMilliseconds(0);
551
552 /**
553 * Number of milliseconds in a day.
554 */
555 var MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
556
557 if (modTime >= today &&
558 modTime < today.getTime() + MILLISECONDS_IN_DAY) {
559 div.textContent = strf('TIME_TODAY', this.timeFormatter_.format(modTime));
560 } else if (modTime >= today - MILLISECONDS_IN_DAY && modTime < today) {
561 div.textContent = strf('TIME_YESTERDAY',
562 this.timeFormatter_.format(modTime));
563 } else {
564 div.textContent =
565 this.dateFormatter_.format(filesystemProps.modificationTime);
566 }
567 };
568
569 /**
570 * Updates the file metadata in the table item.
571 *
572 * @param {Element} item Table item.
573 * @param {Entry} entry File entry.
574 */
575 FileTable.prototype.updateFileMetadata = function(item, entry) {
576 var props = this.metadataCache_.getCached(entry, 'filesystem');
577 this.updateDate_(item.querySelector('.date'), props);
578 this.updateSize_(item.querySelector('.size'), entry, props);
579 };
580
581 /**
582 * Updates list items 'in place' on metadata change.
583 * @param {string} type Type of metadata change.
584 * @param {Object.<string, Object>} propsMap Map from entry URLs to metadata
585 * properties.
586 */
587 FileTable.prototype.updateListItemsMetadata = function(type, propsMap) {
588 var forEachCell = function(selector, callback) {
589 var cells = this.querySelectorAll(selector);
590 for (var i = 0; i < cells.length; i++) {
591 var cell = cells[i];
592 var listItem = this.list_.getListItemAncestor(cell);
593 var entry = this.dataModel.item(listItem.listIndex);
594 if (entry) {
595 var props = propsMap[entry.toURL()];
596 if (props)
597 callback.call(this, cell, entry, props, listItem);
598 }
599 }
600 }.bind(this);
601 if (type === 'filesystem') {
602 forEachCell('.table-row-cell > .date', function(item, entry, props) {
603 this.updateDate_(item, props);
604 });
605 forEachCell('.table-row-cell > .size', function(item, entry, props) {
606 this.updateSize_(item, entry, props);
607 });
608 } else if (type === 'drive') {
609 // The cell name does not matter as the entire list item is needed.
610 forEachCell('.table-row-cell > .date',
611 function(item, entry, props, listItem) {
612 filelist.updateListItemDriveProps(listItem, props);
613 });
614 }
615 };
616
617 /**
618 * Compare by mtime first, then by name.
619 * @param {Entry} a First entry.
620 * @param {Entry} b Second entry.
621 * @return {number} Compare result.
622 * @private
623 */
624 FileTable.prototype.compareName_ = function(a, b) {
625 return this.collator_.compare(a.name, b.name);
626 };
627
628 /**
629 * Compare by mtime first, then by name.
630 * @param {Entry} a First entry.
631 * @param {Entry} b Second entry.
632 * @return {number} Compare result.
633 * @private
634 */
635 FileTable.prototype.compareMtime_ = function(a, b) {
636 var aCachedFilesystem = this.metadataCache_.getCached(a, 'filesystem');
637 var aTime = aCachedFilesystem ? aCachedFilesystem.modificationTime : 0;
638
639 var bCachedFilesystem = this.metadataCache_.getCached(b, 'filesystem');
640 var bTime = bCachedFilesystem ? bCachedFilesystem.modificationTime : 0;
641
642 if (aTime > bTime)
643 return 1;
644
645 if (aTime < bTime)
646 return -1;
647
648 return this.collator_.compare(a.name, b.name);
649 };
650
651 /**
652 * Compare by size first, then by name.
653 * @param {Entry} a First entry.
654 * @param {Entry} b Second entry.
655 * @return {number} Compare result.
656 * @private
657 */
658 FileTable.prototype.compareSize_ = function(a, b) {
659 var aCachedFilesystem = this.metadataCache_.getCached(a, 'filesystem');
660 var aSize = aCachedFilesystem ? aCachedFilesystem.size : 0;
661
662 var bCachedFilesystem = this.metadataCache_.getCached(b, 'filesystem');
663 var bSize = bCachedFilesystem ? bCachedFilesystem.size : 0;
664
665 if (aSize !== bSize) return aSize - bSize;
666 return this.collator_.compare(a.name, b.name);
667 };
668
669 /**
670 * Compare by type first, then by subtype and then by name.
671 * @param {Entry} a First entry.
672 * @param {Entry} b Second entry.
673 * @return {number} Compare result.
674 * @private
675 */
676 FileTable.prototype.compareType_ = function(a, b) {
677 // Directories precede files.
678 if (a.isDirectory !== b.isDirectory)
679 return Number(b.isDirectory) - Number(a.isDirectory);
680
681 var aType = FileType.typeToString(FileType.getType(a));
682 var bType = FileType.typeToString(FileType.getType(b));
683
684 var result = this.collator_.compare(aType, bType);
685 if (result !== 0)
686 return result;
687
688 return this.collator_.compare(a.name, b.name);
689 };
690
691 /**
692 * Renders table row.
693 * @param {function(Entry, cr.ui.Table)} baseRenderFunction Base renderer.
694 * @param {Entry} entry Corresponding entry.
695 * @return {HTMLLiElement} Created element.
696 * @private
697 */
698 FileTable.prototype.renderTableRow_ = function(baseRenderFunction, entry) {
699 var item = baseRenderFunction(entry, this);
700 filelist.decorateListItem(item, entry, this.metadataCache_);
701 return item;
702 };
703
704 /**
705 * Render the type column of the detail table.
706 *
707 * Invoked by cr.ui.Table when a file needs to be rendered.
708 *
709 * @param {Entry} entry The Entry object to render.
710 * @param {string} columnId The id of the column to be rendered.
711 * @param {cr.ui.Table} table The table doing the rendering.
712 * @return {HTMLDivElement} Created element.
713 * @private
714 */
715 FileTable.prototype.renderIconType_ = function(entry, columnId, table) {
716 var icon = this.ownerDocument.createElement('div');
717 icon.className = 'detail-icon';
718 icon.setAttribute('file-type-icon', FileType.getIcon(entry));
719 return icon;
720 };
721
722 /**
723 * Sets the margin height for the transparent preview panel at the bottom.
724 * @param {number} margin Margin to be set in px.
725 */
726 FileTable.prototype.setBottomMarginForPanel = function(margin) {
727 this.list_.style.paddingBottom = margin + 'px';
728 this.scrollBar_.setBottomMarginForPanel(margin);
729 };
730
731 /**
732 * Redraws the UI. Skips multiple consecutive calls.
733 */
734 FileTable.prototype.relayout = function() {
735 this.relayoutAggregation_.run();
736 };
737
738 /**
739 * Redraws the UI immediately.
740 * @private
741 */
742 FileTable.prototype.relayoutImmediately_ = function() {
743 if (this.clientWidth > 0)
744 this.normalizeColumns();
745 this.redraw();
746 cr.dispatchSimpleEvent(this.list, 'relayout');
747 };
748
749 /**
750 * Common item decoration for table's and grid's items.
751 * @param {ListItem} li List item.
752 * @param {Entry} entry The entry.
753 * @param {MetadataCache} metadataCache Cache to retrieve metadada.
754 */
755 filelist.decorateListItem = function(li, entry, metadataCache) {
756 li.classList.add(entry.isDirectory ? 'directory' : 'file');
757 // The metadata may not yet be ready. In that case, the list item will be
758 // updated when the metadata is ready via updateListItemsMetadata. For files
759 // not on Drive, driveProps is not available.
760 var driveProps = metadataCache.getCached(entry, 'drive');
761 if (driveProps)
762 filelist.updateListItemDriveProps(li, driveProps);
763
764 // Overriding the default role 'list' to 'listbox' for better
765 // accessibility on ChromeOS.
766 li.setAttribute('role', 'option');
767
768 Object.defineProperty(li, 'selected', {
769 /**
770 * @this {ListItem}
771 * @return {boolean} True if the list item is selected.
772 */
773 get: function() {
774 return this.hasAttribute('selected');
775 },
776
777 /**
778 * @this {ListItem}
779 */
780 set: function(v) {
781 if (v)
782 this.setAttribute('selected', '');
783 else
784 this.removeAttribute('selected');
785 }
786 });
787 };
788
789 /**
790 * Render filename label for grid and list view.
791 * @param {HTMLDocument} doc Owner document.
792 * @param {Entry} entry The Entry object to render.
793 * @return {HTMLDivElement} The label.
794 */
795 filelist.renderFileNameLabel = function(doc, entry) {
796 // Filename need to be in a '.filename-label' container for correct
797 // work of inplace renaming.
798 var box = doc.createElement('div');
799 box.className = 'filename-label';
800 var fileName = doc.createElement('span');
801 fileName.textContent = entry.name;
802 box.appendChild(fileName);
803
804 return box;
805 };
806
807 /**
808 * Updates grid item or table row for the driveProps.
809 * @param {cr.ui.ListItem} li List item.
810 * @param {Object} driveProps Metadata.
811 */
812 filelist.updateListItemDriveProps = function(li, driveProps) {
813 if (li.classList.contains('file')) {
814 if (driveProps.availableOffline)
815 li.classList.remove('dim-offline');
816 else
817 li.classList.add('dim-offline');
818 // TODO(mtomasz): Consider adding some vidual indication for files which
819 // are not cached on LTE. Currently we show them as normal files.
820 // crbug.com/246611.
821 }
822
823 if (driveProps.customIconUrl) {
824 var iconDiv = li.querySelector('.detail-icon');
825 if (!iconDiv)
826 return;
827 iconDiv.style.backgroundImage = 'url(' + driveProps.customIconUrl + ')';
828 }
829 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698