| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 'use strict'; | |
| 6 | |
| 7 /** | |
| 8 * Object representing an image item (a photo or a video). | |
| 9 * | |
| 10 * @param {FileEntry} entry Image entry. | |
| 11 * @constructor | |
| 12 */ | |
| 13 Gallery.Item = function(entry) { | |
| 14 this.entry_ = entry; | |
| 15 this.original_ = true; | |
| 16 }; | |
| 17 | |
| 18 /** | |
| 19 * @return {FileEntry} Image entry. | |
| 20 */ | |
| 21 Gallery.Item.prototype.getEntry = function() { return this.entry_ }; | |
| 22 | |
| 23 /** | |
| 24 * @return {string} File name. | |
| 25 */ | |
| 26 Gallery.Item.prototype.getFileName = function() { | |
| 27 return this.entry_.name; | |
| 28 }; | |
| 29 | |
| 30 /** | |
| 31 * @return {boolean} True if this image has not been created in this session. | |
| 32 */ | |
| 33 Gallery.Item.prototype.isOriginal = function() { return this.original_ }; | |
| 34 | |
| 35 // TODO: Localize? | |
| 36 /** | |
| 37 * @type {string} Suffix for a edited copy file name. | |
| 38 */ | |
| 39 Gallery.Item.COPY_SIGNATURE = ' - Edited'; | |
| 40 | |
| 41 /** | |
| 42 * Regular expression to match '... - Edited'. | |
| 43 * @type {RegExp} | |
| 44 */ | |
| 45 Gallery.Item.REGEXP_COPY_0 = | |
| 46 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + '$'); | |
| 47 | |
| 48 /** | |
| 49 * Regular expression to match '... - Edited (N)'. | |
| 50 * @type {RegExp} | |
| 51 */ | |
| 52 Gallery.Item.REGEXP_COPY_N = | |
| 53 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + ' \\((\\d+)\\)$'); | |
| 54 | |
| 55 /** | |
| 56 * Creates a name for an edited copy of the file. | |
| 57 * | |
| 58 * @param {Entry} dirEntry Entry. | |
| 59 * @param {function} callback Callback. | |
| 60 * @private | |
| 61 */ | |
| 62 Gallery.Item.prototype.createCopyName_ = function(dirEntry, callback) { | |
| 63 var name = this.getFileName(); | |
| 64 | |
| 65 // If the item represents a file created during the current Gallery session | |
| 66 // we reuse it for subsequent saves instead of creating multiple copies. | |
| 67 if (!this.original_) { | |
| 68 callback(name); | |
| 69 return; | |
| 70 } | |
| 71 | |
| 72 var ext = ''; | |
| 73 var index = name.lastIndexOf('.'); | |
| 74 if (index != -1) { | |
| 75 ext = name.substr(index); | |
| 76 name = name.substr(0, index); | |
| 77 } | |
| 78 | |
| 79 if (!ext.match(/jpe?g/i)) { | |
| 80 // Chrome can natively encode only two formats: JPEG and PNG. | |
| 81 // All non-JPEG images are saved in PNG, hence forcing the file extension. | |
| 82 ext = '.png'; | |
| 83 } | |
| 84 | |
| 85 function tryNext(tries) { | |
| 86 // All the names are used. Let's overwrite the last one. | |
| 87 if (tries == 0) { | |
| 88 setTimeout(callback, 0, name + ext); | |
| 89 return; | |
| 90 } | |
| 91 | |
| 92 // If the file name contains the copy signature add/advance the sequential | |
| 93 // number. | |
| 94 var matchN = Gallery.Item.REGEXP_COPY_N.exec(name); | |
| 95 var match0 = Gallery.Item.REGEXP_COPY_0.exec(name); | |
| 96 if (matchN && matchN[1] && matchN[2]) { | |
| 97 var copyNumber = parseInt(matchN[2], 10) + 1; | |
| 98 name = matchN[1] + Gallery.Item.COPY_SIGNATURE + ' (' + copyNumber + ')'; | |
| 99 } else if (match0 && match0[1]) { | |
| 100 name = match0[1] + Gallery.Item.COPY_SIGNATURE + ' (1)'; | |
| 101 } else { | |
| 102 name += Gallery.Item.COPY_SIGNATURE; | |
| 103 } | |
| 104 | |
| 105 dirEntry.getFile(name + ext, {create: false, exclusive: false}, | |
| 106 tryNext.bind(null, tries - 1), | |
| 107 callback.bind(null, name + ext)); | |
| 108 } | |
| 109 | |
| 110 tryNext(10); | |
| 111 }; | |
| 112 | |
| 113 /** | |
| 114 * Writes the new item content to the file. | |
| 115 * | |
| 116 * @param {Entry} overrideDir Directory to save to. If null, save to the same | |
| 117 * directory as the original. | |
| 118 * @param {boolean} overwrite True if overwrite, false if copy. | |
| 119 * @param {HTMLCanvasElement} canvas Source canvas. | |
| 120 * @param {ImageEncoder.MetadataEncoder} metadataEncoder MetadataEncoder. | |
| 121 * @param {function(boolean)=} opt_callback Callback accepting true for success. | |
| 122 */ | |
| 123 Gallery.Item.prototype.saveToFile = function( | |
| 124 overrideDir, overwrite, canvas, metadataEncoder, opt_callback) { | |
| 125 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); | |
| 126 | |
| 127 var name = this.getFileName(); | |
| 128 | |
| 129 var onSuccess = function(entry) { | |
| 130 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); | |
| 131 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); | |
| 132 this.entry_ = entry; | |
| 133 if (opt_callback) opt_callback(true); | |
| 134 }.bind(this); | |
| 135 | |
| 136 function onError(error) { | |
| 137 console.error('Error saving from gallery', name, error); | |
| 138 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 0, 2); | |
| 139 if (opt_callback) opt_callback(false); | |
| 140 } | |
| 141 | |
| 142 function doSave(newFile, fileEntry) { | |
| 143 fileEntry.createWriter(function(fileWriter) { | |
| 144 function writeContent() { | |
| 145 fileWriter.onwriteend = onSuccess.bind(null, fileEntry); | |
| 146 fileWriter.write(ImageEncoder.getBlob(canvas, metadataEncoder)); | |
| 147 } | |
| 148 fileWriter.onerror = function(error) { | |
| 149 onError(error); | |
| 150 // Disable all callbacks on the first error. | |
| 151 fileWriter.onerror = null; | |
| 152 fileWriter.onwriteend = null; | |
| 153 }; | |
| 154 if (newFile) { | |
| 155 writeContent(); | |
| 156 } else { | |
| 157 fileWriter.onwriteend = writeContent; | |
| 158 fileWriter.truncate(0); | |
| 159 } | |
| 160 }, onError); | |
| 161 } | |
| 162 | |
| 163 function getFile(dir, newFile) { | |
| 164 dir.getFile(name, {create: newFile, exclusive: newFile}, | |
| 165 doSave.bind(null, newFile), onError); | |
| 166 } | |
| 167 | |
| 168 function checkExistence(dir) { | |
| 169 dir.getFile(name, {create: false, exclusive: false}, | |
| 170 getFile.bind(null, dir, false /* existing file */), | |
| 171 getFile.bind(null, dir, true /* create new file */)); | |
| 172 } | |
| 173 | |
| 174 var saveToDir = function(dir) { | |
| 175 if (overwrite) { | |
| 176 checkExistence(dir); | |
| 177 } else { | |
| 178 this.createCopyName_(dir, function(copyName) { | |
| 179 this.original_ = false; | |
| 180 name = copyName; | |
| 181 checkExistence(dir); | |
| 182 }.bind(this)); | |
| 183 } | |
| 184 }.bind(this); | |
| 185 | |
| 186 if (overrideDir) { | |
| 187 saveToDir(overrideDir); | |
| 188 } else { | |
| 189 this.entry_.getParent(saveToDir, onError); | |
| 190 } | |
| 191 }; | |
| 192 | |
| 193 /** | |
| 194 * Renames the file. | |
| 195 * | |
| 196 * @param {string} displayName New display name (without the extension). | |
| 197 * @param {function()} onSuccess Success callback. | |
| 198 * @param {function()} onExists Called if the file with the new name exists. | |
| 199 */ | |
| 200 Gallery.Item.prototype.rename = function(displayName, onSuccess, onExists) { | |
| 201 var newFileName = this.entry_.name.replace( | |
| 202 ImageUtil.getDisplayNameFromName(this.entry_.name), displayName); | |
| 203 | |
| 204 if (newFileName === this.entry_.name) | |
| 205 return; | |
| 206 | |
| 207 var onRenamed = function(entry) { | |
| 208 this.entry_ = entry; | |
| 209 onSuccess(); | |
| 210 }.bind(this); | |
| 211 | |
| 212 var onError = function() { | |
| 213 console.error( | |
| 214 'Rename error: "' + this.entry_.name + '" to "' + newFileName + '"'); | |
| 215 }; | |
| 216 | |
| 217 var moveIfDoesNotExist = function(parentDir) { | |
| 218 parentDir.getFile( | |
| 219 newFileName, | |
| 220 {create: false, exclusive: false}, | |
| 221 onExists, | |
| 222 function() { | |
| 223 this.entry_.moveTo(parentDir, newFileName, onRenamed, onError); | |
| 224 }.bind(this)); | |
| 225 }.bind(this); | |
| 226 | |
| 227 this.entry_.getParent(moveIfDoesNotExist, onError); | |
| 228 }; | |
| OLD | NEW |