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 |