@sumit_yewale/angular2 1.0.4 → 1.0.7-fix.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { SecurityContext, Component, Input } from '@angular/core';
3
- import { generateUuid, buildImportUrl, classifyStructuredMessage, buildInitPayload } from '@sumit_yewale/adapter-core';
3
+ import { createMessageGate, createImportTracker, createFileSender, classifyLegacyMessage, classifyStructuredMessage, generateUuid, buildImportUrl, buildInitPayload } from '@sumit_yewale/adapter-core';
4
4
  import * as i1 from '@angular/platform-browser';
5
5
 
6
6
  let styleElement;
@@ -35,10 +35,59 @@ const insertCSS = () => {
35
35
  styleElement.textContent = GLOBAL_CSS;
36
36
  };
37
37
 
38
+ // How long we wait for the importer to confirm a file it was sent before giving up on it.
39
+ const FILE_ACK_TIMEOUT_MS = 8000;
40
+ const OPEN_IMPORTERS_KEY = '__csvboxOpenImporters';
41
+ const TOP_Z_INDEX = 2147483647;
42
+ function openImporters() {
43
+ const w = window;
44
+ if (!w[OPEN_IMPORTERS_KEY])
45
+ w[OPEN_IMPORTERS_KEY] = { open: [], listeners: [] };
46
+ return w[OPEN_IMPORTERS_KEY];
47
+ }
48
+ function restackOpenImporters(reg) {
49
+ reg.open.forEach((entry, i) => {
50
+ const holder = entry.getHolder();
51
+ if (holder)
52
+ holder.style.zIndex = String(TOP_Z_INDEX - (reg.open.length - 1 - i));
53
+ });
54
+ }
55
+ function registerOpenImporter(entry) {
56
+ const reg = openImporters();
57
+ if (reg.open.indexOf(entry) >= 0)
58
+ return;
59
+ reg.open.push(entry);
60
+ restackOpenImporters(reg);
61
+ reg.listeners.slice().forEach(fn => fn());
62
+ }
63
+ function unregisterOpenImporter(entry) {
64
+ const reg = openImporters();
65
+ const i = reg.open.indexOf(entry);
66
+ if (i < 0)
67
+ return;
68
+ reg.open.splice(i, 1);
69
+ restackOpenImporters(reg);
70
+ reg.listeners.slice().forEach(fn => fn());
71
+ }
72
+ function isBlockedByOpenImporter(entry) {
73
+ const reg = openImporters();
74
+ return reg.open.length > 0 && reg.open.indexOf(entry) < 0;
75
+ }
76
+ function subscribeOpenImporters(fn) {
77
+ const reg = openImporters();
78
+ reg.listeners.push(fn);
79
+ return () => {
80
+ const i = reg.listeners.indexOf(fn);
81
+ if (i >= 0)
82
+ reg.listeners.splice(i, 1);
83
+ };
84
+ }
38
85
  const appVersion = '1.1.16';
