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