@saasquatch/squatch-js 2.8.2-30 → 2.8.2-32

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.
package/dist/squatch.js CHANGED
@@ -1238,112 +1238,438 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1238
1238
  return isVerified && (noContainer || isComponent);
1239
1239
  }
1240
1240
  }
1241
- const _log$6 = browserExports.debug("squatch-js:POPUPwidget");
1242
- let popupId = 0;
1243
- class PopupWidget extends Widget {
1244
- constructor(params, trigger = ".squatchpop") {
1245
- super(params);
1246
- __publicField(this, "trigger");
1247
- __publicField(this, "id");
1248
- __publicField(this, "show", this.open);
1249
- __publicField(this, "hide", this.close);
1250
- this.trigger = trigger;
1251
- if (this.container) {
1252
- this.id = "squatchModal";
1253
- } else {
1254
- this.id = popupId === 0 ? `squatchModal` : `squatchModal__${popupId}`;
1255
- popupId = popupId + 1;
1241
+ const getSkeleton = ({
1242
+ type = "verified-access",
1243
+ height = "500px",
1244
+ skeletonBackgroundColor = "#e0e0e0",
1245
+ skeletonShimmerColor = "#f5f5f5"
1246
+ }) => {
1247
+ const referrerHTML = `
1248
+ <div class="hero-section">
1249
+ <div class="hero-content">
1250
+ <div class="skeleton sk-title-lg"></div>
1251
+ <div class="skeleton sk-text"></div>
1252
+ <div class="skeleton sk-text sk-text-short"></div>
1253
+ </div>
1254
+ <div class="skeleton hero-image"></div>
1255
+ </div>
1256
+
1257
+ <div class="share-section">
1258
+ <div class="skeleton sk-label"></div>
1259
+ <div class="skeleton sk-input"></div>
1260
+ <div class="social-buttons">
1261
+ <div class="skeleton sk-btn-social"></div>
1262
+ <div class="skeleton sk-btn-social"></div>
1263
+ <div class="skeleton sk-btn-social"></div>
1264
+ <div class="skeleton sk-btn-social"></div>
1265
+ </div>
1266
+ </div>
1267
+
1268
+ <div class="skeleton sk-title-md" style="margin-top: 0; width: 30%; margin-left: auto; margin-right: auto"></div>
1269
+ <div class="skeleton sk-text" style="width: 60%; margin-left: auto; margin-right: auto"></div>
1270
+
1271
+ <div class="stats-section">
1272
+ <div class="stat-card">
1273
+ <div class="skeleton sk-stat-num"></div>
1274
+ <div class="skeleton sk-stat-label"></div>
1275
+ </div>
1276
+ <div class="stat-card stat-divider">
1277
+ <div class="skeleton sk-stat-num"></div>
1278
+ <div class="skeleton sk-stat-label"></div>
1279
+ </div>
1280
+ </div>
1281
+
1282
+ <div class="skeleton sk-title-md"></div>
1283
+
1284
+ <div class="table-header">
1285
+ <div class="skeleton sk-th col-user"></div>
1286
+ <div class="skeleton sk-th col-status"></div>
1287
+ <div class="skeleton sk-th col-reward"></div>
1288
+ <div class="skeleton sk-th col-date"></div>
1289
+ </div>
1290
+
1291
+ <div class="table-row">
1292
+ <div class="col-user"><div class="skeleton sk-text" style="width: 70%; margin: 0"></div></div>
1293
+ <div class="col-status"><div class="skeleton sk-badge" style="margin: 0"></div></div>
1294
+ <div class="col-reward"><div class="skeleton sk-reward-block" style="margin: 0"></div></div>
1295
+ <div class="col-date"><div class="skeleton sk-text" style="width: 80%; margin: 0"></div></div>
1296
+ </div>
1297
+
1298
+ <div class="table-row">
1299
+ <div class="col-user"><div class="skeleton sk-text" style="width: 60%; margin: 0"></div></div>
1300
+ <div class="col-status"><div class="skeleton sk-badge" style="margin: 0"></div></div>
1301
+ <div class="col-reward"><div class="skeleton sk-reward-block" style="margin: 0"></div></div>
1302
+ <div class="col-date"><div class="skeleton sk-text" style="width: 80%; margin: 0"></div></div>
1303
+ </div>
1304
+
1305
+ <div class="table-row">
1306
+ <div class="col-user"><div class="skeleton sk-text" style="width: 75%; margin: 0"></div></div>
1307
+ <div class="col-status"><div class="skeleton sk-badge" style="margin: 0"></div></div>
1308
+ <div class="col-reward"><div class="skeleton sk-reward-block" style="margin: 0"></div></div>
1309
+ <div class="col-date"><div class="skeleton sk-text" style="width: 80%; margin: 0"></div></div>
1310
+ </div>
1311
+
1312
+ <div class="pagination">
1313
+ <div class="skeleton sk-btn-page"></div>
1314
+ <div class="skeleton sk-btn-page"></div>
1315
+ </div>
1316
+ `;
1317
+ const instantAccessHTML = `
1318
+ <div class="hero-section instant-access-layout">
1319
+ <div class="skeleton hero-image ia-image"></div>
1320
+
1321
+ <div class="hero-content ia-content">
1322
+ <div class="skeleton sk-title-lg ia-center"></div>
1323
+ <div class="skeleton sk-text ia-center"></div>
1324
+
1325
+ <div class="skeleton sk-btn-action"></div>
1326
+
1327
+ <div class="skeleton sk-label"></div>
1328
+ <div class="input-group">
1329
+ <div class="skeleton sk-input"></div>
1330
+ <div class="skeleton sk-btn-copy"></div>
1331
+ </div>
1332
+
1333
+ <div class="skeleton sk-text-short ia-center" style="margin-top: 20px; width: 30%"></div>
1334
+ <div class="skeleton sk-text-short ia-center" style="width: 20%"></div>
1335
+ </div>
1336
+ </div>
1337
+ `;
1338
+ return `
1339
+ <style>
1340
+ * {
1341
+ box-sizing: border-box;
1342
+ padding: 0;
1343
+ margin: 0;
1344
+ }
1345
+
1346
+ .widget-container {
1347
+ background: white;
1348
+ width: 100%;
1349
+ padding: 40px;
1350
+ box-sizing: border-box;
1351
+ overflow: hidden;
1256
1352
  }
1257
- document.head.insertAdjacentHTML(
1258
- "beforeend",
1259
- `<style>#${this.id}::-webkit-scrollbar { display: none; }</style>`
1260
- );
1261
- }
1262
- _initialiseCTA() {
1263
- if (!this.trigger) return;
1264
- let triggerElement;
1265
- try {
1266
- triggerElement = document.querySelector(this.trigger) || document.querySelector(".impactpop");
1267
- if (this.trigger && !triggerElement)
1268
- _log$6("No element found with trigger selector", this.trigger);
1269
- } catch {
1270
- _log$6("Not a valid selector", this.trigger);
1353
+
1354
+ @keyframes shimmer {
1355
+ 0% { background-position: -100% 0; }
1356
+ 100% { background-position: 100% 0; }
1271
1357
  }
1272
- if (triggerElement) {
1273
- triggerElement.onclick = () => {
1274
- this.open();
1275
- };
1358
+
1359
+ .skeleton {
1360
+ background: ${skeletonBackgroundColor};
1361
+ background: linear-gradient(
1362
+ 90deg,
1363
+ ${skeletonBackgroundColor} 25%,
1364
+ ${skeletonShimmerColor} 50%,
1365
+ ${skeletonBackgroundColor} 75%
1366
+ );
1367
+ background-size: 200% 100%;
1368
+ animation: shimmer 1.5s infinite linear;
1369
+ border-radius: 6px;
1370
+ margin-bottom: 12px;
1276
1371
  }
1277
- }
1278
- _createPopupDialog() {
1279
- var _a2, _b, _c;
1280
- const dialog = document.createElement("dialog");
1281
- const brandingConfig = (_b = (_a2 = this.context.widgetConfig) == null ? void 0 : _a2.values) == null ? void 0 : _b.brandingConfig;
1282
- const sizes = (_c = brandingConfig == null ? void 0 : brandingConfig.widgetSize) == null ? void 0 : _c.popupWidgets;
1283
- const minWidth = (sizes == null ? void 0 : sizes.minWidth) ? formatWidth(sizes.minWidth) : "auto";
1284
- const maxWidth = (sizes == null ? void 0 : sizes.maxWidth) ? formatWidth(sizes.maxWidth) : "500px";
1285
- dialog.id = this.id;
1286
- dialog.setAttribute(
1287
- "style",
1288
- `width: 100%; min-width: ${minWidth}; max-width: ${maxWidth}; border: none; padding: 0;`
1289
- );
1290
- const onClick = (e) => {
1291
- e.stopPropagation();
1292
- if (e.target === dialog) dialog.close();
1293
- };
1294
- dialog.addEventListener("click", onClick);
1295
- return dialog;
1296
- }
1297
- async load() {
1298
- var _a2;
1299
- const frame = this._createFrame();
1300
- this._initialiseCTA();
1301
- const element = this.container ? this._findElement() : document.body;
1302
- const dialogParent = element.shadowRoot || element;
1303
- const dialog = this._createPopupDialog();
1304
- dialog.appendChild(frame);
1305
- if (((_a2 = dialogParent.lastChild) == null ? void 0 : _a2.nodeName) === "DIALOG") {
1306
- dialogParent.replaceChild(dialog, dialogParent.lastChild);
1307
- } else {
1308
- dialogParent.appendChild(dialog);
1372
+
1373
+ /* Typography Skeletons */
1374
+ .sk-title-lg { height: 36px; width: 80%; margin-bottom: 16px; }
1375
+ .sk-title-md { height: 28px; width: 30%; margin-bottom: 20px; margin-top: 40px; }
1376
+ .sk-text { height: 16px; width: 90%; margin-bottom: 8px; }
1377
+ .sk-text-short { width: 40%; }
1378
+ .sk-label { height: 14px; width: 25%; margin-bottom: 10px; }
1379
+
1380
+ /* Layouts */
1381
+ .hero-section {
1382
+ display: flex;
1383
+ gap: 40px;
1384
+ margin-bottom: 40px;
1385
+ padding-bottom: 40px;
1386
+ flex-direction: row;
1387
+ height: 100%;
1388
+ /* Removed border-bottom */
1309
1389
  }
1310
- const { contentWindow } = frame;
1311
- if (!contentWindow) {
1312
- throw new Error("Frame needs a content window");
1390
+
1391
+ .hero-content {
1392
+ flex: 1;
1393
+ display: flex;
1394
+ flex-direction: column;
1395
+ justify-content: center;
1313
1396
  }
1314
- const frameDoc = contentWindow.document;
1315
- frameDoc.open();
1316
- frameDoc.write(this.content);
1317
- frameDoc.write(
1318
- `<script src="${this.npmCdn}/resize-observer-polyfill@1.5.x"><\/script>`
1319
- );
1320
- frameDoc.close();
1321
- _log$6("Popup template loaded into iframe");
1322
- await this._setupResizeHandler(frame);
1323
- }
1324
- async _setupResizeHandler(frame) {
1325
- const { contentWindow } = frame;
1326
- if (!contentWindow) {
1327
- throw new Error("Frame needs a content window");
1397
+
1398
+ .hero-image {
1399
+ flex: 1;
1400
+ height: 300px;
1401
+ border-radius: 12px;
1328
1402
  }
1329
- const frameDoc = contentWindow.document;
1330
- domready(frameDoc, async () => {
1331
- frameDoc.body.style.overflowY = "hidden";
1332
- frame.height = `${frameDoc.body.offsetHeight}px`;
1333
- const ro = new contentWindow["ResizeObserver"]((entries) => {
1334
- for (const entry of entries) {
1335
- const { top, bottom } = entry.contentRect;
1336
- const computedHeight = bottom + top;
1337
- frame.height = computedHeight + "";
1338
- entry.target.style = ``;
1339
- }
1340
- });
1341
- ro.observe(await this._findInnerContainer(frame));
1342
- });
1343
- }
1344
- open() {
1345
- const element = this.container ? this._findElement() : document.body;
1346
- const parent = element.shadowRoot || element;
1403
+
1404
+ /* -- Specific Instant Access Overrides -- */
1405
+ .instant-access-layout {
1406
+ margin-bottom: 0;
1407
+ padding-bottom: 0;
1408
+ align-items: center;
1409
+ }
1410
+ .ia-image {
1411
+ height: 400px;
1412
+ }
1413
+ .ia-center {
1414
+ margin-left: auto;
1415
+ margin-right: auto;
1416
+ }
1417
+ .ia-content {
1418
+ align-items: center;
1419
+ text-align: center;
1420
+ }
1421
+ .sk-btn-action {
1422
+ height: 45px;
1423
+ width: 140px;
1424
+ border-radius: 6px;
1425
+ margin: 24px auto;
1426
+ }
1427
+ .input-group {
1428
+ display: flex;
1429
+ gap: 10px;
1430
+ width: 100%;
1431
+ max-width: 400px;
1432
+ }
1433
+ .sk-btn-copy {
1434
+ height: 50px;
1435
+ width: 120px;
1436
+ border-radius: 8px;
1437
+ }
1438
+ /* ------------------------------------- */
1439
+
1440
+ .share-section { margin-bottom: 40px; }
1441
+ .sk-input { height: 50px; width: 100%; border-radius: 8px; margin-bottom: 16px; }
1442
+
1443
+ .social-buttons { display: flex; gap: 12px; }
1444
+ .sk-btn-social { flex: 1; height: 50px; border-radius: 8px; }
1445
+
1446
+ .stats-section {
1447
+ display: flex;
1448
+ gap: 24px;
1449
+ margin-bottom: 40px;
1450
+ padding: 30px 0;
1451
+ /* Removed border-top and border-bottom */
1452
+ }
1453
+ .stat-card { flex: 1; display: flex; flex-direction: column; align-items: center; }
1454
+ .stat-divider { padding-left: 24px; }
1455
+ .sk-stat-num { height: 48px; width: 120px; margin-bottom: 8px; }
1456
+ .sk-stat-label { height: 18px; width: 80px; }
1457
+
1458
+ /* Table Styles */
1459
+ .table-header { display: flex; gap: 16px; margin-bottom: 16px; }
1460
+ .sk-th { height: 16px; }
1461
+ .table-row {
1462
+ display: flex;
1463
+ align-items: center;
1464
+ gap: 16px;
1465
+ padding: 16px 0;
1466
+ /* Removed border-bottom */
1467
+ }
1468
+
1469
+ .col-user { flex: 2; }
1470
+ .col-status { flex: 1; }
1471
+ .col-reward { flex: 2; }
1472
+ .col-date { flex: 1; }
1473
+
1474
+ .sk-badge { height: 28px; width: 90px; border-radius: 14px; }
1475
+ .sk-reward-block { height: 36px; width: 100%; border-radius: 6px; }
1476
+
1477
+ .pagination { display: flex; justify-content: flex-end; gap: 8px; margin-top: 24px; }
1478
+ .sk-btn-page { height: 36px; width: 64px; border-radius: 6px; margin-bottom: 0; }
1479
+
1480
+ @media (max-width: 768px) {
1481
+ body { padding: 20px; }
1482
+ .widget-container { padding: 24px; }
1483
+
1484
+ .hero-section { flex-direction: column-reverse; gap: 24px; }
1485
+ .instant-access-layout { flex-direction: column; }
1486
+
1487
+ .hero-image { height: 220px; width: 100%; }
1488
+ .sk-title-lg { width: 100%; }
1489
+
1490
+ .col-date { display: none; }
1491
+ }
1492
+ </style>
1493
+
1494
+ <div class="widget-container">
1495
+ ${type === "verified-access" ? referrerHTML : instantAccessHTML}
1496
+ </div>
1497
+ `;
1498
+ };
1499
+ const _log$6 = browserExports.debug("squatch-js:POPUPwidget");
1500
+ let popupId = 0;
1501
+ class PopupWidget extends Widget {
1502
+ constructor(params, trigger = ".squatchpop") {
1503
+ super(params);
1504
+ __publicField(this, "trigger");
1505
+ __publicField(this, "id");
1506
+ __publicField(this, "show", this.open);
1507
+ __publicField(this, "hide", this.close);
1508
+ this.trigger = trigger;
1509
+ if (this.container) {
1510
+ this.id = "squatchModal";
1511
+ } else {
1512
+ this.id = popupId === 0 ? `squatchModal` : `squatchModal__${popupId}`;
1513
+ popupId = popupId + 1;
1514
+ }
1515
+ document.head.insertAdjacentHTML(
1516
+ "beforeend",
1517
+ `<style>#${this.id}::-webkit-scrollbar { display: none; }</style>`
1518
+ );
1519
+ }
1520
+ _initialiseCTA() {
1521
+ if (!this.trigger) return;
1522
+ let triggerElement;
1523
+ try {
1524
+ triggerElement = document.querySelector(this.trigger) || document.querySelector(".impactpop");
1525
+ if (this.trigger && !triggerElement)
1526
+ _log$6("No element found with trigger selector", this.trigger);
1527
+ } catch {
1528
+ _log$6("Not a valid selector", this.trigger);
1529
+ }
1530
+ if (triggerElement) {
1531
+ triggerElement.onclick = () => {
1532
+ this.open();
1533
+ };
1534
+ }
1535
+ }
1536
+ _createPopupDialog() {
1537
+ var _a2, _b, _c;
1538
+ const dialog = document.createElement("dialog");
1539
+ const brandingConfig = (_b = (_a2 = this.context.widgetConfig) == null ? void 0 : _a2.values) == null ? void 0 : _b.brandingConfig;
1540
+ const sizes = (_c = brandingConfig == null ? void 0 : brandingConfig.widgetSize) == null ? void 0 : _c.popupWidgets;
1541
+ const minWidth = (sizes == null ? void 0 : sizes.minWidth) ? formatWidth(sizes.minWidth) : "auto";
1542
+ const maxWidth = (sizes == null ? void 0 : sizes.maxWidth) ? formatWidth(sizes.maxWidth) : "500px";
1543
+ dialog.id = this.id;
1544
+ dialog.setAttribute(
1545
+ "style",
1546
+ `width: 100%; min-width: ${minWidth}; max-width: ${maxWidth}; border: none; padding: 0;`
1547
+ );
1548
+ const onClick = (e) => {
1549
+ e.stopPropagation();
1550
+ if (e.target === dialog) dialog.close();
1551
+ };
1552
+ dialog.addEventListener("click", onClick);
1553
+ return dialog;
1554
+ }
1555
+ /*
1556
+ async load() {
1557
+ const frame = this._createFrame();
1558
+ this._initialiseCTA();
1559
+
1560
+ const element = this.container ? this._findElement() : document.body;
1561
+
1562
+ const dialogParent = element.shadowRoot || element;
1563
+ const dialog = this._createPopupDialog();
1564
+ dialog.appendChild(frame);
1565
+
1566
+ const skeletonHTML = getSkeleton({
1567
+ height: "100%",
1568
+ type: "verified-access",
1569
+ });
1570
+
1571
+ const skeletonContainer = document.createElement("div");
1572
+ skeletonContainer.id = "loading-skeleton";
1573
+ skeletonContainer.innerHTML = skeletonHTML;
1574
+
1575
+ if (dialogParent.lastChild?.nodeName === "DIALOG") {
1576
+ // Was reloaded
1577
+ dialogParent.replaceChild(dialog, dialogParent.lastChild);
1578
+ } else {
1579
+ // First time rendering
1580
+ dialogParent.appendChild(dialog);
1581
+ }
1582
+
1583
+ const { contentWindow } = frame;
1584
+ if (!contentWindow) {
1585
+ throw new Error("Frame needs a content window");
1586
+ }
1587
+
1588
+ const frameDoc = contentWindow.document;
1589
+ frameDoc.open();
1590
+ frameDoc.write(this.content);
1591
+ frameDoc.write(
1592
+ `<script src="${this.npmCdn}/resize-observer-polyfill@1.5.x"><\/script>`
1593
+ );
1594
+ frameDoc.close();
1595
+ _log("Popup template loaded into iframe");
1596
+ await this._setupResizeHandler(frame);
1597
+ }
1598
+ */
1599
+ async load() {
1600
+ var _a2;
1601
+ const frame = this._createFrame();
1602
+ this._initialiseCTA();
1603
+ const element = this.container ? this._findElement() : document.body;
1604
+ const dialogParent = element.shadowRoot || element;
1605
+ const dialog = this._createPopupDialog();
1606
+ const skeletonHTML = getSkeleton({
1607
+ height: "100%",
1608
+ type: "verified-access"
1609
+ });
1610
+ const skeletonContainer = document.createElement("div");
1611
+ skeletonContainer.id = "loading-skeleton";
1612
+ skeletonContainer.innerHTML = skeletonHTML;
1613
+ skeletonContainer.style.width = "100%";
1614
+ frame.style.display = "none";
1615
+ dialog.appendChild(skeletonContainer);
1616
+ dialog.appendChild(frame);
1617
+ if (((_a2 = dialogParent.lastChild) == null ? void 0 : _a2.nodeName) === "DIALOG") {
1618
+ dialogParent.replaceChild(dialog, dialogParent.lastChild);
1619
+ } else {
1620
+ dialogParent.appendChild(dialog);
1621
+ }
1622
+ const removeSkeleton = () => {
1623
+ const skel = dialog.querySelector("#loading-skeleton");
1624
+ if (skel) {
1625
+ skel.remove();
1626
+ }
1627
+ frame.style.display = "block";
1628
+ };
1629
+ frame.addEventListener("sq:load", removeSkeleton);
1630
+ frame.addEventListener("load", removeSkeleton);
1631
+ const safetyTimer = setTimeout(() => {
1632
+ removeSkeleton();
1633
+ }, 5e3);
1634
+ const { contentWindow } = frame;
1635
+ if (!contentWindow) {
1636
+ throw new Error("Frame needs a content window");
1637
+ }
1638
+ const frameDoc = contentWindow.document;
1639
+ frameDoc.open();
1640
+ frameDoc.write(this.content);
1641
+ frameDoc.write(
1642
+ `<script src="${this.npmCdn}/resize-observer-polyfill@1.5.x"><\/script>`
1643
+ );
1644
+ frameDoc.close();
1645
+ _log$6("Popup template loaded into iframe");
1646
+ await this._setupResizeHandler(frame);
1647
+ clearTimeout(safetyTimer);
1648
+ removeSkeleton();
1649
+ }
1650
+ async _setupResizeHandler(frame) {
1651
+ const { contentWindow } = frame;
1652
+ if (!contentWindow) {
1653
+ throw new Error("Frame needs a content window");
1654
+ }
1655
+ const frameDoc = contentWindow.document;
1656
+ domready(frameDoc, async () => {
1657
+ frameDoc.body.style.overflowY = "hidden";
1658
+ frame.height = `${frameDoc.body.offsetHeight}px`;
1659
+ const ro = new contentWindow["ResizeObserver"]((entries) => {
1660
+ for (const entry of entries) {
1661
+ const { top, bottom } = entry.contentRect;
1662
+ const computedHeight = bottom + top;
1663
+ frame.height = computedHeight + "";
1664
+ entry.target.style = ``;
1665
+ }
1666
+ });
1667
+ ro.observe(await this._findInnerContainer(frame));
1668
+ });
1669
+ }
1670
+ open() {
1671
+ const element = this.container ? this._findElement() : document.body;
1672
+ const parent = element.shadowRoot || element;
1347
1673
  const dialog = parent.querySelector(`#${this.id}`);
1348
1674
  if (!dialog) throw new Error("Could not determine container div");
1349
1675
  dialog.showModal();
@@ -1453,672 +1779,414 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1453
1779
  engagementMedium: config.engagementMedium,
1454
1780
  container: config.container,
1455
1781
  trigger: config.trigger,
1456
- widgetConfig: {
1457
- values: {
1458
- brandingConfig: response == null ? void 0 : response.brandingConfig
1459
- }
1460
- }
1461
- }),
1462
- user: response.user
1463
- };
1464
- } catch (err) {
1465
- _log$5(err);
1466
- if (err.apiErrorCode) {
1467
- this._renderErrorWidget(err, config.engagementMedium);
1468
- }
1469
- throw new Error(err);
1470
- }
1471
- }
1472
- /**
1473
- * This function calls the {@link WidgetApi.render} method, and it renders
1474
- * the widget if it is successful. Otherwise it shows the "error" widget.
1475
- *
1476
- * @param {Object} config Config details
1477
- * @param {Object} config.user The user details
1478
- * @param {string} config.user.id The user id
1479
- * @param {string} config.user.accountId The user account id
1480
- * @param {WidgetType} config.widgetType The content of the widget
1481
- * @param {EngagementMedium} config.engagementMedium How to display the widget
1482
- * @param {string} config.jwt the JSON Web Token (JWT) that is used
1483
- * to validate the data (can be disabled)
1484
- *
1485
- * @return {Promise<WidgetResult>} json object if true, with a Widget and user details
1486
- */
1487
- async render(config) {
1488
- const raw = config;
1489
- const clean = validatePasswordlessConfig(raw);
1490
- try {
1491
- const response = await this.api.render(clean);
1492
- return {
1493
- widget: this._renderWidget(response, clean, {
1494
- type: "passwordless",
1495
- engagementMedium: clean.engagementMedium,
1496
- container: clean.container,
1497
- trigger: clean.trigger,
1498
- widgetConfig: {
1499
- values: {
1500
- brandingConfig: response == null ? void 0 : response.brandingConfig
1501
- }
1502
- }
1503
- }),
1504
- user: response.user
1505
- };
1506
- } catch (err) {
1507
- if (err.apiErrorCode) {
1508
- this._renderErrorWidget(err, clean.engagementMedium);
1509
- }
1510
- throw new Error(err);
1511
- }
1512
- }
1513
- /**
1514
- * Autofills a referral code into an element when someone has been referred.
1515
- * Uses {@link WidgetApi.squatchReferralCookie} behind the scenes.
1516
- *
1517
- * @param selector Element class/id selector, or a callback function
1518
- * @returns
1519
- */
1520
- async autofill(selector) {
1521
- const input = selector;
1522
- if (typeof input === "function") {
1523
- try {
1524
- const response = await this.api.squatchReferralCookie();
1525
- input(response);
1526
- } catch (e) {
1527
- _log$5("Autofill error", e);
1528
- throw new Error(e);
1529
- }
1530
- return;
1531
- }
1532
- if (typeof input !== "string")
1533
- throw new Error("Autofill accepts a string or function");
1534
- let elems = document.querySelectorAll(input);
1535
- let elem;
1536
- if (elems.length > 0) {
1537
- elem = elems[0];
1538
- } else {
1539
- _log$5("Element id/class or function missing");
1540
- throw new Error("Element id/class or function missing");
1541
- }
1542
- try {
1543
- const response = await this.api.squatchReferralCookie();
1544
- elem.value = response.codes[0];
1545
- } catch (e) {
1546
- throw new Error(e);
1547
- }
1548
- }
1549
- /**
1550
- * @hidden
1551
- * @param {Object} response The json object return from the WidgetApi
1552
- * @param {Object} config Config details
1553
- * @param {string} config.widgetType The widget type (REFERRER_WIDGET, CONVERSION_WIDGET)
1554
- * @param {string} config.engagementMedium (POPUP, EMBED)
1555
- * @returns {Widget} widget (PopupWidget or EmbedWidget)
1556
- */
1557
- _renderWidget(response, config, context) {
1558
- var _a2;
1559
- _log$5("Rendering Widget...");
1560
- if (!response) throw new Error("Unable to get a response");
1561
- let widget2;
1562
- let displayOnLoad = !!config.displayOnLoad;
1563
- const opts = response.jsOptions || {};
1564
- const params = {
1565
- content: response.template,
1566
- type: config.widgetType || ((_a2 = opts.widget) == null ? void 0 : _a2.defaultWidgetType),
1567
- api: this.api,
1568
- domain: this.domain,
1569
- npmCdn: this.npmCdn,
1570
- context
1571
- };
1572
- if (opts.widgetUrlMappings) {
1573
- opts.widgetUrlMappings.forEach((rule) => {
1574
- var _a3, _b;
1575
- if (Widgets._matchesUrl(rule.url)) {
1576
- if (rule.widgetType !== "CONVERSION_WIDGET" || ((_b = (_a3 = response.user) == null ? void 0 : _a3.referredBy) == null ? void 0 : _b.code)) {
1577
- displayOnLoad = rule.displayOnLoad;
1578
- _log$5(`Display ${rule.widgetType} on ${rule.url}`);
1579
- } else {
1580
- _log$5(
1581
- `Don't display ${rule.widgetType} when no referral on widget rule match ${rule.url}`
1582
- );
1583
- }
1584
- }
1585
- });
1586
- }
1587
- if (opts.fuelTankAutofillUrls) {
1588
- _log$5("We found a fuel tank autofill!");
1589
- opts.fuelTankAutofillUrls.forEach(({ url, formSelector }) => {
1590
- var _a3, _b, _c;
1591
- if (Widgets._matchesUrl(url)) {
1592
- _log$5("Fuel Tank URL matches");
1593
- if ((_b = (_a3 = response.user) == null ? void 0 : _a3.referredBy) == null ? void 0 : _b.code) {
1594
- const formAutofill = document.querySelector(formSelector);
1595
- if (formAutofill) {
1596
- formAutofill.value = ((_c = response.user.referredBy.referredReward) == null ? void 0 : _c.fuelTankCode) || "";
1597
- } else {
1598
- _log$5(
1599
- new Error(
1600
- `Element with id/class ${formSelector} was not found.`
1601
- )
1602
- );
1603
- }
1604
- }
1605
- }
1606
- });
1607
- }
1608
- if (config.engagementMedium === "EMBED") {
1609
- widget2 = this._renderEmbedWidget(params);
1610
- } else {
1611
- widget2 = this._renderPopupWidget(params);
1612
- if (displayOnLoad) widget2.open();
1613
- }
1614
- return widget2;
1615
- }
1616
- _renderPopupWidget(params) {
1617
- const widget2 = new PopupWidget(params, params.context.trigger);
1618
- widget2.load();
1619
- return widget2;
1620
- }
1621
- _renderEmbedWidget(params) {
1622
- const widget2 = new EmbedWidget(params, params.context.container);
1623
- widget2.load();
1624
- return widget2;
1625
- }
1626
- /**
1627
- * @hidden
1628
- * @param {Object} error The json object containing the error details
1629
- * @param {string} em The engagementMedium
1630
- * @returns {void}
1631
- */
1632
- _renderErrorWidget(props, em = "POPUP") {
1633
- const { apiErrorCode, rsCode, message } = props;
1634
- _log$5(new Error(`${apiErrorCode} (${rsCode}) ${message}`));
1635
- const params = {
1636
- content: "error",
1637
- rsCode,
1638
- api: this.api,
1639
- domain: this.domain,
1640
- npmCdn: this.npmCdn,
1641
- type: "ERROR_WIDGET",
1642
- context: { type: "error" }
1643
- };
1644
- let widget2;
1645
- if (em === "EMBED") {
1646
- widget2 = new EmbedWidget(params);
1647
- widget2.load();
1648
- } else if (em === "POPUP") {
1649
- widget2 = new PopupWidget(params);
1650
- widget2.load();
1651
- }
1652
- }
1653
- /**
1654
- * @hidden
1655
- * @param {string} rule A regular expression
1656
- * @returns {boolean} true if rule matches Url, false otherwise
1657
- */
1658
- static _matchesUrl(rule) {
1659
- return window.location.href.match(new RegExp(rule)) ? true : false;
1660
- }
1661
- }
1662
- class EventsApi {
1663
- /**
1664
- * Initialize a new {@link EventsApi} instance.
1665
- *
1666
- * @param {ConfigOptions} config Config details
1667
- *
1668
- * @example <caption>Browser example</caption>
1669
- * var squatchApi = new squatch.EventsApi({tenantAlias:'test_12b5bo1b25125'});
1670
- *
1671
- * @example <caption>Browserify/Webpack example</caption>
1672
- * var EventsApi = require('@saasquatch/squatch-js').EventsApi;
1673
- * var squatchApi = new EventsApi({tenantAlias:'test_12b5bo1b25125'});
1674
- *
1675
- * @example <caption>Babel+Browserify/Webpack example</caption>
1676
- * import {EventsApi} from '@saasquatch/squatch-js';
1677
- * let squatchApi = new EventsApi({tenantAlias:'test_12b5bo1b25125'});
1678
- */
1679
- constructor(config) {
1680
- __publicField(this, "tenantAlias");
1681
- __publicField(this, "domain");
1682
- const raw = config;
1683
- const clean = validateConfig(raw);
1684
- this.tenantAlias = clean.tenantAlias;
1685
- this.domain = clean.domain;
1686
- }
1687
- /**
1688
- * Track an event for a user
1689
- *
1690
- * @param params Parameters for request
1691
- * @param options.jwt the JSON Web Token (JWT) that is used to authenticate the user
1692
- *
1693
- * @return An ID to confirm the event has been accepted for asynchronous processing
1694
- */
1695
- track(params, options) {
1696
- const raw = params;
1697
- const rawOpts = options;
1698
- const body = _validateEvent(raw);
1699
- const { jwt } = _validateTrackOptions(rawOpts);
1700
- const ta = encodeURIComponent(this.tenantAlias);
1701
- const userId = encodeURIComponent(body.userId);
1702
- const accountId = encodeURIComponent(body.accountId);
1703
- const path = `/api/v1/${ta}/open/account/${accountId}/user/${userId}/events`;
1704
- const url = this.domain + path;
1705
- return doPost(url, JSON.stringify(body), jwt);
1706
- }
1707
- }
1708
- function _validateEvent(raw) {
1709
- if (!isObject$1(raw)) throw new Error("tracking parameter must be an object");
1710
- if (!(raw == null ? void 0 : raw["accountId"])) throw new Error("accountId field is required");
1711
- if (!(raw == null ? void 0 : raw["events"])) throw new Error("events field is required");
1712
- if (!(raw == null ? void 0 : raw["userId"])) throw new Error("userId field is required");
1713
- const clean = raw;
1714
- if (!Array.isArray(clean.events))
1715
- throw new Error("'events' should be an array");
1716
- return clean;
1717
- }
1718
- function _validateTrackOptions(raw) {
1719
- if (!isObject$1(raw)) throw new Error("'options' should be an object");
1720
- return raw;
1721
- }
1722
- function asyncLoad() {
1723
- var _a2;
1724
- const namespace = window[IMPACT_NAMESPACE] ? IMPACT_NAMESPACE : DEFAULT_NAMESPACE;
1725
- const cached = ((_a2 = window["_" + namespace]) == null ? void 0 : _a2.ready) || [];
1726
- const declarativeCache = window.impactOnReady || window.squatchOnReady;
1727
- const readyFns = [...cached, declarativeCache].filter((a) => !!a);
1728
- setTimeout(() => {
1729
- if (!window[DEFAULT_NAMESPACE]) return;
1730
- window[IMPACT_NAMESPACE] = window[DEFAULT_NAMESPACE];
1731
- readyFns.forEach((cb) => cb());
1732
- window[DEFAULT_NAMESPACE]._auto();
1733
- window["_" + namespace] = void 0;
1734
- delete window["_" + namespace];
1735
- }, 0);
1736
- }
1737
- const _log$4 = browserExports.debug("squatch-js");
1738
- const isObject = (item) => typeof item === "object" && !Array.isArray(item);
1739
- const deepMerge = (target, source) => {
1740
- const isDeep = (prop) => isObject(source[prop]) && target.hasOwnProperty(prop) && isObject(target[prop]);
1741
- const replaced = Object.getOwnPropertyNames(source).map((prop) => ({
1742
- [prop]: isDeep(prop) ? deepMerge(target[prop], source[prop]) : source[prop]
1743
- })).reduce((a, b) => ({ ...a, ...b }), {});
1744
- return {
1745
- ...target,
1746
- ...replaced
1747
- };
1748
- };
1749
- function b64decode(input) {
1750
- const binary = atob(input.replace(/_/g, "/").replace(/-/g, "+"));
1751
- const bytes = new Uint8Array(binary.length);
1752
- for (let i = 0; i < binary.length; i++) {
1753
- bytes[i] = binary.charCodeAt(i);
1754
- }
1755
- return new TextDecoder("utf8").decode(bytes);
1756
- }
1757
- function b64encode(input) {
1758
- const encodedInput = new TextEncoder().encode(input);
1759
- const binary = Array.from(
1760
- encodedInput,
1761
- (byte) => String.fromCodePoint(byte)
1762
- ).join("");
1763
- return btoa(binary).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
1764
- }
1765
- function getTopDomain() {
1766
- var i, h, weird_cookie = "weird_get_top_level_domain=cookie", hostname = document.location.hostname.split(".");
1767
- for (i = hostname.length - 1; i >= 0; i--) {
1768
- h = hostname.slice(i).join(".");
1769
- document.cookie = weird_cookie + ";domain=." + h + ";";
1770
- if (document.cookie.indexOf(weird_cookie) > -1) {
1771
- document.cookie = weird_cookie.split("=")[0] + "=;domain=." + h + ";expires=Thu, 01 Jan 1970 00:00:01 GMT;";
1772
- return h;
1773
- }
1774
- }
1775
- }
1776
- function _pushCookie() {
1777
- const queryString = window.location.search;
1778
- const urlParams = new URLSearchParams(queryString);
1779
- const refParam = urlParams.get("_saasquatch") || "";
1780
- if (refParam) {
1781
- let paramsJSON = "", existingCookie = "", reEncodedCookie = "";
1782
- try {
1783
- paramsJSON = JSON.parse(b64decode(refParam));
1784
- } catch (error) {
1785
- _log$4("Unable to decode params", error);
1786
- return;
1787
- }
1788
- try {
1789
- existingCookie = JSON.parse(b64decode(api$1.get("_saasquatch")));
1790
- _log$4("existing cookie", existingCookie);
1791
- } catch (error) {
1792
- _log$4("Unable to retrieve cookie", error);
1793
- }
1794
- try {
1795
- const domain = getTopDomain();
1796
- _log$4("domain retrieved:", domain);
1797
- if (existingCookie) {
1798
- const newCookie = deepMerge(existingCookie, paramsJSON);
1799
- reEncodedCookie = b64encode(JSON.stringify(newCookie));
1800
- _log$4("cookie to store:", newCookie);
1801
- } else {
1802
- reEncodedCookie = b64encode(JSON.stringify(paramsJSON));
1803
- _log$4("cookie to store:", paramsJSON);
1804
- }
1805
- api$1.set("_saasquatch", reEncodedCookie, {
1806
- expires: 365,
1807
- secure: false,
1808
- sameSite: "Lax",
1809
- domain,
1810
- path: "/"
1811
- });
1812
- } catch (error) {
1813
- _log$4("Unable to set cookie", error);
1814
- }
1815
- }
1816
- }
1817
- const _log$3 = browserExports.debug("squatch-js");
1818
- function _getAutoConfig() {
1819
- var _a2;
1820
- const queryString = window.location.search;
1821
- const urlParams = new URLSearchParams(queryString);
1822
- const refParam = urlParams.get("_saasquatchExtra") || "";
1823
- if (!refParam) {
1824
- _log$3("No _saasquatchExtra param");
1825
- return;
1826
- }
1827
- const config = validateConfig({
1828
- tenantAlias: "UNKNOWN"
1829
- });
1830
- if (!config.domain) {
1831
- _log$3("domain must be provided in config to use _saasquatchExtra");
1832
- return;
1833
- }
1834
- let raw;
1835
- try {
1836
- raw = JSON.parse(b64decode(refParam));
1837
- } catch (e) {
1838
- _log$3("Unable to decode _saasquatchExtra config");
1839
- return;
1840
- }
1841
- function normalizeDomain(domain) {
1842
- return domain.replace(/^https?:\/\//, "");
1843
- }
1844
- const normalizedDomain = normalizeDomain(config.domain);
1845
- const tenantAlias = Object.keys((raw == null ? void 0 : raw[normalizedDomain]) || {})[0];
1846
- const widgetConfig = (_a2 = raw == null ? void 0 : raw[normalizedDomain]) == null ? void 0 : _a2[tenantAlias];
1847
- if (!widgetConfig) {
1848
- _log$3("_saasquatchExtra did not have an expected structure");
1849
- return void 0;
1850
- }
1851
- const { autoPopupWidgetType, ...rest } = widgetConfig;
1852
- return {
1853
- widgetConfig: {
1854
- widgetType: autoPopupWidgetType,
1855
- displayOnLoad: true,
1856
- ...rest
1857
- },
1858
- squatchConfig: {
1859
- ...config,
1860
- tenantAlias
1861
- }
1862
- };
1863
- }
1864
- const getSkeleton = ({
1865
- type = "verified-access",
1866
- height = "500px",
1867
- skeletonBackgroundColor = "#e0e0e0",
1868
- skeletonShimmerColor = "#f5f5f5"
1869
- }) => {
1870
- const referrerHTML = `
1871
- <div class="hero-section">
1872
- <div class="hero-content">
1873
- <div class="skeleton sk-title-lg"></div>
1874
- <div class="skeleton sk-text"></div>
1875
- <div class="skeleton sk-text sk-text-short"></div>
1876
- </div>
1877
- <div class="skeleton hero-image"></div>
1878
- </div>
1879
-
1880
- <div class="share-section">
1881
- <div class="skeleton sk-label"></div>
1882
- <div class="skeleton sk-input"></div>
1883
- <div class="social-buttons">
1884
- <div class="skeleton sk-btn-social"></div>
1885
- <div class="skeleton sk-btn-social"></div>
1886
- <div class="skeleton sk-btn-social"></div>
1887
- <div class="skeleton sk-btn-social"></div>
1888
- </div>
1889
- </div>
1890
-
1891
- <div class="skeleton sk-title-md" style="margin-top: 0; width: 30%; margin-left: auto; margin-right: auto"></div>
1892
- <div class="skeleton sk-text" style="width: 60%; margin-left: auto; margin-right: auto"></div>
1893
-
1894
- <div class="stats-section">
1895
- <div class="stat-card">
1896
- <div class="skeleton sk-stat-num"></div>
1897
- <div class="skeleton sk-stat-label"></div>
1898
- </div>
1899
- <div class="stat-card stat-divider">
1900
- <div class="skeleton sk-stat-num"></div>
1901
- <div class="skeleton sk-stat-label"></div>
1902
- </div>
1903
- </div>
1904
-
1905
- <div class="skeleton sk-title-md"></div>
1906
-
1907
- <div class="table-header">
1908
- <div class="skeleton sk-th col-user"></div>
1909
- <div class="skeleton sk-th col-status"></div>
1910
- <div class="skeleton sk-th col-reward"></div>
1911
- <div class="skeleton sk-th col-date"></div>
1912
- </div>
1913
-
1914
- <div class="table-row">
1915
- <div class="col-user"><div class="skeleton sk-text" style="width: 70%; margin: 0"></div></div>
1916
- <div class="col-status"><div class="skeleton sk-badge" style="margin: 0"></div></div>
1917
- <div class="col-reward"><div class="skeleton sk-reward-block" style="margin: 0"></div></div>
1918
- <div class="col-date"><div class="skeleton sk-text" style="width: 80%; margin: 0"></div></div>
1919
- </div>
1920
-
1921
- <div class="table-row">
1922
- <div class="col-user"><div class="skeleton sk-text" style="width: 60%; margin: 0"></div></div>
1923
- <div class="col-status"><div class="skeleton sk-badge" style="margin: 0"></div></div>
1924
- <div class="col-reward"><div class="skeleton sk-reward-block" style="margin: 0"></div></div>
1925
- <div class="col-date"><div class="skeleton sk-text" style="width: 80%; margin: 0"></div></div>
1926
- </div>
1927
-
1928
- <div class="table-row">
1929
- <div class="col-user"><div class="skeleton sk-text" style="width: 75%; margin: 0"></div></div>
1930
- <div class="col-status"><div class="skeleton sk-badge" style="margin: 0"></div></div>
1931
- <div class="col-reward"><div class="skeleton sk-reward-block" style="margin: 0"></div></div>
1932
- <div class="col-date"><div class="skeleton sk-text" style="width: 80%; margin: 0"></div></div>
1933
- </div>
1934
-
1935
- <div class="pagination">
1936
- <div class="skeleton sk-btn-page"></div>
1937
- <div class="skeleton sk-btn-page"></div>
1938
- </div>
1939
- `;
1940
- const instantAccessHTML = `
1941
- <div class="hero-section instant-access-layout">
1942
- <div class="skeleton hero-image ia-image"></div>
1943
-
1944
- <div class="hero-content ia-content">
1945
- <div class="skeleton sk-title-lg ia-center"></div>
1946
- <div class="skeleton sk-text ia-center"></div>
1947
-
1948
- <div class="skeleton sk-btn-action"></div>
1949
-
1950
- <div class="skeleton sk-label"></div>
1951
- <div class="input-group">
1952
- <div class="skeleton sk-input"></div>
1953
- <div class="skeleton sk-btn-copy"></div>
1954
- </div>
1955
-
1956
- <div class="skeleton sk-text-short ia-center" style="margin-top: 20px; width: 30%"></div>
1957
- <div class="skeleton sk-text-short ia-center" style="width: 20%"></div>
1958
- </div>
1959
- </div>
1960
- `;
1961
- return `
1962
- <style>
1963
- * {
1964
- box-sizing: border-box;
1965
- padding: 0;
1966
- margin: 0;
1967
- }
1968
-
1969
- .widget-container {
1970
- background: white;
1971
- width: 100%;
1972
- padding: 40px;
1973
- box-sizing: border-box;
1974
- overflow: hidden;
1975
- }
1976
-
1977
- @keyframes shimmer {
1978
- 0% { background-position: -100% 0; }
1979
- 100% { background-position: 100% 0; }
1980
- }
1981
-
1982
- .skeleton {
1983
- background: ${skeletonBackgroundColor};
1984
- background: linear-gradient(
1985
- 90deg,
1986
- ${skeletonBackgroundColor} 25%,
1987
- ${skeletonShimmerColor} 50%,
1988
- ${skeletonBackgroundColor} 75%
1989
- );
1990
- background-size: 200% 100%;
1991
- animation: shimmer 1.5s infinite linear;
1992
- border-radius: 6px;
1993
- margin-bottom: 12px;
1782
+ widgetConfig: {
1783
+ values: {
1784
+ brandingConfig: response == null ? void 0 : response.brandingConfig
1785
+ }
1786
+ }
1787
+ }),
1788
+ user: response.user
1789
+ };
1790
+ } catch (err) {
1791
+ _log$5(err);
1792
+ if (err.apiErrorCode) {
1793
+ this._renderErrorWidget(err, config.engagementMedium);
1794
+ }
1795
+ throw new Error(err);
1994
1796
  }
1995
-
1996
- /* Typography Skeletons */
1997
- .sk-title-lg { height: 36px; width: 80%; margin-bottom: 16px; }
1998
- .sk-title-md { height: 28px; width: 30%; margin-bottom: 20px; margin-top: 40px; }
1999
- .sk-text { height: 16px; width: 90%; margin-bottom: 8px; }
2000
- .sk-text-short { width: 40%; }
2001
- .sk-label { height: 14px; width: 25%; margin-bottom: 10px; }
2002
-
2003
- /* Layouts */
2004
- .hero-section {
2005
- display: flex;
2006
- gap: 40px;
2007
- margin-bottom: 40px;
2008
- padding-bottom: 40px;
2009
- flex-direction: row;
2010
- height: 100%;
2011
- /* Removed border-bottom */
1797
+ }
1798
+ /**
1799
+ * This function calls the {@link WidgetApi.render} method, and it renders
1800
+ * the widget if it is successful. Otherwise it shows the "error" widget.
1801
+ *
1802
+ * @param {Object} config Config details
1803
+ * @param {Object} config.user The user details
1804
+ * @param {string} config.user.id The user id
1805
+ * @param {string} config.user.accountId The user account id
1806
+ * @param {WidgetType} config.widgetType The content of the widget
1807
+ * @param {EngagementMedium} config.engagementMedium How to display the widget
1808
+ * @param {string} config.jwt the JSON Web Token (JWT) that is used
1809
+ * to validate the data (can be disabled)
1810
+ *
1811
+ * @return {Promise<WidgetResult>} json object if true, with a Widget and user details
1812
+ */
1813
+ async render(config) {
1814
+ const raw = config;
1815
+ const clean = validatePasswordlessConfig(raw);
1816
+ try {
1817
+ const response = await this.api.render(clean);
1818
+ return {
1819
+ widget: this._renderWidget(response, clean, {
1820
+ type: "passwordless",
1821
+ engagementMedium: clean.engagementMedium,
1822
+ container: clean.container,
1823
+ trigger: clean.trigger,
1824
+ widgetConfig: {
1825
+ values: {
1826
+ brandingConfig: response == null ? void 0 : response.brandingConfig
1827
+ }
1828
+ }
1829
+ }),
1830
+ user: response.user
1831
+ };
1832
+ } catch (err) {
1833
+ if (err.apiErrorCode) {
1834
+ this._renderErrorWidget(err, clean.engagementMedium);
1835
+ }
1836
+ throw new Error(err);
2012
1837
  }
2013
-
2014
- .hero-content {
2015
- flex: 1;
2016
- display: flex;
2017
- flex-direction: column;
2018
- justify-content: center;
1838
+ }
1839
+ /**
1840
+ * Autofills a referral code into an element when someone has been referred.
1841
+ * Uses {@link WidgetApi.squatchReferralCookie} behind the scenes.
1842
+ *
1843
+ * @param selector Element class/id selector, or a callback function
1844
+ * @returns
1845
+ */
1846
+ async autofill(selector) {
1847
+ const input = selector;
1848
+ if (typeof input === "function") {
1849
+ try {
1850
+ const response = await this.api.squatchReferralCookie();
1851
+ input(response);
1852
+ } catch (e) {
1853
+ _log$5("Autofill error", e);
1854
+ throw new Error(e);
1855
+ }
1856
+ return;
2019
1857
  }
2020
-
2021
- .hero-image {
2022
- flex: 1;
2023
- height: 300px;
2024
- border-radius: 12px;
1858
+ if (typeof input !== "string")
1859
+ throw new Error("Autofill accepts a string or function");
1860
+ let elems = document.querySelectorAll(input);
1861
+ let elem;
1862
+ if (elems.length > 0) {
1863
+ elem = elems[0];
1864
+ } else {
1865
+ _log$5("Element id/class or function missing");
1866
+ throw new Error("Element id/class or function missing");
2025
1867
  }
2026
-
2027
- /* -- Specific Instant Access Overrides -- */
2028
- .instant-access-layout {
2029
- margin-bottom: 0;
2030
- padding-bottom: 0;
2031
- align-items: center;
1868
+ try {
1869
+ const response = await this.api.squatchReferralCookie();
1870
+ elem.value = response.codes[0];
1871
+ } catch (e) {
1872
+ throw new Error(e);
2032
1873
  }
2033
- .ia-image {
2034
- height: 400px;
1874
+ }
1875
+ /**
1876
+ * @hidden
1877
+ * @param {Object} response The json object return from the WidgetApi
1878
+ * @param {Object} config Config details
1879
+ * @param {string} config.widgetType The widget type (REFERRER_WIDGET, CONVERSION_WIDGET)
1880
+ * @param {string} config.engagementMedium (POPUP, EMBED)
1881
+ * @returns {Widget} widget (PopupWidget or EmbedWidget)
1882
+ */
1883
+ _renderWidget(response, config, context) {
1884
+ var _a2;
1885
+ _log$5("Rendering Widget...");
1886
+ if (!response) throw new Error("Unable to get a response");
1887
+ let widget2;
1888
+ let displayOnLoad = !!config.displayOnLoad;
1889
+ const opts = response.jsOptions || {};
1890
+ const params = {
1891
+ content: response.template,
1892
+ type: config.widgetType || ((_a2 = opts.widget) == null ? void 0 : _a2.defaultWidgetType),
1893
+ api: this.api,
1894
+ domain: this.domain,
1895
+ npmCdn: this.npmCdn,
1896
+ context
1897
+ };
1898
+ if (opts.widgetUrlMappings) {
1899
+ opts.widgetUrlMappings.forEach((rule) => {
1900
+ var _a3, _b;
1901
+ if (Widgets._matchesUrl(rule.url)) {
1902
+ if (rule.widgetType !== "CONVERSION_WIDGET" || ((_b = (_a3 = response.user) == null ? void 0 : _a3.referredBy) == null ? void 0 : _b.code)) {
1903
+ displayOnLoad = rule.displayOnLoad;
1904
+ _log$5(`Display ${rule.widgetType} on ${rule.url}`);
1905
+ } else {
1906
+ _log$5(
1907
+ `Don't display ${rule.widgetType} when no referral on widget rule match ${rule.url}`
1908
+ );
1909
+ }
1910
+ }
1911
+ });
2035
1912
  }
