import 'bootstrap.css';
import '@mescius/wijmo.styles/wijmo.css';
import './styles.css';
//
import '@angular/compiler';
import { Component, enableProdMode, ChangeDetectorRef, ɵresolveComponentResources } from '@angular/core';
import { BrowserModule, bootstrapApplication } from '@angular/platform-browser';
import { WjGridModule } from '@mescius/wijmo.angular2.grid';
import { WjGridFilterModule } from '@mescius/wijmo.angular2.grid.filter';
import { WjInputModule } from '@mescius/wijmo.angular2.input';
import { RestCollectionViewFirestore } from './rest-collection-view-firestore';
import { OAuth2 } from '@mescius/wijmo.cloud';
//
// Firestore info
const PROJECT_ID = 'test-9c0be';
const API_KEY = 'AIzaSyBeEwDqO_h1KOMRekRrDizOZweSiTXRj2Y';
const CLIENT_ID = '4727401369-ia1ur90eb7et0udmkvugc8i7c3v7u4t4.apps.googleusercontent.com';
const SCOPES = ['https://www.googleapis.com/auth/userinfo.email'];
//
// field info for Customers table
const fields = 'CustomerID,CompanyName,ContactName,ContactTitle,Address,City,Region,PostalCode,Country,Phone,Fax'.split(',');
//
@Component({
standalone: true,
imports: [WjGridModule, WjGridFilterModule, WjInputModule, BrowserModule],
selector: 'app-component',
templateUrl: 'src/app.component.html'
})
export class AppComponent {
msg = '';
isReadOnly = true;
view = new RestCollectionViewFirestore(PROJECT_ID, API_KEY, 'Customers', {
fields: fields,
pageSize: 8,
sortDescriptions: ['CustomerID'],
});
auth = new OAuth2(API_KEY, CLIENT_ID, SCOPES, {
userChanged: (s: any) => {
let user = s.user;
let oAuthBtn = document.getElementById('auth-btn') as HTMLButtonElement;
// update button text
oAuthBtn.textContent = user ? 'Sign Out' : 'Sign In';
// apply OAuth id token to the RestCollectionViewFirestore
this.view.idToken = s.idToken;
// make the grid read-only if the user is not signed in
this.isReadOnly = user == null;
// update message
this.msg = user
? 'You are signed in as [<b>' + user.eMail + '</b>], so you may edit the grid (if you have permissions).'
: 'You are not signed in, so you cannot edit the grid.';
this._chDetector.detectChanges();
}
});
constructor(private readonly _chDetector: ChangeDetectorRef) {
}
// apply auto-numbering cell template
initGrid(theGrid: any) {
theGrid.topLeftCells.columns[0].cellTemplate = ($: any) => $.text || ($.row.index + 1).toString()
}
// log in/out
logInOut() {
if (this.auth.user) {
this.auth.signOut();
} else {
this.auth.signIn();
}
}
}
//
//
enableProdMode();
// Resolve resources (templateUrl, styleUrls etc), After resolution all URLs have been converted into `template` strings.
ɵresolveComponentResources(fetch).then(() => {
// Bootstrap application
bootstrapApplication(AppComponent).catch(err => console.error(err));
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MESCIUS Wijmo RestCollectionView Firestore</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/fesm2015/zone.min.js"></script>
<!-- SystemJS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.5/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script>
<script src="systemjs.config.js"></script>
<script>
// workaround to load 'rxjs/operators' from the rxjs bundle
System.import('rxjs').then(function (m) {
System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators));
System.import('./src/app.component');
});
</script>
</head>
<body>
<app-component></app-component>
</body>
</html>
<div class="container-fluid">
<label>
Paging
<input id="paging"
type="checkbox"
[checked]="view.pageSize > 0"
(change)="view.pageSize = view.pageSize == 0 ? 8 : 0">
</label>
<button (click)="logInOut()" id="auth-btn" class="btn btn-primary" style="float:right">
Sign In
</button>
<br />
<wj-collection-view-navigator #nav
[cv]="view"
[byPage]="true"
headerFormat="{current:n0}">
</wj-collection-view-navigator>
<wj-flex-grid #theGrid
[allowAddNew]="true"
[allowDelete]="true"
[showMarquee]="true"
selectionMode="MultiRange"
[deferResizing]="true"
[alternatingRowStep]="0"
[isReadOnly]="isReadOnly"
[itemsSource]="view"
(initialized)="initGrid(theGrid)">
<wj-flex-grid-filter></wj-flex-grid-filter>
</wj-flex-grid>
<p [innerHTML]="msg"></p>
</div>
body {
margin-bottom: 36pt;
}
.wj-flexgrid {
max-height: 400px;
}
import { RestCollectionView } from '@mescius/wijmo.rest';
import {
DataType,
httpRequest, assert, copy, getType, changeType, asString, isArray, isObject, isString
} from '@mescius/wijmo';
// overshoot the number of items when paging on server
// will adjust when we hit the last page
const _INITIAL_ITEM_COUNT = 1e9;
/**
* Class that extends {@link RestCollectionView} to support Firestore data sources.
*/
export class RestCollectionViewFirestore extends RestCollectionView {
_projectId: string;
_apiKey: string;
_name: string;
_pendingRequest: XMLHttpRequest;
_idToken: string | null;
_fbToken: string | null;
/**
* Initializes a new instance of the {@link RestCollectionViewFirestore} class.
*
* @param projectId ID of the Firebase app that contains the database.
* @param apiKey Unique identifier used to authenticate requests associated with the app.
* To generate API keys, please go to https://console.cloud.google.com/.
* @param collectionName Name of the collection.
* @param options JavaScript object containing initialization data (property values
* and event handlers) for this {@link Collection}.
*/
constructor(projectId: string, apiKey: string, collectionName: string, options?: any) {
super();
this._projectId = asString(projectId, false);
this._apiKey = asString(apiKey, false);
this._name = asString(collectionName, false);
this._idToken = null;
this._totalItemCount = _INITIAL_ITEM_COUNT;
copy(this, options);
}
/**
* Gets the name of this collection.
*/
get name(): string {
return this._name;
}
/**
* Gets or sets a OAuth 2.0 id token used to access the database.
*
* You can use the {@link OAuth2} class to allow users to log in and
* to obtain the {@link idToken} string.
*
* If you choose this authentication method, Firestore Security Rules
* will be applied as usual to determine which users can read and write
* to the database.
*
* See also the {@link accessToken} property, which bypasses Firestore
* Security Rules and uses Cloud Identity and Access Management (IAM)
* instead.
*/
get idToken(): string | null {
return this._idToken;
}
set idToken(value: string | null) {
if (value != this._idToken) {
// save OAuth idToken
this._idToken = asString(value);
// convert OAuth idToken into Firebase idToken
// https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/signInWithIdp
if (this._idToken) {
let url = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=' + this._apiKey;
httpRequest(url, {
method: 'POST',
data: {
requestUri: window.location.href,
postBody: 'id_token=' + this._idToken + '&providerId=google.com',
returnSecureToken: true,
returnIdpCredential: true
},
success: xhr => {
let result = JSON.parse(xhr.responseText);
this._fbToken = result.idToken;
},
error: xhr => {
this._fbToken = '';
}
});
} else {
this._fbToken = '';
}
}
}
// ** implementation
// gets a URL to the collection or to a document
_getUrl(doc?: any): string {
let base = 'https://firestore.googleapis.com/v1/';
// got object? use it
if (isObject(doc) && doc.name) {
return base + doc.name;
}
// doc not specified? use this collection's name
if (!doc) {
doc = '/' + this.name;
}
// get parent address
let parent = 'projects/' + this._projectId + '/databases/(default)/documents';
// build and return the URL
return base + parent + doc;
}
// gets a structuredQuery object
// that specifies fields, sorting, filtering, paging
_getQuery() {
// collection source
let q: any = {
from: [{
collectionId: this.name
}]
};
// select fields to include
if (this.fields && this.fields.length) {
q.select = {
fields: this.fields.map(field => {
return {
fieldPath: field
}
})
};
};
// server-side filtering
let filters: any = null;
if (this.filterOnServer && this._filterProvider) {
filters = this._getQueryFilters();
}
// build where clause
if (filters && filters.length) {
q.where = filters.length == 1
? filters[0]
: { compositeFilter: { filters: filters, op: 'AND' } }
}
// sorting
let orderBy: any[] = [];
if (filters && filters.length) { // order by range filters (must be first)
filters.forEach((filter: any) => {
let ff = filter.fieldFilter;
if (ff && ff.op != 'IN' && ff.op != 'EQUAL') { // no sortBy with IN/EQUAL operators
orderBy.push({
field: ff.field.fieldPath,
asc: true
});
}
});
}
if (!orderBy.length && this.sortOnServer) { // sort on server
this.sortDescriptions.forEach(sd => {
orderBy.push({
field: sd.property,
asc: sd.ascending
});
});
}
// apply orderBy array
if (orderBy.length) {
q.orderBy = orderBy.map(ob => {
return {
field: { fieldPath: ob.field },
direction: ob.asc ? 'ASCENDING' : 'DESCENDING'
}
});
}
// paging
let pageSize = this.pageSize;
if (this.pageOnServer && pageSize) {
q.limit = pageSize;
q.offset = pageSize * this.pageIndex
}
// done
return { structuredQuery: q }
}
// gets the filter part of a query from a filterProvider (FlexGridFilter)
// https://cloud.google.com/firestore/docs/reference/rest/v1/StructuredQuery#Filter
_getQueryFilters(): any[] {
let filters: any[] = [],
filter = this._filterProvider;
if (filter) {
for (let c = 0; c < filter.grid.columns.length; c++) {
let col = filter.grid.columns[c],
cf = filter.getColumnFilter(col, false);
if (cf && cf.isActive) {
if (cf.conditionFilter && cf.conditionFilter.isActive) {
this._getQueryConditionFilter(filters, cf.conditionFilter);
} else if (cf.valueFilter && cf.valueFilter.isActive) {
this._getQueryValueFilter(filters, cf.valueFilter);
}
break; // cannot have multiple inequality filters on different columns
}
}
}
return filters;
}
_getQueryConditionFilter(filters: any[], cf: any): void {
let path = cf.column.binding,
sf1 = this._getQuerySimpleFilter(cf.condition1, path),
sf2 = this._getQuerySimpleFilter(cf.condition2, path);
if (sf1 && sf2 && cf.and) {
filters.push({
compositeFilter: {
op: cf.and ? 'AND' : 'OR',
filters: [sf1, sf2]
}
});
} else if (sf1) {
filters.push(sf1);
} else if (sf2) {
filters.push(sf2);
}
}
_getQuerySimpleFilter(fc: any, path: string): any {
if (fc.isActive) {
// beginsWith requires two conditions
if (fc.operator == 6 /*OP.BW*/) {
return {
compositeFilter: {
op: 'AND',
filters: [
{ fieldFilter: {
field: { fieldPath: path },
op: 'GREATER_THAN_OR_EQUAL',
value: this._getValueObject(fc.value)
}},
{ fieldFilter: {
field: { fieldPath: path },
op: 'LESS_THAN',
value: this._getValueObject(fc.value + '\uf8ff')
}},
]
}
}
}
// other operators require only one condition
return {
fieldFilter: {
field: { fieldPath: path },
op: this._getFilterOperator(fc.operator),
value: this._getValueObject(fc.value)
}
}
}
return null;
}
_getFilterOperator(op: any): string {
switch (op) {
case 0: // OP.EQ: // equals
return 'EQUAL';
case 1: // OP.NE: // not equal
return 'NOT_EQUAL';
case 2: // OP.GT: // greater
return 'GREATER_THAN';
case 3: // OP.GE: // greater/equal
return 'GREATER_THAN_OR_EQUAL';
case 4: // OP.LT: // less
return 'LESS_THAN';
case 5: // OP.LE: // less/equal
return 'LESS_THAN_OR_EQUAL';
// not supported:
//case OP.CT: // contains
//case OP.EW: // ends with
//case OP.NC: // does not contain
}
assert(false, op + ' operator not supported (use EQ, NE, GT, GE, LT, or LE)');
}
_getQueryValueFilter(filters: any[], vf: any): void {
let col = vf.column,
map = col.dataMap,
values = [];
// build list of values
for (let key in vf.showValues) {
let value = changeType(key, col.dataType, col.format);
if (map && isString(value)) { // TFS 239356
value = map.getKeyValue(value);
}
values.push(value);
if (values.length >= 10) {
break;
}
}
// build condition
if (values.length) {
filters.push({
fieldFilter: {
field: { fieldPath: col.binding },
op: 'IN', // value is a non-empty ArrayValue with at most 10 values.
value: this._getValueObject(values)
}
});
}
}
// authorization
_getRequestHeaders() {
let rh: any = {},
token = this._fbToken;
if (token) {
rh.Authorization = 'Bearer ' + token;
}
return rh;
}
// parse the data received after a get request
_parseData(docs: any[]): any[] {
let arr: any[] = [];
if (isArray(docs)) {
docs.forEach(doc => {
let item = this._docToItem(doc);
if (item) {
arr.push(item);
}
});
}
return arr;
}
// save Firestore document name (key) and collection into plain data items
_saveDocName(doc: any, item: any): boolean {
if (doc.name && !item.$META) {
item.$META = {
name: doc.name
}
return true;
}
return false;
}
// convert Firestore document into plain data item
_docToItem(doc: any): any {
// handle documents wrapped in other items (returned from runQuery)
if (!doc.name) {
doc = doc.document;
}
// the first item returned by runQuery may not be a document
// (e.g. { readTime: xx, skippedResults: yy })
if (!doc || !doc.name) {
return null;
}
// save document name
let item: any = {};
this._saveDocName(doc, item);
// save document fields
for (let fld in doc.fields) {
item[fld] = this._getDocValue(doc.fields[fld]);
}
// done
return item;
}
// convert Firestore value into plain data item
_getDocValue(obj: any): any {
let value = null;
for (let valName in obj) {
value = obj[valName];
switch (valName) {
case 'integerValue': // document stores integers as strings
value = parseInt(value);
break;
case 'timestampValue':
value = new Date(value);
break;
case 'mapValue':
let obj = {};
for (let k in value.fields) {
obj[k] = this._getDocValue(value.fields[k]);
}
value = obj;
break;
case 'arrayValue':
value = value.values
? value.values.map((val: any) => this._getDocValue(val))
: [];
break;
}
}
return value;
}
// convert plain data item into Firestore document
_itemToDoc(item: any): any {
let doc: any = {},
meta = item.$META,
calcFields = this.calculatedFields;
// save document name (key)
if (meta && meta.name) {
doc.name = meta.name
}
// save fields
doc.fields = {};
for (let fld in item) {
if (!calcFields || !(fld in calcFields)) {
if (fld != '$META') {
doc.fields[fld] = this._getValueObject(item[fld]);
}
}
}
// document is ready
return doc;
}
// convert value into value object
// https://cloud.google.com/firestore/docs/reference/rest/v1/Value
_getValueObject(value: any): any {
let valObj: any = {},
DT = DataType;
switch (getType(value)) {
case DT.String:
valObj.stringValue = value;
break;
case DT.Boolean:
valObj.booleanValue = value;
break;
case DT.Date:
valObj.timestampValue = (value as Date).toJSON();
break;
case DT.Number:
if (value == Math.round(value as number)) {
valObj.integerValue = value.toString();
} else {
valObj.doubleValue = value;
}
break;
case DT.Array:
valObj.arrayValue = {
values: value.map((v: any) => this._getValueObject(v))
};
break;
case DT.Object:
let fields = {};
for (let k in value) {
fields[k] = this._getValueObject(value[k]);
}
valObj.mapValue = {
fields: fields
}
break;
default:
assert(false, 'failed to create value object.')
}
return valObj;
}
// ** overrides
// reset page count when filter changes
updateFilterDefinition(filterProvider: any) {
if (this.filterOnServer && this.pageOnServer) {
this._totalItemCount = _INITIAL_ITEM_COUNT;
}
super.updateFilterDefinition(filterProvider);
}
protected async getItems(): Promise<any[]> {
// cancel any pending requests
if (this._pendingRequest) {
//console.log('aborting pending request');
this._pendingRequest.abort();
}
return new Promise<any[]>(resolve => {
this._pendingRequest = httpRequest(this._getUrl(':runQuery'), {
method: 'POST',
data: this._getQuery(),
requestHeaders: this._getRequestHeaders(),
success: xhr => {
// read the data
let data = JSON.parse(xhr.responseText),
arr = this._parseData(data);
// keep track of total item count
if (this.pageOnServer && this.pageSize) {
if (arr.length < this.pageSize) { // not enough items? reached the end
let skipped = data[0].skippedResults || 0;
let cnt = skipped + arr.length; // this is the actual count
if (this._totalItemCount != cnt) {
this._totalItemCount = cnt; // store count
if (!arr.length) { // if we're past the end, move to last page
this.moveToLastPage();
}
}
}
}
// done
resolve(arr);
},
error: xhr => this._raiseError(xhr.responseText, false),
complete: xhr => this._pendingRequest = null // no pending requests
});
});
}
protected addItem(item: any): Promise<any> {
return new Promise<any[]>(resolve => {
let doc = this._itemToDoc(item);
// https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/createDocument
httpRequest(this._getUrl(), {
method: 'POST',
data: { fields: doc.fields },
requestHeaders: this._getRequestHeaders(),
success: xhr => {
let doc = JSON.parse(xhr.responseText);
this._saveDocName(doc, item); // keep new doc's name
this._totalItemCount++;
resolve(item);
},
error: xhr => this._raiseError(xhr, true)
});
});
}
protected patchItem(item: any): Promise<any> {
return new Promise<any[]>(resolve => {
let doc = this._itemToDoc(item);
// https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/patch
httpRequest(this._getUrl(doc), {
method: 'PATCH',
data: { fields: doc.fields },
requestHeaders: this._getRequestHeaders(),
success: xhr => resolve(item),
error: xhr => this._raiseError(xhr, true)
});
});
}
protected deleteItem(item: any): Promise<any> {
return new Promise<any[]>(resolve => {
let doc = this._itemToDoc(item);
// https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/delete
httpRequest(this._getUrl(doc), {
method: 'DELETE',
requestHeaders: this._getRequestHeaders(),
success: xhr => {
this._totalItemCount--;
resolve(item);
},
error: xhr => this._raiseError(xhr, true)
});
});
}
}
(function (global) {
SystemJS.config({
transpiler: './plugin-typescript.js',
typescriptOptions: {
"target": "ES2022",
"module": "system",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
},
baseURL: 'node_modules/',
meta: {
'typescript': {
"exports": "ts"
},
'*.css': { loader: 'systemjs-plugin-css' }
},
paths: {
// paths serve as alias
'npm:': ''
},
packageConfigPaths: [
'/node_modules/*/package.json',
"/node_modules/@angular/*/package.json",
"/node_modules/@mescius/*/package.json"
],
map: {
'core-js': 'https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js',
'typescript': 'https://cdnjs.cloudflare.com/ajax/libs/typescript/5.2.2/typescript.min.js',
"rxjs": "https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js",
'systemjs-plugin-css': 'https://cdn.jsdelivr.net/npm/systemjs-plugin-css@0.1.37/css.js',
'@mescius/wijmo': 'npm:@mescius/wijmo/index.js',
'@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js',
'@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles',
'@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures',
'@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js',
'@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js',
'@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js',
'@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js',
'@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js',
'@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js',
'@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js',
'@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js',
'@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js',
'@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js',
'@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js',
'@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js',
'@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js',
'@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js',
'@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js',
'@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js',
'@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js',
'@mescius/wijmo.grid.style': 'npm:@mescius/wijmo.grid.style/index.js',
'@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js',
'@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js',
'@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js',
'@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js',
'@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js',
'@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js',
'@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js',
'@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js',
'@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js',
'@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js',
'@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js',
'@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js',
'@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js',
'@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js',
'@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js',
'@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js',
'@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js',
'@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js',
'@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js',
'@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js',
'@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js',
'@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js',
'@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js',
'@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js',
'@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js',
"@mescius/wijmo.angular2.chart.analytics": "npm:@mescius/wijmo.angular2.chart.analytics/index.js",
"@mescius/wijmo.angular2.chart.animation": "npm:@mescius/wijmo.angular2.chart.animation/index.js",
"@mescius/wijmo.angular2.chart.annotation": "npm:@mescius/wijmo.angular2.chart.annotation/index.js",
"@mescius/wijmo.angular2.chart.finance.analytics": "npm:@mescius/wijmo.angular2.chart.finance.analytics/index.js",
"@mescius/wijmo.angular2.chart.finance": "npm:@mescius/wijmo.angular2.chart.finance/index.js",
"@mescius/wijmo.angular2.chart.hierarchical": "npm:@mescius/wijmo.angular2.chart.hierarchical/index.js",
"@mescius/wijmo.angular2.chart.interaction": "npm:@mescius/wijmo.angular2.chart.interaction/index.js",
"@mescius/wijmo.angular2.chart.radar": "npm:@mescius/wijmo.angular2.chart.radar/index.js",
'@mescius/wijmo.angular2.chart.map': 'npm:@mescius/wijmo.angular2.chart.map/index.js',
"@mescius/wijmo.angular2.chart": "npm:@mescius/wijmo.angular2.chart/index.js",
"@mescius/wijmo.angular2.core": "npm:@mescius/wijmo.angular2.core/index.js",
"@mescius/wijmo.angular2.gauge": "npm:@mescius/wijmo.angular2.gauge/index.js",
"@mescius/wijmo.angular2.grid.detail": "npm:@mescius/wijmo.angular2.grid.detail/index.js",
"@mescius/wijmo.angular2.grid.filter": "npm:@mescius/wijmo.angular2.grid.filter/index.js",
"@mescius/wijmo.angular2.grid.grouppanel": "npm:@mescius/wijmo.angular2.grid.grouppanel/index.js",
"@mescius/wijmo.angular2.grid.search": "npm:@mescius/wijmo.angular2.grid.search/index.js",
"@mescius/wijmo.angular2.grid.multirow": "npm:@mescius/wijmo.angular2.grid.multirow/index.js",
"@mescius/wijmo.angular2.grid.sheet": "npm:@mescius/wijmo.angular2.grid.sheet/index.js",
'@mescius/wijmo.angular2.grid.transposed': 'npm:@mescius/wijmo.angular2.grid.transposed/index.js',
'@mescius/wijmo.angular2.grid.transposedmultirow': 'npm:@mescius/wijmo.angular2.grid.transposedmultirow/index.js',
"@mescius/wijmo.angular2.grid": "npm:@mescius/wijmo.angular2.grid/index.js",
"@mescius/wijmo.angular2.input": "npm:@mescius/wijmo.angular2.input/index.js",
"@mescius/wijmo.angular2.olap": "npm:@mescius/wijmo.angular2.olap/index.js",
"@mescius/wijmo.angular2.viewer": "npm:@mescius/wijmo.angular2.viewer/index.js",
"@mescius/wijmo.angular2.nav": "npm:@mescius/wijmo.angular2.nav/index.js",
"@mescius/wijmo.angular2.directivebase": "npm:@mescius/wijmo.angular2.directivebase/index.js",
'@mescius/wijmo.angular2.barcode.common': 'npm:@mescius/wijmo.angular2.barcode.common/index.js',
'@mescius/wijmo.angular2.barcode.composite': 'npm:@mescius/wijmo.angular2.barcode.composite/index.js',
'@mescius/wijmo.angular2.barcode.specialized': 'npm:@mescius/wijmo.angular2.barcode.specialized/index.js',
'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css',
'jszip': 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js',
"@angular/common/http": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/http.mjs",
"@angular/core": "https://cdn.jsdelivr.net/npm/@angular/core@16.2.6/fesm2022/core.mjs",
"@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@angular/platform-browser@16.2.6/fesm2022/platform-browser.mjs",
"@angular/common": "https://cdn.jsdelivr.net/npm/@angular/common@16.2.6/fesm2022/common.mjs",
"@angular/compiler": "https://cdn.jsdelivr.net/npm/@angular/compiler@16.2.6/fesm2022/compiler.mjs",
"@angular/forms": "https://cdn.jsdelivr.net/npm/@angular/forms@16.2.6/fesm2022/forms.mjs",
"@angular/localize": "https://cdn.jsdelivr.net/npm/@angular/localize@16.2.6/fesm2022/localize.mjs",
"@angular/platform-browser-dynamic": "https://cdn.jsdelivr.net/npm/@angular/platform-browser-dynamic@16.2.6/fesm2022/platform-browser-dynamic.mjs",
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
"./src": {
defaultExtension: 'ts'
},
"node_modules": {
defaultExtension: 'js'
},
wijmo: {
defaultExtension: 'js',
}
}
});
})(this);