forty2
Version:
Collection of libraries that answer the ultimate question to the meaning of life
241 lines (194 loc) • 9.22 kB
JavaScript
import {extend as extend, throwError as throwError, tryCatch as tryCatch} from './utils.js';
import {promise as promise} from './promise.js';
import {browser as browser} from './browser.js';
let tagList = [];
/**
* @function asyncLoad
* Lazy loader for an exhaustive list of file types
* @param {string} file File name (and path)
* @param {string} [fileType=js] The file type to load. Options = ['js', 'css']
* @param {boolean} [useFiletype=true] Determines whether or not a file extension should be added to the filename
* useFiletype can also be used in second position instead of fileType
* @returns {promise}
*/
const asyncLoad = (params = {}) => {
if(!params.src) {
throwError('No valid source was provided for lazy loading');
}
params.type = (params.type) ? params.type : fileExtension(params.src);
if(!params.type) {
throwError('The file type to lazy load is not valid');
}
let idString = params.src.split('/').pop().replace(/\./g, '');
params.id = (params.id) ?
params.id : (idString.length > 20) ?
'async_'+ idString.substring(0,20) : 'async_'+ idString;
let loaderPromise = promise(),
fileTypes = {
'js':function() {
setTimeout(function() {
let existingFile = fileExists(params, 'js');
if(!params.force && existingFile !== false) {
if(existingFile.loaderPromise) {
existingFile.loaderPromise.then(function() {
existingFile.loaderPromise.then(function() {
loaderPromise.resolve({existedAlready:true, source:existingFile});
});
});
}
else {
setTimeout(function() {
loaderPromise.resolve({existedAlready:true, source:existingFile});
}, 50);
}
return true;
}
if(params.force && existingFile !== false) {
unload(params, 'js');
}
let script = document.createElement('script'),
isLoaded = false;
script.id = params.id;
if(typeof params.contents === 'string') {
script.innerHTML = params.contents;
}
script.setAttribute('type', 'text/javascript');
let resolver = () => {
let justInCaseDelay = 50;
setTimeout(function() {
if(isLoaded === false) {
isLoaded = true;
!!window.console && console.info(`Script successfully loaded: ${params.src}`);
loaderPromise.resolve({existedAlready:false, source:script});
}
}, justInCaseDelay);
};
if(script.readyState) {
script.onreadystatechange = function(event) {
if(script.readyState === 'loaded' || script.readyState === 'complete') {
resolver();
}
}
}
else {
script.onload = function() {
resolver();
}
}
script.onerror = function(error) {
!!window.console && console.error(`Unable to load ${params.src}: ${error.toString()}`);
loaderPromise.reject(error);
};
script.loaderPromise = loaderPromise;
script.src = params.src;
document.getElementsByTagName('head')[0].appendChild(script);
}, 0);
},
/**
* Pretty hacky, but the only cross browser cross device solution
* that will let you resolve a promise after the CSS has
* finished loading
*/
'css':function() {
let existingFile = fileExists(params, 'css');
if(!params.force && existingFile !== false) {
loaderPromise.resolve(existingFile);
return true;
}
if(params.force && existingFile !== false) {
unload(params, 'css');
}
let style = document.createElement('style');
style.id = params.id;
style.type = 'text/css';
style.innerHTML = '@import "'+ fullFilePath +'"';
document.head.appendChild(style);
let cssReady = setInterval(function() {
tryCatch({
'try':function() {
clearInterval(cssReady);
loaderPromise.resolve(style);
}
});
}, 20);
},
'jsonp':function() {
params.callback = params.callback || `RTL.apixl.jsonpCall_${Date.now()}`;
let jsonpPromise = promise(),
callbackFunction = genrateCallbackFunction(params.callback, jsonpPromise);
this['js'](callbackFunction);
return jsonpPromise;
}
};
!!fileTypes[params.type] && fileTypes[params.type]();
return loaderPromise;
},
/**
* @function unload - remove any tag that has an id, src or href matching the fileId
* @param fileId The ID of the file
* @returns {promise} unloaderPromise
*/
unload = (params = {}) => {
let types = {
'js':() => {
let target = document.querySelector(`[src="${params.src}"]`);
return (target) ? target.parentNode.removeChild(target) : `Requested element with src ${params.src} could not be found`;
},
'css':() => {
let target = document.querySelector(`[href="${params.src}"]`);
return (target) ? target.parentNode.removeChild(target) : `Requested element with href ${params.src} could not be found`;
}
};
if(params.id) {
let target = document.getElementById(params.id);
return (target) ? target.parentNode.removeChild(target) : `Requested element with ID ${params.id} could not be found`;
}
else {
return !!types[type] && types[type]();
}
};
function fileExtension(src) {
let match = src.match(/(\.js)$|(\.css)$|(\.js\?[\w|\W]{1,}$)|(\.css\?[\w|\W]{1,}$)/g);
return (match) ? match[0].split('?')[0].replace('.', '') : false;
}
function genrateCallbackFunction(callbackString, jsonpPromise) {
let callbackFunction = window,
callbackArray = callbackString.split('.'),
method = callbackArray.pop();
callbackArray.map(function(method) {
callbackFunction = callbackFunction[method] || {};
});
callbackFunction[method] = function() {
return jsonpPromise.resolve(arguments);
};
return callbackFunction;
}
function fileExists(params, type) {
let fileTypes = {
'js':function() {
let scripts = document.getElementsByTagName('script');
for(let i = 0, iMax = scripts.length; i < iMax; i++) {
let sameSource = !!scripts[i].src && scripts[i].src.replace(/(\w){1,}(:\/\/)/, '') === params.src.replace(/(\w){1,}(:\/\/)/, ''),
sameId = !!scripts[i].src && !!scripts[i].id && scripts[i].id === params.id;
if(sameSource || sameId) {
return scripts[i];
}
}
return false;
},
'css':function() {
let styles = document.getElementsByTagName('link');
for(let i = 0, iMax = styles.length; i < iMax; i++) {
let sameSource = !!styles[i].href && styles[i].href.replace(/(\w){1,}(:\/\/)/, '') === params.src.replace(/(\w){1,}(:\/\/)/, ''),
sameId = !!styles[i].href && !!styles[i].id && styles[i].id === params.id;
if(sameSource || sameId) {
return styles[i];
}
}
return false;
}
};
var forcedLayout = document.documentElement.offsetHeight;
return fileTypes[type]();
}
export { asyncLoad, unload };