- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / file_manager / foreground / js / photo / gallery_item.js
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 };