Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(349)

Side by Side Diff: ui/file_manager/gallery/js/gallery_item.js

Issue 1408533002: Turn on verbose flag for compiling file_manager. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase and address review comments. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * Object representing an image item (a photo). 6 * Object representing an image item (a photo).
7 * 7 *
8 * @param {!FileEntry} entry Image entry. 8 * @param {!FileEntry} entry Image entry.
9 * @param {!EntryLocation} locationInfo Entry location information. 9 * @param {!EntryLocation} locationInfo Entry location information.
10 * @param {MetadataItem} metadataItem 10 * @param {MetadataItem} metadataItem
11 * @param {ThumbnailMetadataItem} thumbnailMetadataItem 11 * @param {ThumbnailMetadataItem} thumbnailMetadataItem
12 * @param {boolean} original Whether the entry is original or edited. 12 * @param {boolean} original Whether the entry is original or edited.
13 * @constructor 13 * @constructor
14 * @struct 14 * @struct
15 */ 15 */
16 Gallery.Item = function( 16 function GalleryItem(
17 entry, locationInfo, metadataItem, thumbnailMetadataItem, original) { 17 entry, locationInfo, metadataItem, thumbnailMetadataItem, original) {
18 /** 18 /**
19 * @private {!FileEntry} 19 * @private {!FileEntry}
20 */ 20 */
21 this.entry_ = entry; 21 this.entry_ = entry;
22 22
23 /** 23 /**
24 * @private {!EntryLocation} 24 * @private {!EntryLocation}
25 */ 25 */
26 this.locationInfo_ = locationInfo; 26 this.locationInfo_ = locationInfo;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 /** 64 /**
65 * @type {boolean} 65 * @type {boolean}
66 * @private 66 * @private
67 */ 67 */
68 this.original_ = original; 68 this.original_ = original;
69 }; 69 };
70 70
71 /** 71 /**
72 * @return {!FileEntry} Image entry. 72 * @return {!FileEntry} Image entry.
73 */ 73 */
74 Gallery.Item.prototype.getEntry = function() { return this.entry_; }; 74 GalleryItem.prototype.getEntry = function() { return this.entry_; };
75 75
76 /** 76 /**
77 * @return {!EntryLocation} Entry location information. 77 * @return {!EntryLocation} Entry location information.
78 */ 78 */
79 Gallery.Item.prototype.getLocationInfo = function() { 79 GalleryItem.prototype.getLocationInfo = function() {
80 return this.locationInfo_; 80 return this.locationInfo_;
81 }; 81 };
82 82
83 /** 83 /**
84 * @return {MetadataItem} Metadata. 84 * @return {MetadataItem} Metadata.
85 */ 85 */
86 Gallery.Item.prototype.getMetadataItem = function() { 86 GalleryItem.prototype.getMetadataItem = function() {
87 return this.metadataItem_; 87 return this.metadataItem_;
88 }; 88 };
89 89
90 /** 90 /**
91 * @param {!MetadataItem} metadata 91 * @param {!MetadataItem} metadata
92 */ 92 */
93 Gallery.Item.prototype.setMetadataItem = function(metadata) { 93 GalleryItem.prototype.setMetadataItem = function(metadata) {
94 this.metadataItem_ = metadata; 94 this.metadataItem_ = metadata;
95 }; 95 };
96 96
97 /** 97 /**
98 * @return {ThumbnailMetadataItem} Thumbnail metadata item. 98 * @return {ThumbnailMetadataItem} Thumbnail metadata item.
99 */ 99 */
100 Gallery.Item.prototype.getThumbnailMetadataItem = function() { 100 GalleryItem.prototype.getThumbnailMetadataItem = function() {
101 return this.thumbnailMetadataItem_; 101 return this.thumbnailMetadataItem_;
102 }; 102 };
103 103
104 /** 104 /**
105 * @param {!ThumbnailMetadataItem} item Thumbnail metadata item. 105 * @param {!ThumbnailMetadataItem} item Thumbnail metadata item.
106 */ 106 */
107 Gallery.Item.prototype.setThumbnailMetadataItem = function(item) { 107 GalleryItem.prototype.setThumbnailMetadataItem = function(item) {
108 this.thumbnailMetadataItem_ = item; 108 this.thumbnailMetadataItem_ = item;
109 }; 109 };
110 110
111 /** 111 /**
112 * @return {string} File name. 112 * @return {string} File name.
113 */ 113 */
114 Gallery.Item.prototype.getFileName = function() { 114 GalleryItem.prototype.getFileName = function() {
115 return this.entry_.name; 115 return this.entry_.name;
116 }; 116 };
117 117
118 /** 118 /**
119 * @return {boolean} True if this image has not been created in this session. 119 * @return {boolean} True if this image has not been created in this session.
120 */ 120 */
121 Gallery.Item.prototype.isOriginal = function() { return this.original_; }; 121 GalleryItem.prototype.isOriginal = function() { return this.original_; };
122 122
123 /** 123 /**
124 * Sets an item as original. 124 * Sets an item as original.
125 */ 125 */
126 Gallery.Item.prototype.setAsOriginal = function() { 126 GalleryItem.prototype.setAsOriginal = function() {
127 this.original_ = true; 127 this.original_ = true;
128 }; 128 };
129 129
130 /** 130 /**
131 * Obtains the last accessed date. 131 * Obtains the last accessed date.
132 * @return {number} Last accessed date. 132 * @return {number} Last accessed date.
133 */ 133 */
134 Gallery.Item.prototype.getLastAccessedDate = function() { 134 GalleryItem.prototype.getLastAccessedDate = function() {
135 return this.lastAccessed_; 135 return this.lastAccessed_;
136 }; 136 };
137 137
138 /** 138 /**
139 * Updates the last accessed date. 139 * Updates the last accessed date.
140 */ 140 */
141 Gallery.Item.prototype.touch = function() { 141 GalleryItem.prototype.touch = function() {
142 this.lastAccessed_ = Date.now(); 142 this.lastAccessed_ = Date.now();
143 }; 143 };
144 144
145 // TODO: Localize? 145 // TODO: Localize?
146 /** 146 /**
147 * @type {string} Suffix for a edited copy file name. 147 * @type {string} Suffix for a edited copy file name.
148 * @const 148 * @const
149 */ 149 */
150 Gallery.Item.COPY_SIGNATURE = ' - Edited'; 150 GalleryItem.COPY_SIGNATURE = ' - Edited';
151 151
152 /** 152 /**
153 * Regular expression to match '... - Edited'. 153 * Regular expression to match '... - Edited'.
154 * @type {!RegExp} 154 * @type {!RegExp}
155 * @const 155 * @const
156 */ 156 */
157 Gallery.Item.REGEXP_COPY_0 = 157 GalleryItem.REGEXP_COPY_0 =
158 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + '$'); 158 new RegExp('^(.+)' + GalleryItem.COPY_SIGNATURE + '$');
159 159
160 /** 160 /**
161 * Regular expression to match '... - Edited (N)'. 161 * Regular expression to match '... - Edited (N)'.
162 * @type {!RegExp} 162 * @type {!RegExp}
163 * @const 163 * @const
164 */ 164 */
165 Gallery.Item.REGEXP_COPY_N = 165 GalleryItem.REGEXP_COPY_N =
166 new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + ' \\((\\d+)\\)$'); 166 new RegExp('^(.+)' + GalleryItem.COPY_SIGNATURE + ' \\((\\d+)\\)$');
167 167
168 /** 168 /**
169 * Creates a name for an edited copy of the file. 169 * Creates a name for an edited copy of the file.
170 * 170 *
171 * @param {!DirectoryEntry} dirEntry Entry. 171 * @param {!DirectoryEntry} dirEntry Entry.
172 * @param {string} newMimeType Mime type of new image. 172 * @param {string} newMimeType Mime type of new image.
173 * @param {function(string)} callback Callback. 173 * @param {function(string)} callback Callback.
174 * @private 174 * @private
175 */ 175 */
176 Gallery.Item.prototype.createCopyName_ = function( 176 GalleryItem.prototype.createCopyName_ = function(
Dan Beam 2015/10/20 00:33:37 nit: you'd have needed to change Gallery.Item -> G
fukino 2015/10/20 04:13:07 For consistency with other code under file_manager
177 dirEntry, newMimeType, callback) { 177 dirEntry, newMimeType, callback) {
178 var name = this.getFileName(); 178 var name = this.getFileName();
179 179
180 // If the item represents a file created during the current Gallery session 180 // If the item represents a file created during the current Gallery session
181 // we reuse it for subsequent saves instead of creating multiple copies. 181 // we reuse it for subsequent saves instead of creating multiple copies.
182 if (!this.original_) { 182 if (!this.original_) {
183 callback(name); 183 callback(name);
184 return; 184 return;
185 } 185 }
186 186
187 var baseName = name.replace(/\.[^\.\/]+$/, ''); 187 var baseName = name.replace(/\.[^\.\/]+$/, '');
188 var ext = newMimeType === 'image/jpeg' ? '.jpg' : '.png'; 188 var ext = newMimeType === 'image/jpeg' ? '.jpg' : '.png';
189 189
190 function tryNext(tries) { 190 function tryNext(tries) {
191 // All the names are used. Let's overwrite the last one. 191 // All the names are used. Let's overwrite the last one.
192 if (tries == 0) { 192 if (tries == 0) {
193 setTimeout(callback, 0, baseName + ext); 193 setTimeout(callback, 0, baseName + ext);
194 return; 194 return;
195 } 195 }
196 196
197 // If the file name contains the copy signature add/advance the sequential 197 // If the file name contains the copy signature add/advance the sequential
198 // number. 198 // number.
199 var matchN = Gallery.Item.REGEXP_COPY_N.exec(baseName); 199 var matchN = GalleryItem.REGEXP_COPY_N.exec(baseName);
200 var match0 = Gallery.Item.REGEXP_COPY_0.exec(baseName); 200 var match0 = GalleryItem.REGEXP_COPY_0.exec(baseName);
201 if (matchN && matchN[1] && matchN[2]) { 201 if (matchN && matchN[1] && matchN[2]) {
202 var copyNumber = parseInt(matchN[2], 10) + 1; 202 var copyNumber = parseInt(matchN[2], 10) + 1;
203 baseName = matchN[1] + Gallery.Item.COPY_SIGNATURE + 203 baseName = matchN[1] + GalleryItem.COPY_SIGNATURE +
204 ' (' + copyNumber + ')'; 204 ' (' + copyNumber + ')';
205 } else if (match0 && match0[1]) { 205 } else if (match0 && match0[1]) {
206 baseName = match0[1] + Gallery.Item.COPY_SIGNATURE + ' (1)'; 206 baseName = match0[1] + GalleryItem.COPY_SIGNATURE + ' (1)';
207 } else { 207 } else {
208 baseName += Gallery.Item.COPY_SIGNATURE; 208 baseName += GalleryItem.COPY_SIGNATURE;
209 } 209 }
210 210
211 dirEntry.getFile(baseName + ext, {create: false, exclusive: false}, 211 dirEntry.getFile(baseName + ext, {create: false, exclusive: false},
212 tryNext.bind(null, tries - 1), 212 tryNext.bind(null, tries - 1),
213 callback.bind(null, baseName + ext)); 213 callback.bind(null, baseName + ext));
214 } 214 }
215 215
216 tryNext(10); 216 tryNext(10);
217 }; 217 };
218 218
219 /** 219 /**
220 * Returns true if the original format is writable format of Gallery. 220 * Returns true if the original format is writable format of Gallery.
221 * @return {boolean} True if the original format is writable format. 221 * @return {boolean} True if the original format is writable format.
222 */ 222 */
223 Gallery.Item.prototype.isWritableFormat = function() { 223 GalleryItem.prototype.isWritableFormat = function() {
224 var type = FileType.getType(this.entry_); 224 var type = FileType.getType(this.entry_);
225 return type.type === 'image' && 225 return type.type === 'image' &&
226 (type.subtype === 'JPEG' || type.subtype === 'PNG'); 226 (type.subtype === 'JPEG' || type.subtype === 'PNG');
227 }; 227 };
228 228
229 /** 229 /**
230 * Returns true if the entry of item is writable. 230 * Returns true if the entry of item is writable.
231 * @param {!VolumeManagerWrapper} volumeManager Volume manager. 231 * @param {!VolumeManagerWrapper} volumeManager Volume manager.
232 * @return {boolean} True if the entry of item is writable. 232 * @return {boolean} True if the entry of item is writable.
233 */ 233 */
234 Gallery.Item.prototype.isWritableFile = function(volumeManager) { 234 GalleryItem.prototype.isWritableFile = function(volumeManager) {
235 return this.isWritableFormat() && 235 return this.isWritableFormat() &&
236 !this.locationInfo_.isReadOnly && 236 !this.locationInfo_.isReadOnly &&
237 !GalleryUtil.isOnMTPVolume(this.entry_, volumeManager); 237 !GalleryUtil.isOnMTPVolume(this.entry_, volumeManager);
238 }; 238 };
239 239
240 /** 240 /**
241 * Returns mime type for saving an edit of this item. 241 * Returns mime type for saving an edit of this item.
242 * @return {string} Mime type. 242 * @return {string} Mime type.
243 * @private 243 * @private
244 */ 244 */
245 Gallery.Item.prototype.getNewMimeType_ = function() { 245 GalleryItem.prototype.getNewMimeType_ = function() {
246 return this.getFileName().match(/\.jpe?g$/i) || FileType.isRaw(this.entry_) ? 246 return this.getFileName().match(/\.jpe?g$/i) || FileType.isRaw(this.entry_) ?
247 'image/jpeg' : 'image/png'; 247 'image/jpeg' : 'image/png';
248 }; 248 };
249 249
250 /** 250 /**
251 * Return copy name of this item. 251 * Return copy name of this item.
252 * @param {!DirectoryEntry} dirEntry Parent directory entry of copied item. 252 * @param {!DirectoryEntry} dirEntry Parent directory entry of copied item.
253 * @return {!Promise<string>} A promise which will be fulfilled with copy name. 253 * @return {!Promise<string>} A promise which will be fulfilled with copy name.
254 */ 254 */
255 Gallery.Item.prototype.getCopyName = function(dirEntry) { 255 GalleryItem.prototype.getCopyName = function(dirEntry) {
256 return new Promise(this.createCopyName_.bind( 256 return new Promise(this.createCopyName_.bind(
257 this, dirEntry, this.getNewMimeType_())); 257 this, dirEntry, this.getNewMimeType_()));
258 }; 258 };
259 259
260 /** 260 /**
261 * Writes the new item content to either the existing or a new file. 261 * Writes the new item content to either the existing or a new file.
262 * 262 *
263 * @param {!VolumeManagerWrapper} volumeManager Volume manager instance. 263 * @param {!VolumeManagerWrapper} volumeManager Volume manager instance.
264 * @param {!MetadataModel} metadataModel 264 * @param {!MetadataModel} metadataModel
265 * @param {!DirectoryEntry} fallbackDir Fallback directory in case the current 265 * @param {!DirectoryEntry} fallbackDir Fallback directory in case the current
266 * directory is read only. 266 * directory is read only.
267 * @param {!HTMLCanvasElement} canvas Source canvas. 267 * @param {!HTMLCanvasElement} canvas Source canvas.
268 * @param {boolean} overwrite Set true to overwrite original if it's possible. 268 * @param {boolean} overwrite Set true to overwrite original if it's possible.
269 * @param {function(boolean)} callback Callback accepting true for success. 269 * @param {function(boolean)} callback Callback accepting true for success.
270 */ 270 */
271 Gallery.Item.prototype.saveToFile = function( 271 GalleryItem.prototype.saveToFile = function(
272 volumeManager, metadataModel, fallbackDir, canvas, overwrite, callback) { 272 volumeManager, metadataModel, fallbackDir, canvas, overwrite, callback) {
273 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); 273 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime'));
274 var saveResultRecorded = false; 274 var saveResultRecorded = false;
275 275
276 Promise.all([this.getEntryToWrite_(overwrite, fallbackDir, volumeManager), 276 Promise.all([this.getEntryToWrite_(overwrite, fallbackDir, volumeManager),
277 this.getBlobForSave_(canvas, metadataModel)]).then(function(results) { 277 this.getBlobForSave_(canvas, metadataModel)]).then(function(results) {
278 // Write content to the entry. 278 // Write content to the entry.
279 var fileEntry = results[0]; 279 var fileEntry = results[0];
280 var blob = results[1]; 280 var blob = results[1];
281 281
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 345
346 /** 346 /**
347 * Returns file entry to write. 347 * Returns file entry to write.
348 * @param {boolean} overwrite True to overwrite original file. 348 * @param {boolean} overwrite True to overwrite original file.
349 * @param {!DirectoryEntry} fallbackDirectory Directory to fallback if current 349 * @param {!DirectoryEntry} fallbackDirectory Directory to fallback if current
350 * directory is not writable. 350 * directory is not writable.
351 * @param {!VolumeManagerWrapper} volumeManager 351 * @param {!VolumeManagerWrapper} volumeManager
352 * @return {!Promise<!FileEntry>} 352 * @return {!Promise<!FileEntry>}
353 * @private 353 * @private
354 */ 354 */
355 Gallery.Item.prototype.getEntryToWrite_ = function( 355 GalleryItem.prototype.getEntryToWrite_ = function(
356 overwrite, fallbackDirectory, volumeManager) { 356 overwrite, fallbackDirectory, volumeManager) {
357 return new Promise(function(resolve, reject) { 357 return new Promise(function(resolve, reject) {
358 // Since in-place editing is not supported on MTP volume, Gallery.app 358 // Since in-place editing is not supported on MTP volume, Gallery.app
359 // handles MTP volume as read only volume. 359 // handles MTP volume as read only volume.
360 if (this.locationInfo_.isReadOnly || 360 if (this.locationInfo_.isReadOnly ||
361 GalleryUtil.isOnMTPVolume(this.entry_, volumeManager)) { 361 GalleryUtil.isOnMTPVolume(this.entry_, volumeManager)) {
362 resolve(fallbackDirectory); 362 resolve(fallbackDirectory);
363 } else { 363 } else {
364 this.entry_.getParent(resolve, reject); 364 this.entry_.getParent(resolve, reject);
365 } 365 }
(...skipping 20 matching lines...) Expand all
386 }.bind(this)); 386 }.bind(this));
387 }; 387 };
388 388
389 /** 389 /**
390 * Returns blob to be saved. 390 * Returns blob to be saved.
391 * @param {!HTMLCanvasElement} canvas 391 * @param {!HTMLCanvasElement} canvas
392 * @param {!MetadataModel} metadataModel 392 * @param {!MetadataModel} metadataModel
393 * @return {!Promise<!Blob>} 393 * @return {!Promise<!Blob>}
394 * @private 394 * @private
395 */ 395 */
396 Gallery.Item.prototype.getBlobForSave_ = function(canvas, metadataModel) { 396 GalleryItem.prototype.getBlobForSave_ = function(canvas, metadataModel) {
397 return metadataModel.get( 397 return metadataModel.get(
398 [this.entry_], 398 [this.entry_],
399 ['mediaMimeType', 'contentMimeType', 'ifd', 'exifLittleEndian'] 399 ['mediaMimeType', 'contentMimeType', 'ifd', 'exifLittleEndian']
400 ).then(function(metadataItems) { 400 ).then(function(metadataItems) {
401 // Create the blob of new image. 401 // Create the blob of new image.
402 var metadataItem = metadataItems[0]; 402 var metadataItem = metadataItems[0];
403 metadataItem.modificationTime = new Date(); 403 metadataItem.modificationTime = new Date();
404 metadataItem.mediaMimeType = this.getNewMimeType_(); 404 metadataItem.mediaMimeType = this.getNewMimeType_();
405 var metadataEncoder = ImageEncoder.encodeMetadata( 405 var metadataEncoder = ImageEncoder.encodeMetadata(
406 metadataItem, canvas, /* quality for thumbnail*/ 0.8); 406 metadataItem, canvas, /* quality for thumbnail*/ 0.8);
407 // Contrary to what one might think 1.0 is not a good default. Opening 407 // Contrary to what one might think 1.0 is not a good default. Opening
408 // and saving an typical photo taken with consumer camera increases 408 // and saving an typical photo taken with consumer camera increases
409 // its file size by 50-100%. Experiments show that 0.9 is much better. 409 // its file size by 50-100%. Experiments show that 0.9 is much better.
410 // It shrinks some photos a bit, keeps others about the same size, but 410 // It shrinks some photos a bit, keeps others about the same size, but
411 // does not visibly lower the quality. 411 // does not visibly lower the quality.
412 return ImageEncoder.getBlob(canvas, metadataEncoder, 0.9); 412 return ImageEncoder.getBlob(canvas, metadataEncoder, 0.9);
413 }.bind(this)); 413 }.bind(this));
414 }; 414 };
415 415
416 /** 416 /**
417 * Renames the item. 417 * Renames the item.
418 * 418 *
419 * @param {string} displayName New display name (without the extension). 419 * @param {string} displayName New display name (without the extension).
420 * @return {!Promise} Promise fulfilled with when renaming completes, or 420 * @return {!Promise} Promise fulfilled with when renaming completes, or
421 * rejected with the error message. 421 * rejected with the error message.
422 */ 422 */
423 Gallery.Item.prototype.rename = function(displayName) { 423 GalleryItem.prototype.rename = function(displayName) {
424 var newFileName = this.entry_.name.replace( 424 var newFileName = this.entry_.name.replace(
425 ImageUtil.getDisplayNameFromName(this.entry_.name), displayName); 425 ImageUtil.getDisplayNameFromName(this.entry_.name), displayName);
426 426
427 if (newFileName === this.entry_.name) 427 if (newFileName === this.entry_.name)
428 return Promise.reject('NOT_CHANGED'); 428 return Promise.reject('NOT_CHANGED');
429 429
430 if (/^\s*$/.test(displayName)) 430 if (/^\s*$/.test(displayName))
431 return Promise.reject(str('ERROR_WHITESPACE_NAME')); 431 return Promise.reject(str('ERROR_WHITESPACE_NAME'));
432 432
433 var parentDirectoryPromise = new Promise( 433 var parentDirectoryPromise = new Promise(
434 this.entry_.getParent.bind(this.entry_)); 434 this.entry_.getParent.bind(this.entry_));
435 return parentDirectoryPromise.then(function(parentDirectory) { 435 return parentDirectoryPromise.then(function(parentDirectory) {
436 var nameValidatingPromise = 436 var nameValidatingPromise =
437 util.validateFileName(parentDirectory, newFileName, true); 437 util.validateFileName(parentDirectory, newFileName, true);
438 return nameValidatingPromise.then(function() { 438 return nameValidatingPromise.then(function() {
439 var existingFilePromise = new Promise(parentDirectory.getFile.bind( 439 var existingFilePromise = new Promise(parentDirectory.getFile.bind(
440 parentDirectory, newFileName, {create: false, exclusive: false})); 440 parentDirectory, newFileName, {create: false, exclusive: false}));
441 return existingFilePromise.then(function() { 441 return existingFilePromise.then(function() {
442 return Promise.reject(str('GALLERY_FILE_EXISTS')); 442 return Promise.reject(str('GALLERY_FILE_EXISTS'));
443 }, function() { 443 }, function() {
444 return new Promise( 444 return new Promise(
445 this.entry_.moveTo.bind(this.entry_, parentDirectory, newFileName)); 445 this.entry_.moveTo.bind(this.entry_, parentDirectory, newFileName));
446 }.bind(this)); 446 }.bind(this));
447 }.bind(this)); 447 }.bind(this));
448 }.bind(this)).then(function(entry) { 448 }.bind(this)).then(function(entry) {
449 this.entry_ = entry; 449 this.entry_ = entry;
450 }.bind(this)); 450 }.bind(this));
451 }; 451 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698