Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / file_grid.js
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  * FileGrid constructor.
9  *
10  * Represents grid for the Grid View in the File Manager.
11  * @constructor
12  * @extends {cr.ui.Grid}
13  */
14
15 function FileGrid() {
16   throw new Error('Use FileGrid.decorate');
17 }
18
19 /**
20  * Thumbnail quality.
21  * @enum {number}
22  */
23 FileGrid.ThumbnailQuality = {
24   LOW: 0,
25   HIGH: 1
26 };
27
28 /**
29  * Inherits from cr.ui.Grid.
30  */
31 FileGrid.prototype.__proto__ = cr.ui.Grid.prototype;
32
33 /**
34  * Decorates an HTML element to be a FileGrid.
35  * @param {HTMLElement} self The grid to decorate.
36  * @param {MetadataCache} metadataCache Metadata cache to find entries
37  *                                      metadata.
38  * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
39  */
40 FileGrid.decorate = function(self, metadataCache, volumeManager) {
41   cr.ui.Grid.decorate(self);
42   self.__proto__ = FileGrid.prototype;
43   self.metadataCache_ = metadataCache;
44   self.volumeManager_ = volumeManager;
45
46   self.scrollBar_ = new MainPanelScrollBar();
47   self.scrollBar_.initialize(self.parentNode, self);
48   self.setBottomMarginForPanel(0);
49
50   self.itemConstructor = function(entry) {
51     var item = self.ownerDocument.createElement('LI');
52     FileGrid.Item.decorate(item, entry, self);
53     return item;
54   };
55
56   self.relayoutRateLimiter_ =
57       new AsyncUtil.RateLimiter(self.relayoutImmediately_.bind(self));
58 };
59
60 /**
61  * Updates items to reflect metadata changes.
62  * @param {string} type Type of metadata changed.
63  * @param {Array.<Entry>} entries Entries whose metadata changed.
64  */
65 FileGrid.prototype.updateListItemsMetadata = function(type, entries) {
66   var urls = util.entriesToURLs(entries);
67   var boxes = this.querySelectorAll('.img-container');
68   for (var i = 0; i < boxes.length; i++) {
69     var box = boxes[i];
70     var listItem = this.getListItemAncestor(box);
71     var entry = listItem && this.dataModel.item(listItem.listIndex);
72     if (!entry || urls.indexOf(entry.toURL()) === -1)
73       continue;
74
75     FileGrid.decorateThumbnailBox(box,
76                                   entry,
77                                   this.metadataCache_,
78                                   this.volumeManager_,
79                                   ThumbnailLoader.FillMode.FIT,
80                                   FileGrid.ThumbnailQuality.LOW);
81   }
82 };
83
84 /**
85  * Redraws the UI. Skips multiple consecutive calls.
86  */
87 FileGrid.prototype.relayout = function() {
88   this.relayoutRateLimiter_.run();
89 };
90
91 /**
92  * Redraws the UI immediately.
93  * @private
94  */
95 FileGrid.prototype.relayoutImmediately_ = function() {
96   this.startBatchUpdates();
97   this.columns = 0;
98   this.redraw();
99   this.endBatchUpdates();
100   cr.dispatchSimpleEvent(this, 'relayout');
101 };
102
103 /**
104  * Decorates thumbnail.
105  * @param {HTMLElement} li List item.
106  * @param {Entry} entry Entry to render a thumbnail for.
107  * @param {MetadataCache} metadataCache To retrieve metadata.
108  * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
109  */
110 FileGrid.decorateThumbnail = function(li, entry, metadataCache, volumeManager) {
111   li.className = 'thumbnail-item';
112   if (entry)
113     filelist.decorateListItem(li, entry, metadataCache);
114
115   var frame = li.ownerDocument.createElement('div');
116   frame.className = 'thumbnail-frame';
117   li.appendChild(frame);
118
119   var box = li.ownerDocument.createElement('div');
120   if (entry) {
121     FileGrid.decorateThumbnailBox(box,
122                                   entry,
123                                   metadataCache,
124                                   volumeManager,
125                                   ThumbnailLoader.FillMode.AUTO,
126                                   FileGrid.ThumbnailQuality.LOW);
127   }
128   frame.appendChild(box);
129
130   var bottom = li.ownerDocument.createElement('div');
131   bottom.className = 'thumbnail-bottom';
132   bottom.appendChild(filelist.renderFileNameLabel(li.ownerDocument, entry));
133   frame.appendChild(bottom);
134 };
135
136 /**
137  * Decorates the box containing a centered thumbnail image.
138  *
139  * @param {HTMLDivElement} box Box to decorate.
140  * @param {Entry} entry Entry which thumbnail is generating for.
141  * @param {MetadataCache} metadataCache To retrieve metadata.
142  * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
143  * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
144  * @param {FileGrid.ThumbnailQuality} quality Thumbnail quality.
145  * @param {function(HTMLElement)=} opt_imageLoadCallback Callback called when
146  *     the image has been loaded before inserting it into the DOM.
147  */
148 FileGrid.decorateThumbnailBox = function(
149     box, entry, metadataCache, volumeManager, fillMode, quality,
150     opt_imageLoadCallback) {
151   var locationInfo = volumeManager.getLocationInfo(entry);
152   box.className = 'img-container';
153
154   if (entry.isDirectory) {
155     box.setAttribute('generic-thumbnail', 'folder');
156     if (locationInfo && locationInfo.isDriveBased) {
157       metadataCache.getOne(entry, 'external', function(metadata) {
158         if (metadata.shared)
159           box.classList.add('shared');
160       });
161     }
162     if (opt_imageLoadCallback)
163       setTimeout(opt_imageLoadCallback, 0, null /* callback parameter */);
164     return;
165   }
166
167   var metadataTypes = 'thumbnail|filesystem|external|media';
168
169   // Drive provides high quality thumbnails via USE_EMBEDDED, however local
170   // images usually provide very tiny thumbnails, therefore USE_EMBEDDE can't
171   // be used to obtain high quality output.
172   var useEmbedded;
173   switch (quality) {
174     case FileGrid.ThumbnailQuality.LOW:
175       useEmbedded = ThumbnailLoader.UseEmbedded.USE_EMBEDDED;
176       break;
177     case FileGrid.ThumbnailQuality.HIGH:
178       // TODO(mtomasz): Use Entry instead of paths.
179       useEmbedded = (locationInfo && locationInfo.isDriveBased) ?
180           ThumbnailLoader.UseEmbedded.USE_EMBEDDED :
181           ThumbnailLoader.UseEmbedded.NO_EMBEDDED;
182       break;
183   }
184
185   metadataCache.getOne(entry, metadataTypes,
186       function(metadata) {
187         new ThumbnailLoader(entry,
188                             ThumbnailLoader.LoaderType.IMAGE,
189                             metadata,
190                             undefined,  // opt_mediaType
191                             useEmbedded).
192             load(box,
193                 fillMode,
194                 ThumbnailLoader.OptimizationMode.DISCARD_DETACHED,
195                 opt_imageLoadCallback);
196       });
197 };
198
199 /**
200  * Item for the Grid View.
201  * @constructor
202  */
203 FileGrid.Item = function() {
204   throw new Error();
205 };
206
207 /**
208  * Inherits from cr.ui.ListItem.
209  */
210 FileGrid.Item.prototype.__proto__ = cr.ui.ListItem.prototype;
211
212 Object.defineProperty(FileGrid.Item.prototype, 'label', {
213   /**
214    * @this {FileGrid.Item}
215    * @return {string} Label of the item.
216    */
217   get: function() {
218     return this.querySelector('filename-label').textContent;
219   }
220 });
221
222 /**
223  * @param {Element} li List item element.
224  * @param {Entry} entry File entry.
225  * @param {FileGrid} grid Owner.
226  */
227 FileGrid.Item.decorate = function(li, entry, grid) {
228   li.__proto__ = FileGrid.Item.prototype;
229   // TODO(mtomasz): Pass the metadata cache and the volume manager directly
230   // instead of accessing private members of grid.
231   FileGrid.decorateThumbnail(
232       li, entry, grid.metadataCache_, grid.volumeManager_, true);
233
234   // Override the default role 'listitem' to 'option' to match the parent's
235   // role (listbox).
236   li.setAttribute('role', 'option');
237 };
238
239 /**
240  * Sets the margin height for the transparent preview panel at the bottom.
241  * @param {number} margin Margin to be set in px.
242  */
243 FileGrid.prototype.setBottomMarginForPanel = function(margin) {
244   // +20 bottom margin is needed to match the bottom margin size with the
245   // margin between its items.
246   this.style.paddingBottom = (margin + 20) + 'px';
247   this.scrollBar_.setBottomMarginForPanel(margin);
248 };
249
250 /**
251  * Obtains if the drag selection should be start or not by referring the mouse
252  * event.
253  * @param {MouseEvent} event Drag start event.
254  * @return {boolean} True if the mouse is hit to the background of the list.
255  */
256 FileGrid.prototype.shouldStartDragSelection = function(event) {
257   var pos = DragSelector.getScrolledPosition(this, event);
258   return this.getHitElements(pos.x, pos.y).length === 0;
259 };
260
261 /**
262  * Obtains the column/row index that the coordinate points.
263  * @param {number} coordinate Vertical/horizontal coordinate value that points
264  *     column/row.
265  * @param {number} step Length from a column/row to the next one.
266  * @param {number} threshold Threshold that determines whether 1 offset is added
267  *     to the return value or not. This is used in order to handle the margin of
268  *     column/row.
269  * @return {number} Index of hit column/row.
270  * @private
271  */
272 FileGrid.prototype.getHitIndex_ = function(coordinate, step, threshold) {
273   var index = ~~(coordinate / step);
274   return (coordinate % step >= threshold) ? index + 1 : index;
275 };
276
277 /**
278  * Obtains the index list of elements that are hit by the point or the
279  * rectangle.
280  *
281  * We should match its argument interface with FileList.getHitElements.
282  *
283  * @param {number} x X coordinate value.
284  * @param {number} y Y coordinate value.
285  * @param {number=} opt_width Width of the coordinate.
286  * @param {number=} opt_height Height of the coordinate.
287  * @return {Array.<number>} Index list of hit elements.
288  */
289 FileGrid.prototype.getHitElements = function(x, y, opt_width, opt_height) {
290   var currentSelection = [];
291   var right = x + (opt_width || 0);
292   var bottom = y + (opt_height || 0);
293   var itemMetrics = this.measureItem();
294   var horizontalStartIndex = this.getHitIndex_(
295       x, itemMetrics.width, itemMetrics.width - itemMetrics.marginRight);
296   var horizontalEndIndex = Math.min(this.columns, this.getHitIndex_(
297       right, itemMetrics.width, itemMetrics.marginLeft));
298   var verticalStartIndex = this.getHitIndex_(
299       y, itemMetrics.height, itemMetrics.height - itemMetrics.bottom);
300   var verticalEndIndex = this.getHitIndex_(
301       bottom, itemMetrics.height, itemMetrics.marginTop);
302   for (var verticalIndex = verticalStartIndex;
303        verticalIndex < verticalEndIndex;
304        verticalIndex++) {
305     var indexBase = this.getFirstItemInRow(verticalIndex);
306     for (var horizontalIndex = horizontalStartIndex;
307          horizontalIndex < horizontalEndIndex;
308          horizontalIndex++) {
309       var index = indexBase + horizontalIndex;
310       if (0 <= index && index < this.dataModel.length)
311         currentSelection.push(index);
312     }
313   }
314   return currentSelection;
315 };