UNPKG

forty2

Version:

Collection of libraries that answer the ultimate question to the meaning of life

241 lines (194 loc) 9.22 kB
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 };