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