clinic-connect-widget 1.0.2 → 1.0.3
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/clinic-widget.es.js +123 -33
- package/dist/clinic-widget.umd.js +1 -1
- package/package.json +1 -1
package/dist/clinic-widget.es.js
CHANGED
|
@@ -117,7 +117,7 @@ const collapsedHtml = `<div class="td-widget-fab" id="td-widget-fab">\r
|
|
|
117
117
|
<div class="td-info">\r
|
|
118
118
|
<div class="td-brand">\r
|
|
119
119
|
<span class="material-symbols-outlined" style="font-size: 14px;">local_hospital</span>\r
|
|
120
|
-
<span>
|
|
120
|
+
<span>Tư vấn ngay</span>\r
|
|
121
121
|
</div>\r
|
|
122
122
|
<h3 class="td-cta">Khám online ngay</h3>\r
|
|
123
123
|
<p class="td-sub">Bác sĩ đang Online</p>\r
|
|
@@ -277,7 +277,7 @@ const consultationHtml = `<div class="td-consultation-modal">\r
|
|
|
277
277
|
</div>\r
|
|
278
278
|
</div>\r
|
|
279
279
|
</div>`;
|
|
280
|
-
const patientFormHtml = '<div class="td-widget-expanded" id="td-patient-form">\r\n <header class="td-header">\r\n <div class="td-header-title">\r\n <div class="td-icon-box">\r\n <span class="material-symbols-outlined">medical_services</span>\r\n </div>\r\n <span>
|
|
280
|
+
const patientFormHtml = '<div class="td-widget-expanded" id="td-patient-form">\r\n <header class="td-header">\r\n <div class="td-header-title">\r\n <div class="td-icon-box">\r\n <span class="material-symbols-outlined">medical_services</span>\r\n </div>\r\n <span>Tư vấn trực tuyến</span>\r\n </div>\r\n <button class="td-close-btn" id="td-form-close">\r\n <span class="material-symbols-outlined">close</span>\r\n </button>\r\n </header>\r\n\r\n <div class="td-content">\r\n <div class="td-form-container">\r\n <div class="td-form-page-header">\r\n <h1 class="td-form-title">Kết nối với Bác sĩ</h1>\r\n <p class="td-form-desc">Vui lòng nhập thông tin để bắt đầu tư vấn trực tuyến.</p>\r\n </div>\r\n\r\n <form class="td-form" id="td-info-form">\r\n <div class="td-input-group">\r\n <label class="td-label">Họ và tên</label>\r\n <input type="text" class="td-input" name="name" placeholder="VD: Nguyễn Văn A" required />\r\n </div>\r\n\r\n <div class="td-input-group">\r\n <label class="td-label">Số điện thoại</label>\r\n <div style="position: relative;">\r\n <input type="tel" class="td-input" name="phone" placeholder="VD: 0912 345 678" required />\r\n <!-- <div\r\n style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); color: var(--td-primary);">\r\n <span class="material-symbols-outlined" style="font-size: 20px;">smartphone</span>\r\n </div> -->\r\n </div>\r\n </div>\r\n\r\n <!-- <div class="td-input-group">\r\n <label class="td-label">Triệu chứng của bạn?</label>\r\n <div class="td-chips-group">\r\n <button type="button" class="td-chip active">Sốt <span class="material-symbols-outlined"\r\n style="font-size: 16px;">check</span></button>\r\n <button type="button" class="td-chip">Ho / Cảm cúm</button>\r\n <button type="button" class="td-chip">Đau đầu</button>\r\n <button type="button" class="td-chip">Đau bụng</button>\r\n </div>\r\n </div> -->\r\n\r\n <div class="td-input-group">\r\n <label class="td-label">Mô tả thêm <span style="color: var(--td-text-sub); font-weight: 400;">(Tùy\r\n chọn)</span></label>\r\n <textarea class="td-textarea" name="symptoms"\r\n placeholder="Bạn đang cảm thấy như thế nào?"></textarea>\r\n </div>\r\n\r\n <div style="margin-top: 8px;">\r\n <button type="submit" class="td-btn td-btn-primary" id="td-form-submit">\r\n Bắt đầu tư vấn\r\n <span class="material-symbols-outlined" style="margin-left: 8px;">arrow_forward</span>\r\n </button>\r\n </div>\r\n\r\n <div class="td-secure-note">\r\n <span class="material-symbols-outlined" style="font-size: 14px;">lock</span>\r\n Thông tin y tế được bảo mật 100%\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n</div>';
|
|
281
281
|
const postConsultationHtml = `<div class="td-widget-expanded" id="td-summary-view">\r
|
|
282
282
|
<header class="td-header">\r
|
|
283
283
|
<h2 class="td-header-title" style="font-size: 16px;">Tổng kết tư vấn</h2>\r
|
|
@@ -433,7 +433,7 @@ const inlineTriggerHtml = `<style>\r
|
|
|
433
433
|
<span class="td-inline-text">Bác sĩ đang Online</span>\r
|
|
434
434
|
</div>\r
|
|
435
435
|
\r
|
|
436
|
-
<button id="td-inline-btn" style="\r
|
|
436
|
+
<button id="td-inline-btn" type="button" style="\r
|
|
437
437
|
background: #0066ff; \r
|
|
438
438
|
color: white; \r
|
|
439
439
|
border: none; \r
|
|
@@ -454,6 +454,7 @@ const inlineTriggerHtml = `<style>\r
|
|
|
454
454
|
</button>\r
|
|
455
455
|
</div>\r
|
|
456
456
|
</div>`;
|
|
457
|
+
const permissionModalHtml = '<div class="modalOverlay" id="td-permission-modal">\r\n <div class="modal">\r\n <div class="modalHead">\r\n <h2>Cho phép truy cập Microphone và Camera</h2>\r\n <button id="td-perm-close" class="btn btn-icon" aria-label="Đóng">\r\n <span class="material-symbols-outlined" style="font-size: 20px;">close</span>\r\n </button>\r\n </div>\r\n <div class="modalBody">\r\n Website sắp yêu cầu quyền <strong>Microphone</strong> và <strong>Camera</strong> để:\r\n <ul>\r\n <li>Gọi video trong phiên tư vấn.</li>\r\n <li>Kiểm tra thiết bị trước khi bắt đầu.</li>\r\n </ul>\r\n <div class="note">\r\n Sau khi bấm “Tiếp tục”, trình duyệt sẽ hiện thông báo hệ thống. Vui lòng chọn <strong>Allow</strong>.\r\n </div>\r\n </div>\r\n <div class="modalFoot">\r\n <button id="td-perm-cancel" class="btn">Hủy</button>\r\n <button id="td-perm-allow" class="btn primary">Tiếp tục</button>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<style>\r\n #td-permission-modal {\r\n --bg: var(--td-bg-light, #ffffff);\r\n --text: var(--td-text-main, #0d131c);\r\n --muted: var(--td-text-sub, #49699c);\r\n --border: var(--td-border, #e7ecf4);\r\n --primary: var(--td-primary, #0e65f1);\r\n --primary-hover: var(--td-primary-hover, #0b50c0);\r\n --shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\r\n --radius: 12px;\r\n font-family: var(--font-family, system-ui, -apple-system, sans-serif);\r\n z-index: 2147483647;\r\n }\r\n\r\n /* Modal Overlay */\r\n .modalOverlay {\r\n position: fixed;\r\n inset: 0;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, .5);\r\n backdrop-filter: blur(2px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 18px;\r\n z-index: 2147483647;\r\n }\r\n\r\n .modal {\r\n width: min(500px, 94vw);\r\n border-radius: var(--radius);\r\n border: 1px solid var(--border);\r\n background: var(--bg);\r\n box-shadow: var(--shadow);\r\n overflow: hidden;\r\n color: var(--text);\r\n font-size: 14px;\r\n line-height: 1.5;\r\n }\r\n\r\n .modalHead {\r\n padding: 16px 20px;\r\n border-bottom: 1px solid var(--border);\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 10px;\r\n background: #f8f9fc;\r\n }\r\n\r\n .modalHead h2 {\r\n margin: 0;\r\n font-size: 16px;\r\n font-weight: 700;\r\n color: var(--text);\r\n }\r\n\r\n .modalBody {\r\n padding: 20px;\r\n color: var(--text);\r\n }\r\n\r\n .modalBody ul {\r\n margin: 12px 0 16px 0;\r\n padding-left: 20px;\r\n color: var(--muted);\r\n }\r\n\r\n .modalBody li {\r\n margin-bottom: 6px;\r\n }\r\n\r\n .modalFoot {\r\n padding: 16px 20px;\r\n display: flex;\r\n gap: 12px;\r\n justify-content: flex-end;\r\n border-top: 1px solid var(--border);\r\n background: #f8f9fc;\r\n }\r\n\r\n .note {\r\n margin-top: 12px;\r\n color: var(--muted);\r\n font-size: 13px;\r\n background: rgba(14, 101, 241, 0.05);\r\n padding: 10px;\r\n border-radius: 8px;\r\n border: 1px dashed var(--primary);\r\n }\r\n\r\n .note strong {\r\n color: var(--primary);\r\n }\r\n\r\n /* Buttons */\r\n .btn {\r\n appearance: none;\r\n border: 1px solid var(--border);\r\n background: white;\r\n color: var(--text);\r\n padding: 10px 16px;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.2s;\r\n font-weight: 600;\r\n font-family: inherit;\r\n font-size: 14px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n\r\n .btn:hover {\r\n background: #f1f5f9;\r\n border-color: #cbd5e1;\r\n }\r\n\r\n .btn-icon {\r\n padding: 4px;\r\n border: none;\r\n background: transparent;\r\n color: #64748b;\r\n border-radius: 50%;\r\n }\r\n\r\n .btn-icon:hover {\r\n background: rgba(0, 0, 0, 0.05);\r\n border-color: transparent;\r\n color: var(--text);\r\n }\r\n\r\n .btn.primary {\r\n background: var(--primary);\r\n border-color: var(--primary);\r\n color: white;\r\n }\r\n\r\n .btn.primary:hover {\r\n background: var(--primary-hover);\r\n border-color: var(--primary-hover);\r\n }\r\n</style>';
|
|
457
458
|
const render = (template, data) => {
|
|
458
459
|
var _a, _b, _c;
|
|
459
460
|
let output = template;
|
|
@@ -471,6 +472,7 @@ const getConsultationHtml = (data) => render(consultationHtml, data);
|
|
|
471
472
|
const getPatientFormHtml = (data) => render(patientFormHtml, data);
|
|
472
473
|
const getPostConsultationHtml = (data) => render(postConsultationHtml, data);
|
|
473
474
|
const getInlineTriggerHtml = (data) => render(inlineTriggerHtml, data);
|
|
475
|
+
const getPermissionModalHtml = (data) => render(permissionModalHtml, data);
|
|
474
476
|
const PACKET_TYPES = /* @__PURE__ */ Object.create(null);
|
|
475
477
|
PACKET_TYPES["open"] = "0";
|
|
476
478
|
PACKET_TYPES["close"] = "1";
|
|
@@ -28017,6 +28019,41 @@ class LiveKitService {
|
|
|
28017
28019
|
constructor() {
|
|
28018
28020
|
this.room = null;
|
|
28019
28021
|
this.wrapper = null;
|
|
28022
|
+
this.preAcquiredTracks = [];
|
|
28023
|
+
}
|
|
28024
|
+
/**
|
|
28025
|
+
* Request Camera & Mic permissions early
|
|
28026
|
+
*/
|
|
28027
|
+
async requestPermissions() {
|
|
28028
|
+
try {
|
|
28029
|
+
console.log("[LiveKit] Requesting early permissions...");
|
|
28030
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
|
|
28031
|
+
this.preAcquiredTracks = stream.getTracks();
|
|
28032
|
+
console.log("[LiveKit] Permissions granted, tracks cached:", this.preAcquiredTracks.length);
|
|
28033
|
+
return true;
|
|
28034
|
+
} catch (error) {
|
|
28035
|
+
console.error("[LiveKit] Permission request failed:", error);
|
|
28036
|
+
console.log(`Không thể xin quyền: ${error.name} - ${error.message}`);
|
|
28037
|
+
return false;
|
|
28038
|
+
}
|
|
28039
|
+
}
|
|
28040
|
+
/**
|
|
28041
|
+
* Check if permissions are already granted
|
|
28042
|
+
* @returns {Promise<boolean>}
|
|
28043
|
+
*/
|
|
28044
|
+
async checkPermissions() {
|
|
28045
|
+
if (!navigator.permissions || !navigator.permissions.query) {
|
|
28046
|
+
return false;
|
|
28047
|
+
}
|
|
28048
|
+
try {
|
|
28049
|
+
const cam = await navigator.permissions.query({ name: "camera" });
|
|
28050
|
+
const mic = await navigator.permissions.query({ name: "microphone" });
|
|
28051
|
+
console.log("[LiveKit] Permissions check:", { cam: cam.state, mic: mic.state });
|
|
28052
|
+
return cam.state === "granted" && mic.state === "granted";
|
|
28053
|
+
} catch (error) {
|
|
28054
|
+
console.warn("[LiveKit] Permission check not supported or failed:", error);
|
|
28055
|
+
return false;
|
|
28056
|
+
}
|
|
28020
28057
|
}
|
|
28021
28058
|
/**
|
|
28022
28059
|
* Connect to LiveKit Room
|
|
@@ -28064,8 +28101,23 @@ class LiveKitService {
|
|
|
28064
28101
|
async publishLocalTracks() {
|
|
28065
28102
|
const localContainer = this.wrapper.querySelector(".td-pip");
|
|
28066
28103
|
try {
|
|
28067
|
-
|
|
28068
|
-
|
|
28104
|
+
let videoPub = null;
|
|
28105
|
+
if (this.preAcquiredTracks && this.preAcquiredTracks.length > 0) {
|
|
28106
|
+
console.log("[LiveKit] Publishing pre-acquired tracks...");
|
|
28107
|
+
const videoTrack = this.preAcquiredTracks.find((t) => t.kind === "video");
|
|
28108
|
+
const audioTrack = this.preAcquiredTracks.find((t) => t.kind === "audio");
|
|
28109
|
+
if (videoTrack) {
|
|
28110
|
+
videoPub = await this.room.localParticipant.publishTrack(videoTrack, { source: "camera" });
|
|
28111
|
+
}
|
|
28112
|
+
if (audioTrack) {
|
|
28113
|
+
await this.room.localParticipant.publishTrack(audioTrack, { source: "microphone" });
|
|
28114
|
+
}
|
|
28115
|
+
this.preAcquiredTracks = [];
|
|
28116
|
+
} else {
|
|
28117
|
+
await this.room.localParticipant.setCameraEnabled(true);
|
|
28118
|
+
await this.room.localParticipant.setMicrophoneEnabled(true);
|
|
28119
|
+
videoPub = Array.from(this.room.localParticipant.videoTrackPublications.values()).find((p) => p.source === "camera");
|
|
28120
|
+
}
|
|
28069
28121
|
if (videoPub && videoPub.track && localContainer) {
|
|
28070
28122
|
localContainer.innerHTML = "";
|
|
28071
28123
|
localContainer.style.backgroundImage = "none";
|
|
@@ -28076,24 +28128,15 @@ class LiveKitService {
|
|
|
28076
28128
|
videoEl.style.transform = "scale(-1, 1)";
|
|
28077
28129
|
localContainer.appendChild(videoEl);
|
|
28078
28130
|
console.log("[LiveKit] Local video attached");
|
|
28131
|
+
} else {
|
|
28132
|
+
console.warn("[LiveKit] No Video Publication found to attach!");
|
|
28079
28133
|
}
|
|
28080
28134
|
} catch (e2) {
|
|
28081
|
-
console.error("[LiveKit] Failed to
|
|
28082
|
-
if (e2.name === "NotAllowedError") {
|
|
28083
|
-
alert("Không thể mở Camera. Hãy kiểm tra icon ổ khóa trên thanh địa chỉ hoặc cài đặt Quyền Riêng Tư (Privacy) của máy tính.");
|
|
28084
|
-
}
|
|
28085
|
-
}
|
|
28086
|
-
try {
|
|
28087
|
-
const devices = await Room.getLocalDevices("audioinput");
|
|
28088
|
-
console.log("[LiveKit] Audio Input Devices:", devices);
|
|
28089
|
-
if (devices.length === 0) {
|
|
28090
|
-
alert("Không tìm thấy Micro nào trên thiết bị của bạn!");
|
|
28091
|
-
}
|
|
28092
|
-
await this.room.localParticipant.setMicrophoneEnabled(true);
|
|
28093
|
-
} catch (e2) {
|
|
28094
|
-
console.error("[LiveKit] Failed to enable Microphone:", e2);
|
|
28135
|
+
console.error("[LiveKit] Failed to publish tracks:", e2);
|
|
28095
28136
|
if (e2.name === "NotAllowedError") {
|
|
28096
|
-
alert("Không thể mở
|
|
28137
|
+
alert("Không thể mở Camera/Microphone. Hãy kiểm tra icon ổ khóa trên thanh địa chỉ hoặc cài đặt quyền riêng tư.");
|
|
28138
|
+
} else {
|
|
28139
|
+
alert(`Lỗi Camera: ${e2.message}`);
|
|
28097
28140
|
}
|
|
28098
28141
|
}
|
|
28099
28142
|
}
|
|
@@ -28262,7 +28305,9 @@ class ClinicWidget {
|
|
|
28262
28305
|
}
|
|
28263
28306
|
if (consultBtn) {
|
|
28264
28307
|
consultBtn.addEventListener("click", () => {
|
|
28265
|
-
|
|
28308
|
+
consultBtn.addEventListener("click", () => {
|
|
28309
|
+
store.setState({ status: WidgetStates.PATIENT_FORM });
|
|
28310
|
+
});
|
|
28266
28311
|
});
|
|
28267
28312
|
}
|
|
28268
28313
|
} else if (status === WidgetStates.PATIENT_FORM) {
|
|
@@ -28274,16 +28319,16 @@ class ClinicWidget {
|
|
|
28274
28319
|
});
|
|
28275
28320
|
}
|
|
28276
28321
|
if (form) {
|
|
28277
|
-
form.addEventListener("submit", (e2) => {
|
|
28322
|
+
form.addEventListener("submit", async (e2) => {
|
|
28278
28323
|
e2.preventDefault();
|
|
28279
28324
|
const formData = new FormData(form);
|
|
28280
28325
|
const name = formData.get("name");
|
|
28281
28326
|
const phone = formData.get("phone");
|
|
28282
|
-
this.triggerCallback("onConsultationStarted", { name, phone });
|
|
28283
28327
|
store.setState({
|
|
28284
|
-
user: { name, phone }
|
|
28285
|
-
status: WidgetStates.CONSULTATION
|
|
28328
|
+
user: { name, phone }
|
|
28286
28329
|
});
|
|
28330
|
+
this.triggerCallback("onConsultationStarted", { name, phone });
|
|
28331
|
+
store.setState({ status: WidgetStates.CONSULTATION });
|
|
28287
28332
|
});
|
|
28288
28333
|
}
|
|
28289
28334
|
} else if (status === WidgetStates.CONSULTATION) {
|
|
@@ -28291,7 +28336,9 @@ class ClinicWidget {
|
|
|
28291
28336
|
const endBtn = this.shadowRoot.getElementById("td-consult-end");
|
|
28292
28337
|
if (endBtn) {
|
|
28293
28338
|
endBtn.addEventListener("click", () => {
|
|
28294
|
-
this.
|
|
28339
|
+
if (this.livekit) {
|
|
28340
|
+
this.livekit.disconnect();
|
|
28341
|
+
}
|
|
28295
28342
|
store.setState({ status: WidgetStates.COMPLETED });
|
|
28296
28343
|
});
|
|
28297
28344
|
}
|
|
@@ -28353,7 +28400,9 @@ class ClinicWidget {
|
|
|
28353
28400
|
const btn = el.querySelector("#td-inline-btn");
|
|
28354
28401
|
if (btn) {
|
|
28355
28402
|
btn.addEventListener("click", () => {
|
|
28356
|
-
|
|
28403
|
+
btn.addEventListener("click", () => {
|
|
28404
|
+
store.setState({ status: WidgetStates.PATIENT_FORM });
|
|
28405
|
+
});
|
|
28357
28406
|
});
|
|
28358
28407
|
}
|
|
28359
28408
|
} else {
|
|
@@ -28394,18 +28443,26 @@ class ClinicWidget {
|
|
|
28394
28443
|
store.setState({ doctorOnline: isOnline });
|
|
28395
28444
|
}
|
|
28396
28445
|
});
|
|
28397
|
-
this.socket.on("customer:token", (data) => {
|
|
28446
|
+
this.socket.on("customer:token", async (data) => {
|
|
28398
28447
|
console.log("Received token from server:", data);
|
|
28399
28448
|
if (data.token) {
|
|
28400
28449
|
if (!this.livekit) {
|
|
28401
28450
|
this.livekit = new LiveKitService();
|
|
28402
28451
|
}
|
|
28403
|
-
const
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28452
|
+
const connectLiveKit = () => {
|
|
28453
|
+
const modal = this.shadowRoot.querySelector(".td-consultation-modal");
|
|
28454
|
+
if (modal) {
|
|
28455
|
+
console.log("[Widget] LiveKit Params:", { url: CONFIG.LIVEKIT_URL, token: data.token });
|
|
28456
|
+
this.livekit.connect(CONFIG.LIVEKIT_URL, data.token, modal);
|
|
28457
|
+
} else {
|
|
28458
|
+
console.error("Consultation modal not found for video rendering");
|
|
28459
|
+
}
|
|
28460
|
+
};
|
|
28461
|
+
const granted = await this.livekit.checkPermissions();
|
|
28462
|
+
if (granted) {
|
|
28463
|
+
connectLiveKit();
|
|
28407
28464
|
} else {
|
|
28408
|
-
|
|
28465
|
+
this.showPermissionModal(connectLiveKit);
|
|
28409
28466
|
}
|
|
28410
28467
|
}
|
|
28411
28468
|
});
|
|
@@ -28454,6 +28511,39 @@ class ClinicWidget {
|
|
|
28454
28511
|
this.socket = null;
|
|
28455
28512
|
}
|
|
28456
28513
|
}
|
|
28514
|
+
showPermissionModal(onSuccessCallback) {
|
|
28515
|
+
if (this.shadowRoot.getElementById("td-permission-modal")) return;
|
|
28516
|
+
const state = store.getState();
|
|
28517
|
+
const div = document.createElement("div");
|
|
28518
|
+
div.innerHTML = getPermissionModalHtml(state);
|
|
28519
|
+
Array.from(div.children).forEach((child) => {
|
|
28520
|
+
this.shadowRoot.appendChild(child);
|
|
28521
|
+
});
|
|
28522
|
+
const modalEl = this.shadowRoot.getElementById("td-permission-modal");
|
|
28523
|
+
const closeBtn = modalEl.querySelector("#td-perm-close");
|
|
28524
|
+
const cancelBtn = modalEl.querySelector("#td-perm-cancel");
|
|
28525
|
+
const allowBtn = modalEl.querySelector("#td-perm-allow");
|
|
28526
|
+
const closeModal = () => {
|
|
28527
|
+
modalEl.remove();
|
|
28528
|
+
};
|
|
28529
|
+
const onAllow = async () => {
|
|
28530
|
+
closeModal();
|
|
28531
|
+
if (!this.livekit) {
|
|
28532
|
+
this.livekit = new LiveKitService();
|
|
28533
|
+
}
|
|
28534
|
+
const ok = await this.livekit.requestPermissions();
|
|
28535
|
+
if (ok) {
|
|
28536
|
+
if (typeof onSuccessCallback === "function") {
|
|
28537
|
+
onSuccessCallback();
|
|
28538
|
+
}
|
|
28539
|
+
} else {
|
|
28540
|
+
console.warn("User denied permissions via browser prompt");
|
|
28541
|
+
}
|
|
28542
|
+
};
|
|
28543
|
+
if (closeBtn) closeBtn.addEventListener("click", closeModal);
|
|
28544
|
+
if (cancelBtn) cancelBtn.addEventListener("click", closeModal);
|
|
28545
|
+
if (allowBtn) allowBtn.addEventListener("click", onAllow);
|
|
28546
|
+
}
|
|
28457
28547
|
}
|
|
28458
28548
|
(function() {
|
|
28459
28549
|
const currentScript = document.querySelector("script[data-widget-id]");
|