import { ICssFile } from './BrandingTypes'; interface ISharedCssEntry { element: HTMLLinkElement; references: number; owned: boolean; } export class BrandingCssLoader { private static _registry: { [url: string]: ISharedCssEntry } = {}; private _loadedKeys: string[] = []; constructor(private ownerId: string, private debugLog: (message: string, data?: any) => void) { } // tslint:disable-line:no-any public load(files: ICssFile[]): void { for (let i: number = 0; i < files.length; i++) { this.loadOne(files[i]); } } public dispose(): void { for (let i: number = 0; i < this._loadedKeys.length; i++) { const key: string = this._loadedKeys[i]; const entry: ISharedCssEntry = BrandingCssLoader._registry[key]; if (!entry) { continue; } entry.references--; if (entry.references <= 0) { if (entry.owned && entry.element.parentNode) { entry.element.parentNode.removeChild(entry.element); } delete BrandingCssLoader._registry[key]; } } this._loadedKeys = []; } private loadOne(file: ICssFile): void { const absoluteUrl: string = this.toAbsoluteUrl(file.path); const key: string = absoluteUrl.toLowerCase(); if (this._loadedKeys.indexOf(key) >= 0) { return; } let entry: ISharedCssEntry = BrandingCssLoader._registry[key]; if (entry) { entry.references++; this._loadedKeys.push(key); return; } const existing: HTMLLinkElement = this.findExistingStylesheet(key); if (existing) { BrandingCssLoader._registry[key] = { element: existing, references: 1, owned: false }; this._loadedKeys.push(key); return; } const link: HTMLLinkElement = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = absoluteUrl; link.media = file.media || 'all'; link.setAttribute('data-custom-branding-owner', this.ownerId); link.setAttribute('data-custom-branding-url', key); const timeout: number = window.setTimeout((): void => { this.debugLog('Stylesheet load timed out.', { path: file.path }); }, 10000); link.onload = (): void => { window.clearTimeout(timeout); this.debugLog('Stylesheet loaded.', { path: file.path }); }; link.onerror = (): void => { window.clearTimeout(timeout); this.debugLog('Stylesheet failed to load.', { path: file.path }); }; document.head.appendChild(link); entry = { element: link, references: 1, owned: true }; BrandingCssLoader._registry[key] = entry; this._loadedKeys.push(key); } private toAbsoluteUrl(path: string): string { const anchor: HTMLAnchorElement = document.createElement('a'); anchor.href = path; return anchor.href; } private findExistingStylesheet(key: string): HTMLLinkElement | undefined { const links: NodeListOf = document.getElementsByTagName('link'); for (let i: number = 0; i < links.length; i++) { if (String(links[i].rel || '').toLowerCase() === 'stylesheet' && String(links[i].href || '').toLowerCase() === key) { return links[i]; } } return undefined; } }