/*
* Copyright (c) 2015-2017, Michael A. Updike All rights reserved.
* Licensed under the BSD-3-Clause
* https://opensource.org/licenses/BSD-3-Clause
* https://github.com/opus1269/photo-screen-saver/blob/master/LICENSE.md
*/
window.app = window.app || {};
/**
* Collection of {@link app.SSView} objects
* @namespace
*/
app.SSViews = (function() {
'use strict';
new ExceptionHandler();
/**
* Max number of views
* @type {int}
* @const
* @private
* @memberOf app.SSViews
*/
const _MAX_VIEWS = 20;
/**
* The array of views
* @type {Array<app.SSView>}
* @const
* @private
* @memberOf app.SSViews
*/
const _views = [];
/**
* The neon-animated-pages
* @type {?Element}
* @private
* @memberOf app.SSViews
*/
let _pages = null;
/**
* Enum for view type
* @typedef {int} app.SSViews.Type
* @readonly
* @enum {int}
* @memberOf app.SSViews
*/
const Type = {
UNDEFINED: -1,
LETTERBOX: 0,
ZOOM: 1,
FRAME: 2,
FULL: 3,
RANDOM: 4,
};
/**
* The view type
* @type {int}
* @private
* @memberOf app.SSViews
*/
let _type = Type.UNDEFINED;
/**
* Process settings related to the photo's appearance
* @private
* @memberOf app.SSViews
*/
function _setViewType() {
_type = Chrome.Storage.getInt('photoSizing', 0);
if (_type === Type.RANDOM) {
// pick random sizing
_type = Chrome.Utils.getRandomInt(0, 3);
}
let type = 'contain';
switch (_type) {
case Type.LETTERBOX:
type = 'contain';
break;
case Type.ZOOM:
type = 'cover';
break;
case Type.FRAME:
case Type.FULL:
type = null;
break;
default:
break;
}
app.Screensaver.setSizingType(type);
}
return {
Type: Type,
/**
* Create the {@link app.SSView} pages
* @param {app.Screensaver.Template} t
* @memberOf app.SSViews
*/
create: function(t) {
_pages = t.$.pages;
_setViewType();
const len = Math.min(app.SSPhotos.getCount(), _MAX_VIEWS);
for (let i = 0; i < len; i++) {
const photo = app.SSPhotos.get(i);
const view = app.SSView.createView(photo, _type);
_views.push(view);
}
app.SSPhotos.setCurrentIndex(len);
// set and force render of animated pages
t.set('_views', _views);
t.$.repeatTemplate.render();
// set the Elements of each view
_views.forEach((view, index) => {
const el = _pages.querySelector('#view' + index);
const image = el.querySelector('.image');
const author = el.querySelector('.author');
const time = el.querySelector('.time');
const location = el.querySelector('.location');
const model = t.$.repeatTemplate.modelForElement(el);
view.setElements(image, author, time, location, model);
});
},
/**
* Get the type of view
* @returns {app.SSViews.Type}
* @memberOf app.SSViews
*/
getType: function() {
if (_type === Type.UNDEFINED) {
_setViewType();
}
return _type;
},
/**
* Get number of views
* @returns {int}
* @memberOf app.SSViews
*/
getCount: function() {
return _views.length;
},
/**
* Get the {@link app.SSView} at the given index
* @param {int} idx - The index
* @returns {app.SSView}
* @memberOf app.SSViews
*/
get: function(idx) {
return _views[idx];
},
/**
* Get the selected index
* @returns {int|undefined} The index
* @memberOf app.SSViews
*/
getSelectedIndex: function() {
if (_pages) {
return _pages.selected;
}
// noinspection UnnecessaryReturnStatementJS
return;
},
/**
* Set the selected index
* @param {int} selected
* @memberOf app.SSViews
*/
setSelectedIndex: function(selected) {
_pages.selected = selected;
},
/**
* Is the given idx the selected index
* @param {int} idx - index into {@link app.SSViews}
* @returns {boolean} true if selected
* @memberOf app.SSViews
*/
isSelectedIndex: function(idx) {
let ret = false;
if (_pages && (idx === _pages.selected)) {
ret = true;
}
return ret;
},
/**
* Is the given {@link app.SSPhoto} in one of the {@link _views}
* @param {app.SSPhoto} photo
* @returns {boolean} true if in {@link _views}
* @memberOf app.SSViews
*/
hasPhoto: function(photo) {
let ret = false;
for (const view of _views) {
if (view.photo.getId() === photo.getId()) {
ret = true;
break;
}
}
return ret;
},
/**
* Do we have a view with a usable photo
* @returns {boolean} true if at least one photo is valid
* @memberOf app.SSViews
*/
hasUsable: function() {
let ret = false;
for (let i = 0; i < _views.length; i++) {
const view = _views[i];
if (app.SSRunner.isCurrentPair(i)) {
// don't check current animation pair
continue;
}
if (!view.photo.isBad()) {
ret = true;
break;
}
}
return ret;
},
/**
* Replace all views
* @memberOf app.SSViews
*/
replaceAll: function() {
for (let i = 0; i < _views.length; i++) {
if (app.SSRunner.isCurrentPair(i)) {
// don't replace current animation pair
continue;
}
const view = _views[i];
const photo = app.SSPhotos.getNextUsable();
if (photo) {
view.setPhoto(photo);
} else {
// all bad
break;
}
}
app.SSHistory.clear();
},
/**
* Try to find a photo that has finished loading
* @param {int} idx - index into {@link _views}
* @returns {int} index into {@link _views}, -1 if none are loaded
* @memberOf app.SSViews
*/
findLoadedPhoto: function(idx) {
if (!app.SSViews.hasUsable()) {
// replace the photos
app.SSViews.replaceAll();
}
if (_views[idx].isLoaded()) {
return idx;
}
// wrap-around loop: https://stackoverflow.com/a/28430482/4468645
for (let i = 0; i < _views.length; i++) {
const index = (i + idx) % _views.length;
const view = _views[index];
if (app.SSRunner.isCurrentPair(index)) {
// don't use current animation pair
continue;
}
if (view.isLoaded()) {
return index;
} else if (view.isError() && !view.photo.isBad()) {
view.photo.markBad();
if (!app.SSPhotos.hasUsable()) {
// all photos bad
app.Screensaver.setNoPhotos();
return -1;
}
}
}
return -1;
},
};
})();