@sumit_yewale/angular 1.1.18 → 1.1.21-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
- import { SecurityContext, Component, ViewChild, Input, NgModule } from '@angular/core';
1
+ import { SecurityContext, Component, ChangeDetectorRef, NgZone, Input, NgModule } from '@angular/core';
2
2
  import { DomSanitizer } from '@angular/platform-browser';
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
 
5
5
  /**
6
6
  * @fileoverview added by tsickle
@@ -34,11 +34,129 @@ function () {
34
34
  * Generated from: lib/csvbox-button.component.ts
35
35
  * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
36
36
  */
37
+ // How long we wait for the importer to confirm a file it was sent before giving up on it.
37
38
  /** @type {?} */
38
- var appVersion = '1.1.16';
39
+ var FILE_ACK_TIMEOUT_MS = 8000;
40
+ /**
41
+ * @record
42
+ */
43
+ function OpenImporterEntry() { }
44
+ if (false) {
45
+ /** @type {?} */
46
+ OpenImporterEntry.prototype.getHolder;
47
+ }
48
+ /**
49
+ * @record
50
+ */
51
+ function OpenImporters() { }
52
+ if (false) {
53
+ /** @type {?} */
54
+ OpenImporters.prototype.open;
55
+ /** @type {?} */
56
+ OpenImporters.prototype.listeners;
57
+ }
58
+ /** @type {?} */
59
+ var OPEN_IMPORTERS_KEY = '__csvboxOpenImporters';
60
+ /** @type {?} */
61
+ var TOP_Z_INDEX = 2147483647;
62
+ /**
63
+ * @return {?}
64
+ */
65
+ function openImporters() {
66
+ /** @type {?} */
67
+ var w = (/** @type {?} */ (window));
68
+ if (!w[OPEN_IMPORTERS_KEY])
69
+ w[OPEN_IMPORTERS_KEY] = { open: [], listeners: [] };
70
+ return w[OPEN_IMPORTERS_KEY];
71
+ }
72
+ /**
73
+ * @param {?} reg
74
+ * @return {?}
75
+ */
76
+ function restackOpenImporters(reg) {
77
+ reg.open.forEach((/**
78
+ * @param {?} entry
79
+ * @param {?} i
80
+ * @return {?}
81
+ */
82
+ function (entry, i) {
83
+ /** @type {?} */
84
+ var holder = entry.getHolder();
85
+ if (holder)
86
+ holder.style.zIndex = String(TOP_Z_INDEX - (reg.open.length - 1 - i));
87
+ }));
88
+ }
89
+ /**
90
+ * @param {?} entry
91
+ * @return {?}
92
+ */
93
+ function registerOpenImporter(entry) {
94
+ /** @type {?} */
95
+ var reg = openImporters();
96
+ if (reg.open.indexOf(entry) >= 0)
97
+ return;
98
+ reg.open.push(entry);
99
+ restackOpenImporters(reg);
100
+ reg.listeners.slice().forEach((/**
101
+ * @param {?} fn
102
+ * @return {?}
103
+ */
104
+ function (fn) { return fn(); }));
105
+ }
106
+ /**
107
+ * @param {?} entry
108
+ * @return {?}
109
+ */
110
+ function unregisterOpenImporter(entry) {
111
+ /** @type {?} */
112
+ var reg = openImporters();
113
+ /** @type {?} */
114
+ var i = reg.open.indexOf(entry);
115
+ if (i < 0)
116
+ return;
117
+ reg.open.splice(i, 1);
118
+ restackOpenImporters(reg);
119
+ reg.listeners.slice().forEach((/**
120
+ * @param {?} fn
121
+ * @return {?}
122
+ */
123
+ function (fn) { return fn(); }));
124
+ }
125
+ /**
126
+ * @param {?} entry
127
+ * @return {?}
128
+ */
129
+ function isBlockedByOpenImporter(entry) {
130
+ /** @type {?} */
131
+ var reg = openImporters();
132
+ return reg.open.length > 0 && reg.open.indexOf(entry) < 0;
133
+ }
134
+ /**
135
+ * @param {?} fn
136
+ * @return {?}
137
+ */
138
+ function subscribeOpenImporters(fn) {
139
+ /** @type {?} */
140
+ var reg = openImporters();
141
+ reg.listeners.push(fn);
142
+ return (/**
143
+ * @return {?}
144
+ */
145
+ function () {
146
+ /** @type {?} */
147
+ var i = reg.listeners.indexOf(fn);
148
+ if (i >= 0)
149
+ reg.listeners.splice(i, 1);
150
+ });
151
+ }
152
+ /** @type {?} */
153
+ var appVersion = '1.1.20';
39
154
  var CSVBoxButtonComponent = /** @class */ (function () {
40
- function CSVBoxButtonComponent(sanitizer) {
155
+ function CSVBoxButtonComponent(sanitizer, cdr, zone) {
156
+ var _this = this;
41
157
  this.sanitizer = sanitizer;
158
+ this.cdr = cdr;
159
+ this.zone = zone;
42
160
  this.isModalShown = false;
43
161
  this.licenseKey = null;
44
162
  this.uuid = null;
@@ -51,6 +169,175 @@ var CSVBoxButtonComponent = /** @class */ (function () {
51
169
  this.lazy = false;
52
170
  this.iframe = null;
53
171
  this.disabled = true;
172
+ // True while a different importer on the page is open: the button stays disabled.
173
+ this.blockedByOther = false;
174
+ this.messageGate = createMessageGate();
175
+ this.importTracker = createImportTracker();
176
+ this.registryEntry = { getHolder: (/**
177
+ * @return {?}
178
+ */
179
+ function () { return _this.holder; }) };
180
+ this.fileSender = createFileSender({
181
+ getIframe: (/**
182
+ * @return {?}
183
+ */
184
+ function () { return _this.iframe; }),
185
+ getToken: (/**
186
+ * @return {?}
187
+ */
188
+ function () { return (/** @type {?} */ (_this.uuid)); }),
189
+ getTargetOrigin: (/**
190
+ * @return {?}
191
+ */
192
+ function () { return (/** @type {?} */ (_this.targetOrigin)); }),
193
+ getBaseDomain: (/**
194
+ * @return {?}
195
+ */
196
+ function () { return _this.baseDomain; }),
197
+ takeFile: (/**
198
+ * @return {?}
199
+ */
200
+ function () {
201
+ /** @type {?} */
202
+ var file = _this.pendingFile;
203
+ _this.pendingFile = null;
204
+ return file;
205
+ }),
206
+ onSent: (/**
207
+ * @return {?}
208
+ */
209
+ function () { return _this.startFileAckTimer(); })
210
+ });
211
+ this.unsubscribeOpenImporters = null;
212
+ this.pendingFile = null;
213
+ // Data-location sheets redirect to another host, so the iframe origin can differ from
214
+ // targetOrigin. baseDomain is used to check the real origin before trusting it.
215
+ this.baseDomain = "app.csvbox.io";
216
+ this.fileAckTimer = null;
217
+ this.messageListener = (/**
218
+ * @param {?} event
219
+ * @return {?}
220
+ */
221
+ function (event) {
222
+ // Older importers send these as bare strings, without a token.
223
+ /** @type {?} */
224
+ var legacy = classifyLegacyMessage(event.data);
225
+ if (legacy) {
226
+ // Legacy messages carry no token, so the only way to tell which importer sent one is its
227
+ // window. Ignore those that came from another instance's iframe (or anywhere else).
228
+ if (!_this.iframe || event.source !== _this.iframe.contentWindow) {
229
+ return;
230
+ }
231
+ // A newer importer sends the typed message as well; act on that one only.
232
+ if (!_this.messageGate.allowLegacy()) {
233
+ return;
234
+ }
235
+ /** @type {?} */
236
+ var ended_1 = _this.importTracker.observe(legacy);
237
+ if (legacy.type === "mainModalHidden") {
238
+ _this.handleModalClosed();
239
+ }
240
+ else if (legacy.type === "uploadSuccessful") {
241
+ if (_this.onImport)
242
+ _this.onImport(true);
243
+ if (_this.isImported)
244
+ _this.isImported(true);
245
+ if (_this.imported)
246
+ _this.imported(true);
247
+ }
248
+ else if (legacy.type === "uploadFailed") {
249
+ if (_this.onImport)
250
+ _this.onImport(false);
251
+ if (_this.isImported)
252
+ _this.isImported(false);
253
+ if (_this.imported)
254
+ _this.imported(false);
255
+ }
256
+ _this.finishImport(ended_1);
257
+ return;
258
+ }
259
+ // Set-file handshake messages. The shared core does not classify these, so they are
260
+ // read here, behind the same unique_token check the core applies.
261
+ /** @type {?} */
262
+ var raw = event.data;
263
+ if (raw && typeof raw === "object" && raw.data && raw.data.unique_token == _this.uuid) {
264
+ _this.fileSender.learnOrigin(event);
265
+ if (raw.type === "csvbox-set-file-rejected" && raw.data.reason === "not-a-file") {
266
+ // Reply to our probe (it has no file). It only tells us the origin, so do nothing.
267
+ return;
268
+ }
269
+ if (raw.type === "csvbox-modal-shown") {
270
+ _this.clearFileAckTimer();
271
+ registerOpenImporter(_this.registryEntry);
272
+ // What the file path in openModal() is waiting for. display as well as
273
+ // pointer-events: a second file declined while the first was still opening the
274
+ // modal has put the holder away in the meantime.
275
+ _this.isModalShown = true;
276
+ if (_this.holder) {
277
+ _this.holder.style.display = 'block';
278
+ _this.holder.style.pointerEvents = 'auto';
279
+ }
280
+ return;
281
+ }
282
+ if (raw.type === "csvbox-set-file-rejected") {
283
+ _this.clearFileAckTimer();
284
+ // The importer has the file and will not take it. Put the holder away only if the
285
+ // modal was never confirmed up: a file declined while it is open leaves that modal
286
+ // where it is, and its own 'csvbox-modal-hidden' will put the holder away.
287
+ if (!_this.isModalShown && _this.holder) {
288
+ _this.holder.style.display = 'none';
289
+ _this.holder.style.pointerEvents = 'auto';
290
+ unregisterOpenImporter(_this.registryEntry);
291
+ }
292
+ console.warn("[csvbox] importer declined the supplied file: " + raw.data.reason);
293
+ return;
294
+ }
295
+ }
296
+ /** @type {?} */
297
+ var message = classifyStructuredMessage(event.data, (/** @type {?} */ (_this.uuid)));
298
+ if (!message) {
299
+ return;
300
+ }
301
+ _this.messageGate.markTyped();
302
+ /** @type {?} */
303
+ var ended = _this.importTracker.observe(message);
304
+ if (message.type === "data-on-submit") {
305
+ if (_this.onSubmit)
306
+ _this.onSubmit(message.metadata);
307
+ if (_this.isSubmitted)
308
+ _this.isSubmitted(message.metadata);
309
+ if (_this.submitted)
310
+ _this.submitted(message.metadata);
311
+ }
312
+ else if (message.type === "data-push-status") {
313
+ if (_this.onImport)
314
+ _this.onImport(message.success, message.metadata);
315
+ if (_this.isImported)
316
+ _this.isImported(message.success, message.metadata);
317
+ if (_this.imported)
318
+ _this.imported(message.success, message.metadata);
319
+ }
320
+ else if (message.type === "csvbox-modal-hidden") {
321
+ _this.handleModalClosed();
322
+ }
323
+ else if (message.type === "csvbox-upload-successful") {
324
+ if (_this.onImport)
325
+ _this.onImport(true);
326
+ if (_this.isImported)
327
+ _this.isImported(true);
328
+ if (_this.imported)
329
+ _this.imported(true);
330
+ }
331
+ else if (message.type === "csvbox-upload-failed") {
332
+ if (_this.onImport)
333
+ _this.onImport(false);
334
+ if (_this.isImported)
335
+ _this.isImported(false);
336
+ if (_this.imported)
337
+ _this.imported(false);
338
+ }
339
+ _this.finishImport(ended);
340
+ });
54
341
  }
55
342
  /**
56
343
  * @return {?}
@@ -69,6 +356,9 @@ var CSVBoxButtonComponent = /** @class */ (function () {
69
356
  theme: this.theme,
70
357
  environment: this.environment
71
358
  }, "angular", appVersion);
359
+ // Default place to send the file. A file is only ever sent to a named origin, never "*".
360
+ this.targetOrigin = new URL(iframeUrl).origin;
361
+ this.baseDomain = this.customDomain || "app.csvbox.io";
72
362
  this.safeUrl = this.sanitizer.sanitize(SecurityContext.RESOURCE_URL, this.sanitizer.bypassSecurityTrustResourceUrl(iframeUrl));
73
363
  };
74
364
  /**
@@ -108,55 +398,12 @@ var CSVBoxButtonComponent = /** @class */ (function () {
108
398
  */
109
399
  function () {
110
400
  var _this = this;
111
- window.addEventListener("message", (/**
112
- * @param {?} event
401
+ window.addEventListener("message", this.messageListener, false);
402
+ this.syncBlocked();
403
+ this.unsubscribeOpenImporters = subscribeOpenImporters((/**
113
404
  * @return {?}
114
405
  */
115
- function (event) {
116
- /** @type {?} */
117
- var message = classifyStructuredMessage(event.data, (/** @type {?} */ (_this.uuid)));
118
- if (!message) {
119
- return;
120
- }
121
- if (message.type === "data-on-submit") {
122
- if (_this.onSubmit)
123
- _this.onSubmit(message.metadata);
124
- if (_this.isSubmitted)
125
- _this.isSubmitted(message.metadata);
126
- if (_this.submitted)
127
- _this.submitted(message.metadata);
128
- }
129
- else if (message.type === "data-push-status") {
130
- if (!message.success) {
131
- delete message.metadata["unique_token"];
132
- }
133
- if (_this.onImport)
134
- _this.onImport(message.success, message.metadata);
135
- if (_this.isImported)
136
- _this.isImported(message.success, message.metadata);
137
- if (_this.imported)
138
- _this.imported(message.success, message.metadata);
139
- }
140
- else if (message.type === "csvbox-modal-hidden") {
141
- _this.handleModalClosed();
142
- }
143
- else if (message.type === "csvbox-upload-successful") {
144
- if (_this.onImport)
145
- _this.onImport(true);
146
- if (_this.isImported)
147
- _this.isImported(true);
148
- if (_this.imported)
149
- _this.imported(true);
150
- }
151
- else if (message.type === "csvbox-upload-failed") {
152
- if (_this.onImport)
153
- _this.onImport(false);
154
- if (_this.isImported)
155
- _this.isImported(false);
156
- if (_this.imported)
157
- _this.imported(false);
158
- }
159
- }), false);
406
+ function () { return _this.syncBlocked(); }));
160
407
  if (this.lazy) {
161
408
  this.disabled = false;
162
409
  }
@@ -172,15 +419,22 @@ var CSVBoxButtonComponent = /** @class */ (function () {
172
419
  * @return {?}
173
420
  */
174
421
  function () {
175
- if (this.holder)
176
- this.holder.style.display = 'none';
177
- this.isModalShown = false;
178
- this.isIframeLoaded = false;
179
- this.openModalOnIframeLoad = false;
180
- if (this.holder)
181
- this.holder.remove();
182
- this.holder = null;
183
- this.iframe = null;
422
+ if (this.importTracker.isInFlight()) {
423
+ // Closed after submitting and before the result arrived: keep the iframe and its listener
424
+ // so the late result still reaches onImport. It is removed once that result has been
425
+ // delivered (see finishImport), so the next open is a fresh load.
426
+ if (this.holder) {
427
+ this.holder.style.display = 'none';
428
+ this.holder.style.pointerEvents = 'auto';
429
+ }
430
+ this.isModalShown = false;
431
+ this.openModalOnIframeLoad = false;
432
+ this.clearFileAckTimer();
433
+ unregisterOpenImporter(this.registryEntry);
434
+ }
435
+ else {
436
+ this.destroyImporter();
437
+ }
184
438
  if (this.onClose)
185
439
  this.onClose();
186
440
  if (this.isClosed)
@@ -188,6 +442,57 @@ var CSVBoxButtonComponent = /** @class */ (function () {
188
442
  if (this.closed)
189
443
  this.closed();
190
444
  };
445
+ // Called after the result of an import has been delivered. If the modal was closed in the
446
+ // meantime the iframe was only hidden, so remove it now.
447
+ // Called after the result of an import has been delivered. If the modal was closed in the
448
+ // meantime the iframe was only hidden, so remove it now.
449
+ /**
450
+ * @private
451
+ * @param {?} ended
452
+ * @return {?}
453
+ */
454
+ CSVBoxButtonComponent.prototype.finishImport =
455
+ // Called after the result of an import has been delivered. If the modal was closed in the
456
+ // meantime the iframe was only hidden, so remove it now.
457
+ /**
458
+ * @private
459
+ * @param {?} ended
460
+ * @return {?}
461
+ */
462
+ function (ended) {
463
+ if (ended && !this.isModalShown && this.iframe) {
464
+ this.destroyImporter();
465
+ }
466
+ };
467
+ // Puts the importer away: the next open builds a new iframe with a new token.
468
+ // Puts the importer away: the next open builds a new iframe with a new token.
469
+ /**
470
+ * @private
471
+ * @return {?}
472
+ */
473
+ CSVBoxButtonComponent.prototype.destroyImporter =
474
+ // Puts the importer away: the next open builds a new iframe with a new token.
475
+ /**
476
+ * @private
477
+ * @return {?}
478
+ */
479
+ function () {
480
+ if (this.holder) {
481
+ this.holder.style.display = 'none';
482
+ this.holder.style.pointerEvents = 'auto';
483
+ this.holder.remove();
484
+ }
485
+ this.holder = null;
486
+ this.iframe = null;
487
+ this.isModalShown = false;
488
+ this.isIframeLoaded = false;
489
+ this.openModalOnIframeLoad = false;
490
+ this.messageGate.reset();
491
+ this.importTracker.reset();
492
+ // A new iframe is built on the next open, so the origin is learned again.
493
+ this.clearProbeTimer();
494
+ unregisterOpenImporter(this.registryEntry);
495
+ };
191
496
  /**
192
497
  * @return {?}
193
498
  */
@@ -198,11 +503,14 @@ var CSVBoxButtonComponent = /** @class */ (function () {
198
503
  if (this.loadStarted)
199
504
  this.loadStarted();
200
505
  this.uuid = generateUuid();
506
+ this.messageGate.reset();
507
+ this.importTracker.reset();
201
508
  insertCSS();
202
509
  /** @type {?} */
203
510
  var iframe = document.createElement("iframe");
204
511
  this.iframe = iframe;
205
512
  iframe.setAttribute("src", this.safeUrl);
513
+ iframe.setAttribute("allow", "clipboard-read; clipboard-write *");
206
514
  iframe.frameBorder = "0";
207
515
  /** @type {?} */
208
516
  var self = this;
@@ -210,17 +518,19 @@ var CSVBoxButtonComponent = /** @class */ (function () {
210
518
  * @return {?}
211
519
  */
212
520
  function () {
521
+ self.disabled = false;
522
+ self.isIframeLoaded = true;
523
+ // The handshake goes first: a consumer that calls openModalWithFile() from the ready
524
+ // callback needs the importer to already have its customer, columns, options and token.
525
+ if (self.iframe && self.iframe.contentWindow) {
526
+ self.iframe.contentWindow.postMessage(buildInitPayload(self.user, self.dynamicColumns, self.options, (/** @type {?} */ (self.uuid))), "*");
527
+ }
213
528
  if (self.onReady)
214
529
  self.onReady();
215
530
  if (self.isReady)
216
531
  self.isReady();
217
532
  if (self.importerReady)
218
533
  self.importerReady();
219
- self.disabled = false;
220
- self.isIframeLoaded = true;
221
- if (self.iframe && self.iframe.contentWindow) {
222
- self.iframe.contentWindow.postMessage(buildInitPayload(self.user, self.dynamicColumns, self.options, (/** @type {?} */ (self.uuid))), "*");
223
- }
224
534
  if (self.openModalOnIframeLoad) {
225
535
  self.openModal();
226
536
  }
@@ -230,6 +540,8 @@ var CSVBoxButtonComponent = /** @class */ (function () {
230
540
  this.holder.setAttribute('id', "csvbox-embed-" + this.uuid);
231
541
  this.holder.appendChild(iframe);
232
542
  document.body.insertAdjacentElement('beforeend', this.holder);
543
+ // The holder did not exist yet if openModal() asked for the importer before it was built.
544
+ restackOpenImporters(openImporters());
233
545
  };
234
546
  /**
235
547
  * @return {?}
@@ -238,6 +550,9 @@ var CSVBoxButtonComponent = /** @class */ (function () {
238
550
  * @return {?}
239
551
  */
240
552
  function () {
553
+ // Counts as open from the moment it is requested, so the other buttons are disabled while
554
+ // this importer is still loading, and so it is stacked above any importer opened earlier.
555
+ registerOpenImporter(this.registryEntry);
241
556
  if (!this.iframe) {
242
557
  this.openModalOnIframeLoad = true;
243
558
  this.initImporter();
@@ -245,11 +560,36 @@ var CSVBoxButtonComponent = /** @class */ (function () {
245
560
  }
246
561
  if (!this.isModalShown) {
247
562
  if (this.isIframeLoaded) {
248
- this.isModalShown = true;
249
- if (this.holder)
250
- this.holder.style.display = 'block';
251
- if (this.iframe && this.iframe.contentWindow) {
252
- this.iframe.contentWindow.postMessage('openModal', '*');
563
+ if (this.pendingFile) {
564
+ // The importer opens its own modal once it has the file, so that it can inject on
565
+ // 'shown.bs.modal'. Sending 'openModal' as well would race that.
566
+ //
567
+ // It can also decline: wrong step, an import file URL configured on the sheet, or
568
+ // an importer too old to know the message at all. So the holder goes up
569
+ // click-through and stays that way until 'csvbox-modal-shown' confirms the modal
570
+ // is really up. It covers the viewport at z-index 2147483647 and the importer
571
+ // renders transparent with its modal closed, so arming it on an unanswered message
572
+ // leaves an invisible sheet over the host page eating every click.
573
+ //
574
+ // display:block regardless, so the iframe lays out and animates normally; only
575
+ // pointer-events is held back. isModalShown likewise waits for the confirmation,
576
+ // or a declined file would latch the importer shut for good.
577
+ if (this.holder) {
578
+ this.holder.style.pointerEvents = 'none';
579
+ this.holder.style.display = 'block';
580
+ }
581
+ this.fileSender.send();
582
+ }
583
+ else {
584
+ this.isModalShown = true;
585
+ if (this.holder) {
586
+ // Explicit: a previously declined file leaves the holder click-through.
587
+ this.holder.style.pointerEvents = 'auto';
588
+ this.holder.style.display = 'block';
589
+ }
590
+ if (this.iframe && this.iframe.contentWindow) {
591
+ this.iframe.contentWindow.postMessage('openModal', '*');
592
+ }
253
593
  }
254
594
  }
255
595
  else {
@@ -257,18 +597,177 @@ var CSVBoxButtonComponent = /** @class */ (function () {
257
597
  }
258
598
  }
259
599
  };
600
+ /**
601
+ * Open the importer on a File the host page already has, instead of the file picker.
602
+ *
603
+ * Reachable through a template reference (`#importer` then `importer.openModalWithFile(file)`)
604
+ * or `@ViewChild(CSVBoxButtonComponent)`.
605
+ *
606
+ * The File crosses to the iframe by structured clone, so the importer receives the real
607
+ * object and applies its own extension, worksheet and size rules to it -- this does not
608
+ * bypass any of them. Pass a File; a Blob has no name for the importer to read an
609
+ * extension from.
610
+ */
611
+ /**
612
+ * Open the importer on a File the host page already has, instead of the file picker.
613
+ *
614
+ * Reachable through a template reference (`#importer` then `importer.openModalWithFile(file)`)
615
+ * or `\@ViewChild(CSVBoxButtonComponent)`.
616
+ *
617
+ * The File crosses to the iframe by structured clone, so the importer receives the real
618
+ * object and applies its own extension, worksheet and size rules to it -- this does not
619
+ * bypass any of them. Pass a File; a Blob has no name for the importer to read an
620
+ * extension from.
621
+ * @param {?} file
622
+ * @return {?}
623
+ */
624
+ CSVBoxButtonComponent.prototype.openModalWithFile = /**
625
+ * Open the importer on a File the host page already has, instead of the file picker.
626
+ *
627
+ * Reachable through a template reference (`#importer` then `importer.openModalWithFile(file)`)
628
+ * or `\@ViewChild(CSVBoxButtonComponent)`.
629
+ *
630
+ * The File crosses to the iframe by structured clone, so the importer receives the real
631
+ * object and applies its own extension, worksheet and size rules to it -- this does not
632
+ * bypass any of them. Pass a File; a Blob has no name for the importer to read an
633
+ * extension from.
634
+ * @param {?} file
635
+ * @return {?}
636
+ */
637
+ function (file) {
638
+ if (typeof File === 'undefined' || !(file instanceof File)) {
639
+ return;
640
+ }
641
+ this.pendingFile = file;
642
+ // Already open: hand it over now rather than holding it for the next open. The importer
643
+ // takes it only while it is still on the upload step, and ignores it once the user has a
644
+ // dataset and a mapping in progress.
645
+ if (this.isModalShown && this.iframe && this.isIframeLoaded) {
646
+ this.fileSender.send();
647
+ return;
648
+ }
649
+ this.openModal();
650
+ };
651
+ /**
652
+ * @return {?}
653
+ */
654
+ CSVBoxButtonComponent.prototype.clearProbeTimer = /**
655
+ * @return {?}
656
+ */
657
+ function () {
658
+ this.clearFileAckTimer();
659
+ this.fileSender.reset();
660
+ };
661
+ /**
662
+ * @return {?}
663
+ */
664
+ CSVBoxButtonComponent.prototype.startFileAckTimer = /**
665
+ * @return {?}
666
+ */
667
+ function () {
668
+ var _this = this;
669
+ this.clearFileAckTimer();
670
+ this.fileAckTimer = setTimeout((/**
671
+ * @return {?}
672
+ */
673
+ function () {
674
+ _this.fileAckTimer = null;
675
+ _this.giveUpOnFile();
676
+ }), FILE_ACK_TIMEOUT_MS);
677
+ };
678
+ /**
679
+ * @return {?}
680
+ */
681
+ CSVBoxButtonComponent.prototype.clearFileAckTimer = /**
682
+ * @return {?}
683
+ */
684
+ function () {
685
+ if (this.fileAckTimer) {
686
+ clearTimeout(this.fileAckTimer);
687
+ this.fileAckTimer = null;
688
+ }
689
+ };
690
+ // The importer never confirmed the file it was sent (an origin we do not trust, or an importer too
691
+ // old to know the message). Without this the holder would stay up, invisible and click-through,
692
+ // and the other instances' buttons would stay disabled for good.
693
+ // The importer never confirmed the file it was sent (an origin we do not trust, or an importer too
694
+ // old to know the message). Without this the holder would stay up, invisible and click-through,
695
+ // and the other instances' buttons would stay disabled for good.
696
+ /**
697
+ * @return {?}
698
+ */
699
+ CSVBoxButtonComponent.prototype.giveUpOnFile =
700
+ // The importer never confirmed the file it was sent (an origin we do not trust, or an importer too
701
+ // old to know the message). Without this the holder would stay up, invisible and click-through,
702
+ // and the other instances' buttons would stay disabled for good.
703
+ /**
704
+ * @return {?}
705
+ */
706
+ function () {
707
+ if (this.isModalShown) {
708
+ return;
709
+ }
710
+ /** @type {?} */
711
+ var holder = this.holder;
712
+ if (holder) {
713
+ holder.style.display = 'none';
714
+ holder.style.pointerEvents = 'auto';
715
+ }
716
+ unregisterOpenImporter(this.registryEntry);
717
+ console.warn("[csvbox] the importer did not respond to the supplied file");
718
+ };
719
+ /**
720
+ * @private
721
+ * @return {?}
722
+ */
723
+ CSVBoxButtonComponent.prototype.syncBlocked = /**
724
+ * @private
725
+ * @return {?}
726
+ */
727
+ function () {
728
+ var _this = this;
729
+ /** @type {?} */
730
+ var blocked = isBlockedByOpenImporter(this.registryEntry);
731
+ if (blocked !== this.blockedByOther) {
732
+ // The registry can call back from outside Angular's zone (another instance, another app).
733
+ this.zone.run((/**
734
+ * @return {?}
735
+ */
736
+ function () {
737
+ _this.blockedByOther = blocked;
738
+ _this.cdr.markForCheck();
739
+ }));
740
+ }
741
+ };
742
+ /**
743
+ * @return {?}
744
+ */
745
+ CSVBoxButtonComponent.prototype.ngOnDestroy = /**
746
+ * @return {?}
747
+ */
748
+ function () {
749
+ window.removeEventListener("message", this.messageListener);
750
+ // Takes the holder out of document.body: left there, it would keep covering the page with
751
+ // an invisible full-screen layer that nothing is left to hide.
752
+ this.destroyImporter();
753
+ if (this.unsubscribeOpenImporters) {
754
+ this.unsubscribeOpenImporters();
755
+ this.unsubscribeOpenImporters = null;
756
+ }
757
+ };
260
758
  CSVBoxButtonComponent.decorators = [
261
759
  { type: Component, args: [{
262
760
  selector: 'csvbox-button',
263
- template: "\n <div>\n <button [disabled]=\"disabled\" #initiator (click)=\"openModal()\" [attr.data-csvbox-token]=\"uuid\">\n <ng-content></ng-content>\n </button>\n </div>\n "
761
+ template: "\n <div>\n <button [disabled]=\"disabled || blockedByOther\" (click)=\"openModal()\" [attr.data-csvbox-token]=\"uuid\">\n <ng-content></ng-content>\n </button>\n </div>\n "
264
762
  }] }
265
763
  ];
266
764
  /** @nocollapse */
267
765
  CSVBoxButtonComponent.ctorParameters = function () { return [
268
- { type: DomSanitizer }
766
+ { type: DomSanitizer },
767
+ { type: ChangeDetectorRef },
768
+ { type: NgZone }
269
769
  ]; };
270
770
  CSVBoxButtonComponent.propDecorators = {
271
- initiator: [{ type: ViewChild, args: ['initiator', { static: false },] }],
272
771
  onImport: [{ type: Input }],
273
772
  onReady: [{ type: Input }],
274
773
  onClose: [{ type: Input }],
@@ -303,8 +802,6 @@ if (false) {
303
802
  /** @type {?} */
304
803
  CSVBoxButtonComponent.prototype.isModalShown;
305
804
  /** @type {?} */
306
- CSVBoxButtonComponent.prototype.initiator;
307
- /** @type {?} */
308
805
  CSVBoxButtonComponent.prototype.onImport;
309
806
  /** @type {?} */
310
807
  CSVBoxButtonComponent.prototype.onReady;
@@ -363,9 +860,62 @@ if (false) {
363
860
  /** @type {?} */
364
861
  CSVBoxButtonComponent.prototype.disabled;
365
862
  /** @type {?} */
863
+ CSVBoxButtonComponent.prototype.blockedByOther;
864
+ /**
865
+ * @type {?}
866
+ * @private
867
+ */
868
+ CSVBoxButtonComponent.prototype.messageGate;
869
+ /**
870
+ * @type {?}
871
+ * @private
872
+ */
873
+ CSVBoxButtonComponent.prototype.importTracker;
874
+ /**
875
+ * @type {?}
876
+ * @private
877
+ */
878
+ CSVBoxButtonComponent.prototype.registryEntry;
879
+ /**
880
+ * @type {?}
881
+ * @private
882
+ */
883
+ CSVBoxButtonComponent.prototype.fileSender;
884
+ /**
885
+ * @type {?}
886
+ * @private
887
+ */
888
+ CSVBoxButtonComponent.prototype.unsubscribeOpenImporters;
889
+ /** @type {?} */
366
890
  CSVBoxButtonComponent.prototype.holder;
367
891
  /** @type {?} */
892
+ CSVBoxButtonComponent.prototype.pendingFile;
893
+ /** @type {?} */
894
+ CSVBoxButtonComponent.prototype.targetOrigin;
895
+ /** @type {?} */
896
+ CSVBoxButtonComponent.prototype.baseDomain;
897
+ /**
898
+ * @type {?}
899
+ * @private
900
+ */
901
+ CSVBoxButtonComponent.prototype.fileAckTimer;
902
+ /**
903
+ * @type {?}
904
+ * @private
905
+ */
906
+ CSVBoxButtonComponent.prototype.messageListener;
907
+ /** @type {?} */
368
908
  CSVBoxButtonComponent.prototype.sanitizer;
909
+ /**
910
+ * @type {?}
911
+ * @private
912
+ */
913
+ CSVBoxButtonComponent.prototype.cdr;
914
+ /**
915
+ * @type {?}
916
+ * @private
917
+ */
918
+ CSVBoxButtonComponent.prototype.zone;
369
919
  }
370
920
 
371
921
  /**