@cisstech/nge
Version:
NG Essentials is a collection of libraries for Angular developers.
336 lines (328 loc) • 13.3 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, createNgModule, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Observable, of, from, forkJoin } from 'rxjs';
import { take, shareReplay, concatMap } from 'rxjs/operators';
/** Service to programmatically open file dialog. */
class PickerBrowserService {
/**
* Open file dialog to select files.
* @param options picker options.
* @returns A promise that resolves with the picked files or and empty array.
*/
pickFiles(options) {
return new Promise(async (resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.multiple = options.multiple ?? true;
input.accept = options.accept ?? '*';
input.style.visibility = 'hidden';
input.onchange = (event) => {
const target = event.target || event.srcElement;
if (target.value.length > 0) {
input.remove();
resolve(target.files);
}
else {
input.remove();
resolve([]);
}
};
// handle cancel button click.
setTimeout(() => {
let onfocus;
onfocus = () => {
if (!input.value.length) {
resolve([]);
input.remove();
}
window.removeEventListener('click', onfocus);
};
window.addEventListener('click', onfocus);
}, 300);
document.body.appendChild(input);
input.focus();
input.click();
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: PickerBrowserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: PickerBrowserService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: PickerBrowserService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class ClipboardService {
copy(data) {
return navigator.clipboard.writeText(data);
}
read() {
return navigator.clipboard.readText();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: ClipboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: ClipboardService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: ClipboardService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class CompilerService {
constructor() {
this.modules = [];
}
async render(options) {
const { component } = await this.resolveComponent(options.type, options.container.injector);
return await this.renderComponent(options.inputs, options.container.createComponent(component, {
index: 0,
injector: options.container.injector,
}));
}
async resolveComponent(type, injector) {
// https://blog.ninja-squad.com/2019/05/07/what-is-angular-ivy/
// https://juristr.com/blog/2019/10/lazyload-module-ivy-viewengine
if (!('ɵmod' in type) && !('ɵcmp' in type)) {
throw new Error(`[render]: type "${type.name}" does not refers to a Component or a NgModule`);
}
let component = type;
let componentInjector = injector;
if ('ɵmod' in type) {
const module = await this.resolveModuleInfo(type, injector);
componentInjector = module.injector;
component = module.instance.component;
}
return {
component,
injector: componentInjector,
};
}
async renderComponent(inputs, componentRef) {
const changes = {};
const { instance, changeDetectorRef } = componentRef;
if (inputs) {
Object.keys(inputs).forEach((k) => {
instance[k] = inputs[k];
changes[k] = {
currentValue: inputs[k],
previousValue: undefined,
firstChange: true,
isFirstChange: () => true,
};
});
}
if (instance.ngOnChanges) {
await instance.ngOnChanges(changes);
}
changeDetectorRef.markForCheck();
return componentRef;
}
async resolveModuleInfo(type, injector) {
const moduleInfo = this.modules.find((e) => e.type === type);
if (moduleInfo) {
return moduleInfo.module;
}
const module = createNgModule(type, injector);
if (typeof module.instance.component !== 'function') {
throw new Error(`[compiler]: The module "${type.name}" does not define a public "component" field.`);
}
this.modules.push({ type: type, module: module });
return module;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: CompilerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: CompilerService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: CompilerService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class InjectorService {
constructor(injector) {
this.injector = injector;
}
get(token) {
const service = this.injector.get(token, []).find((e) => e.injectable());
if (!service) {
throw new Error('[InjectorService]: No provider found for ' + token);
}
return service;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: InjectorService, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: InjectorService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: InjectorService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i0.Injector }] });
class ResourceLoaderConfig {
}
class LoadRequest {
get isFinished() {
return this.finished;
}
constructor(asset, document, config) {
this.asset = asset;
this.document = document;
this.config = config;
this.finished = false;
}
run() {
if (this.asset[0] === 'style') {
return this.loadStyle();
}
return this.loadScript();
}
loadStyle() {
return (this.request ??
(this.request = new Observable((observer) => {
const url = this.buildUrl(this.asset[1]);
const style = this.document.createElement('link');
style.href = url;
style.rel = 'stylesheet';
style.onload = () => {
observer.next(this.asset);
observer.complete();
this.finished = true;
};
style.onerror = (err) => {
observer.error(err);
this.finished = true;
};
const attributes = this.asset[2];
if (attributes) {
for (const key in attributes) {
if (attributes.hasOwnProperty(key)) {
style.setAttribute(key, attributes[key]);
}
}
}
this.document.head.appendChild(style);
}).pipe(take(1), shareReplay(1))));
}
loadScript() {
return (this.request ??
(this.request = new Observable((observer) => {
const url = this.buildUrl(this.asset[1]);
const script = this.document.createElement('script');
script.src = url;
script.onload = () => {
observer.next(this.asset);
observer.complete();
this.finished = true;
};
script.onerror = (err) => {
observer.error(err);
this.finished = true;
};
const attributes = this.asset[2];
if (attributes) {
for (const key in attributes) {
if (attributes.hasOwnProperty(key)) {
script.setAttribute(key, attributes[key]);
}
}
}
this.document.head.appendChild(script);
}).pipe(take(1), shareReplay(1))));
}
buildUrl(url) {
if (!this.config?.useDocumentBaseURI) {
return url;
}
if (url.startsWith('http')) {
return url;
}
if (!url.startsWith(document.baseURI)) {
url = url.startsWith('/') ? url.slice(1) : url;
return document.baseURI + url;
}
return url;
}
}
/**
* Services that dynamically inject scripts and styles elements to the DOM.
*/
class ResourceLoaderService {
constructor() {
this.document = inject(DOCUMENT);
this.config = inject(ResourceLoaderConfig, { optional: true });
this.requests = new Map();
}
waitForPendings() {
return new Promise((resolve) => {
const interval = setInterval(() => {
for (const request of this.requests.values()) {
if (!request.isFinished) {
return;
}
}
clearInterval(interval);
resolve();
}, 100);
});
}
/**
* Injects styles and scripts from given urls to the head of the DOM.
* This method loads assets from same url once and the assets are
* loaded in the same order that given.
* @param resources Resources to load.
*/
loadAllSync(resources) {
if (!resources.length) {
return of([]);
}
const requests = this.createRequests(resources);
return new Observable((observer) => {
const response = [];
const subs = from(requests)
.pipe(concatMap((e) => e.run()))
.subscribe((e) => {
response.push(e);
if (response.length === resources.length) {
observer.next(response);
observer.complete();
}
});
return () => {
subs?.unsubscribe();
};
});
}
/**
* Injects styles and scripts from given urls to target place in DOM
* This method loads style and script from same url once.
* @param resources Resources to load.
*/
loadAllAsync(resources) {
if (!resources.length) {
return of([]);
}
const loaders = this.createRequests(resources);
return forkJoin(loaders.map((e) => e.run()));
}
createRequests(resources) {
return resources.map((asset) => {
const url = asset[1];
let request = this.requests.get(url);
if (!request) {
this.requests.set(url, (request = new LoadRequest(asset, this.document, this.config)));
}
return request;
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: ResourceLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: ResourceLoaderService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: ResourceLoaderService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
const ResourceLoaderConfigProvider = (config) => ({
provide: ResourceLoaderConfig,
useValue: config,
});
/**
* Generated bundle index. Do not edit.
*/
export { ClipboardService, CompilerService, InjectorService, PickerBrowserService, ResourceLoaderConfig, ResourceLoaderConfigProvider, ResourceLoaderService };
//# sourceMappingURL=cisstech-nge-services.mjs.map