/*
* 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
*/
(function() {
'use strict';
window.app = window.app || {};
new ExceptionHandler();
/**
* Extension's redirect uri
* @type {string}
* @const
* @private
* @memberOf app.RedditSource
*/
const _REDIRECT_URI =
`https://${chrome.runtime.id}.chromiumapp.org/reddit`;
/**
* Reddit rest API authorization key
* @type {string}
* @const
* @private
* @memberOf app.RedditSource
*/
const _KEY = 'bATkDOUNW_tOlg';
/**
* Max photos to return
* @type {int}
* @const
* @default
* @private
* @memberOf app.RedditSource
*/
const _MAX_PHOTOS = 100;
/**
* Min size of photo to use
* @type {int}
* @const
* @default
* @private
* @memberOf app.RedditSource
*/
const _MIN_SIZE = 750;
/**
* Max size of photo to use
* @type {int}
* @const
* @default
* @private
* @memberOf app.RedditSource
*/
const _MAX_SIZE = 3500;
/**
* Expose reddit API
* @type {Function}
* @private
* @memberOf app.RedditSource
*/
let _snoocore;
/**
* A potential source of photos from reddit
* @alias app.RedditSource
*/
app.RedditSource = class extends app.PhotoSource {
/**
* Create a new photo source
* @param {string} useKey - The key for if the source is selected
* @param {string} photosKey - The key for the collection of photos
* @param {string} type - A descriptor of the photo source
* @param {string} desc - A human readable description of the source
* @param {boolean} isDaily - Should the source be updated daily
* @param {boolean} isArray - Is the source an Array of photo Arrays
* @param {?Object} [loadArg=null] - optional arg for load function
* @constructor
*/
constructor(useKey, photosKey, type, desc, isDaily, isArray,
loadArg = null) {
super(useKey, photosKey, type, desc, isDaily, isArray, loadArg);
}
/**
* Parse the size from the submission title.
* this is the old way reddit did it
* @param {string} title - submission title
* @returns {{width: int, height: int}} Photo size
* @private
*/
static _getSize(title) {
const ret = {width: -1, height: -1};
const regex = /\[(\d*)\D*(\d*)\]/;
const res = title.match(regex);
if (res) {
ret.width = parseInt(res[1], 10);
ret.height = parseInt(res[2], 10);
}
return ret;
}
/**
* Build the list of photos for one page of items
* @param {Array} children - Array of objects from reddit
* @returns {app.PhotoSource.Photo[]} Array of photos
* @private
*/
static _processChildren(children) {
const photos = [];
let url;
let width = 1;
let height = 1;
for (const child of children) {
const data = child.data;
if (!data.over_18) {
// skip NSFW
if (data.preview && data.preview.images) {
// new way. has full size image and array of reduced
// resolutions
let item = data.preview.images[0];
url = item.source.url;
width = parseInt(item.source.width, 10);
height = parseInt(item.source.height, 10);
if (Math.max(width, height) > _MAX_SIZE) {
// too big. get the largest reduced resolution image
item = item.resolutions[item.resolutions.length - 1];
url = item.url.replace(/&/g, '&');
width = parseInt(item.width, 10);
height = parseInt(item.height, 10);
}
} else if (data.title) {
// old way of specifying images - parse size from title
const size = app.RedditSource._getSize(data.title);
url = data.url;
width = size.width;
height = size.height;
}
}
const asp = width / height;
const author = data.author;
if (asp && !isNaN(asp) && (Math.max(width, height) >= _MIN_SIZE) &&
(Math.max(width, height) <= _MAX_SIZE)) {
app.PhotoSource.addPhoto(photos, url, author, asp, data.url);
}
}
return photos;
}
/**
* Fetch the photos for this source
* @returns {Promise<app.PhotoSource.Photo[]>} Array of photos
*/
fetchPhotos() {
let photos = [];
if (!_snoocore) {
return Promise.reject(new Error('Snoocore library failed to load'));
}
return _snoocore(`${this._loadArg}hot`).listing({
limit: _MAX_PHOTOS,
}).then((slice) => {
photos =
photos.concat(app.RedditSource._processChildren(slice.children));
return slice.next();
}).then((slice) => {
photos =
photos.concat(app.RedditSource._processChildren(slice.children));
return Promise.resolve(photos);
}).catch((err) => {
let msg = err.message;
if (msg) {
// extract first sentence
const idx = msg.indexOf('.');
if (idx !== -1) {
msg = msg.substring(0, idx + 1);
}
} else {
msg = 'Unknown Error';
}
return Promise.reject(new Error(msg));
});
}
};
/**
* Event: called when document and resources are loaded
* @private
* @memberOf Background
*/
function _onLoad() {
try {
const Snoocore = window.Snoocore;
if (typeof Snoocore !== 'undefined') {
_snoocore = new Snoocore({
userAgent: 'photo-screen-saver',
throttle: 0,
oauth: {
type: 'implicit',
key: _KEY,
redirectUri: _REDIRECT_URI,
scope: ['read'],
},
});
}
} catch (ex) {
Chrome.GA.exception(ex, 'Snoocore library failed to load', false);
_snoocore = null;
}
}
// listen for document and resources loaded
window.addEventListener('load', _onLoad);
})(window);