2016-10-10 11:49:08 +00:00
|
|
|
var _ = require('lodash');
|
|
|
|
var index = 0;
|
|
|
|
|
|
|
|
// number of images to show in the album preview grid
|
2016-11-02 00:35:27 +00:00
|
|
|
var PREVIEW_COUNT = 4;
|
2016-10-10 11:49:08 +00:00
|
|
|
|
|
|
|
var SORT_ALBUMS_BY = {
|
2016-11-03 05:00:50 +00:00
|
|
|
'title': function(album) { return album.title; },
|
|
|
|
'start-date': function(album) { return album.stats.fromDate; },
|
|
|
|
'end-date': function(album) { return album.stats.toDate; }
|
2016-10-10 11:49:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
var SORT_MEDIA_BY = {
|
2016-11-03 05:00:50 +00:00
|
|
|
'filename': function(file) { return file.filename; },
|
|
|
|
'date': function(file) { return file.date; }
|
2016-10-10 11:49:08 +00:00
|
|
|
};
|
|
|
|
|
2016-10-16 11:24:07 +00:00
|
|
|
var PREVIEW_MISSING = {
|
|
|
|
urls: {
|
|
|
|
thumb: 'public/missing.png'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-10-10 11:49:08 +00:00
|
|
|
function Album(opts) {
|
|
|
|
if (typeof opts === 'string') opts = { title: opts };
|
2016-11-02 00:35:27 +00:00
|
|
|
this.id = opts.id || ++index;
|
2016-10-26 10:56:20 +00:00
|
|
|
this.title = opts.title || ('Album ' + this.id);
|
2016-10-10 11:49:08 +00:00
|
|
|
this.filename = sanitise(this.title);
|
|
|
|
this.files = opts.files || [];
|
|
|
|
this.albums = opts.albums || [];
|
|
|
|
this.depth = 0;
|
2016-10-13 12:13:04 +00:00
|
|
|
this.home = false;
|
2016-10-10 11:49:08 +00:00
|
|
|
this.stats = null;
|
|
|
|
this.previews = null;
|
2016-11-02 00:35:27 +00:00
|
|
|
this.allFiles = [];
|
2016-10-10 11:49:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Album.prototype.finalize = function(options) {
|
2016-10-13 12:13:04 +00:00
|
|
|
// is this the top-level album?
|
|
|
|
this.home = this.depth === 0;
|
2016-10-16 11:24:07 +00:00
|
|
|
// finalize all nested albums first (recursive)
|
2016-10-10 11:49:08 +00:00
|
|
|
// and set a nested filename
|
|
|
|
for (var i = 0; i < this.albums.length; ++i) {
|
|
|
|
this.albums[i].filename = this.filename + '-' + this.albums[i].filename;
|
|
|
|
this.albums[i].depth = this.depth + 1;
|
|
|
|
this.albums[i].finalize();
|
|
|
|
}
|
2016-10-16 11:24:07 +00:00
|
|
|
// perform stats & other calculations
|
|
|
|
// once the nested albums have been finalized too
|
2016-10-10 11:49:08 +00:00
|
|
|
this.calculateStats();
|
|
|
|
this.calculateSummary();
|
|
|
|
this.sort(options);
|
|
|
|
this.pickPreviews();
|
2016-11-02 00:35:27 +00:00
|
|
|
this.aggregateAllFiles();
|
2016-10-10 11:49:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Album.prototype.calculateStats = function() {
|
|
|
|
// nested albums
|
|
|
|
var nestedPhotos = _.map(this.albums, 'stats.photos');
|
|
|
|
var nestedVideos = _.map(this.albums, 'stats.videos');
|
|
|
|
var nestedFromDates = _.map(this.albums, 'stats.fromDate');
|
|
|
|
var nestedToDates = _.map(this.albums, 'stats.toDate');
|
|
|
|
// current level
|
|
|
|
var currentPhotos = _.filter(this.files, {isVideo: false}).length;
|
|
|
|
var currentVideos = _.filter(this.files, {isVideo: true}).length;
|
|
|
|
var currentFromDate = _.map(this.files, 'date');
|
|
|
|
var currentToDate = _.map(this.files, 'date');
|
|
|
|
// aggregate all stats
|
|
|
|
this.stats = {
|
|
|
|
albums: this.albums.length,
|
|
|
|
photos: _.sum(_.compact(_.concat(nestedPhotos, currentPhotos))) || 0,
|
|
|
|
videos: _.sum(_.compact(_.concat(nestedVideos, currentVideos))) || 0,
|
|
|
|
fromDate: _.min(_.compact(_.concat(nestedFromDates, currentFromDate))),
|
|
|
|
toDate: _.max(_.compact(_.concat(nestedToDates, currentToDate)))
|
|
|
|
};
|
|
|
|
this.stats.total = this.stats.photos + this.stats.videos;
|
|
|
|
}
|
|
|
|
|
|
|
|
Album.prototype.calculateSummary = function() {
|
|
|
|
var items = [
|
|
|
|
itemCount(this.stats.albums, 'album'),
|
|
|
|
itemCount(this.stats.photos, 'photo'),
|
|
|
|
itemCount(this.stats.videos, 'video')
|
|
|
|
];
|
|
|
|
this.summary = _.compact(items).join(', ');
|
|
|
|
};
|
|
|
|
|
|
|
|
Album.prototype.sort = function(options) {
|
2016-11-03 05:00:50 +00:00
|
|
|
this.files = _.orderBy(this.files, SORT_MEDIA_BY[options.sortMediaBy], options.sortMediaDirection);
|
|
|
|
this.albums = _.orderBy(this.albums, SORT_ALBUMS_BY[options.sortAlbumsBy], options.sortAlbumsDirection);
|
2016-10-10 11:49:08 +00:00
|
|
|
this.albums.forEach(function(nested) {
|
|
|
|
nested.sort(options);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
Album.prototype.pickPreviews = function() {
|
2016-10-16 11:24:07 +00:00
|
|
|
// also consider previews from nested albums
|
|
|
|
var nestedPicks = _.flatten(_.map(this.albums, 'previews')).filter(function(file) {
|
|
|
|
return file !== PREVIEW_MISSING;
|
|
|
|
});
|
|
|
|
// then pick the top 4 overall
|
|
|
|
var potentialPicks = _.concat(this.files, nestedPicks);
|
|
|
|
this.previews = potentialPicks.slice(0, PREVIEW_COUNT);
|
|
|
|
// and fill the gap with a placeholder
|
2016-10-10 11:49:08 +00:00
|
|
|
var missing = PREVIEW_COUNT - this.previews.length;
|
|
|
|
for (var i = 0; i < missing; ++i) {
|
2016-10-16 11:24:07 +00:00
|
|
|
this.previews.push(PREVIEW_MISSING);
|
2016-10-10 11:49:08 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-02 00:35:27 +00:00
|
|
|
Album.prototype.aggregateAllFiles = function() {
|
|
|
|
var nestedFiles = _.flatten(_.map(this.albums, 'allFiles'))
|
|
|
|
this.allFiles = _.concat(nestedFiles, this.files);
|
|
|
|
};
|
|
|
|
|
2016-10-10 11:49:08 +00:00
|
|
|
function sanitise(filename) {
|
|
|
|
return filename.replace(/[^a-z0-9-_]/ig, '');
|
|
|
|
}
|
|
|
|
|
|
|
|
function itemCount(count, type) {
|
|
|
|
if (count === 0) return '';
|
|
|
|
var plural = (count > 1) ? 's' : '';
|
|
|
|
return '' + count + ' ' + type + plural;
|
|
|
|
}
|
|
|
|
|
2016-11-02 00:35:27 +00:00
|
|
|
// for testing purposes
|
|
|
|
Album.resetIds = function() {
|
|
|
|
index = 0;
|
|
|
|
};
|
|
|
|
|
2016-10-10 11:49:08 +00:00
|
|
|
module.exports = Album;
|