39
86
  class CSVBoxButtonComponent {
40
- constructor(sanitizer) {
87
+ constructor(sanitizer, cdr, zone) {
41
88
  this.sanitizer = sanitizer;
89
+ this.cdr = cdr;
90
+ this.zone = zone;
42
91
  this.isModalShown = false;
43
92
  this.user = null;
44
93
  this.dynamicColumns = null;
@@ -56,51 +105,117 @@ class CSVBoxButtonComponent {
56
105
  this.safeUrl = null;
57
106
  this.iframe = null;
58
107
  this.disabled = true;
108
+ // True while a different importer on the page is open: the button stays disabled.
109
+ this.blockedByOther = false;
110
+ this.messageGate = createMessageGate();
111
+ this.importTracker = createImportTracker();
112
+ this.registryEntry = { getHolder: () => this.holder };
113
+ this.fileSender = createFileSender({
114
+ getIframe: () => this.iframe,
115
+ getToken: () => this.uuid,
116
+ getTargetOrigin: () => this.targetOrigin,
117
+ getBaseDomain: () => this.baseDomain,
118
+ takeFile: () => {
119
+ const file = this.pendingFile;
120
+ this.pendingFile = null;
121
+ return file;
122
+ },
123
+ onSent: () => this.startFileAckTimer()
124
+ });
125
+ this.unsubscribeOpenImporters = null;
59
126
  this.holder = null;
60
- }
61
- ngOnInit() {
62
- this.uuid = generateUuid();
63
- const iframeUrl = buildImportUrl({
64
- licenseKey: this.licenseKey,
65
- customDomain: this.customDomain,
66
- dataLocation: this.dataLocation,
67
- language: this.language,
68
- theme: this.theme,
69
- environment: this.environment
70
- }, "angular", appVersion);
71
- this.safeUrl = this.sanitizer.sanitize(SecurityContext.RESOURCE_URL, this.sanitizer.bypassSecurityTrustResourceUrl(iframeUrl));
72
- }
73
- ngOnChanges(changes) {
74
- if (changes['user'] && changes['user'].currentValue !== changes['user'].previousValue) {
75
- this.updateUserVariable(changes['user'].currentValue);
76
- }
77
- }
78
- updateUserVariable(data) {
79
- this.user = data;
80
- if (this.iframe && this.iframe.contentWindow) {
81
- this.iframe.contentWindow.postMessage({
82
- "customer": data
83
- }, "*");
84
- }
85
- }
86
- ngAfterContentInit() {
87
- window.addEventListener("message", (event) => {
127
+ this.pendingFile = null;
128
+ // Data-location sheets redirect to another host, so the iframe origin can differ from
129
+ // targetOrigin. baseDomain is used to check the real origin before trusting it.
130
+ this.baseDomain = "app.csvbox.io";
131
+ this.fileAckTimer = null;
132
+ this.messageListener = (event) => {
133
+ // Older importers send these as bare strings, without a token.
134
+ const legacy = classifyLegacyMessage(event.data);
135
+ if (legacy) {
136
+ // Legacy messages carry no token, so the only way to tell which importer sent one is its
137
+ // window. Ignore those that came from another instance's iframe (or anywhere else).
138
+ if (!this.iframe || event.source !== this.iframe.contentWindow) {
139
+ return;
140
+ }
141
+ // A newer importer sends the typed message as well; act on that one only.
142
+ if (!this.messageGate.allowLegacy()) {
143
+ return;
144
+ }
145
+ const ended = this.importTracker.observe(legacy);
146
+ if (legacy.type === "mainModalHidden") {
147
+ this.handleModalClosed();
148
+ }
149
+ else if (legacy.type === "uploadSuccessful") {
150
+ if (this.onImport)
151
+ this.onImport(true);
152
+ if (this.isImported)
153
+ this.isImported(true);
154
+ if (this.imported)
155
+ this.imported(true);
156
+ }
157
+ else if (legacy.type === "uploadFailed") {
158
+ if (this.onImport)
159
+ this.onImport(false);
160
+ if (this.isImported)
161
+ this.isImported(false);
162
+ if (this.imported)
163
+ this.imported(false);
164
+ }
165
+ this.finishImport(ended);
166
+ return;
167
+ }
168
+ // Set-file handshake messages. The shared core does not classify these, so they are
169
+ // read here, behind the same unique_token check the core applies.
170
+ const raw = event.data;
171
+ if (raw && typeof raw === "object" && raw.data && raw.data.unique_token == this.uuid) {
172
+ this.fileSender.learnOrigin(event);
173
+ if (raw.type === "csvbox-set-file-rejected" && raw.data.reason === "not-a-file") {
174
+ // Reply to our probe (it has no file). It only tells us the origin, so do nothing.
175
+ return;
176
+ }
177
+ if (raw.type === "csvbox-modal-shown") {
178
+ this.clearFileAckTimer();
179
+ registerOpenImporter(this.registryEntry);
180
+ // What the file path in openModal() is waiting for. display as well as
181
+ // pointer-events: a second file declined while the first was still opening the
182
+ // modal has put the holder away in the meantime.
183
+ this.isModalShown = true;
184
+ if (this.holder) {
185
+ this.holder.style.display = 'block';
186
+ this.holder.style.pointerEvents = 'auto';
187
+ }
188
+ return;
189
+ }
190
+ if (raw.type === "csvbox-set-file-rejected") {
191
+ this.clearFileAckTimer();
192
+ // The importer has the file and will not take it. Put the holder away only if the
193
+ // modal was never confirmed up: a file declined while it is open leaves that modal
194
+ // where it is, and its own 'csvbox-modal-hidden' will put the holder away.
195
+ if (!this.isModalShown && this.holder) {
196
+ this.holder.style.display = 'none';
197
+ this.holder.style.pointerEvents = 'auto';
198
+ unregisterOpenImporter(this.registryEntry);
199
+ }
200
+ console.warn("[csvbox] importer declined the supplied file: " + raw.data.reason);
201
+ return;
202
+ }
203
+ }
88
204
  const message = classifyStructuredMessage(event.data, this.uuid);
89
205
  if (!message) {
90
206
  return;
91
207
  }
208
+ this.messageGate.markTyped();
209
+ const ended = this.importTracker.observe(message);
92
210
  if (message.type === "data-on-submit") {
93
211
  if (this.onSubmit)
94
212
  this.onSubmit(message.metadata);
95
213
  if (this.isSubmitted)
96
214
  this.isSubmitted(message.metadata);
97
215
  if (this.submitted)
98
- this.submitted();
216
+ this.submitted(message.metadata);
99
217
  }
100
218
  else if (message.type === "data-push-status") {
101
- if (!message.success) {
102
- delete message.metadata["unique_token"];
103
- }
104
219
  if (this.onImport)
105
220
  this.onImport(message.success, message.metadata);
106
221
  if (this.isImported)
@@ -127,7 +242,41 @@ class CSVBoxButtonComponent {
127
242
  if (this.imported)
128
243
  this.imported(false);
129
244
  }
130
- }, false);
245
+ this.finishImport(ended);
246
+ };
247
+ }
248
+ ngOnInit() {
249
+ this.uuid = generateUuid();
250
+ const iframeUrl = buildImportUrl({
251
+ licenseKey: this.licenseKey,
252
+ customDomain: this.customDomain,
253
+ dataLocation: this.dataLocation,
254
+ language: this.language,
255
+ theme: this.theme,
256
+ environment: this.environment
257
+ }, "angular", appVersion);
258
+ // Default place to send the file. A file is only ever sent to a named origin, never "*".
259
+ this.targetOrigin = new URL(iframeUrl).origin;
260
+ this.baseDomain = this.customDomain || "app.csvbox.io";
261
+ this.safeUrl = this.sanitizer.sanitize(SecurityContext.RESOURCE_URL, this.sanitizer.bypassSecurityTrustResourceUrl(iframeUrl));
262
+ }
263
+ ngOnChanges(changes) {
264
+ if (changes['user'] && changes['user'].currentValue !== changes['user'].previousValue) {
265
+ this.updateUserVariable(changes['user'].currentValue);
266
+ }
267
+ }
268
+ updateUserVariable(data) {
269
+ this.user = data;
270
+ if (this.iframe && this.iframe.contentWindow) {
271
+ this.iframe.contentWindow.postMessage({
272
+ "customer": data
273
+ }, "*");
274
+ }
275
+ }
276
+ ngAfterContentInit() {
277
+ window.addEventListener("message", this.messageListener, false);
278
+ this.syncBlocked();
279
+ this.unsubscribeOpenImporters = subscribeOpenImporters(() => this.syncBlocked());
131
280
  if (this.lazy) {
132
281
  this.disabled = false;
133
282
  }
@@ -136,16 +285,43 @@ class CSVBoxButtonComponent {
136
285
  this.initImporter();
137
286
  }
138
287
  }
288
+ syncBlocked() {
289
+ const blocked = isBlockedByOpenImporter(this.registryEntry);
290
+ if (blocked !== this.blockedByOther) {
291
+ // The registry can call back from outside Angular's zone (another instance, another app).
292
+ this.zone.run(() => {
293
+ this.blockedByOther = blocked;
294
+ this.cdr.markForCheck();
295
+ });
296
+ }
297
+ }
298
+ ngOnDestroy() {
299
+ window.removeEventListener("message", this.messageListener);
300
+ // Takes the holder out of document.body: left there, it would keep covering the page with
301
+ // an invisible full-screen layer that nothing is left to hide.
302
+ this.destroyImporter();
303
+ if (this.unsubscribeOpenImporters) {
304
+ this.unsubscribeOpenImporters();
305
+ this.unsubscribeOpenImporters = null;
306
+ }
307
+ }
139
308
  handleModalClosed() {
140
- if (this.holder)
141
- this.holder.style.display = 'none';
142
- this.isModalShown = false;
143
- this.isIframeLoaded = false;
144
- this.openModalOnIframeLoad = false;
145
- if (this.holder)
146
- this.holder.remove();
147
- this.holder = null;
148
- this.iframe = null;
309
+ if (this.importTracker.isInFlight()) {
310
+ // Closed after submitting and before the result arrived: keep the iframe and its listener
311
+ // so the late result still reaches onImport. It is removed once that result has been
312
+ // delivered (see finishImport), so the next open is a fresh load.
313
+ if (this.holder) {
314
+ this.holder.style.display = 'none';
315
+ this.holder.style.pointerEvents = 'auto';
316
+ }
317
+ this.isModalShown = false;
318
+ this.openModalOnIframeLoad = false;
319
+ this.clearFileAckTimer();
320
+ unregisterOpenImporter(this.registryEntry);
321
+ }
322
+ else {
323
+ this.destroyImporter();
324
+ }
149
325
  if (this.onClose)
150
326
  this.onClose();
151
327
  if (this.isClosed)
@@ -153,31 +329,60 @@ class CSVBoxButtonComponent {
153
329
  if (this.closed)
154
330
  this.closed();
155
331
  }
332
+ // Called after the result of an import has been delivered. If the modal was closed in the
333
+ // meantime the iframe was only hidden, so remove it now.
334
+ finishImport(ended) {
335
+ if (ended && !this.isModalShown && this.iframe) {
336
+ this.destroyImporter();
337
+ }
338
+ }
339
+ // Puts the importer away: the next open builds a new iframe with a new token.
340
+ destroyImporter() {
341
+ if (this.holder) {
342
+ this.holder.style.display = 'none';
343
+ this.holder.style.pointerEvents = 'auto';
344
+ this.holder.remove();
345
+ }
346
+ this.holder = null;
347
+ this.iframe = null;
348
+ this.isModalShown = false;
349
+ this.isIframeLoaded = false;
350
+ this.openModalOnIframeLoad = false;
351
+ this.messageGate.reset();
352
+ this.importTracker.reset();
353
+ // A new iframe is built on the next open, so the origin is learned again.
354
+ this.clearProbeTimer();
355
+ unregisterOpenImporter(this.registryEntry);
356
+ }
156
357
  initImporter() {
157
358
  var _a;
158
359
  if (this.loadStarted)
159
360
  this.loadStarted();
160
361
  this.uuid = generateUuid();
362
+ this.messageGate.reset();
363
+ this.importTracker.reset();
161
364
  insertCSS();
162
365
  const iframe = document.createElement("iframe");
163
366
  this.iframe = iframe;
164
367
  iframe.setAttribute("src", ((_a = this.safeUrl) === null || _a === void 0 ? void 0 : _a.toString()) || '');
368
+ iframe.setAttribute("allow", "clipboard-read; clipboard-write *");
165
369
  iframe.frameBorder = "0";
166
- const self = this;
167
- iframe.onload = function () {
168
- if (self.onReady)
169
- self.onReady();
170
- if (self.isReady)
171
- self.isReady();
172
- if (self.importerReady)
173
- self.importerReady();
174
- self.disabled = false;
175
- self.isIframeLoaded = true;
176
- if (self.iframe && self.iframe.contentWindow) {
177
- self.iframe.contentWindow.postMessage(buildInitPayload(self.user, self.dynamicColumns, self.options, self.uuid), "*");
370
+ iframe.onload = () => {
371
+ this.disabled = false;
372
+ this.isIframeLoaded = true;
373
+ // The handshake goes first: a consumer that calls openModalWithFile() from the ready
374
+ // callback needs the importer to already have its customer, columns, options and token.
375
+ if (this.iframe && this.iframe.contentWindow) {
376
+ this.iframe.contentWindow.postMessage(buildInitPayload(this.user, this.dynamicColumns, this.options, this.uuid), "*");
178
377
  }
179
- if (self.openModalOnIframeLoad) {
180
- self.openModal();
378
+ if (this.onReady)
379
+ this.onReady();
380
+ if (this.isReady)
381
+ this.isReady();
382
+ if (this.importerReady)
383
+ this.importerReady();
384
+ if (this.openModalOnIframeLoad) {
385
+ this.openModal();
181
386
  }
182
387
  };
183
388
  this.holder = document.createElement('div');
@@ -185,8 +390,13 @@ class CSVBoxButtonComponent {
185
390
  this.holder.setAttribute('id', `csvbox-embed-${this.uuid}`);
186
391
  this.holder.appendChild(iframe);
187
392
  document.body.insertAdjacentElement('beforeend', this.holder);
393
+ // The holder did not exist yet if openModal() asked for the importer before it was built.
394
+ restackOpenImporters(openImporters());
188
395
  }
189
396
  openModal() {
397
+ // Counts as open from the moment it is requested, so the other buttons are disabled while
398
+ // this importer is still loading, and so it is stacked above any importer opened earlier.
399
+ registerOpenImporter(this.registryEntry);
190
400
  if (!this.iframe) {
191
401
  this.openModalOnIframeLoad = true;
192
402
  this.initImporter();
@@ -194,11 +404,36 @@ class CSVBoxButtonComponent {
194
404
  }
195
405
  if (!this.isModalShown) {
196
406
  if (this.isIframeLoaded) {
197
- this.isModalShown = true;
198
- if (this.holder)
199
- this.holder.style.display = 'block';
200
- if (this.iframe && this.iframe.contentWindow) {
201
- this.iframe.contentWindow.postMessage('openModal', '*');
407
+ if (this.pendingFile) {
408
+ // The importer opens its own modal once it has the file, so that it can inject on
409
+ // 'shown.bs.modal'. Sending 'openModal' as well would race that.
410
+ //
411
+ // It can also decline: wrong step, an import file URL configured on the sheet, or
412
+ // an importer too old to know the message at all. So the holder goes up
413
+ // click-through and stays that way until 'csvbox-modal-shown' confirms the modal
414
+ // is really up. It covers the viewport at z-index 2147483647 and the importer
415
+ // renders transparent with its modal closed, so arming it on an unanswered message
416
+ // leaves an invisible sheet over the host page eating every click.
417
+ //
418
+ // display:block regardless, so the iframe lays out and animates normally; only
419
+ // pointer-events is held back. isModalShown likewise waits for the confirmation,
420
+ // or a declined file would latch the importer shut for good.
421
+ if (this.holder) {
422
+ this.holder.style.pointerEvents = 'none';
423
+ this.holder.style.display = 'block';
424
+ }
425
+ this.fileSender.send();
426
+ }
427
+ else {
428
+ this.isModalShown = true;
429
+ if (this.holder) {
430
+ // Explicit: a previously declined file leaves the holder click-through.
431
+ this.holder.style.pointerEvents = 'auto';
432
+ this.holder.style.display = 'block';
433
+ }
434
+ if (this.iframe && this.iframe.contentWindow) {
435
+ this.iframe.contentWindow.postMessage('openModal', '*');
436
+ }
202
437
  }
203
438
  }
204
439
  else {
@@ -206,11 +441,68 @@ class CSVBoxButtonComponent {
206
441
  }
207
442
  }
208
443
  }
444
+ /**
445
+ * Open the importer on a File the host page already has, instead of the file picker.
446
+ *
447
+ * Reachable through a template reference (`#importer` then `importer.openModalWithFile(file)`)
448
+ * or `@ViewChild(CSVBoxButtonComponent)`.
449
+ *
450
+ * The File crosses to the iframe by structured clone, so the importer receives the real
451
+ * object and applies its own extension, worksheet and size rules to it -- this does not
452
+ * bypass any of them. Pass a File; a Blob has no name for the importer to read an
453
+ * extension from.
454
+ */
455
+ openModalWithFile(file) {
456
+ if (typeof File === 'undefined' || !(file instanceof File)) {
457
+ return;
458
+ }
459
+ this.pendingFile = file;
460
+ // Already open: hand it over now rather than holding it for the next open. The importer
461
+ // takes it only while it is still on the upload step, and ignores it once the user has a
462
+ // dataset and a mapping in progress.
463
+ if (this.isModalShown && this.iframe && this.isIframeLoaded) {
464
+ this.fileSender.send();
465
+ return;
466
+ }
467
+ this.openModal();
468
+ }
469
+ clearProbeTimer() {
470
+ this.clearFileAckTimer();
471
+ this.fileSender.reset();
472
+ }
473
+ startFileAckTimer() {
474
+ this.clearFileAckTimer();
475
+ this.fileAckTimer = setTimeout(() => {
476
+ this.fileAckTimer = null;
477
+ this.giveUpOnFile();
478
+ }, FILE_ACK_TIMEOUT_MS);
479
+ }
480
+ clearFileAckTimer() {
481
+ if (this.fileAckTimer) {
482
+ clearTimeout(this.fileAckTimer);
483
+ this.fileAckTimer = null;
484
+ }
485
+ }
486
+ // The importer never confirmed the file it was sent (an origin we do not trust, or an importer too
487
+ // old to know the message). Without this the holder would stay up, invisible and click-through,
488
+ // and the other instances' buttons would stay disabled for good.
489
+ giveUpOnFile() {
490
+ if (this.isModalShown) {
491
+ return;
492
+ }
493
+ const holder = this.holder;
494
+ if (holder) {
495
+ holder.style.display = 'none';
496
+ holder.style.pointerEvents = 'auto';
497
+ }
498
+ unregisterOpenImporter(this.registryEntry);
499
+ console.warn("[csvbox] the importer did not respond to the supplied file");
500
+ }
209
501
  }
210
- CSVBoxButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CSVBoxButtonComponent, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
502
+ CSVBoxButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CSVBoxButtonComponent, deps: [{ token: i1.DomSanitizer }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
211
503
  CSVBoxButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: CSVBoxButtonComponent, isStandalone: true, selector: "csvbox-button", inputs: { onImport: "onImport", onReady: "onReady", onClose: "onClose", onSubmit: "onSubmit", isImported: "isImported", isReady: "isReady", isClosed: "isClosed", isSubmitted: "isSubmitted", importerReady: "importerReady", closed: "closed", submitted: "submitted", imported: "imported", loadStarted: "loadStarted", user: "user", dynamicColumns: "dynamicColumns", licenseKey: "licenseKey", options: "options", uuid: "uuid", customDomain: "customDomain", dataLocation: "dataLocation", language: "language", environment: "environment", theme: "theme", isIframeLoaded: "isIframeLoaded", openModalOnIframeLoad: "openModalOnIframeLoad", lazy: "lazy", disabled: "disabled" }, usesOnChanges: true, ngImport: i0, template: `
212
504
  <div>
213
- <button [disabled]="disabled" (click)="openModal()" [attr.data-csvbox-token]="uuid">
505
+ <button [disabled]="disabled || blockedByOther" (click)="openModal()" [attr.data-csvbox-token]="uuid">
214
506
  <ng-content></ng-content>
215
507
  </button>
216
508
  </div>
@@ -222,13 +514,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
222
514
  standalone: true,
223
515
  template: `
224
516
  <div>
225
- <button [disabled]="disabled" (click)="openModal()" [attr.data-csvbox-token]="uuid">
517
+ <button [disabled]="disabled || blockedByOther" (click)="openModal()" [attr.data-csvbox-token]="uuid">
226
518
  <ng-content></ng-content>
227
519
  </button>
228
520
  </div>
229
521
  `
230
522
  }]
231
- }], ctorParameters: function () { return [{ type: i1.DomSanitizer }]; }, propDecorators: { onImport: [{
523
+ }], ctorParameters: function () { return [{ type: i1.DomSanitizer }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }]; }, propDecorators: { onImport: [{
232
524
  type: Input
233
525
  }], onReady: [{
234
526
  type: Input