Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Loads a thumbnail using provided url. In CANVAS mode, loaded images | 6 * Loads a thumbnail using provided url. In CANVAS mode, loaded images |
| 7 * are attached as <canvas> element, while in IMAGE mode as <img>. | 7 * are attached as <canvas> element, while in IMAGE mode as <img>. |
| 8 * <canvas> renders faster than <img>, however has bigger memory overhead. | 8 * <canvas> renders faster than <img>, however has bigger memory overhead. |
| 9 * | 9 * |
| 10 * @param {string} url File URL. | 10 * @param {string} url File URL. |
| 11 * @param {ThumbnailLoader.LoaderType=} opt_loaderType Canvas or Image loader, | 11 * @param {ThumbnailLoader.LoaderType=} opt_loaderType Canvas or Image loader, |
| 12 * default: IMAGE. | 12 * default: IMAGE. |
| 13 * @param {Object=} opt_metadata Metadata object. | 13 * @param {Object=} opt_metadata Metadata object. |
| 14 * @param {string=} opt_mediaType Media type. | 14 * @param {string=} opt_mediaType Media type. |
| 15 * @param {ThumbnailLoader.UseEmbedded=} opt_useEmbedded If to use embedded | |
| 16 * jpeg thumbnail if available. Default: USE_EMBEDDED. | |
| 15 * @constructor | 17 * @constructor |
| 16 */ | 18 */ |
| 17 function ThumbnailLoader(url, opt_loaderType, opt_metadata, opt_mediaType) { | 19 function ThumbnailLoader( |
| 20 url, opt_loaderType, opt_metadata, opt_mediaType, opt_useEmbedded) { | |
| 21 opt_useEmbedded = opt_useEmbedded || ThumbnailLoader.UseEmbedded.USE_EMBEDDED; | |
| 22 | |
| 18 this.mediaType_ = opt_mediaType || FileType.getMediaType(url); | 23 this.mediaType_ = opt_mediaType || FileType.getMediaType(url); |
| 19 this.loaderType_ = opt_loaderType || ThumbnailLoader.LoaderType.IMAGE; | 24 this.loaderType_ = opt_loaderType || ThumbnailLoader.LoaderType.IMAGE; |
| 20 | 25 |
| 21 if (!opt_metadata) { | 26 if (!opt_metadata) { |
| 22 this.thumbnailUrl_ = url; // Use the URL directly. | 27 this.thumbnailUrl_ = url; // Use the URL directly. |
| 23 return; | 28 return; |
| 24 } | 29 } |
| 25 | 30 |
| 26 this.fallbackUrl_ = null; | 31 this.fallbackUrl_ = null; |
| 27 this.thumbnailUrl_ = null; | 32 this.thumbnailUrl_ = null; |
| 28 if (opt_metadata.drive) { | 33 if (opt_metadata.drive) { |
| 29 var apps = opt_metadata.drive.driveApps; | 34 var apps = opt_metadata.drive.driveApps; |
| 30 for (var i = 0; i < apps.length; ++i) { | 35 for (var i = 0; i < apps.length; ++i) { |
| 31 if (apps[i].docIcon && apps[i].isPrimary) { | 36 if (apps[i].docIcon && apps[i].isPrimary) { |
| 32 this.fallbackUrl_ = apps[i].docIcon; | 37 this.fallbackUrl_ = apps[i].docIcon; |
| 33 break; | 38 break; |
| 34 } | 39 } |
| 35 } | 40 } |
| 36 } | 41 } |
| 37 | 42 |
| 38 if (opt_metadata.thumbnail && opt_metadata.thumbnail.url) { | 43 if (opt_metadata.thumbnail && opt_metadata.thumbnail.url && |
| 44 opt_useEmbedded == ThumbnailLoader.UseEmbedded.USE_EMBEDDED) { | |
| 39 this.thumbnailUrl_ = opt_metadata.thumbnail.url; | 45 this.thumbnailUrl_ = opt_metadata.thumbnail.url; |
| 40 this.transform_ = opt_metadata.thumbnail.transform; | 46 this.transform_ = opt_metadata.thumbnail.transform; |
| 41 } else if (FileType.isImage(url)) { | 47 } else if (FileType.isImage(url)) { |
| 42 this.thumbnailUrl_ = url; | 48 this.thumbnailUrl_ = url; |
| 43 this.transform_ = opt_metadata.media && opt_metadata.media.imageTransform; | 49 this.transform_ = opt_metadata.media && opt_metadata.media.imageTransform; |
| 44 } else if (this.fallbackUrl_) { | 50 } else if (this.fallbackUrl_) { |
| 45 // Use fallback as the primary thumbnail. | 51 // Use fallback as the primary thumbnail. |
| 46 this.thumbnailUrl_ = this.fallbackUrl_; | 52 this.thumbnailUrl_ = this.fallbackUrl_; |
| 47 this.fallbackUrl_ = null; | 53 this.fallbackUrl_ = null; |
| 48 } // else the generic thumbnail based on the media type will be used. | 54 } // else the generic thumbnail based on the media type will be used. |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 78 /** | 84 /** |
| 79 * Type of element to store the image. | 85 * Type of element to store the image. |
| 80 * @enum | 86 * @enum |
| 81 */ | 87 */ |
| 82 ThumbnailLoader.LoaderType = { | 88 ThumbnailLoader.LoaderType = { |
| 83 IMAGE: 0, | 89 IMAGE: 0, |
| 84 CANVAS: 1 | 90 CANVAS: 1 |
| 85 }; | 91 }; |
| 86 | 92 |
| 87 /** | 93 /** |
| 94 * Whether to use the embedded thumbnail, or not. The embedded thumbnail may | |
| 95 * be small. | |
| 96 * @enum | |
|
yoshiki
2013/02/26 08:21:44
@enum {number}
The @enum tag must be followed by
mtomasz
2013/02/26 08:39:20
We have to fix it in lots of places. Separate cl?
yoshiki
2013/02/26 08:50:27
Thanks. We need to fix the others as well in separ
mtomasz
2013/02/27 00:55:31
Done.
| |
| 97 */ | |
| 98 ThumbnailLoader.UseEmbedded = { | |
| 99 USE_EMBEDDED: 0, | |
| 100 NO_EMBEDDED: 1 | |
| 101 }; | |
| 102 | |
| 103 /** | |
| 88 * Maximum thumbnail's width when generating from the full resolution image. | 104 * Maximum thumbnail's width when generating from the full resolution image. |
| 89 * @const | 105 * @const |
| 90 * @type {number} | 106 * @type {number} |
| 91 */ | 107 */ |
| 92 ThumbnailLoader.THUMBNAIL_MAX_WIDTH = 500; | 108 ThumbnailLoader.THUMBNAIL_MAX_WIDTH = 500; |
| 93 | 109 |
| 94 /** | 110 /** |
| 95 * Maximum thumbnail's height when generating from the full resolution image. | 111 * Maximum thumbnail's height when generating from the full resolution image. |
| 96 * @const | 112 * @const |
| 97 * @type {number} | 113 * @type {number} |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 115 opt_optimizationMode = opt_optimizationMode || | 131 opt_optimizationMode = opt_optimizationMode || |
| 116 ThumbnailLoader.OptimizationMode.NEVER_DISCARD; | 132 ThumbnailLoader.OptimizationMode.NEVER_DISCARD; |
| 117 | 133 |
| 118 if (!this.thumbnailUrl_) { | 134 if (!this.thumbnailUrl_) { |
| 119 // Relevant CSS rules are in file_types.css. | 135 // Relevant CSS rules are in file_types.css. |
| 120 box.setAttribute('generic-thumbnail', this.mediaType_); | 136 box.setAttribute('generic-thumbnail', this.mediaType_); |
| 121 if (opt_onGeneric) opt_onGeneric(); | 137 if (opt_onGeneric) opt_onGeneric(); |
| 122 return; | 138 return; |
| 123 } | 139 } |
| 124 | 140 |
| 141 this.cancel(); | |
| 125 this.canvasUpToDate_ = false; | 142 this.canvasUpToDate_ = false; |
| 126 this.image_ = new Image(); | 143 this.image_ = new Image(); |
| 127 this.image_.onload = function() { | 144 this.image_.onload = function() { |
| 128 this.attachImage(box, fillMode); | 145 this.attachImage(box, fillMode); |
| 129 if (opt_onSuccess) | 146 if (opt_onSuccess) |
| 130 opt_onSuccess(this.image_, this.transform_); | 147 opt_onSuccess(this.image_, this.transform_); |
| 131 }.bind(this); | 148 }.bind(this); |
| 132 this.image_.onerror = function() { | 149 this.image_.onerror = function() { |
| 133 if (opt_onError) | 150 if (opt_onError) |
| 134 opt_onError(); | 151 opt_onError(); |
| 135 if (this.fallbackUrl_) { | 152 if (this.fallbackUrl_) { |
| 136 new ThumbnailLoader(this.fallbackUrl_, | 153 new ThumbnailLoader(this.fallbackUrl_, |
| 137 this.loaderType_, | 154 this.loaderType_, |
| 138 null, | 155 null, |
| 139 this.mediaType_). | 156 this.mediaType_). |
| 140 load(box, fillMode, opt_onSuccess); | 157 load(box, fillMode, opt_onSuccess); |
| 141 } else { | 158 } else { |
| 142 box.setAttribute('generic-thumbnail', this.mediaType_); | 159 box.setAttribute('generic-thumbnail', this.mediaType_); |
| 143 } | 160 } |
| 144 }.bind(this); | 161 }.bind(this); |
| 145 | 162 |
| 146 if (this.image_.src) { | 163 if (this.image_.src) { |
| 147 console.warn('Thumbnail already loaded: ' + this.thumbnailUrl_); | 164 console.warn('Thumbnail already loaded: ' + this.thumbnailUrl_); |
| 148 return; | 165 return; |
| 149 } | 166 } |
| 150 | 167 |
| 151 // TODO(mtomasz): Smarter calculation of the requested size. | 168 // TODO(mtomasz): Smarter calculation of the requested size. |
| 152 var wasAttached = box.ownerDocument.contains(box); | 169 var wasAttached = box.ownerDocument.contains(box); |
| 153 var taskId = util.loadImage( | 170 this.taskId_ = util.loadImage( |
| 154 this.image_, | 171 this.image_, |
| 155 this.thumbnailUrl_, | 172 this.thumbnailUrl_, |
| 156 { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH, | 173 { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH, |
| 157 maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT, | 174 maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT, |
| 158 cache: true }, | 175 cache: true }, |
| 159 function() { | 176 function() { |
| 160 if (opt_optimizationMode == | 177 if (opt_optimizationMode == |
| 161 ThumbnailLoader.OptimizationMode.DISCARD_DETACHED && | 178 ThumbnailLoader.OptimizationMode.DISCARD_DETACHED && |
| 162 !box.ownerDocument.contains(box)) { | 179 !box.ownerDocument.contains(box)) { |
| 163 // If the container is not attached, then invalidate the download. | 180 // If the container is not attached, then invalidate the download. |
| 164 return false; | 181 return false; |
| 165 } | 182 } |
| 166 return true; | 183 return true; |
| 167 }); | 184 }); |
| 168 | 185 |
| 169 if (!taskId) | 186 if (!this.taskId_) |
| 170 this.image_.classList.add('cached'); | 187 this.image_.classList.add('cached'); |
| 171 }; | 188 }; |
| 172 | 189 |
| 173 /** | 190 /** |
| 191 * Cancels loading the current image. | |
| 192 */ | |
| 193 ThumbnailLoader.prototype.cancel = function() { | |
| 194 if (this.taskId_) { | |
| 195 this.image_.onload = function() {}; | |
| 196 this.image_.onerror = function() {}; | |
| 197 util.cancelLoadImage(this.taskId_); | |
| 198 } | |
| 199 }; | |
| 200 | |
| 201 /** | |
| 174 * @return {boolean} True if a valid image is loaded. | 202 * @return {boolean} True if a valid image is loaded. |
| 175 */ | 203 */ |
| 176 ThumbnailLoader.prototype.hasValidImage = function() { | 204 ThumbnailLoader.prototype.hasValidImage = function() { |
| 177 return !!(this.image_ && this.image_.width && this.image_.height); | 205 return !!(this.image_ && this.image_.width && this.image_.height); |
| 178 }; | 206 }; |
| 179 | 207 |
| 180 /** | 208 /** |
| 181 * @return {boolean} True if the image is rotated 90 degrees left or right. | 209 * @return {boolean} True if the image is rotated 90 degrees left or right. |
| 182 * @private | 210 * @private |
| 183 */ | 211 */ |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 204 * | 232 * |
| 205 * @param {function(boolean)} callback Callback, parameter is true if the image | 233 * @param {function(boolean)} callback Callback, parameter is true if the image |
| 206 * has loaded successfully or a stock icon has been used. | 234 * has loaded successfully or a stock icon has been used. |
| 207 */ | 235 */ |
| 208 ThumbnailLoader.prototype.loadDetachedImage = function(callback) { | 236 ThumbnailLoader.prototype.loadDetachedImage = function(callback) { |
| 209 if (!this.thumbnailUrl_) { | 237 if (!this.thumbnailUrl_) { |
| 210 callback(true); | 238 callback(true); |
| 211 return; | 239 return; |
| 212 } | 240 } |
| 213 | 241 |
| 242 this.cancel(); | |
| 214 this.canvasUpToDate_ = false; | 243 this.canvasUpToDate_ = false; |
| 215 this.image_ = new Image(); | 244 this.image_ = new Image(); |
| 216 this.image_.onload = callback.bind(null, true); | 245 this.image_.onload = callback.bind(null, true); |
| 217 this.image_.onerror = callback.bind(null, false); | 246 this.image_.onerror = callback.bind(null, false); |
| 218 | 247 |
| 219 // TODO(mtomasz): Smarter calculation of the requested size. | 248 // TODO(mtomasz): Smarter calculation of the requested size. |
| 220 var taskId = util.loadImage( | 249 this.taskId_ = util.loadImage( |
| 221 this.image_, | 250 this.image_, |
| 222 this.thumbnailUrl_, | 251 this.thumbnailUrl_, |
| 223 { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH, | 252 { maxWidth: ThumbnailLoader.THUMBNAIL_MAX_WIDTH, |
| 224 maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT, | 253 maxHeight: ThumbnailLoader.THUMBNAIL_MAX_HEIGHT, |
| 225 cache: true }); | 254 cache: true }); |
| 226 | 255 |
| 227 if (!taskId) | 256 if (!this.taskId_) |
| 228 this.image_.classList.add('cached'); | 257 this.image_.classList.add('cached'); |
| 229 }; | 258 }; |
| 230 | 259 |
| 231 /** | 260 /** |
| 232 * Attach the image to a given element. | 261 * Attach the image to a given element. |
| 233 * @param {Element} container Parent element. | 262 * @param {Element} container Parent element. |
| 234 * @param {ThumbnailLoader.FillMode} fillMode Fill mode. | 263 * @param {ThumbnailLoader.FillMode} fillMode Fill mode. |
| 235 */ | 264 */ |
| 236 ThumbnailLoader.prototype.attachImage = function(container, fillMode) { | 265 ThumbnailLoader.prototype.attachImage = function(container, fillMode) { |
| 237 if (!this.hasValidImage()) { | 266 if (!this.hasValidImage()) { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 256 // Canvas will be attached. | 285 // Canvas will be attached. |
| 257 attachableMedia = this.canvas_; | 286 attachableMedia = this.canvas_; |
| 258 } else { | 287 } else { |
| 259 // Image will be attached. | 288 // Image will be attached. |
| 260 attachableMedia = this.image_; | 289 attachableMedia = this.image_; |
| 261 } | 290 } |
| 262 | 291 |
| 263 util.applyTransform(container, this.transform_); | 292 util.applyTransform(container, this.transform_); |
| 264 ThumbnailLoader.centerImage_( | 293 ThumbnailLoader.centerImage_( |
| 265 container, attachableMedia, fillMode, this.isRotated_()); | 294 container, attachableMedia, fillMode, this.isRotated_()); |
| 266 if (this.image_.parentNode != container) { | 295 if (attachableMedia.parentNode != container) { |
| 267 container.textContent = ''; | 296 container.textContent = ''; |
| 268 container.appendChild(attachableMedia); | 297 container.appendChild(attachableMedia); |
| 269 } | 298 } |
| 270 }; | 299 }; |
| 271 | 300 |
| 272 /** | 301 /** |
| 273 * Update the image style to fit/fill the container. | 302 * Update the image style to fit/fill the container. |
| 274 * | 303 * |
| 275 * Using webkit center packing does not align the image properly, so we need | 304 * Using webkit center packing does not align the image properly, so we need |
| 276 * to wait until the image loads and its dimensions are known, then manually | 305 * to wait until the image loads and its dimensions are known, then manually |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 340 | 369 |
| 341 function percent(fraction) { | 370 function percent(fraction) { |
| 342 return (fraction * 100).toFixed(2) + '%'; | 371 return (fraction * 100).toFixed(2) + '%'; |
| 343 } | 372 } |
| 344 | 373 |
| 345 img.style.width = percent(fractionX); | 374 img.style.width = percent(fractionX); |
| 346 img.style.height = percent(fractionY); | 375 img.style.height = percent(fractionY); |
| 347 img.style.left = percent((1 - fractionX) / 2); | 376 img.style.left = percent((1 - fractionX) / 2); |
| 348 img.style.top = percent((1 - fractionY) / 2); | 377 img.style.top = percent((1 - fractionY) / 2); |
| 349 }; | 378 }; |
| OLD | NEW |