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