2036
- .ia-center {
2037
- margin-left: auto;
2038
- margin-right: auto;
1913
+ if (opts.fuelTankAutofillUrls) {
1914
+ _log$5("We found a fuel tank autofill!");
1915
+ opts.fuelTankAutofillUrls.forEach(({ url, formSelector }) => {
1916
+ var _a3, _b, _c;
1917
+ if (Widgets._matchesUrl(url)) {
1918
+ _log$5("Fuel Tank URL matches");
1919
+ if ((_b = (_a3 = response.user) == null ? void 0 : _a3.referredBy) == null ? void 0 : _b.code) {
1920
+ const formAutofill = document.querySelector(formSelector);
1921
+ if (formAutofill) {
1922
+ formAutofill.value = ((_c = response.user.referredBy.referredReward) == null ? void 0 : _c.fuelTankCode) || "";
1923
+ } else {
1924
+ _log$5(
1925
+ new Error(
1926
+ `Element with id/class ${formSelector} was not found.`
1927
+ )
1928
+ );
1929
+ }
1930
+ }
1931
+ }
1932
+ });
2039
1933
  }
2040
- .ia-content {
2041
- align-items: center;
2042
- text-align: center;
1934
+ if (config.engagementMedium === "EMBED") {
1935
+ widget2 = this._renderEmbedWidget(params);
1936
+ } else {
1937
+ widget2 = this._renderPopupWidget(params);
1938
+ if (displayOnLoad) widget2.open();
2043
1939
  }
2044
- .sk-btn-action {
2045
- height: 45px;
2046
- width: 140px;
2047
- border-radius: 6px;
2048
- margin: 24px auto;
1940
+ return widget2;
1941
+ }
1942
+ _renderPopupWidget(params) {
1943
+ const widget2 = new PopupWidget(params, params.context.trigger);
1944
+ widget2.load();
1945
+ return widget2;
1946
+ }
1947
+ _renderEmbedWidget(params) {
1948
+ const widget2 = new EmbedWidget(params, params.context.container);
1949
+ widget2.load();
1950
+ return widget2;
1951
+ }
1952
+ /**
1953
+ * @hidden
1954
+ * @param {Object} error The json object containing the error details
1955
+ * @param {string} em The engagementMedium
1956
+ * @returns {void}
1957
+ */
1958
+ _renderErrorWidget(props, em = "POPUP") {
1959
+ const { apiErrorCode, rsCode, message } = props;
1960
+ _log$5(new Error(`${apiErrorCode} (${rsCode}) ${message}`));
1961
+ const params = {
1962
+ content: "error",
1963
+ rsCode,
1964
+ api: this.api,
1965
+ domain: this.domain,
1966
+ npmCdn: this.npmCdn,
1967
+ type: "ERROR_WIDGET",
1968
+ context: { type: "error" }
1969
+ };
1970
+ let widget2;
1971
+ if (em === "EMBED") {
1972
+ widget2 = new EmbedWidget(params);
1973
+ widget2.load();
1974
+ } else if (em === "POPUP") {
1975
+ widget2 = new PopupWidget(params);
1976
+ widget2.load();
2049
1977
  }
2050
- .input-group {
2051
- display: flex;
2052
- gap: 10px;
2053
- width: 100%;
2054
- max-width: 400px;
1978
+ }
1979
+ /**
1980
+ * @hidden
1981
+ * @param {string} rule A regular expression
1982
+ * @returns {boolean} true if rule matches Url, false otherwise
1983
+ */
1984
+ static _matchesUrl(rule) {
1985
+ return window.location.href.match(new RegExp(rule)) ? true : false;
1986
+ }
1987
+ }
1988
+ class EventsApi {
1989
+ /**
1990
+ * Initialize a new {@link EventsApi} instance.
1991
+ *
1992
+ * @param {ConfigOptions} config Config details
1993
+ *
1994
+ * @example <caption>Browser example</caption>
1995
+ * var squatchApi = new squatch.EventsApi({tenantAlias:'test_12b5bo1b25125'});
1996
+ *
1997
+ * @example <caption>Browserify/Webpack example</caption>
1998
+ * var EventsApi = require('@saasquatch/squatch-js').EventsApi;
1999
+ * var squatchApi = new EventsApi({tenantAlias:'test_12b5bo1b25125'});
2000
+ *
2001
+ * @example <caption>Babel+Browserify/Webpack example</caption>
2002
+ * import {EventsApi} from '@saasquatch/squatch-js';
2003
+ * let squatchApi = new EventsApi({tenantAlias:'test_12b5bo1b25125'});
2004
+ */
2005
+ constructor(config) {
2006
+ __publicField(this, "tenantAlias");
2007
+ __publicField(this, "domain");
2008
+ const raw = config;
2009
+ const clean = validateConfig(raw);
2010
+ this.tenantAlias = clean.tenantAlias;
2011
+ this.domain = clean.domain;
2012
+ }
2013
+ /**
2014
+ * Track an event for a user
2015
+ *
2016
+ * @param params Parameters for request
2017
+ * @param options.jwt the JSON Web Token (JWT) that is used to authenticate the user
2018
+ *
2019
+ * @return An ID to confirm the event has been accepted for asynchronous processing
2020
+ */
2021
+ track(params, options) {
2022
+ const raw = params;
2023
+ const rawOpts = options;
2024
+ const body = _validateEvent(raw);
2025
+ const { jwt } = _validateTrackOptions(rawOpts);
2026
+ const ta = encodeURIComponent(this.tenantAlias);
2027
+ const userId = encodeURIComponent(body.userId);
2028
+ const accountId = encodeURIComponent(body.accountId);
2029
+ const path = `/api/v1/${ta}/open/account/${accountId}/user/${userId}/events`;
2030
+ const url = this.domain + path;
2031
+ return doPost(url, JSON.stringify(body), jwt);
2032
+ }
2033
+ }
2034
+ function _validateEvent(raw) {
2035
+ if (!isObject$1(raw)) throw new Error("tracking parameter must be an object");
2036
+ if (!(raw == null ? void 0 : raw["accountId"])) throw new Error("accountId field is required");
2037
+ if (!(raw == null ? void 0 : raw["events"])) throw new Error("events field is required");
2038
+ if (!(raw == null ? void 0 : raw["userId"])) throw new Error("userId field is required");
2039
+ const clean = raw;
2040
+ if (!Array.isArray(clean.events))
2041
+ throw new Error("'events' should be an array");
2042
+ return clean;
2043
+ }
2044
+ function _validateTrackOptions(raw) {
2045
+ if (!isObject$1(raw)) throw new Error("'options' should be an object");
2046
+ return raw;
2047
+ }
2048
+ function asyncLoad() {
2049
+ var _a2;
2050
+ const namespace = window[IMPACT_NAMESPACE] ? IMPACT_NAMESPACE : DEFAULT_NAMESPACE;
2051
+ const cached = ((_a2 = window["_" + namespace]) == null ? void 0 : _a2.ready) || [];
2052
+ const declarativeCache = window.impactOnReady || window.squatchOnReady;
2053
+ const readyFns = [...cached, declarativeCache].filter((a) => !!a);
2054
+ setTimeout(() => {
2055
+ if (!window[DEFAULT_NAMESPACE]) return;
2056
+ window[IMPACT_NAMESPACE] = window[DEFAULT_NAMESPACE];
2057
+ readyFns.forEach((cb) => cb());
2058
+ window[DEFAULT_NAMESPACE]._auto();
2059
+ window["_" + namespace] = void 0;
2060
+ delete window["_" + namespace];
2061
+ }, 0);
2062
+ }
2063
+ const _log$4 = browserExports.debug("squatch-js");
2064
+ const isObject = (item) => typeof item === "object" && !Array.isArray(item);
2065
+ const deepMerge = (target, source) => {
2066
+ const isDeep = (prop) => isObject(source[prop]) && target.hasOwnProperty(prop) && isObject(target[prop]);
2067
+ const replaced = Object.getOwnPropertyNames(source).map((prop) => ({
2068
+ [prop]: isDeep(prop) ? deepMerge(target[prop], source[prop]) : source[prop]
2069
+ })).reduce((a, b) => ({ ...a, ...b }), {});
2070
+ return {
2071
+ ...target,
2072
+ ...replaced
2073
+ };
2074
+ };
2075
+ function b64decode(input) {
2076
+ const binary = atob(input.replace(/_/g, "/").replace(/-/g, "+"));
2077
+ const bytes = new Uint8Array(binary.length);
2078
+ for (let i = 0; i < binary.length; i++) {
2079
+ bytes[i] = binary.charCodeAt(i);
2080
+ }
2081
+ return new TextDecoder("utf8").decode(bytes);
2082
+ }
2083
+ function b64encode(input) {
2084
+ const encodedInput = new TextEncoder().encode(input);
2085
+ const binary = Array.from(
2086
+ encodedInput,
2087
+ (byte) => String.fromCodePoint(byte)
2088
+ ).join("");
2089
+ return btoa(binary).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
2090
+ }
2091
+ function getTopDomain() {
2092
+ var i, h, weird_cookie = "weird_get_top_level_domain=cookie", hostname = document.location.hostname.split(".");
2093
+ for (i = hostname.length - 1; i >= 0; i--) {
2094
+ h = hostname.slice(i).join(".");
2095
+ document.cookie = weird_cookie + ";domain=." + h + ";";
2096
+ if (document.cookie.indexOf(weird_cookie) > -1) {
2097
+ document.cookie = weird_cookie.split("=")[0] + "=;domain=." + h + ";expires=Thu, 01 Jan 1970 00:00:01 GMT;";
2098
+ return h;
2055
2099
  }
2056
- .sk-btn-copy {
2057
- height: 50px;
2058
- width: 120px;
2059
- border-radius: 8px;
2100
+ }
2101
+ }
2102
+ function _pushCookie() {
2103
+ const queryString = window.location.search;
2104
+ const urlParams = new URLSearchParams(queryString);
2105
+ const refParam = urlParams.get("_saasquatch") || "";
2106
+ if (refParam) {
2107
+ let paramsJSON = "", existingCookie = "", reEncodedCookie = "";
2108
+ try {
2109
+ paramsJSON = JSON.parse(b64decode(refParam));
2110
+ } catch (error) {
2111
+ _log$4("Unable to decode params", error);
2112
+ return;
2060
2113
  }
2061
- /* ------------------------------------- */
2062
-
2063
- .share-section { margin-bottom: 40px; }
2064
- .sk-input { height: 50px; width: 100%; border-radius: 8px; margin-bottom: 16px; }
2065
-
2066
- .social-buttons { display: flex; gap: 12px; }
2067
- .sk-btn-social { flex: 1; height: 50px; border-radius: 8px; }
2068
-
2069
- .stats-section {
2070
- display: flex;
2071
- gap: 24px;
2072
- margin-bottom: 40px;
2073
- padding: 30px 0;
2074
- /* Removed border-top and border-bottom */
2114
+ try {
2115
+ existingCookie = JSON.parse(b64decode(api$1.get("_saasquatch")));
2116
+ _log$4("existing cookie", existingCookie);
2117
+ } catch (error) {
2118
+ _log$4("Unable to retrieve cookie", error);
2075
2119
  }
2076
- .stat-card { flex: 1; display: flex; flex-direction: column; align-items: center; }
2077
- .stat-divider { padding-left: 24px; }
2078
- .sk-stat-num { height: 48px; width: 120px; margin-bottom: 8px; }
2079
- .sk-stat-label { height: 18px; width: 80px; }
2080
-
2081
- /* Table Styles */
2082
- .table-header { display: flex; gap: 16px; margin-bottom: 16px; }
2083
- .sk-th { height: 16px; }
2084
- .table-row {
2085
- display: flex;
2086
- align-items: center;
2087
- gap: 16px;
2088
- padding: 16px 0;
2089
- /* Removed border-bottom */
2120
+ try {
2121
+ const domain = getTopDomain();
2122
+ _log$4("domain retrieved:", domain);
2123
+ if (existingCookie) {
2124
+ const newCookie = deepMerge(existingCookie, paramsJSON);
2125
+ reEncodedCookie = b64encode(JSON.stringify(newCookie));
2126
+ _log$4("cookie to store:", newCookie);
2127
+ } else {
2128
+ reEncodedCookie = b64encode(JSON.stringify(paramsJSON));
2129
+ _log$4("cookie to store:", paramsJSON);
2130
+ }
2131
+ api$1.set("_saasquatch", reEncodedCookie, {
2132
+ expires: 365,
2133
+ secure: false,
2134
+ sameSite: "Lax",
2135
+ domain,
2136
+ path: "/"
2137
+ });
2138
+ } catch (error) {
2139
+ _log$4("Unable to set cookie", error);
2090
2140
  }
2091
-
2092
- .col-user { flex: 2; }
2093
- .col-status { flex: 1; }
2094
- .col-reward { flex: 2; }
2095
- .col-date { flex: 1; }
2096
-
2097
- .sk-badge { height: 28px; width: 90px; border-radius: 14px; }
2098
- .sk-reward-block { height: 36px; width: 100%; border-radius: 6px; }
2099
-
2100
- .pagination { display: flex; justify-content: flex-end; gap: 8px; margin-top: 24px; }
2101
- .sk-btn-page { height: 36px; width: 64px; border-radius: 6px; margin-bottom: 0; }
2102
-
2103
- @media (max-width: 768px) {
2104
- body { padding: 20px; }
2105
- .widget-container { padding: 24px; }
2106
-
2107
- .hero-section { flex-direction: column-reverse; gap: 24px; }
2108
- .instant-access-layout { flex-direction: column; }
2109
-
2110
- .hero-image { height: 220px; width: 100%; }
2111
- .sk-title-lg { width: 100%; }
2112
-
2113
- .col-date { display: none; }
2141
+ }
2142
+ }
2143
+ const _log$3 = browserExports.debug("squatch-js");
2144
+ function _getAutoConfig() {
2145
+ var _a2;
2146
+ const queryString = window.location.search;
2147
+ const urlParams = new URLSearchParams(queryString);
2148
+ const refParam = urlParams.get("_saasquatchExtra") || "";
2149
+ if (!refParam) {
2150
+ _log$3("No _saasquatchExtra param");
2151
+ return;
2152
+ }
2153
+ const config = validateConfig({
2154
+ tenantAlias: "UNKNOWN"
2155
+ });
2156
+ if (!config.domain) {
2157
+ _log$3("domain must be provided in config to use _saasquatchExtra");
2158
+ return;
2159
+ }
2160
+ let raw;
2161
+ try {
2162
+ raw = JSON.parse(b64decode(refParam));
2163
+ } catch (e) {
2164
+ _log$3("Unable to decode _saasquatchExtra config");
2165
+ return;
2166
+ }
2167
+ function normalizeDomain(domain) {
2168
+ return domain.replace(/^https?:\/\//, "");
2169
+ }
2170
+ const normalizedDomain = normalizeDomain(config.domain);
2171
+ const tenantAlias = Object.keys((raw == null ? void 0 : raw[normalizedDomain]) || {})[0];
2172
+ const widgetConfig = (_a2 = raw == null ? void 0 : raw[normalizedDomain]) == null ? void 0 : _a2[tenantAlias];
2173
+ if (!widgetConfig) {
2174
+ _log$3("_saasquatchExtra did not have an expected structure");
2175
+ return void 0;
2176
+ }
2177
+ const { autoPopupWidgetType, ...rest } = widgetConfig;
2178
+ return {
2179
+ widgetConfig: {
2180
+ widgetType: autoPopupWidgetType,
2181
+ displayOnLoad: true,
2182
+ ...rest
2183
+ },
2184
+ squatchConfig: {
2185
+ ...config,
2186
+ tenantAlias
2114
2187
  }
2115
- </style>
2116
-
2117
- <div class="widget-container">
2118
- ${type === "verified-access" ? referrerHTML : instantAccessHTML}
2119
- </div>
2120
- `;
2121
- };
2188
+ };
2189
+ }
2122
2190
  const _log$2 = browserExports.debug("squatch-js:decodeUserJwt");
2123
2191
  function decodeUserJwt(tokenStr) {
2124
2192
  var _a2;
@@ -2416,8 +2484,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
2416
2484
  skeletonContainer.id = "loading-skeleton";
2417
2485
  skeletonContainer.innerHTML = skeletonHTML;
2418
2486
  const root = this.shadowRoot || this.attachShadow({ mode: "open" });
2419
- root.innerHTML = "";
2420
- root.appendChild(skeletonContainer);
2487
+ const container = root.getElementById("#squatchModal");
2488
+ console.log("Container is ", container);
2489
+ if (container) {
2490
+ container.innerHTML = "";
2491
+ container.appendChild(skeletonContainer);
2492
+ }
2421
2493
  await this.renderWidget();
2422
2494
  const loadingElement = root.getElementById("loading-skeleton");
2423
2495
  if (loadingElement) {