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 {string} url Image url. | |
11 * @constructor | |
12 */ | |
13 Gallery.Item = function(url) { | |
14 this.url_ = url; | |
15 this.original_ = true; | |
16 }; | |
17 | |
18 /** | |
19 * @return {string} Image url. | |
20 */ | |
21 Gallery.Item.prototype.getUrl = function() { return this.url_ }; | |
22 | |
23 /** | |
24 * @param {string} url New url. | |
25 */ | |
26 Gallery.Item.prototype.setUrl = function(url) { this.url_ = url }; | |
27 | |
28 /** | |
29 * @return {string} File name. | |
30 */ | |
31 Gallery.Item.prototype.getFileName = function() { | |
32 return ImageUtil.getFullNameFromUrl(this.url_); | |
33 }; | |
34 | |
35 /** | |
36 * @return {boolean} True if this image has not been created in this session. | |
37 */ | |
38 Gallery.Item.prototype.isOriginal = function() { return this.original_ }; | |
39 | |
40 // TODO: Localize? | |
41 /** | |
42 * @type {string} Suffix for a edited copy file name. | |
43 */ | |
44 Gallery.Item.COPY_SIGNATURE = ' - Edited'; | |
45 | |
46 /** | |
47 * Regular expression to match '... - Edited'. | |
48 * @type {RegExp} | |
49 */ | |
50 Gallery.Item.REGEXP_COPY_0 = | |
51 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + '$'); | |
52 | |
53 /** | |
54 * Regular expression to match '... - Edited (N)'. | |
55 * @type {RegExp} | |
56 */ | |
57 Gallery.Item.REGEXP_COPY_N = | |
58 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + ' \\((\\d+)\\)$'); | |
59 | |
60 /** | |
61 * Create a name for an edited copy of the file. | |
62 * | |
63 * @param {Entry} dirEntry Entry. | |
64 * @param {function} callback Callback. | |
65 * @private | |
66 */ | |
67 Gallery.Item.prototype.createCopyName_ = function(dirEntry, callback) { | |
68 var name = this.getFileName(); | |
69 | |
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_) { | |
73 callback(name); | |
74 return; | |
75 } | |
76 | |
77 var ext = ''; | |
78 var index = name.lastIndexOf('.'); | |
79 if (index != -1) { | |
80 ext = name.substr(index); | |
81 name = name.substr(0, index); | |
82 } | |
83 | |
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. | |
87 ext = '.png'; | |
88 } | |
89 | |
90 function tryNext(tries) { | |
91 // All the names are used. Let's overwrite the last one. | |
92 if (tries == 0) { | |
93 setTimeout(callback, 0, name + ext); | |
94 return; | |
95 } | |
96 | |
97 // If the file name contains the copy signature add/advance the sequential | |
98 // number. | |
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)'; | |
106 } else { | |
107 name += Gallery.Item.COPY_SIGNATURE; | |
108 } | |
109 | |
110 dirEntry.getFile(name + ext, {create: false, exclusive: false}, | |
111 tryNext.bind(null, tries - 1), | |
112 callback.bind(null, name + ext)); | |
113 } | |
114 | |
115 tryNext(10); | |
116 }; | |
117 | |
118 /** | |
119 * Write the new item content to the file. | |
120 * | |
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. | |
127 */ | |
128 Gallery.Item.prototype.saveToFile = function( | |
129 overrideDir, overwrite, canvas, metadataEncoder, opt_callback) { | |
130 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); | |
131 | |
132 var name = this.getFileName(); | |
133 | |
134 var onSuccess = function(url) { | |
135 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); | |
136 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); | |
137 this.setUrl(url); | |
138 if (opt_callback) opt_callback(true); | |
139 }.bind(this); | |
140 | |
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); | |
145 } | |
146 | |
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)); | |
152 } | |
153 fileWriter.onerror = function(error) { | |
154 onError(error); | |
155 // Disable all callbacks on the first error. | |
156 fileWriter.onerror = null; | |
157 fileWriter.onwriteend = null; | |
158 }; | |
159 if (newFile) { | |
160 writeContent(); | |
161 } else { | |
162 fileWriter.onwriteend = writeContent; | |
163 fileWriter.truncate(0); | |
164 } | |
165 }, onError); | |
166 } | |
167 | |
168 function getFile(dir, newFile) { | |
169 dir.getFile(name, {create: newFile, exclusive: newFile}, | |
170 doSave.bind(null, newFile), onError); | |
171 } | |
172 | |
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 */)); | |
177 } | |
178 | |
179 var saveToDir = function(dir) { | |
180 if (overwrite) { | |
181 checkExistence(dir); | |
182 } else { | |
183 this.createCopyName_(dir, function(copyName) { | |
184 this.original_ = false; | |
185 name = copyName; | |
186 checkExistence(dir); | |
187 }.bind(this)); | |
188 } | |
189 }.bind(this); | |
190 | |
191 if (overrideDir) { | |
192 saveToDir(overrideDir); | |
193 } else { | |
194 webkitResolveLocalFileSystemURL(this.getUrl(), | |
195 function(entry) { entry.getParent(saveToDir, onError)}, | |
196 onError); | |
197 } | |
198 }; | |
199 | |
200 /** | |
201 * Rename the file. | |
202 * | |
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. | |
206 */ | |
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); | |
212 } | |
213 var newName = ImageUtil.replaceFileNameInFullName(oldName, name); | |
214 if (oldName == newName) return; | |
215 | |
216 function onError() { | |
217 console.error('Rename error: "' + oldName + '" to "' + newName + '"'); | |
218 } | |
219 | |
220 var onRenamed = function(entry) { | |
221 this.setUrl(entry.toURL()); | |
222 onSuccess(); | |
223 }.bind(this); | |
224 | |
225 function moveIfDoesNotExist(entry, parentDir) { | |
226 parentDir.getFile(newName, {create: false, exclusive: false}, onExists, | |
227 function() { entry.moveTo(parentDir, newName, onRenamed, onError) }); | |
228 } | |
229 | |
230 webkitResolveLocalFileSystemURL(this.getUrl(), | |
231 function(entry) { | |
232 entry.getParent(moveIfDoesNotExist.bind(null, entry), onError); | |
233 }, | |
234 onError); | |
235 }; | |
OLD | NEW |