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

Side by Side Diff: chrome/browser/resources/file_manager/foreground/js/ui/preview_panel.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) 2013 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 * PreviewPanel UI class.
9 * @param {HTMLElement} element DOM Element of preview panel.
10 * @param {PreviewPanel.VisibilityType} visibilityType Initial value of the
11 * visibility type.
12 * @param {MetadataCache} metadataCache Metadata cache.
13 * @param {VolumeManagerWrapper} volumeManager Volume manager.
14 * @constructor
15 * @extends {cr.EventTarget}
16 */
17 var PreviewPanel = function(element,
18 visibilityType,
19 metadataCache,
20 volumeManager) {
21 /**
22 * The cached height of preview panel.
23 * @type {number}
24 * @private
25 */
26 this.height_ = 0;
27
28 /**
29 * Visibility type of the preview panel.
30 * @type {PreviewPanel.VisiblityType}
31 * @private
32 */
33 this.visibilityType_ = visibilityType;
34
35 /**
36 * Current entry to be displayed.
37 * @type {Entry}
38 * @private
39 */
40 this.currentEntry_ = null;
41
42 /**
43 * Dom element of the preview panel.
44 * @type {HTMLElement}
45 * @private
46 */
47 this.element_ = element;
48
49 /**
50 * @type {BreadcrumbsController}
51 */
52 this.breadcrumbs = new BreadcrumbsController(
53 element.querySelector('#search-breadcrumbs'),
54 metadataCache,
55 volumeManager);
56
57 /**
58 * @type {PreviewPanel.Thumbnails}
59 */
60 this.thumbnails = new PreviewPanel.Thumbnails(
61 element.querySelector('.preview-thumbnails'),
62 metadataCache,
63 volumeManager);
64
65 /**
66 * @type {HTMLElement}
67 * @private
68 */
69 this.summaryElement_ = element.querySelector('.preview-summary');
70
71 /**
72 * @type {PreviewPanel.CalculatingSizeLabel}
73 * @private
74 */
75 this.calculatingSizeLabel_ = new PreviewPanel.CalculatingSizeLabel(
76 this.summaryElement_.querySelector('.calculating-size'));
77
78 /**
79 * @type {HTMLElement}
80 * @private
81 */
82 this.previewText_ = element.querySelector('.preview-text');
83
84 /**
85 * FileSelection to be displayed.
86 * @type {FileSelection}
87 * @private
88 */
89 this.selection_ = {entries: [], computeBytes: function() {}};
90
91 /**
92 * Sequence value that is incremented by every selection update and is used to
93 * check if the callback is up to date or not.
94 * @type {number}
95 * @private
96 */
97 this.sequence_ = 0;
98
99 /**
100 * @type {VolumeManagerWrapper}
101 * @private
102 */
103 this.volumeManager_ = volumeManager;
104
105 cr.EventTarget.call(this);
106 };
107
108 /**
109 * Name of PreviewPanels's event.
110 * @enum {string}
111 * @const
112 */
113 PreviewPanel.Event = Object.freeze({
114 // Event to be triggered at the end of visibility change.
115 VISIBILITY_CHANGE: 'visibilityChange'
116 });
117
118 /**
119 * Visibility type of the preview panel.
120 */
121 PreviewPanel.VisibilityType = Object.freeze({
122 // Preview panel always shows.
123 ALWAYS_VISIBLE: 'alwaysVisible',
124 // Preview panel shows when the selection property are set.
125 AUTO: 'auto',
126 // Preview panel does not show.
127 ALWAYS_HIDDEN: 'alwaysHidden'
128 });
129
130 /**
131 * @private
132 */
133 PreviewPanel.Visibility_ = Object.freeze({
134 VISIBLE: 'visible',
135 HIDING: 'hiding',
136 HIDDEN: 'hidden'
137 });
138
139 PreviewPanel.prototype = {
140 __proto__: cr.EventTarget.prototype,
141
142 /**
143 * Setter for the current entry.
144 * @param {Entry} entry New entry.
145 */
146 set currentEntry(entry) {
147 if (util.isSameEntry(this.currentEntry_, entry))
148 return;
149 this.currentEntry_ = entry;
150 this.updateVisibility_();
151 this.updatePreviewArea_();
152 },
153
154 /**
155 * Setter for the visibility type.
156 * @param {PreviewPanel.VisibilityType} visibilityType New value of visibility
157 * type.
158 */
159 set visibilityType(visibilityType) {
160 this.visibilityType_ = visibilityType;
161 this.updateVisibility_();
162 // Also update the preview area contents, because the update is surpressed
163 // while the visibility is hiding or hidden.
164 this.updatePreviewArea_();
165 },
166
167 get visible() {
168 return this.element_.getAttribute('visibility') ==
169 PreviewPanel.Visibility_.VISIBLE;
170 },
171
172 /**
173 * Obtains the height of preview panel.
174 * @return {number} Height of preview panel.
175 */
176 get height() {
177 this.height_ = this.height_ || this.element_.clientHeight;
178 return this.height_;
179 }
180 };
181
182 /**
183 * Initializes the element.
184 */
185 PreviewPanel.prototype.initialize = function() {
186 this.element_.addEventListener('webkitTransitionEnd',
187 this.onTransitionEnd_.bind(this));
188 this.updateVisibility_();
189 // Also update the preview area contents, because the update is surpressed
190 // while the visibility is hiding or hidden.
191 this.updatePreviewArea_();
192 };
193
194 /**
195 * Apply the selection and update the view of the preview panel.
196 * @param {FileSelection} selection Selection to be applied.
197 */
198 PreviewPanel.prototype.setSelection = function(selection) {
199 this.sequence_++;
200 this.selection_ = selection;
201 this.updateVisibility_();
202 this.updatePreviewArea_();
203 };
204
205 /**
206 * Update the visibility of the preview panel.
207 * @private
208 */
209 PreviewPanel.prototype.updateVisibility_ = function() {
210 // Get the new visibility value.
211 var visibility = this.element_.getAttribute('visibility');
212 var newVisible = null;
213 switch (this.visibilityType_) {
214 case PreviewPanel.VisibilityType.ALWAYS_VISIBLE:
215 newVisible = true;
216 break;
217 case PreviewPanel.VisibilityType.AUTO:
218 newVisible =
219 this.selection_.entries.length !== 0 ||
220 (this.currentEntry_ &&
221 !this.volumeManager_.getLocationInfo(
222 this.currentEntry_).isRootEntry);
223 break;
224 case PreviewPanel.VisibilityType.ALWAYS_HIDDEN:
225 newVisible = false;
226 break;
227 default:
228 console.error('Invalid visibilityType.');
229 return;
230 }
231
232 // If the visibility has been already the new value, just return.
233 if ((visibility == PreviewPanel.Visibility_.VISIBLE && newVisible) ||
234 (visibility == PreviewPanel.Visibility_.HIDDEN && !newVisible))
235 return;
236
237 // Set the new visibility value.
238 if (newVisible) {
239 this.element_.setAttribute('visibility', PreviewPanel.Visibility_.VISIBLE);
240 cr.dispatchSimpleEvent(this, PreviewPanel.Event.VISIBILITY_CHANGE);
241 } else {
242 this.element_.setAttribute('visibility', PreviewPanel.Visibility_.HIDING);
243 }
244 };
245
246 /**
247 * Update the text in the preview panel.
248 *
249 * @param {boolean} breadCrumbsVisible Whether the bread crumbs is visible or
250 * not.
251 * @private
252 */
253 PreviewPanel.prototype.updatePreviewArea_ = function(breadCrumbsVisible) {
254 // If the previw panel is hiding, does not update the current view.
255 if (!this.visible)
256 return;
257 var selection = this.selection_;
258
259 // Update thumbnails.
260 this.thumbnails.selection = selection.totalCount !== 0 ?
261 selection : {entries: [this.currentEntry_]};
262
263 // Check if the breadcrumb list should show instead on the preview text.
264 var entry;
265 if (this.selection_.totalCount == 1)
266 entry = this.selection_.entries[0];
267 else if (this.selection_.totalCount == 0)
268 entry = this.currentEntry_;
269
270 if (entry) {
271 this.breadcrumbs.show(entry);
272 this.calculatingSizeLabel_.hidden = true;
273 this.previewText_.textContent = '';
274 return;
275 }
276 this.breadcrumbs.hide();
277
278 // Obtains the preview text.
279 var text;
280 if (selection.directoryCount == 0)
281 text = strf('MANY_FILES_SELECTED', selection.fileCount);
282 else if (selection.fileCount == 0)
283 text = strf('MANY_DIRECTORIES_SELECTED', selection.directoryCount);
284 else
285 text = strf('MANY_ENTRIES_SELECTED', selection.totalCount);
286
287 // Obtains the size of files.
288 this.calculatingSizeLabel_.hidden = selection.bytesKnown;
289 if (selection.bytesKnown && selection.showBytes)
290 text += ', ' + util.bytesToString(selection.bytes);
291
292 // Set the preview text to the element.
293 this.previewText_.textContent = text;
294
295 // Request the byte calculation if needed.
296 if (!selection.bytesKnown) {
297 this.selection_.computeBytes(function(sequence) {
298 // Selection has been already updated.
299 if (this.sequence_ != sequence)
300 return;
301 this.updatePreviewArea_();
302 }.bind(this, this.sequence_));
303 }
304 };
305
306 /**
307 * Event handler to be called at the end of hiding transition.
308 * @param {Event} event The webkitTransitionEnd event.
309 * @private
310 */
311 PreviewPanel.prototype.onTransitionEnd_ = function(event) {
312 if (event.target != this.element_ || event.propertyName != 'opacity')
313 return;
314 var visibility = this.element_.getAttribute('visibility');
315 if (visibility != PreviewPanel.Visibility_.HIDING)
316 return;
317 this.element_.setAttribute('visibility', PreviewPanel.Visibility_.HIDDEN);
318 cr.dispatchSimpleEvent(this, PreviewPanel.Event.VISIBILITY_CHANGE);
319 };
320
321 /**
322 * Animating label that is shown during the bytes of selection entries is being
323 * calculated.
324 *
325 * This label shows dots and varying the number of dots every
326 * CalculatingSizeLabel.PERIOD milliseconds.
327 * @param {HTMLElement} element DOM element of the label.
328 * @constructor
329 */
330 PreviewPanel.CalculatingSizeLabel = function(element) {
331 this.element_ = element;
332 this.count_ = 0;
333 this.intervalID_ = null;
334 Object.seal(this);
335 };
336
337 /**
338 * Time period in milliseconds.
339 * @const {number}
340 */
341 PreviewPanel.CalculatingSizeLabel.PERIOD = 500;
342
343 PreviewPanel.CalculatingSizeLabel.prototype = {
344 /**
345 * Set visibility of the label.
346 * When it is displayed, the text is animated.
347 * @param {boolean} hidden Whether to hide the label or not.
348 */
349 set hidden(hidden) {
350 this.element_.hidden = hidden;
351 if (!hidden) {
352 if (this.intervalID_ != null)
353 return;
354 this.count_ = 2;
355 this.intervalID_ =
356 setInterval(this.onStep_.bind(this),
357 PreviewPanel.CalculatingSizeLabel.PERIOD);
358 this.onStep_();
359 } else {
360 if (this.intervalID_ == null)
361 return;
362 clearInterval(this.intervalID_);
363 this.intervalID_ = null;
364 }
365 }
366 };
367
368 /**
369 * Increments the counter and updates the number of dots.
370 * @private
371 */
372 PreviewPanel.CalculatingSizeLabel.prototype.onStep_ = function() {
373 var text = str('CALCULATING_SIZE');
374 for (var i = 0; i < ~~(this.count_ / 2) % 4; i++) {
375 text += '.';
376 }
377 this.element_.textContent = text;
378 this.count_++;
379 };
380
381 /**
382 * Thumbnails on the preview panel.
383 *
384 * @param {HTMLElement} element DOM Element of thumbnail container.
385 * @param {MetadataCache} metadataCache MetadataCache.
386 * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
387 * @constructor
388 */
389 PreviewPanel.Thumbnails = function(element, metadataCache, volumeManager) {
390 this.element_ = element;
391 this.metadataCache_ = metadataCache;
392 this.volumeManager_ = volumeManager;
393 this.sequence_ = 0;
394 Object.seal(this);
395 };
396
397 /**
398 * Maximum number of thumbnails.
399 * @const {number}
400 */
401 PreviewPanel.Thumbnails.MAX_THUMBNAIL_COUNT = 4;
402
403 /**
404 * Edge length of the thumbnail square.
405 * @const {number}
406 */
407 PreviewPanel.Thumbnails.THUMBNAIL_SIZE = 35;
408
409 /**
410 * Longer edge length of zoomed thumbnail rectangle.
411 * @const {number}
412 */
413 PreviewPanel.Thumbnails.ZOOMED_THUMBNAIL_SIZE = 200;
414
415 PreviewPanel.Thumbnails.prototype = {
416 /**
417 * Sets entries to be displayed in the view.
418 * @param {Array.<Entry>} value Entries.
419 */
420 set selection(value) {
421 this.sequence_++;
422 this.loadThumbnails_(value);
423 }
424 };
425
426 /**
427 * Loads thumbnail images.
428 * @param {FileSelection} selection Selection containing entries that are
429 * sources of images.
430 * @private
431 */
432 PreviewPanel.Thumbnails.prototype.loadThumbnails_ = function(selection) {
433 var entries = selection.entries;
434 this.element_.classList.remove('has-zoom');
435 this.element_.innerText = '';
436 var clickHandler = selection.tasks &&
437 selection.tasks.executeDefault.bind(selection.tasks);
438 var length = Math.min(entries.length,
439 PreviewPanel.Thumbnails.MAX_THUMBNAIL_COUNT);
440 for (var i = 0; i < length; i++) {
441 // Create a box.
442 var box = this.element_.ownerDocument.createElement('div');
443 box.style.zIndex = PreviewPanel.Thumbnails.MAX_THUMBNAIL_COUNT + 1 - i;
444
445 // Load the image.
446 if (entries[i]) {
447 FileGrid.decorateThumbnailBox(box,
448 entries[i],
449 this.metadataCache_,
450 this.volumeManager_,
451 ThumbnailLoader.FillMode.FILL,
452 FileGrid.ThumbnailQuality.LOW,
453 i == 0 && length == 1 &&
454 this.setZoomedImage_.bind(this));
455 }
456
457 // Register the click handler.
458 if (clickHandler)
459 box.addEventListener('click', clickHandler);
460
461 // Append
462 this.element_.appendChild(box);
463 }
464 };
465
466 /**
467 * Create the zoomed version of image and set it to the DOM element to show the
468 * zoomed image.
469 *
470 * @param {Image} image Image to be source of the zoomed image.
471 * @param {transform} transform Transformation to be applied to the image.
472 * @private
473 */
474 PreviewPanel.Thumbnails.prototype.setZoomedImage_ = function(image, transform) {
475 if (!image)
476 return;
477 var width = image.width || 0;
478 var height = image.height || 0;
479 if (width == 0 ||
480 height == 0 ||
481 (width < PreviewPanel.Thumbnails.THUMBNAIL_SIZE * 2 &&
482 height < PreviewPanel.Thumbnails.THUMBNAIL_SIZE * 2))
483 return;
484
485 var scale = Math.min(1,
486 PreviewPanel.Thumbnails.ZOOMED_THUMBNAIL_SIZE /
487 Math.max(width, height));
488 var imageWidth = ~~(width * scale);
489 var imageHeight = ~~(height * scale);
490 var zoomedImage = this.element_.ownerDocument.createElement('img');
491
492 if (scale < 0.3) {
493 // Scaling large images kills animation. Downscale it in advance.
494 // Canvas scales images with liner interpolation. Make a larger
495 // image (but small enough to not kill animation) and let IMAGE
496 // scale it smoothly.
497 var INTERMEDIATE_SCALE = 3;
498 var canvas = this.element_.ownerDocument.createElement('canvas');
499 canvas.width = imageWidth * INTERMEDIATE_SCALE;
500 canvas.height = imageHeight * INTERMEDIATE_SCALE;
501 var ctx = canvas.getContext('2d');
502 ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
503 // Using bigger than default compression reduces image size by
504 // several times. Quality degradation compensated by greater resolution.
505 zoomedImage.src = canvas.toDataURL('image/jpeg', 0.6);
506 } else {
507 zoomedImage.src = image.src;
508 }
509
510 var boxWidth = Math.max(PreviewPanel.Thumbnails.THUMBNAIL_SIZE, imageWidth);
511 var boxHeight = Math.max(PreviewPanel.Thumbnails.THUMBNAIL_SIZE, imageHeight);
512 if (transform && transform.rotate90 % 2 == 1) {
513 var t = boxWidth;
514 boxWidth = boxHeight;
515 boxHeight = t;
516 }
517
518 util.applyTransform(zoomedImage, transform);
519
520 var zoomedBox = this.element_.ownerDocument.createElement('div');
521 zoomedBox.className = 'popup';
522 zoomedBox.style.width = boxWidth + 'px';
523 zoomedBox.style.height = boxHeight + 'px';
524 zoomedBox.appendChild(zoomedImage);
525
526 this.element_.appendChild(zoomedBox);
527 this.element_.classList.add('has-zoom');
528 return;
529 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698