103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'use strict';
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Loads a thumbnail using provided url. In CANVAS mode, loaded images
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * are attached as <canvas> element, while in IMAGE mode as <img>.
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * <canvas> renders faster than <img>, however has bigger memory overhead.
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param {FileEntry} entry File entry.
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {ThumbnailLoader.LoaderType=} opt_loaderType Canvas or Image loader,
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *     default: IMAGE.
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object=} opt_metadata Metadata object.
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string=} opt_mediaType Media type.
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {ThumbnailLoader.UseEmbedded=} opt_useEmbedded If to use embedded
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *     jpeg thumbnail if available. Default: USE_EMBEDDED.
1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {number=} opt_priority Priority, the highest is 0. default: 2.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function ThumbnailLoader(entry, opt_loaderType, opt_metadata, opt_mediaType,
2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    opt_useEmbedded, opt_priority) {
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  opt_useEmbedded = opt_useEmbedded || ThumbnailLoader.UseEmbedded.USE_EMBEDDED;
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  this.mediaType_ = opt_mediaType || FileType.getMediaType(entry);
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.loaderType_ = opt_loaderType || ThumbnailLoader.LoaderType.IMAGE;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.metadata_ = opt_metadata;
2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  this.priority_ = (opt_priority !== undefined) ? opt_priority : 2;
304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  this.transform_ = null;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!opt_metadata) {
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    this.thumbnailUrl_ = entry.toURL();  // Use the URL directly.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.fallbackUrl_ = null;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.thumbnailUrl_ = null;
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (opt_metadata.external && opt_metadata.external.customIconUrl)
401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    this.fallbackUrl_ = opt_metadata.external.customIconUrl;
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Fetch the rotation from the external properties (if available).
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  var externalTransform;
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (opt_metadata.external &&
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      opt_metadata.external.imageRotation !== undefined) {
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    externalTransform = {
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      scaleX: 1,
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      scaleY: 1,
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rotate90: opt_metadata.external.imageRotation / 90
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    };
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (((opt_metadata.thumbnail && opt_metadata.thumbnail.url) ||
541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci       (opt_metadata.external && opt_metadata.external.thumbnailUrl)) &&
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      opt_useEmbedded === ThumbnailLoader.UseEmbedded.USE_EMBEDDED) {
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // If the thumbnail generated from the local cache (metadata.thumbnail.url)
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // is available, use it. If not, use the one passed from the external
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // provider (metadata.external.thumbnailUrl).
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    this.thumbnailUrl_ =
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        (opt_metadata.thumbnail && opt_metadata.thumbnail.url) ||
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        (opt_metadata.external && opt_metadata.external.thumbnailUrl);
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    this.transform_ = externalTransform !== undefined ? externalTransform :
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        (opt_metadata.thumbnail && opt_metadata.thumbnail.transform);
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (FileType.isImage(entry)) {
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    this.thumbnailUrl_ = entry.toURL();
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    this.transform_ = externalTransform !== undefined ? externalTransform :
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        opt_metadata.media && opt_metadata.media.imageTransform;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (this.fallbackUrl_) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Use fallback as the primary thumbnail.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.thumbnailUrl_ = this.fallbackUrl_;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.fallbackUrl_ = null;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } // else the generic thumbnail based on the media type will be used.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * In percents (0.0 - 1.0), how much area can be cropped to fill an image
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * in a container, when loading a thumbnail in FillMode.AUTO mode.
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * The specified 30% value allows to fill 16:9, 3:2 pictures in 4:3 element.
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @type {number}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.AUTO_FILL_THRESHOLD = 0.3;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Type of displaying a thumbnail within a box.
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @enum {number}
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.FillMode = {
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FILL: 0,  // Fill whole box. Image may be cropped.
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FIT: 1,   // Keep aspect ratio, do not crop.
907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  OVER_FILL: 2,  // Fill whole box with possible stretching.
917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  AUTO: 3   // Try to fill, but if incompatible aspect ratio, then fit.
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Optimization mode for downloading thumbnails.
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @enum {number}
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.OptimizationMode = {
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NEVER_DISCARD: 0,    // Never discards downloading. No optimization.
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISCARD_DETACHED: 1  // Canceled if the container is not attached anymore.
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Type of element to store the image.
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @enum {number}
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.LoaderType = {
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IMAGE: 0,
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CANVAS: 1
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Whether to use the embedded thumbnail, or not. The embedded thumbnail may
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * be small.
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @enum {number}
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.UseEmbedded = {
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  USE_EMBEDDED: 0,
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NO_EMBEDDED: 1
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Maximum thumbnail's width when generating from the full resolution image.
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @const
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @type {number}
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.THUMBNAIL_MAX_WIDTH = 500;
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Maximum thumbnail's height when generating from the full resolution image.
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @const
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @type {number}
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.THUMBNAIL_MAX_HEIGHT = 500;
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Loads and attaches an image.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} box Container element.
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {ThumbnailLoader.OptimizationMode=} opt_optimizationMode Optimization
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *     for downloading thumbnails. By default optimizations are disabled.
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function(Image, Object)=} opt_onSuccess Success callback,
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *     accepts the image and the transform.
1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function()=} opt_onError Error callback.
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function()=} opt_onGeneric Callback for generic image used.
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.prototype.load = function(box, fillMode, opt_optimizationMode,
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    opt_onSuccess, opt_onError, opt_onGeneric) {
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  opt_optimizationMode = opt_optimizationMode ||
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ThumbnailLoader.OptimizationMode.NEVER_DISCARD;
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.thumbnailUrl_) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Relevant CSS rules are in file_types.css.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    box.setAttribute('generic-thumbnail', this.mediaType_);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (opt_onGeneric) opt_onGeneric();
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.cancel();
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.canvasUpToDate_ = false;
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.image_ = new Image();
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.image_.onload = function() {
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.attachImage(box, fillMode);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (opt_onSuccess)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_onSuccess(this.image_, this.transform_);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.image_.onerror = function() {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (opt_onError)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_onError();
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (this.fallbackUrl_) {
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.thumbnailUrl_ = this.fallbackUrl_;
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.fallbackUrl_ = null;
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.load(box, fillMode, opt_optimizationMode, opt_onSuccess);
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      box.setAttribute('generic-thumbnail', this.mediaType_);
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (this.image_.src) {
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    console.warn('Thumbnail already loaded: ' + this.thumbnailUrl_);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(mtomasz): Smarter calculation of the requested size.
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var wasAttached = box.ownerDocument.contains(box);
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var modificationTime = this.metadata_ &&
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         this.metadata_.filesystem &&
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         this.metadata_.filesystem.modificationTime &&
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         this.metadata_.filesystem.modificationTime.getTime();
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.taskId_ = util.loadImage(
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.image_,
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.thumbnailUrl_,
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        cache: true,
19790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        priority: this.priority_,
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        timestamp: modificationTime },
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function() {
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (opt_optimizationMode ==
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            ThumbnailLoader.OptimizationMode.DISCARD_DETACHED &&
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            !box.ownerDocument.contains(box)) {
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // If the container is not attached, then invalidate the download.
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return false;
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return true;
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      });
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Cancels loading the current image.
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.prototype.cancel = function() {
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (this.taskId_) {
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.image_.onload = function() {};
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.image_.onerror = function() {};
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    util.cancelLoadImage(this.taskId_);
218eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    this.taskId_ = null;
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if a valid image is loaded.
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ThumbnailLoader.prototype.hasValidImage = function() {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !!(this.image_ && this.image_.width && this.image_.height);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the image is rotated 90 degrees left or right.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ThumbnailLoader.prototype.isRotated_ = function() {
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return this.transform_ && (this.transform_.rotate90 % 2 === 1);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Image width (corrected for rotation).
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ThumbnailLoader.prototype.getWidth = function() {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.isRotated_() ? this.image_.height : this.image_.width;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Image height (corrected for rotation).
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ThumbnailLoader.prototype.getHeight = function() {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.isRotated_() ? this.image_.width : this.image_.height;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Load an image but do not attach it.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(boolean)} callback Callback, parameter is true if the image
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *     has loaded successfully or a stock icon has been used.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ThumbnailLoader.prototype.loadDetachedImage = function(callback) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.thumbnailUrl_) {
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    callback(true);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.cancel();
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.canvasUpToDate_ = false;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.image_ = new Image();
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.image_.onload = callback.bind(null, true);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.image_.onerror = callback.bind(null, false);
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(mtomasz): Smarter calculation of the requested size.
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var modificationTime = this.metadata_ &&
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         this.metadata_.filesystem &&
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         this.metadata_.filesystem.modificationTime &&
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         this.metadata_.filesystem.modificationTime.getTime();
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.taskId_ = util.loadImage(
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.image_,
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.thumbnailUrl_,
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH,
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT,
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        cache: true,
28090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        priority: this.priority_,
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        timestamp: modificationTime });
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Renders the thumbnail into either canvas or an image element.
2864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @private
2874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
2884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)ThumbnailLoader.prototype.renderMedia_ = function() {
2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (this.loaderType_ !== ThumbnailLoader.LoaderType.CANVAS)
2904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
2914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!this.canvas_)
2934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    this.canvas_ = document.createElement('canvas');
2944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Copy the image to a canvas if the canvas is outdated.
2964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!this.canvasUpToDate_) {
2974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    this.canvas_.width = this.image_.width;
2984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    this.canvas_.height = this.image_.height;
2994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    var context = this.canvas_.getContext('2d');
3004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    context.drawImage(this.image_, 0, 0);
3014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    this.canvasUpToDate_ = true;
3024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
3034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)};
3044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Attach the image to a given element.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Element} container Parent element.
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.prototype.attachImage = function(container, fillMode) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.hasValidImage()) {
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.setAttribute('generic-thumbnail', this.mediaType_);
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  this.renderMedia_();
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  util.applyTransform(container, this.transform_);
3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  var attachableMedia = this.loaderType_ === ThumbnailLoader.LoaderType.CANVAS ?
3194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      this.canvas_ : this.image_;
3204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ThumbnailLoader.centerImage_(
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      container, attachableMedia, fillMode, this.isRotated_());
3234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (attachableMedia.parentNode !== container) {
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.textContent = '';
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    container.appendChild(attachableMedia);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!this.taskId_)
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    attachableMedia.classList.add('cached');
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Gets the loaded image.
3354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * TODO(mtomasz): Apply transformations.
3364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) *
3374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @return {Image|HTMLCanvasElement} Either image or a canvas object.
3384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
3394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)ThumbnailLoader.prototype.getImage = function() {
3404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  this.renderMedia_();
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return this.loaderType_ === ThumbnailLoader.LoaderType.CANVAS ? this.canvas_ :
3424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      this.image_;
3434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)};
3444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update the image style to fit/fill the container.
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Using webkit center packing does not align the image properly, so we need
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to wait until the image loads and its dimensions are known, then manually
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * position it at the center.
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} box Containing element.
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Image|HTMLCanvasElement} img Element containing an image.
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} rotate True if the image should be rotated 90 degrees.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ThumbnailLoader.centerImage_ = function(box, img, fillMode, rotate) {
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var imageWidth = img.width;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var imageHeight = img.height;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var fractionX;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var fractionY;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var boxWidth = box.clientWidth;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var boxHeight = box.clientHeight;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var fill;
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (fillMode) {
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ThumbnailLoader.FillMode.FILL:
3717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    case ThumbnailLoader.FillMode.OVER_FILL:
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fill = true;
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ThumbnailLoader.FillMode.FIT:
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fill = false;
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ThumbnailLoader.FillMode.AUTO:
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var imageRatio = imageWidth / imageHeight;
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var boxRatio = 1.0;
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (boxWidth && boxHeight)
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        boxRatio = boxWidth / boxHeight;
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Cropped area in percents.
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      var ratioFactor = boxRatio / imageRatio;
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fill = (ratioFactor >= 1.0 - ThumbnailLoader.AUTO_FILL_THRESHOLD) &&
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             (ratioFactor <= 1.0 + ThumbnailLoader.AUTO_FILL_THRESHOLD);
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (boxWidth && boxHeight) {
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // When we know the box size we can position the image correctly even
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // in a non-square box.
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var fitScaleX = (rotate ? boxHeight : boxWidth) / imageWidth;
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var fitScaleY = (rotate ? boxWidth : boxHeight) / imageHeight;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var scale = fill ?
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Math.max(fitScaleX, fitScaleY) :
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Math.min(fitScaleX, fitScaleY);
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (fillMode !== ThumbnailLoader.FillMode.OVER_FILL)
4001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      scale = Math.min(scale, 1);  // Never overscale.
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fractionX = imageWidth * scale / boxWidth;
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fractionY = imageHeight * scale / boxHeight;
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We do not know the box size so we assume it is square.
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Compute the image position based only on the image dimensions.
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // First try vertical fit or horizontal fill.
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fractionX = imageWidth / imageHeight;
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fractionY = 1;
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if ((fractionX < 1) === !!fill) {  // Vertical fill or horizontal fit.
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fractionY = 1 / fractionX;
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fractionX = 1;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function percent(fraction) {
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (fraction * 100).toFixed(2) + '%';
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  img.style.width = percent(fractionX);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  img.style.height = percent(fractionY);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  img.style.left = percent((1 - fractionX) / 2);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  img.style.top = percent((1 - fractionY) / 2);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
425