goog.module('nbsrc.provider.MediaProvider');
const Completer = goog.require('clulib.async.Completer');
const XhrIo = goog.require('goog.net.XhrIo');
const ResponseType = goog.require('goog.net.XhrIo.ResponseType');
const {listen} = goog.require('goog.events');
const ImageLoader = goog.require('goog.net.ImageLoader');
const EventTarget = goog.require('goog.events.EventTarget');
const EventType = goog.require('goog.events.EventType');
const NetEventType = goog.require('goog.net.EventType');
/**
* When the window.devicePixelRatio is greater than 1 use retina images.
* @type {boolean}
* @const
*/
const useRetinaImages = window.devicePixelRatio > 1;
/**
* This provider (singleton) takes care of loading, parsing and providing external media content (image, json).
* @extends {EventTarget}
*/
class MediaProvider extends EventTarget
{
constructor()
{
super();
/**
* Defines if all load promises should be stored in the source list.
* @type {boolean}
* @private
*/
this.autoStoreSources_ = true;
/**
* List of stored loading promises, where key ist the file path.
* @type {Map<string,Promise>}
* @private
*/
this.sources_ = new Map();
/**
* Relative default asset path.
* @type {string}
* @private
*/
this.mediaAssetPath_ = 'asset/';
/**
* It is an absolute asset URL if the variable `window.ASSET_URL` is set, otherwise it is the relative {@Link MediaProvider#mediaAssetPath_}.
* @type {string}
* @private
*/
this.mediaAssetUrl_ = window['ASSETS_URL'] ? window['ASSETS_URL'] + this.mediaAssetPath_ : this.mediaAssetPath_;
}
/**
* Loads a media source and returns a promise that is fulfilled when the source is loaded.
* @param {Array<string>|string} source path
* @param {string=} additionalPathSegment additional path segment between {@Link MediaProvider#mediaAssetPath_} and the given source.
* @param {string|null=} loadType Loading type like json or blob. If nothing is specified, the file extension is analyzed to determine the loading method.
* @param {boolean=} storeSource Store loading promise in the {@Link MediaProvider#sources_} list.
* @param {boolean=} preventCDNService Prevent loading over a cdn url, which is part of {@Link MediaProvider#mediaAssetUrl_}. Loading path will be relative stating with {@Link MediaProvider#mediaAssetPath_}.
* @return {Promise}
*/
load(source, additionalPathSegment = '', loadType = null, storeSource = false, preventCDNService = false)
{
let promises = [];
if(typeof source == 'string')
{
let fileType = source.split('.').pop();
if(additionalPathSegment.length > 0 && additionalPathSegment.substr(additionalPathSegment.length-1) != '/')
additionalPathSegment+='/';
let url = additionalPathSegment+source;
if(this.sources_.has(url))
return this.sources_.get(url);
let responseType = this.getResponseType_(loadType);
loadType = loadType || fileType;
let promise = Promise.resolve();
switch(loadType)
{
case 'json':
promise = this.loadJson_(url, preventCDNService);
break;
case 'svg':
case 'png':
case 'jpg':
case 'gif':
promise = this.loadImage_(url, preventCDNService);
break;
default:
promise = this.load_(url, responseType, preventCDNService);
break;
}
if(storeSource === true || this.autoStoreSources_ === true)
this.sources_.set(url, promise);
return promise;
}
else if(Array.isArray(source))
{
source.forEach((url) => {
promises.push(this.load(url, additionalPathSegment, loadType, storeSource, preventCDNService));
})
}
return Promise.all(promises);
}
/**
* Gets the response type by the given load type
* @param loadType
* @return {string}
* @private
*/
getResponseType_(loadType)
{
if(loadType == 'blob')
return ResponseType.BLOB;
else
return ResponseType.DEFAULT;
}
/**
* Loading an image and returns a promise that is fulfilled when the source is loaded.
* @param {string} url
* @param {boolean=} preventCDNService Prevent loading over a cdn url, which is part of {@Link MediaProvider#mediaAssetUrl_}. Loading path will be relative stating with {@Link MediaProvider#mediaAssetPath_}.
* @return {Promise}
* @private
*/
loadImage_(url, preventCDNService = false)
{
let completer = new Completer();
let imageLoader = new ImageLoader();
let sourcePath = preventCDNService ? this.mediaAssetPath_ + url : this.mediaAssetUrl_ + url;
imageLoader.addImage(url, sourcePath);
listen(imageLoader, EventType.LOAD, (event) => {
completer.resolve(event.target);
}, false, this);
listen(imageLoader, EventType.ERROR, () => {
completer.reject('Couldn\'t load source ' + url + '.');
}, false, this);
imageLoader.start();
return completer.getPromise();
}
/**
* Loading a json and parse the result as a json object. Returns a promise that is fulfilled when the source is loaded.
* @param {string} url Source path
* @param {boolean=} preventCDNService Prevent loading over a cdn url, which is part of {@Link MediaProvider#mediaAssetUrl_}. Loading path will be relative stating with {@Link MediaProvider#mediaAssetPath_}.
* @return {Promise}
* @private
*/
loadJson_(url, preventCDNService = false)
{
return this.load_(url, ResponseType.DEFAULT, preventCDNService).then((result) => {
return JSON.parse(result);
});
}
/**
* Loading a source and returns a promise that is fulfilled when the source is loaded.
* @param {string} url Source path
* @param {string=} responseType Optional xhr response type
* @param {boolean=} preventCDNService Prevent loading over a cdn url, which is part of {@Link MediaProvider#mediaAssetUrl_}. Loading path will be relative stating with {@Link MediaProvider#mediaAssetPath_}.
* @return {Promise}
* @private
*/
load_(url, responseType = '', preventCDNService = false)
{
let completer = new Completer();
let request = new XhrIo();
request.setResponseType(/** @type {ResponseType} */ (responseType));
listen(request, NetEventType.SUCCESS, (event) => {
completer.resolve(event.currentTarget.getResponse());
}, false, this);
listen(request, EventType.ERROR, () => {
completer.reject('Couldn\'t load source ' + url + '.');
}, false, this);
let sourcePath = preventCDNService ? this.mediaAssetPath_ + url : this.mediaAssetUrl_ + url;
request.send(sourcePath);
return completer.getPromise();
}
/**
* Retrieve a source by key (file path). If the source has not been loaded yet, load it.
* @param {string} key
* @return {Promise}
*/
getSource(key)
{
if(this.sources_.has(key))
{
return this.sources_.get(key);
}
else if(this.autoStoreSources_ === true)
{
return this.load(key);
}
return Promise.reject('No source for '+key+' was loaded!');
}
/**
* Setter
* @param {boolean} autoStoreSources
*/
set autoStoreSources(autoStoreSources)
{
this.autoStoreSources_ = autoStoreSources;
}
/**
* Getter
* @return {boolean}
*/
get autoStoreSources()
{
return this.autoStoreSources_;
}
/**
* Setter
* @param {string} mediaAssetPath
*/
set mediaAssetPath(mediaAssetPath)
{
this.mediaAssetPath_ = mediaAssetPath;
this.mediaAssetUrl_ = window['ASSETS_URL'] ? window['ASSETS_URL']+mediaAssetPath : mediaAssetPath;
}
/**
* Getter
* @return {string}
*/
get mediaAssetPath()
{
return this.mediaAssetPath_;
}
/**
* Getter
* @return {string}
*/
get mediaAssetUrl()
{
return this.mediaAssetUrl_;
}
}
goog.addSingletonGetter(MediaProvider);
exports = {MediaProvider, useRetinaImages};