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.
8 * Object representing an image item (a photo or a video).
10 * @param {string} url Image url.
13 Gallery.Item = function(url) {
15 this.original_ = true;
19 * @return {string} Image url.
21 Gallery.Item.prototype.getUrl = function() { return this.url_ };
24 * @param {string} url New url.
26 Gallery.Item.prototype.setUrl = function(url) { this.url_ = url };
29 * @return {string} File name.
31 Gallery.Item.prototype.getFileName = function() {
32 return ImageUtil.getFullNameFromUrl(this.url_);
36 * @return {boolean} True if this image has not been created in this session.
38 Gallery.Item.prototype.isOriginal = function() { return this.original_ };
42 * @type {string} Suffix for a edited copy file name.
44 Gallery.Item.COPY_SIGNATURE = ' - Edited';
47 * Regular expression to match '... - Edited'.
50 Gallery.Item.REGEXP_COPY_0 =
51 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + '$');
54 * Regular expression to match '... - Edited (N)'.
57 Gallery.Item.REGEXP_COPY_N =
58 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + ' \\((\\d+)\\)$');
61 * Create a name for an edited copy of the file.
63 * @param {Entry} dirEntry Entry.
64 * @param {function} callback Callback.
67 Gallery.Item.prototype.createCopyName_ = function(dirEntry, callback) {
68 var name = this.getFileName();
70 // If the item represents a file created during the current Gallery session
71 // we reuse it for subsequent saves instead of creating multiple copies.
72 if (!this.original_) {
78 var index = name.lastIndexOf('.');
80 ext = name.substr(index);
81 name = name.substr(0, index);
84 if (!ext.match(/jpe?g/i)) {
85 // Chrome can natively encode only two formats: JPEG and PNG.
86 // All non-JPEG images are saved in PNG, hence forcing the file extension.
90 function tryNext(tries) {
91 // All the names are used. Let's overwrite the last one.
93 setTimeout(callback, 0, name + ext);
97 // If the file name contains the copy signature add/advance the sequential
99 var matchN = Gallery.Item.REGEXP_COPY_N.exec(name);
100 var match0 = Gallery.Item.REGEXP_COPY_0.exec(name);
101 if (matchN && matchN[1] && matchN[2]) {
102 var copyNumber = parseInt(matchN[2], 10) + 1;
103 name = matchN[1] + Gallery.Item.COPY_SIGNATURE + ' (' + copyNumber + ')';
104 } else if (match0 && match0[1]) {
105 name = match0[1] + Gallery.Item.COPY_SIGNATURE + ' (1)';
107 name += Gallery.Item.COPY_SIGNATURE;
110 dirEntry.getFile(name + ext, {create: false, exclusive: false},
111 tryNext.bind(null, tries - 1),
112 callback.bind(null, name + ext));
119 * Write the new item content to the file.
121 * @param {Entry} overrideDir Directory to save to. If null, save to the same
122 * directory as the original.
123 * @param {boolean} overwrite True if overwrite, false if copy.
124 * @param {HTMLCanvasElement} canvas Source canvas.
125 * @param {ImageEncoder.MetadataEncoder} metadataEncoder MetadataEncoder.
126 * @param {function(boolean)=} opt_callback Callback accepting true for success.
128 Gallery.Item.prototype.saveToFile = function(
129 overrideDir, overwrite, canvas, metadataEncoder, opt_callback) {
130 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime'));
132 var name = this.getFileName();
134 var onSuccess = function(url) {
135 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2);
136 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime'));
138 if (opt_callback) opt_callback(true);
141 function onError(error) {
142 console.error('Error saving from gallery', name, error);
143 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 0, 2);
144 if (opt_callback) opt_callback(false);
147 function doSave(newFile, fileEntry) {
148 fileEntry.createWriter(function(fileWriter) {
149 function writeContent() {
150 fileWriter.onwriteend = onSuccess.bind(null, fileEntry.toURL());
151 fileWriter.write(ImageEncoder.getBlob(canvas, metadataEncoder));
153 fileWriter.onerror = function(error) {
155 // Disable all callbacks on the first error.
156 fileWriter.onerror = null;
157 fileWriter.onwriteend = null;
162 fileWriter.onwriteend = writeContent;
163 fileWriter.truncate(0);
168 function getFile(dir, newFile) {
169 dir.getFile(name, {create: newFile, exclusive: newFile},
170 doSave.bind(null, newFile), onError);
173 function checkExistence(dir) {
174 dir.getFile(name, {create: false, exclusive: false},
175 getFile.bind(null, dir, false /* existing file */),
176 getFile.bind(null, dir, true /* create new file */));
179 var saveToDir = function(dir) {
183 this.createCopyName_(dir, function(copyName) {
184 this.original_ = false;
192 saveToDir(overrideDir);
194 webkitResolveLocalFileSystemURL(this.getUrl(),
195 function(entry) { entry.getParent(saveToDir, onError)},
203 * @param {string} name New file name.
204 * @param {function} onSuccess Success callback.
205 * @param {function} onExists Called if the file with the new name exists.
207 Gallery.Item.prototype.rename = function(name, onSuccess, onExists) {
208 var oldName = this.getFileName();
209 if (ImageUtil.getExtensionFromFullName(name) ==
210 ImageUtil.getExtensionFromFullName(oldName)) {
211 name = ImageUtil.getFileNameFromFullName(name);
213 var newName = ImageUtil.replaceFileNameInFullName(oldName, name);
214 if (oldName == newName) return;
217 console.error('Rename error: "' + oldName + '" to "' + newName + '"');
220 var onRenamed = function(entry) {
221 this.setUrl(entry.toURL());
225 function moveIfDoesNotExist(entry, parentDir) {
226 parentDir.getFile(newName, {create: false, exclusive: false}, onExists,
227 function() { entry.moveTo(parentDir, newName, onRenamed, onError) });
230 webkitResolveLocalFileSystemURL(this.getUrl(),
232 entry.getParent(moveIfDoesNotExist.bind(null, entry), onError);