import Promise = require ( "bluebird" ) ; import "core-js" ; import { assignIn , forEach } from "lodash" ; export interface ScriptState { _callbacks ? : Array < ( ) => void > ; hasLoaded : boolean ; wasRejected : boolean ; error ? : any ; promise : Promise < ScriptState > ; tag : HTMLScriptElement ; } type { Map < string , ScriptState > } export const loadedScripts = new Map < string , ScriptState > ( ) ; type { { } } export const scriptTags = { } ; export interface Scripts { [ name ] : string ; } export type Callback = ( error : any , result ? : any ) => void ; export type AllCallback = ( errors : any [ ] , results ? : any [ ] ) => void ; export interface ScriptTag { name : string ; script : string ; tag : HTMLScriptElement ; onLoad ( callback ) : void ; } export interface ScriptTags { [ name ] : ScriptTag ; } param scripts { Scripts } export function cache ( scripts ) { forEach ( scripts , ( script , name ) => { assignIn ( scriptTags , { [ name ] : { name , onLoad : onLoad . bind ( null , name ) , script , tag : getScript ( script , name ) , } , } ) ; } ) ; } export function getScriptStub ( name ) { return scriptTags [ name ] ; } param name { string } param callback { Callback } export function onLoad ( name , callback ) { const stored = loadedScripts . get ( name ) ; if ( stored . hasLoaded ) { callback ( null , stored ) ; } else if ( stored ) { stored . promise . then ( ( ) => { stored . wasRejected ? callback ( stored . error ) : callback ( null , stored ) ; } ) ; } } param callback { Function } export function onAllLoad ( callback ) { const promises = [ ] ; const results = [ ] ; loadedScripts . forEach ( ( value ) => { if ( value . hasLoaded ) { results . push ( value ) ; } else { promises . push ( value . promise ) ; } } ) ; if ( promises . length > 0 ) { Promise . all ( promises ) . then ( ( res ) => callback ( null , res ) ) . catch ( ( errs ) => callback ( errs , null ) ) ; } else { callback ( null , results ) ; } } param name { string } param url { string } export function getScript ( url , name ) { if ( ! loadedScripts . has ( name ) && ! document . querySelector ( `script[src="${ url }"]` ) ) { const tag = document . createElement ( "script" ) ; const promise = new Promise < ScriptState > ( ( resolve , reject ) => { const body = document . getElementsByTagName ( "body" ) [ 0 ] ; assignIn ( tag , { async : false , type : "text/javascript" , } ) ; function handleResult ( event ) { const stored = loadedScripts . get ( name ) ; if ( event . type === "load" ) { stored . hasLoaded = true ; resolve ( stored ) ; } else if ( event . type === "error" ) { stored . wasRejected = true ; reject ( stored . error ) ; } } tag . addEventListener ( "load" , handleResult ) ; tag . addEventListener ( "error" , handleResult ) ; assignIn ( tag , { src : url } ) ; body . appendChild ( tag ) ; } ) ; const scriptObject = { hasLoaded : false , promise , tag , wasRejected : false , } ; loadedScripts . set ( name , scriptObject ) ; } return loadedScripts . get ( name ) ; } export default cache ;