src/helper/loader.helper.ts
import assetsLoaderHelper from './assets.helper'
import { IAppOption } from '../model/IAppOption'
import { hashCode } from './app.helper'
import { generateIFrameID } from './dom.utils'
function loadScriptPromise(src: string, iframeEl: any) {
return new Promise((resolve, reject) => {
if (document.getElementById(hashCode(src))) {
return resolve()
}
const script = assetsLoaderHelper.createScriptTag(src)
script.onload = () => {
resolve()
}
script.onerror = err => {
reject(err)
}
if (iframeEl) {
if (iframeEl && iframeEl.contentWindow) {
iframeEl.contentWindow.document.head.appendChild(script)
}
} else {
document.head.appendChild(script)
}
})
}
const loadScriptTag = (src: string, iframeEl?: any) => {
return () => {
return loadScriptPromise(src, iframeEl)
}
}
const loadLinkTag = (url: string, iframeEl?: any) => {
return () => {
return new Promise((resolve, reject) => {
if (document.getElementById(hashCode(url))) {
return resolve()
}
const link = assetsLoaderHelper.createLinkTag(url)
link.onload = () => {
resolve()
}
link.onerror = err => {
reject(err)
}
if (iframeEl) {
if (iframeEl && iframeEl.contentWindow) {
iframeEl.contentWindow.document.head.appendChild(link)
}
} else {
document.head.appendChild(link)
}
})
}
}
function loadAllAssets(opts: any) {
return new Promise((resolve, reject) => {
const scriptsPromise = opts.scripts.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(loadScriptTag(`${opts.baseScriptUrl}/${fileName}`)),
Promise.resolve(undefined)
)
const stylesPromise = opts.styles.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(loadLinkTag(`${opts.baseScriptUrl}/${fileName}`)),
Promise.resolve(undefined)
)
Promise.all([scriptsPromise, stylesPromise]).then(resolve, reject)
})
}
function loadAllAssetsForIframe(opts: any) {
const iframeId = generateIFrameID(opts.name)
let iframeEl: any = document.getElementById(iframeId)
if (!iframeEl) {
return new Promise((resolve, reject) => {
reject()
})
}
return new Promise((resolve, reject) => {
const scriptsPromise = opts.scripts.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(loadScriptTag(`${opts.baseScriptUrl}/${fileName}`, iframeEl)),
Promise.resolve(undefined)
)
const stylesPromise = opts.styles.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(loadLinkTag(`${opts.baseScriptUrl}/${fileName}`)),
Promise.resolve(undefined)
)
let promiseArray = [scriptsPromise, stylesPromise]
if (opts.includeZone) {
const zonejsPromise = loadScriptPromise(`/assets/zone.min.js`, iframeEl)
promiseArray.push(zonejsPromise)
}
Promise.all(promiseArray).then(resolve, reject)
})
}
function loadAllAssetsForIframeAndUrl(opts: any) {
const iframeId = generateIFrameID(opts.name)
let iframeEl: any = document.getElementById(iframeId)
if (!iframeEl) {
return new Promise((resolve, reject) => {
reject()
})
}
return new Promise((resolve, reject) => {
transformOptsWithAssets(opts).then(() => {
const scriptsPromise = opts.scripts.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(
loadScriptTag(`${opts.baseScriptUrl}/${fileName}`, iframeEl)
),
Promise.resolve(undefined)
)
const stylesPromise = opts.styles.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(loadLinkTag(`${opts.baseScriptUrl}/${fileName}`)),
Promise.resolve(undefined)
)
let promiseArray = [scriptsPromise, stylesPromise]
if (opts.includeZone) {
const zonejsPromise = loadScriptPromise(`/assets/zone.min.js`, iframeEl)
promiseArray.push(zonejsPromise)
}
Promise.all(promiseArray).then(resolve, reject)
})
})
}
const xmlToAssets = (
xml: string
): { styles: (string | null)[]; scripts: (string | null)[] } => {
let dom = document.createElement('html')
let urlRegex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/
dom.innerHTML = xml
const linksEls = dom.querySelectorAll('link[rel="stylesheet"]')
const scriptsEls = dom.querySelectorAll('script[type="text/javascript"]')
return {
styles: Array.from(linksEls)
.map(el => el.getAttribute('href'))
.filter(src => {
if (src) {
return !urlRegex.test(src)
}
}),
scripts: Array.from(scriptsEls)
.map(el => el.getAttribute('src'))
.filter(src => {
if (src) {
return !(/zone\.js/.test(src) && urlRegex.test(src))
}
})
}
}
const transformOptsWithAssets = (opts: any): Promise<null> => {
const url = `${opts.baseScriptUrl}/index.html`
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest()
req.onreadystatechange = () => {
if (req.readyState === XMLHttpRequest.DONE) {
if (req.status >= 200 && req.status < 400) {
const res = xmlToAssets(req.responseText)
opts.styles = res.styles
opts.scripts = res.scripts
resolve(null)
} else {
reject(
`Try to load ${url}, status : ${req.status} => ${req.statusText}`
)
}
}
}
req.open('GET', url, true)
req.send(null)
})
}
const loadAllAssetsByUrl = (opts: any) => {
return new Promise((resolve, reject) => {
transformOptsWithAssets(opts).then(() => {
const scriptsPromise = opts.scripts.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(loadScriptTag(`${opts.baseScriptUrl}/${fileName}`)),
Promise.resolve(undefined)
)
const stylesPromise = opts.styles.reduce(
(prev: Promise<undefined>, fileName: string) =>
prev.then(loadLinkTag(`${opts.baseScriptUrl}/${fileName}`)),
Promise.resolve(undefined)
)
Promise.all([scriptsPromise, stylesPromise]).then(resolve, reject)
}, reject)
})
}
function unloadTag(opts: IAppOption, scriptName: string) {
return () => {
return new Promise((resolve, reject) => {
const tag = document.getElementById(
hashCode(`${opts.baseScriptUrl}/${scriptName}`)
)
if (tag) {
document.head.removeChild(tag)
}
resolve()
})
}
}
const LoaderHelper = {
loadAllAssets: loadAllAssets,
loadAllAssetsByUrl: loadAllAssetsByUrl,
loadAllAssetsForIframe: loadAllAssetsForIframe,
loadAllAssetsForIframeAndUrl: loadAllAssetsForIframeAndUrl,
unloadTag: unloadTag
}
export default LoaderHelper