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

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

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

Powered by Google App Engine
This is Rietveld 408576698