blob: 837d1dd9e558a4bf3940ab33050e29e9e38617e0 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Data model for gallery.
*
* @param {!MetadataModel} metadataModel
* @param {!EntryListWatcher=} opt_watcher Entry list watcher.
* @constructor
* @extends {cr.ui.ArrayDataModel}
*/
function GalleryDataModel(metadataModel, opt_watcher) {
cr.ui.ArrayDataModel.call(this, []);
/**
* File system metadata.
* @private {!MetadataModel}
* @const
*/
this.metadataModel_ = metadataModel;
/**
* Directory where the image is saved if the image is located in a read-only
* volume.
* @public {DirectoryEntry}
*/
this.fallbackSaveDirectory = null;
// Start to watch file system entries.
var watcher = opt_watcher ? opt_watcher : new EntryListWatcher(this);
watcher.getEntry = function(item) {
return item.getEntry();
};
this.addEventListener('splice', this.onSplice_.bind(this));
}
/**
* Maximum number of full size image cache.
* @type {number}
* @const
* @private
*/
GalleryDataModel.MAX_FULL_IMAGE_CACHE_ = 3;
GalleryDataModel.prototype = {
__proto__: cr.ui.ArrayDataModel.prototype
};
/**
* Saves new image.
*
* @param {!VolumeManager} volumeManager Volume manager instance.
* @param {!GalleryItem} item Original gallery item.
* @param {!HTMLCanvasElement} canvas Canvas containing new image.
* @param {boolean} overwrite Set true to overwrite original if it's possible.
* @return {!Promise} Promise to be fulfilled with when the operation completes.
*/
GalleryDataModel.prototype.saveItem = function(
volumeManager, item, canvas, overwrite) {
var oldEntry = item.getEntry();
var oldLocationInfo = item.getLocationInfo();
var oldIsOriginal = item.isOriginal();
return new Promise(function(fulfill, reject) {
item.saveToFile(
volumeManager,
this.metadataModel_,
assert(this.fallbackSaveDirectory),
canvas,
overwrite,
function(success) {
if (!success) {
reject('Failed to save the image.');
return;
}
// Current entry is updated.
// Dispatch an event.
var event = new Event('content');
event.item = item;
event.oldEntry = oldEntry;
event.thumbnailChanged = true;
this.dispatchEvent(event);
if (!util.isSameEntry(oldEntry, item.getEntry())) {
Promise.all([
this.metadataModel_.get(
[oldEntry], GalleryItem.PREFETCH_PROPERTY_NAMES),
new ThumbnailModel(this.metadataModel_).get([oldEntry])
]).then(function(itemLists) {
// New entry is added and the item now tracks it.
// Add another item for the old entry.
var anotherItem = new GalleryItem(
oldEntry,
oldLocationInfo,
itemLists[0][0],
itemLists[1][0],
oldIsOriginal);
// The item must be added behind the existing item so that it does
// not change the index of the existing item.
// TODO(hirono): Update the item index of the selection model
// correctly.
this.splice(this.indexOf(item) + 1, 0, anotherItem);
}.bind(this)).then(fulfill, reject);
} else {
fulfill();
}
}.bind(this));
}.bind(this));
};
/**
* Evicts image caches in the items.
*/
GalleryDataModel.prototype.evictCache = function() {
// Sort the item by the last accessed date.
var sorted = this.slice().sort(function(a, b) {
return b.getLastAccessedDate() - a.getLastAccessedDate();
});
// Evict caches.
var contentCacheCount = 0;
var screenCacheCount = 0;
for (var i = 0; i < sorted.length; i++) {
if (sorted[i].contentImage) {
if (++contentCacheCount > GalleryDataModel.MAX_FULL_IMAGE_CACHE_) {
if (sorted[i].contentImage.parentNode) {
console.error('The content image has a parent node.');
} else {
// Force to free the buffer of the canvas by assigning zero size.
sorted[i].contentImage.width = 0;
sorted[i].contentImage.height = 0;
sorted[i].contentImage = null;
}
}
}
}
};
/**
* Handles entry delete.
* @param {!Event} event
* @private
*/
GalleryDataModel.prototype.onSplice_ = function(event) {
if (!event.removed || !event.removed.length) {
return;
}
var removedURLs = event.removed.map(function(item) {
return item.getEntry().toURL();
});
this.metadataModel_.notifyEntriesRemoved(removedURLs);
};