OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * Object representing an image item (a photo). | 8 * Object representing an image item (a photo). |
9 * | 9 * |
10 * @param {FileEntry} entry Image entry. | 10 * @param {FileEntry} entry Image entry. |
11 * @param {function():Promise} fethcedMediaProvider Function to provide the | 11 * @param {EntryLocation} locationInfo Entry location information. |
12 * fetchedMedia metadata. | 12 * @param {Object} metadata Metadata for the entry. |
| 13 * @param {MetadataCache} metadataCache Metadata cache instance. |
13 * @param {boolean} original Whether the entry is original or edited. | 14 * @param {boolean} original Whether the entry is original or edited. |
14 * @param {boolean} readonly Whether the entry is located at readonly directory | |
15 * or not. | |
16 * @constructor | 15 * @constructor |
17 */ | 16 */ |
18 Gallery.Item = function(entry, metadata, metadataCache, original, readonly) { | 17 Gallery.Item = function( |
| 18 entry, locationInfo, metadata, metadataCache, original) { |
19 /** | 19 /** |
20 * @type {FileEntry} | 20 * @type {FileEntry} |
21 * @private | 21 * @private |
22 */ | 22 */ |
23 this.entry_ = entry; | 23 this.entry_ = entry; |
24 | 24 |
25 /** | 25 /** |
| 26 * @type {EntryLocation} |
| 27 * @private |
| 28 */ |
| 29 this.locationInfo_ = locationInfo; |
| 30 |
| 31 /** |
26 * @type {Object} | 32 * @type {Object} |
27 * @private | 33 * @private |
28 */ | 34 */ |
29 this.metadata_ = Object.freeze(metadata); | 35 this.metadata_ = Object.freeze(metadata); |
30 | 36 |
31 /** | 37 /** |
32 * @type {MetadataCache} | 38 * @type {MetadataCache} |
33 * @private | 39 * @private |
34 */ | 40 */ |
35 this.metadataCache_ = metadataCache; | 41 this.metadataCache_ = metadataCache; |
(...skipping 18 matching lines...) Expand all Loading... |
54 /** | 60 /** |
55 * Last accessed date to be used for selecting items whose cache are evicted. | 61 * Last accessed date to be used for selecting items whose cache are evicted. |
56 * @type {number} | 62 * @type {number} |
57 */ | 63 */ |
58 this.lastAccessed_ = Date.now(); | 64 this.lastAccessed_ = Date.now(); |
59 | 65 |
60 /** | 66 /** |
61 * @type {boolean} | 67 * @type {boolean} |
62 * @private | 68 * @private |
63 */ | 69 */ |
64 this.isReadOnly_ = readonly; | |
65 | |
66 /** | |
67 * @type {boolean} | |
68 * @private | |
69 */ | |
70 this.original_ = original; | 70 this.original_ = original; |
71 | 71 |
72 Object.seal(this); | 72 Object.seal(this); |
73 }; | 73 }; |
74 | 74 |
75 /** | 75 /** |
76 * @return {FileEntry} Image entry. | 76 * @return {FileEntry} Image entry. |
77 */ | 77 */ |
78 Gallery.Item.prototype.getEntry = function() { return this.entry_; }; | 78 Gallery.Item.prototype.getEntry = function() { return this.entry_; }; |
79 | 79 |
80 /** | 80 /** |
| 81 * @return {EntryLocation} Entry location information. |
| 82 */ |
| 83 Gallery.Item.prototype.getLocationInfo = function() { |
| 84 return this.locationInfo_; |
| 85 }; |
| 86 |
| 87 /** |
81 * @return {Object} Metadata. | 88 * @return {Object} Metadata. |
82 */ | 89 */ |
83 Gallery.Item.prototype.getMetadata = function() { return this.metadata_; }; | 90 Gallery.Item.prototype.getMetadata = function() { return this.metadata_; }; |
84 | 91 |
85 /** | 92 /** |
86 * Obtains the latest media metadata. | 93 * Obtains the latest media metadata. |
87 * | 94 * |
88 * This is a heavy operation since it forces to load the image data to obtain | 95 * This is a heavy operation since it forces to load the image data to obtain |
89 * the metadata. | 96 * the metadata. |
90 * @return {Promise} Promise to be fulfilled with fetched metadata. | 97 * @return {Promise} Promise to be fulfilled with fetched metadata. |
(...skipping 26 matching lines...) Expand all Loading... |
117 Gallery.Item.prototype.getFileName = function() { | 124 Gallery.Item.prototype.getFileName = function() { |
118 return this.entry_.name; | 125 return this.entry_.name; |
119 }; | 126 }; |
120 | 127 |
121 /** | 128 /** |
122 * @return {boolean} True if this image has not been created in this session. | 129 * @return {boolean} True if this image has not been created in this session. |
123 */ | 130 */ |
124 Gallery.Item.prototype.isOriginal = function() { return this.original_; }; | 131 Gallery.Item.prototype.isOriginal = function() { return this.original_; }; |
125 | 132 |
126 /** | 133 /** |
127 * @return {boolean} Whther the item is located at a readonly directory. | |
128 */ | |
129 Gallery.Item.prototype.isReadOnly = function() { | |
130 return this.isReadOnly_; | |
131 }; | |
132 | |
133 /** | |
134 * Obtains the item is on the drive volume or not. | |
135 * @return {boolean} True if the item is on the drive volume. | |
136 */ | |
137 Gallery.Item.prototype.isOnDrive = function() { | |
138 return !!this.metadata_.drive; | |
139 }; | |
140 | |
141 /** | |
142 * Obtains the last accessed date. | 134 * Obtains the last accessed date. |
143 * @return {number} Last accessed date. | 135 * @return {number} Last accessed date. |
144 */ | 136 */ |
145 Gallery.Item.prototype.getLastAccessedDate = function() { | 137 Gallery.Item.prototype.getLastAccessedDate = function() { |
146 return this.lastAccessed_; | 138 return this.lastAccessed_; |
147 }; | 139 }; |
148 | 140 |
149 /** | 141 /** |
150 * Updates the last accessed date. | 142 * Updates the last accessed date. |
151 */ | 143 */ |
152 Gallery.Item.prototype.touch = function() { | 144 Gallery.Item.prototype.touch = function() { |
153 this.lastAccessed_ = Date.now(); | 145 this.lastAccessed_ = Date.now(); |
154 }; | 146 }; |
155 | 147 |
156 | |
157 // TODO: Localize? | 148 // TODO: Localize? |
158 /** | 149 /** |
159 * @type {string} Suffix for a edited copy file name. | 150 * @type {string} Suffix for a edited copy file name. |
160 */ | 151 */ |
161 Gallery.Item.COPY_SIGNATURE = ' - Edited'; | 152 Gallery.Item.COPY_SIGNATURE = ' - Edited'; |
162 | 153 |
163 /** | 154 /** |
164 * Regular expression to match '... - Edited'. | 155 * Regular expression to match '... - Edited'. |
165 * @type {RegExp} | 156 * @type {RegExp} |
166 */ | 157 */ |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 | 217 |
227 dirEntry.getFile(name + ext, {create: false, exclusive: false}, | 218 dirEntry.getFile(name + ext, {create: false, exclusive: false}, |
228 tryNext.bind(null, tries - 1), | 219 tryNext.bind(null, tries - 1), |
229 callback.bind(null, name + ext)); | 220 callback.bind(null, name + ext)); |
230 } | 221 } |
231 | 222 |
232 tryNext(10); | 223 tryNext(10); |
233 }; | 224 }; |
234 | 225 |
235 /** | 226 /** |
236 * Writes the new item content to the file. | 227 * Writes the new item content to either the existing or a new file. |
237 * | 228 * |
238 * @param {DirectoryEntry} fallbackDir If the entry is readonly, the edited | 229 * @param {VolumeManager} volumeManager Volume manager instance. |
239 * image is saved to the directory. | 230 * @param {string} fallbackDir Fallback directory in case the current directory |
240 * @param {boolean} overwrite True if overwrite, false if copy. | 231 * is read only. |
241 * @param {HTMLCanvasElement} canvas Source canvas. | 232 * @param {HTMLCanvasElement} canvas Source canvas. |
242 * @param {ImageEncoder.MetadataEncoder} metadataEncoder MetadataEncoder. | 233 * @param {ImageEncoder.MetadataEncoder} metadataEncoder MetadataEncoder. |
243 * @param {function(boolean)=} opt_callback Callback accepting true for success. | 234 * @param {function(boolean)=} opt_callback Callback accepting true for success. |
244 */ | 235 */ |
245 Gallery.Item.prototype.saveToFile = function( | 236 Gallery.Item.prototype.saveToFile = function( |
246 fallbackDir, overwrite, canvas, metadataEncoder, opt_callback) { | 237 volumeManager, fallbackDir, overwrite, canvas, metadataEncoder, |
| 238 opt_callback) { |
247 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); | 239 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); |
248 | 240 |
249 var name = this.getFileName(); | 241 var name = this.getFileName(); |
250 | 242 |
251 var onSuccess = function(entry) { | 243 var onSuccess = function(entry, locationInfo) { |
252 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); | 244 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); |
253 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); | 245 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); |
| 246 |
254 this.entry_ = entry; | 247 this.entry_ = entry; |
255 this.isReadOnly_ = false; | 248 this.locationInfo_ = locationInfo; |
| 249 |
256 this.metadataCache_.clear([this.entry_], 'fetchedMedia'); | 250 this.metadataCache_.clear([this.entry_], 'fetchedMedia'); |
257 if (opt_callback) | 251 if (opt_callback) |
258 opt_callback(true); | 252 opt_callback(true); |
259 }.bind(this); | 253 }.bind(this); |
260 | 254 |
261 function onError(error) { | 255 var onError = function(error) { |
262 console.error('Error saving from gallery', name, error); | 256 console.error('Error saving from gallery', name, error); |
263 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 0, 2); | 257 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 0, 2); |
264 if (opt_callback) | 258 if (opt_callback) |
265 opt_callback(false); | 259 opt_callback(false); |
266 } | 260 } |
267 | 261 |
268 function doSave(newFile, fileEntry) { | 262 var doSave = function(newFile, fileEntry) { |
269 fileEntry.createWriter(function(fileWriter) { | 263 fileEntry.createWriter(function(fileWriter) { |
270 function writeContent() { | 264 function writeContent() { |
271 fileWriter.onwriteend = onSuccess.bind(null, fileEntry); | 265 fileWriter.onwriteend = onSuccess.bind(null, fileEntry); |
272 fileWriter.write(ImageEncoder.getBlob(canvas, metadataEncoder)); | 266 fileWriter.write(ImageEncoder.getBlob(canvas, metadataEncoder)); |
273 } | 267 } |
274 fileWriter.onerror = function(error) { | 268 fileWriter.onerror = function(error) { |
275 onError(error); | 269 onError(error); |
276 // Disable all callbacks on the first error. | 270 // Disable all callbacks on the first error. |
277 fileWriter.onerror = null; | 271 fileWriter.onerror = null; |
278 fileWriter.onwriteend = null; | 272 fileWriter.onwriteend = null; |
279 }; | 273 }; |
280 if (newFile) { | 274 if (newFile) { |
281 writeContent(); | 275 writeContent(); |
282 } else { | 276 } else { |
283 fileWriter.onwriteend = writeContent; | 277 fileWriter.onwriteend = writeContent; |
284 fileWriter.truncate(0); | 278 fileWriter.truncate(0); |
285 } | 279 } |
286 }, onError); | 280 }, onError); |
287 } | 281 } |
288 | 282 |
289 function getFile(dir, newFile) { | 283 var getFile = function(dir, newFile) { |
290 dir.getFile(name, {create: newFile, exclusive: newFile}, | 284 dir.getFile(name, {create: newFile, exclusive: newFile}, |
291 doSave.bind(null, newFile), onError); | 285 function(fileEntry) { |
292 } | 286 var locationInfo = volumeManager.getLocationInfo(fileEntry); |
| 287 // If the volume is gone, then abort the saving operation. |
| 288 if (!locationInfo) { |
| 289 onError('NotFound'); |
| 290 return; |
| 291 } |
| 292 doSave(newFile, fileEntry, locationInfo); |
| 293 }.bind(this), onError); |
| 294 }.bind(this); |
293 | 295 |
294 function checkExistence(dir) { | 296 var checkExistence = function(dir) { |
295 dir.getFile(name, {create: false, exclusive: false}, | 297 dir.getFile(name, {create: false, exclusive: false}, |
296 getFile.bind(null, dir, false /* existing file */), | 298 getFile.bind(null, dir, false /* existing file */), |
297 getFile.bind(null, dir, true /* create new file */)); | 299 getFile.bind(null, dir, true /* create new file */)); |
298 } | 300 } |
299 | 301 |
300 var saveToDir = function(dir) { | 302 var saveToDir = function(dir) { |
301 if (overwrite && !this.isReadOnly_) { | 303 if (overwrite && !this.locationInfo_.isReadOnly) { |
302 checkExistence(dir); | 304 checkExistence(dir); |
303 } else { | 305 } else { |
304 this.createCopyName_(dir, function(copyName) { | 306 this.createCopyName_(dir, function(copyName) { |
305 this.original_ = false; | 307 this.original_ = false; |
306 name = copyName; | 308 name = copyName; |
307 checkExistence(dir); | 309 checkExistence(dir); |
308 }.bind(this)); | 310 }.bind(this)); |
309 } | 311 } |
310 }.bind(this); | 312 }.bind(this); |
311 | 313 |
312 if (this.isReadOnly_) { | 314 if (this.locationInfo_.isReadOnly) { |
313 saveToDir(fallbackDir); | 315 saveToDir(fallbackDir); |
314 } else { | 316 } else { |
315 this.entry_.getParent(saveToDir, onError); | 317 this.entry_.getParent(saveToDir, onError); |
316 } | 318 } |
317 }; | 319 }; |
318 | 320 |
319 /** | 321 /** |
320 * Renames the item. | 322 * Renames the item. |
321 * | 323 * |
322 * @param {string} displayName New display name (without the extension). | 324 * @param {string} displayName New display name (without the extension). |
(...skipping 22 matching lines...) Expand all Loading... |
345 return Promise.reject(str('GALLERY_FILE_EXISTS')); | 347 return Promise.reject(str('GALLERY_FILE_EXISTS')); |
346 }, function() { | 348 }, function() { |
347 return new Promise( | 349 return new Promise( |
348 this.entry_.moveTo.bind(this.entry_, parentDirectory, newFileName)); | 350 this.entry_.moveTo.bind(this.entry_, parentDirectory, newFileName)); |
349 }.bind(this)); | 351 }.bind(this)); |
350 }.bind(this)); | 352 }.bind(this)); |
351 }.bind(this)).then(function(entry) { | 353 }.bind(this)).then(function(entry) { |
352 this.entry_ = entry; | 354 this.entry_ = entry; |
353 }.bind(this)); | 355 }.bind(this)); |
354 }; | 356 }; |
OLD | NEW |