enable us option
This commit is contained in:
parent
b7aa76ed45
commit
087eec0e29
|
|
@ -0,0 +1,565 @@
|
|||
/**
|
||||
* ==============================================================================
|
||||
* SUPPORT WIDGET — Web Component (Shadow DOM)
|
||||
* ==============================================================================
|
||||
* Shadow DOM cô lập CSS hoàn toàn, tránh conflict/flicker với host page.
|
||||
* 3 tabs: SMS (AU Only) / Email / WhatsApp
|
||||
* ==============================================================================
|
||||
*/
|
||||
(function (w, d) {
|
||||
"use strict";
|
||||
|
||||
if (w.__MSW_LOADED__) return;
|
||||
w.__MSW_LOADED__ = true;
|
||||
|
||||
var P = w.PROLOGY_CONFIG || {};
|
||||
var CFG = {
|
||||
apiEndpoint:
|
||||
P.CHAT_API_URL ||
|
||||
P.API_URL ||
|
||||
"https://prologyms.nswteam.net/chat-plugin/api/support",
|
||||
whatsappNumber: P.CHAT_WHATSAPP_NUMBER || "84901234567",
|
||||
accentColor: P.CHAT_ACCENT_COLOR || "#4f46e5",
|
||||
position: P.CHAT_POSITION || "right",
|
||||
rateLimit: {
|
||||
max: P.CHAT_RATE_LIMIT_MAX != null ? P.CHAT_RATE_LIMIT_MAX : 3,
|
||||
windowMin:
|
||||
P.CHAT_RATE_LIMIT_WINDOW_MIN != null ? P.CHAT_RATE_LIMIT_WINDOW_MIN : 10,
|
||||
},
|
||||
minFillMs: P.CHAT_MIN_FILL_MS != null ? P.CHAT_MIN_FILL_MS : 3000,
|
||||
fabImageUrl:
|
||||
P.CHAT_FAB_IMAGE_URL ||
|
||||
"https://prology.nswteam.net/media/wysiwyg/image_2026-04-02_15-43-14.png",
|
||||
};
|
||||
|
||||
/* ── Utilities ── */
|
||||
function sanitize(str, maxLen) {
|
||||
if (typeof str !== "string") return "";
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
.replace(/\//g, "/")
|
||||
.trim()
|
||||
.slice(0, maxLen || 2000);
|
||||
}
|
||||
function nonce() {
|
||||
var a = new Uint8Array(16);
|
||||
(w.crypto || w.msCrypto).getRandomValues(a);
|
||||
return Array.from(a, function (b) {
|
||||
return ("0" + b.toString(16)).slice(-2);
|
||||
}).join("");
|
||||
}
|
||||
var RL_KEY = "_msw_rl";
|
||||
function getRl() {
|
||||
try {
|
||||
return (
|
||||
JSON.parse(sessionStorage.getItem(RL_KEY)) || { n: 0, t: Date.now() }
|
||||
);
|
||||
} catch (e) {
|
||||
return { n: 0, t: Date.now() };
|
||||
}
|
||||
}
|
||||
function setRl(o) {
|
||||
try {
|
||||
sessionStorage.setItem(RL_KEY, JSON.stringify(o));
|
||||
} catch (e) {}
|
||||
}
|
||||
function isLimited() {
|
||||
var cfg = CFG.rateLimit,
|
||||
rl = getRl(),
|
||||
win = cfg.windowMin * 60 * 1000;
|
||||
if (Date.now() - rl.t > win) {
|
||||
setRl({ n: 0, t: Date.now() });
|
||||
return false;
|
||||
}
|
||||
return rl.n >= cfg.max;
|
||||
}
|
||||
function limitCooldown() {
|
||||
var rl = getRl(),
|
||||
win = CFG.rateLimit.windowMin * 60 * 1000;
|
||||
return Math.max(0, Math.ceil((rl.t + win - Date.now()) / 1000));
|
||||
}
|
||||
function bumpRl() {
|
||||
var rl = getRl();
|
||||
rl.n++;
|
||||
setRl(rl);
|
||||
}
|
||||
function csrfToken() {
|
||||
var el = d.querySelector('input[name="form_key"]');
|
||||
return el ? el.value : "";
|
||||
}
|
||||
function validateSMS(f) {
|
||||
var e = [];
|
||||
var name = (f.name || "").trim();
|
||||
var phone = (f.phone || "").trim();
|
||||
var message = (f.message || "").trim();
|
||||
if (!name || !/^[\p{L}\p{M}'\-\s]{2,80}$/u.test(name)) e.push("sms-name");
|
||||
if (
|
||||
!phone ||
|
||||
!/^[\d\s\+\-\(\)]{7,15}$/.test(phone) ||
|
||||
!/\d{5,}/.test(phone)
|
||||
)
|
||||
e.push("sms-phone");
|
||||
if (!message || message.length < 10) e.push("sms-msg");
|
||||
return e;
|
||||
}
|
||||
function validateEmail(f) {
|
||||
var e = [];
|
||||
var name = (f.name || "").trim();
|
||||
var email = (f.email || "").trim();
|
||||
var message = (f.message || "").trim();
|
||||
if (!name || !/^[\p{L}\p{M}'\-\s]{2,80}$/u.test(name)) e.push("email-name");
|
||||
if (!email || !/^\w+([.\-]\w+)*@([\w\-]+\.)+[a-zA-Z]{2,12}$/.test(email))
|
||||
e.push("email-email");
|
||||
if (!message || message.length < 10) e.push("email-msg");
|
||||
return e;
|
||||
}
|
||||
|
||||
/* ── SVG assets ── */
|
||||
var SVG_ARROW =
|
||||
'<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M2 21L23 12 2 3v7l15 2-15 2z"/></svg>';
|
||||
var WA_PATH =
|
||||
"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347zM11.979 0C5.37 0 0 5.373 0 11.979c0 2.11.553 4.094 1.518 5.818L.057 24l6.349-1.663A11.938 11.938 0 0011.979 24C18.588 24 24 18.626 24 11.979 24 5.373 18.588 0 11.979 0zm0 21.818a9.839 9.839 0 01-5.012-1.369l-.36-.214-3.73.978.995-3.636-.235-.374a9.806 9.806 0 01-1.506-5.224c0-5.42 4.413-9.833 9.848-9.833 5.437 0 9.851 4.413 9.851 9.833 0 5.421-4.414 9.839-9.851 9.839z";
|
||||
function waSVG(size) {
|
||||
return (
|
||||
'<svg viewBox="0 0 24 24" width="' +
|
||||
size +
|
||||
'" height="' +
|
||||
size +
|
||||
'" fill="currentColor"><path d="' +
|
||||
WA_PATH +
|
||||
'"/></svg>'
|
||||
);
|
||||
}
|
||||
var SVG_CHECK =
|
||||
'<svg viewBox="0 0 24 24" width="28" height="28" fill="currentColor"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>';
|
||||
|
||||
/* ── Shadow CSS ── */
|
||||
var ac = CFG.accentColor;
|
||||
var pos = CFG.position === "left" ? "left:24px;" : "right:24px;";
|
||||
|
||||
var SHADOW_CSS = [
|
||||
':host{all:initial;display:block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;}',
|
||||
/* FAB — no transition on background/box-shadow to kill flicker; only transform transitions */
|
||||
"#fab{position:fixed;bottom:24px;" + pos + "z-index:99998;",
|
||||
"width:60px;height:60px;border-radius:50%;",
|
||||
"background-image:url(" + CFG.fabImageUrl + ");",
|
||||
"background-size:cover;background-position:center;background-repeat:no-repeat;",
|
||||
"border:none;cursor:pointer;outline:none;",
|
||||
"box-shadow:0 6px 20px rgba(79,70,229,.4);",
|
||||
"transform:translateZ(0);transition:transform .22s cubic-bezier(.34,1.56,.64,1);}",
|
||||
"#fab:hover{transform:scale(1.08) translateY(-3px) translateZ(0);}",
|
||||
"#badge{position:absolute;top:-4px;right:-4px;width:20px;height:20px;border-radius:50%;",
|
||||
"background:#ef4444;font-size:11px;font-weight:700;color:#fff;",
|
||||
"display:none;align-items:center;justify-content:center;border:2px solid #1a1f2e;}",
|
||||
/* Panel */
|
||||
"#panel{position:fixed;bottom:92px;" + pos + "z-index:99999;width:360px;",
|
||||
"background:linear-gradient(145deg,rgb(42,59,84),rgb(30,41,59));border-radius:18px;overflow:hidden;",
|
||||
"box-shadow:0 20px 60px rgba(0,0,0,.55),0 4px 16px rgba(0,0,0,.3);",
|
||||
"transform:scale(.9) translateY(16px) translateZ(0);opacity:0;pointer-events:none;",
|
||||
"transition:transform .28s cubic-bezier(.34,1.56,.64,1),opacity .2s ease;",
|
||||
"will-change:transform,opacity;}",
|
||||
"#panel.open{transform:scale(1) translateY(0) translateZ(0);opacity:1;pointer-events:auto;}",
|
||||
/* Tabs */
|
||||
"#tabs{display:flex;gap:3px;background:#242938;margin:14px 14px 0;border-radius:11px;padding:4px;}",
|
||||
".tab{flex:1;padding:8px 4px;font-size:11.5px;font-weight:700;text-align:center;line-height:1.35;",
|
||||
"color:#8b92a8;background:transparent;border:none;cursor:pointer;border-radius:8px;",
|
||||
"transition:background .18s,color .18s;font-family:inherit;letter-spacing:.3px;}",
|
||||
".tab:hover{color:#e0e2ea;}",
|
||||
".tab.active{background:" + ac + ";color:#fff;}",
|
||||
/* Panes */
|
||||
".pane{display:none;padding:14px 14px 18px;}",
|
||||
".pane.active{display:block;}",
|
||||
/* Fields */
|
||||
".field{margin-bottom:11px;}",
|
||||
".field input,.field textarea{width:100%;padding:11px 13px;font-size:14px;font-family:inherit;",
|
||||
"background:#252b3b;color:#dde0eb;border:1px solid #333b52;border-radius:9px;outline:none;",
|
||||
"transition:border-color .18s,box-shadow .18s;box-sizing:border-box;-webkit-appearance:none;}",
|
||||
".field input::placeholder,.field textarea::placeholder{color:#5d6478;}",
|
||||
".field input:focus,.field textarea:focus{border-color:" +
|
||||
ac +
|
||||
";box-shadow:0 0 0 3px rgba(79,70,229,.22);}",
|
||||
".field input.err,.field textarea.err{border-color:#ef4444;}",
|
||||
".field textarea{resize:vertical;min-height:88px;}",
|
||||
".hint{font-size:11.5px;color:#f87171;margin-top:4px;display:none;}",
|
||||
".hint.show{display:block;}",
|
||||
".hp{position:absolute;left:-9999px;opacity:0;height:0;width:0;overflow:hidden;pointer-events:none;}",
|
||||
/* Submit */
|
||||
".btn-submit{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:12px;",
|
||||
"background:" + ac + ";color:#fff;border:none;border-radius:9px;",
|
||||
"font-size:14.5px;font-weight:600;font-family:inherit;cursor:pointer;margin-top:6px;",
|
||||
"transition:background .18s;}",
|
||||
".btn-submit:hover{background:#4338ca;}",
|
||||
".btn-submit:active{transform:scale(.98);}",
|
||||
".btn-submit:disabled{background:#4a4f61;cursor:not-allowed;}",
|
||||
/* Status */
|
||||
".status{font-size:13px;font-weight:500;margin-top:9px;text-align:center;",
|
||||
"border-radius:7px;padding:8px 12px;display:none;}",
|
||||
".status.ok{display:block;background:#0a2e1a;color:#34d399;}",
|
||||
".status.fail{display:block;background:#2e0a0a;color:#f87171;}",
|
||||
".status.warn{display:block;background:#2c1f00;color:#fbbf24;}",
|
||||
/* WhatsApp pane */
|
||||
".wa-wrap{text-align:center;padding:4px 0 2px;}",
|
||||
".wa-icon-wrap{width:54px;height:54px;border-radius:50%;background:#1a2e20;",
|
||||
"display:flex;align-items:center;justify-content:center;margin:0 auto 14px;color:#25d366;}",
|
||||
".wa-desc{font-size:13.5px;color:#8b92a8;line-height:1.6;margin:0 0 18px;}",
|
||||
".btn-wa{display:flex;align-items:center;justify-content:center;gap:9px;width:100%;padding:13px;",
|
||||
"background:#25d366;color:#fff;border:none;border-radius:9px;",
|
||||
"font-size:14.5px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .18s;}",
|
||||
".btn-wa:hover{background:#1db954;}",
|
||||
/* Success */
|
||||
"#success{display:none;flex-direction:column;align-items:center;text-align:center;padding:36px 20px;}",
|
||||
".success-icon{width:58px;height:58px;border-radius:50%;background:#0a2e1a;",
|
||||
"display:flex;align-items:center;justify-content:center;color:#34d399;margin-bottom:15px;}",
|
||||
"#success h3{font-size:18px;font-weight:700;color:#dde0eb;margin:0 0 8px;}",
|
||||
"#success p{font-size:13.5px;color:#8b92a8;margin:0 0 22px;line-height:1.55;}",
|
||||
".btn-new{padding:10px 24px;font-size:13.5px;font-weight:600;color:" +
|
||||
ac +
|
||||
";",
|
||||
"background:rgba(79,70,229,.13);border:none;border-radius:8px;cursor:pointer;",
|
||||
"transition:background .18s;font-family:inherit;}",
|
||||
".btn-new:hover{background:rgba(79,70,229,.22);}",
|
||||
/* Responsive */
|
||||
"@media(max-width:420px){#panel{width:calc(100vw - 28px);" +
|
||||
(CFG.position === "left" ? "left:14px;" : "right:14px;") +
|
||||
"}",
|
||||
"#fab{bottom:20px;}#panel{bottom:88px;}}",
|
||||
].join("");
|
||||
|
||||
/* ── Inner HTML ── */
|
||||
var INNER_HTML = [
|
||||
'<button id="fab" aria-label="Open support" aria-expanded="false"><span id="badge">1</span></button>',
|
||||
'<div id="panel" role="dialog" aria-modal="true" aria-label="Contact Support">',
|
||||
'<div id="tabs">',
|
||||
'<button class="tab active" data-tab="sms">SMS (AU<br>ONLY)</button>',
|
||||
'<button class="tab" data-tab="email">EMAIL</button>',
|
||||
'<button class="tab" data-tab="whatsapp">WHATSAPP</button>',
|
||||
"</div>",
|
||||
/* SMS */
|
||||
'<div class="pane active" id="pane-sms">',
|
||||
'<div class="field"><input id="sms-name" type="text" maxlength="80" autocomplete="name" placeholder="Your Name"><div class="hint" id="h-sms-name">Please enter your full name</div></div>',
|
||||
'<div class="field"><input id="sms-phone" type="tel" maxlength="15" autocomplete="tel" placeholder="Mobile Number (Australia)"><div class="hint" id="h-sms-phone">Enter a valid AU mobile number</div></div>',
|
||||
'<div class="field"><textarea id="sms-msg" maxlength="1000" placeholder="How can we help?"></textarea><div class="hint" id="h-sms-msg">Please provide more details (min 10 chars)</div></div>',
|
||||
'<div class="hp"><input type="text" id="hp-sms" tabindex="-1" autocomplete="off"></div>',
|
||||
'<button class="btn-submit" id="btn-sms">Send SMS ' +
|
||||
SVG_ARROW +
|
||||
"</button>",
|
||||
'<div class="status" id="st-sms"></div>',
|
||||
"</div>",
|
||||
/* Email */
|
||||
'<div class="pane" id="pane-email">',
|
||||
'<div class="field"><input id="email-name" type="text" maxlength="80" autocomplete="name" placeholder="Your Name"><div class="hint" id="h-email-name">Please enter your full name</div></div>',
|
||||
'<div class="field"><input id="email-email" type="email" maxlength="120" autocomplete="email" placeholder="Your Email Address"><div class="hint" id="h-email-email">Please enter a valid email address</div></div>',
|
||||
'<div class="field"><textarea id="email-msg" maxlength="1000" placeholder="How can we help?"></textarea><div class="hint" id="h-email-msg">Please provide more details (min 10 chars)</div></div>',
|
||||
'<div class="hp"><input type="text" id="hp-email" tabindex="-1" autocomplete="off"></div>',
|
||||
'<button class="btn-submit" id="btn-email">Send Email ' +
|
||||
SVG_ARROW +
|
||||
"</button>",
|
||||
'<div class="status" id="st-email"></div>',
|
||||
"</div>",
|
||||
/* WhatsApp */
|
||||
'<div class="pane" id="pane-whatsapp">',
|
||||
'<div class="wa-wrap">',
|
||||
'<div class="wa-icon-wrap">' + waSVG(26) + "</div>",
|
||||
'<p class="wa-desc">Chat with us on WhatsApp for support,<br>inquiries, and immediate expert advice.</p>',
|
||||
'<button class="btn-wa" id="btn-wa">' +
|
||||
waSVG(20) +
|
||||
" Chat Now on WhatsApp</button>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
/* Success */
|
||||
'<div id="success">',
|
||||
'<div class="success-icon">' + SVG_CHECK + "</div>",
|
||||
"<h3>Message Received!</h3>",
|
||||
"<p>Thanks for reaching out.<br>We'll get back to you shortly.</p>",
|
||||
'<button class="btn-new" id="btn-new">Send another message</button>',
|
||||
"</div>",
|
||||
"</div>",
|
||||
].join("");
|
||||
|
||||
/* ── Web Component ── */
|
||||
function SupportWidget() {
|
||||
return Reflect.construct(HTMLElement, [], SupportWidget);
|
||||
}
|
||||
Object.setPrototypeOf(SupportWidget.prototype, HTMLElement.prototype);
|
||||
Object.setPrototypeOf(SupportWidget, HTMLElement);
|
||||
|
||||
SupportWidget.prototype.connectedCallback = function () {
|
||||
var shadow = this.attachShadow({ mode: "open" });
|
||||
var style = d.createElement("style");
|
||||
style.textContent = SHADOW_CSS;
|
||||
shadow.appendChild(style);
|
||||
var root = d.createElement("div");
|
||||
root.innerHTML = INNER_HTML;
|
||||
shadow.appendChild(root);
|
||||
this._boot(shadow);
|
||||
};
|
||||
|
||||
SupportWidget.prototype._boot = function (s) {
|
||||
var isOpen = false;
|
||||
var formStartTs = Date.now();
|
||||
var $fab = s.getElementById("fab");
|
||||
var $panel = s.getElementById("panel");
|
||||
var $badge = s.getElementById("badge");
|
||||
var $success = s.getElementById("success");
|
||||
var $tabs = s.getElementById("tabs");
|
||||
|
||||
function openPanel() {
|
||||
isOpen = true;
|
||||
$panel.classList.add("open");
|
||||
$fab.setAttribute("aria-expanded", "true");
|
||||
$badge.style.display = "none";
|
||||
formStartTs = Date.now();
|
||||
}
|
||||
function closePanel() {
|
||||
isOpen = false;
|
||||
$panel.classList.remove("open");
|
||||
$fab.setAttribute("aria-expanded", "false");
|
||||
}
|
||||
|
||||
$fab.addEventListener("click", function (e) {
|
||||
e.stopPropagation();
|
||||
isOpen ? closePanel() : openPanel();
|
||||
});
|
||||
d.addEventListener("click", function (e) {
|
||||
if (!isOpen) return;
|
||||
var path = e.composedPath ? e.composedPath() : [];
|
||||
if (
|
||||
!path.some(function (n) {
|
||||
return n === $panel || n === $fab;
|
||||
})
|
||||
)
|
||||
closePanel();
|
||||
});
|
||||
d.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Escape" && isOpen) closePanel();
|
||||
});
|
||||
|
||||
/* Tabs */
|
||||
s.querySelectorAll(".tab").forEach(function (tab) {
|
||||
tab.addEventListener("click", function () {
|
||||
s.querySelectorAll(".tab").forEach(function (el) {
|
||||
el.classList.toggle("active", el === tab);
|
||||
});
|
||||
s.querySelectorAll(".pane").forEach(function (el) {
|
||||
el.classList.remove("active");
|
||||
});
|
||||
var pane = s.getElementById("pane-" + tab.dataset.tab);
|
||||
if (pane) pane.classList.add("active");
|
||||
});
|
||||
});
|
||||
|
||||
/* WhatsApp */
|
||||
s.getElementById("btn-wa").addEventListener("click", function () {
|
||||
w.open(
|
||||
"https://wa.me/" + CFG.whatsappNumber,
|
||||
"_blank",
|
||||
"noopener,noreferrer",
|
||||
);
|
||||
});
|
||||
|
||||
/* Helpers */
|
||||
function clearErr(prefix, fields) {
|
||||
fields.forEach(function (f) {
|
||||
var inp = s.getElementById(prefix + "-" + f),
|
||||
hint = s.getElementById("h-" + prefix + "-" + f);
|
||||
if (inp) inp.classList.remove("err");
|
||||
if (hint) hint.classList.remove("show");
|
||||
});
|
||||
var st = s.getElementById("st-" + prefix);
|
||||
if (st) {
|
||||
st.className = "status";
|
||||
st.textContent = "";
|
||||
}
|
||||
}
|
||||
function markErr(ids) {
|
||||
ids.forEach(function (id) {
|
||||
var inp = s.getElementById(id),
|
||||
hint = s.getElementById("h-" + id);
|
||||
if (inp) inp.classList.add("err");
|
||||
if (hint) hint.classList.add("show");
|
||||
});
|
||||
}
|
||||
function setStatus(prefix, msg, type) {
|
||||
var st = s.getElementById("st-" + prefix);
|
||||
if (!st) return;
|
||||
st.textContent = msg;
|
||||
st.className = "status " + type;
|
||||
}
|
||||
function showSuccess() {
|
||||
s.querySelectorAll(".pane").forEach(function (el) {
|
||||
el.classList.remove("active");
|
||||
});
|
||||
$tabs.style.display = "none";
|
||||
$success.style.display = "flex";
|
||||
}
|
||||
function doSubmit(prefix, payload, $btn, label) {
|
||||
if (isLimited()) {
|
||||
setStatus(
|
||||
prefix,
|
||||
"Too many requests. Try again in " + limitCooldown() + "s.",
|
||||
"warn",
|
||||
);
|
||||
return;
|
||||
}
|
||||
$btn.disabled = true;
|
||||
/* Preserve the SVG node — only update the leading text node */
|
||||
var tn = $btn.firstChild;
|
||||
if (tn && tn.nodeType === 3) tn.textContent = "Sending... ";
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", CFG.apiEndpoint, true);
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
xhr.setRequestHeader("X-Form-Key", csrfToken());
|
||||
xhr.withCredentials = false;
|
||||
xhr.timeout = 12000;
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState !== 4) return;
|
||||
$btn.disabled = false;
|
||||
if (tn && tn.nodeType === 3) tn.textContent = label + " ";
|
||||
if (xhr.status === 200 || xhr.status === 201) {
|
||||
bumpRl();
|
||||
showSuccess();
|
||||
} else if (xhr.status === 429)
|
||||
setStatus(prefix, "Server busy. Please try again later.", "warn");
|
||||
else setStatus(prefix, "Failed to send. Please try again.", "fail");
|
||||
};
|
||||
xhr.ontimeout = function () {
|
||||
$btn.disabled = false;
|
||||
if (tn && tn.nodeType === 3) tn.textContent = label + " ";
|
||||
setStatus(prefix, "Connection timeout. Check your network.", "fail");
|
||||
};
|
||||
xhr.send(JSON.stringify(payload));
|
||||
}
|
||||
|
||||
/* SMS submit */
|
||||
s.getElementById("btn-sms").addEventListener("click", function () {
|
||||
clearErr("sms", ["name", "phone", "msg"]);
|
||||
var hp = s.getElementById("hp-sms");
|
||||
if (hp && hp.value) return;
|
||||
var remaining = Math.ceil(
|
||||
(CFG.minFillMs - (Date.now() - formStartTs)) / 1000,
|
||||
);
|
||||
if (remaining > 0) {
|
||||
setStatus(
|
||||
"sms",
|
||||
"Please wait " + remaining + "s before submitting.",
|
||||
"warn",
|
||||
);
|
||||
return;
|
||||
}
|
||||
var raw = {
|
||||
name: s.getElementById("sms-name").value,
|
||||
phone: s.getElementById("sms-phone").value,
|
||||
message: s.getElementById("sms-msg").value,
|
||||
};
|
||||
var errs = validateSMS(raw);
|
||||
if (errs.length) {
|
||||
markErr(errs);
|
||||
return;
|
||||
}
|
||||
doSubmit(
|
||||
"sms",
|
||||
{
|
||||
channel: "sms",
|
||||
name: sanitize(raw.name, 80),
|
||||
phone: sanitize(raw.phone, 15),
|
||||
message: sanitize(raw.message, 1000),
|
||||
nonce: nonce(),
|
||||
source: w.location.href.slice(0, 200),
|
||||
ts: Date.now(),
|
||||
},
|
||||
this,
|
||||
"Send SMS",
|
||||
);
|
||||
});
|
||||
|
||||
/* Email submit */
|
||||
s.getElementById("btn-email").addEventListener("click", function () {
|
||||
clearErr("email", ["name", "email", "msg"]);
|
||||
var hp = s.getElementById("hp-email");
|
||||
if (hp && hp.value) return;
|
||||
var remaining = Math.ceil(
|
||||
(CFG.minFillMs - (Date.now() - formStartTs)) / 1000,
|
||||
);
|
||||
if (remaining > 0) {
|
||||
setStatus(
|
||||
"email",
|
||||
"Please wait " + remaining + "s before submitting.",
|
||||
"warn",
|
||||
);
|
||||
return;
|
||||
}
|
||||
var raw = {
|
||||
name: s.getElementById("email-name").value,
|
||||
email: s.getElementById("email-email").value,
|
||||
message: s.getElementById("email-msg").value,
|
||||
};
|
||||
var errs = validateEmail(raw);
|
||||
if (errs.length) {
|
||||
markErr(errs);
|
||||
return;
|
||||
}
|
||||
doSubmit(
|
||||
"email",
|
||||
{
|
||||
channel: "email",
|
||||
name: sanitize(raw.name, 80),
|
||||
email: sanitize(raw.email, 120),
|
||||
message: sanitize(raw.message, 1000),
|
||||
nonce: nonce(),
|
||||
source: w.location.href.slice(0, 200),
|
||||
ts: Date.now(),
|
||||
},
|
||||
this,
|
||||
"Send Email",
|
||||
);
|
||||
});
|
||||
|
||||
/* New message */
|
||||
s.getElementById("btn-new").addEventListener("click", function () {
|
||||
$success.style.display = "none";
|
||||
$tabs.style.display = "flex";
|
||||
s.querySelectorAll(".tab").forEach(function (el) {
|
||||
el.classList.toggle("active", el.dataset.tab === "sms");
|
||||
});
|
||||
s.querySelectorAll(".pane").forEach(function (el) {
|
||||
el.classList.remove("active");
|
||||
});
|
||||
s.getElementById("pane-sms").classList.add("active");
|
||||
[
|
||||
"sms-name",
|
||||
"sms-phone",
|
||||
"sms-msg",
|
||||
"email-name",
|
||||
"email-email",
|
||||
"email-msg",
|
||||
].forEach(function (id) {
|
||||
var el = s.getElementById(id);
|
||||
if (el) el.value = "";
|
||||
});
|
||||
clearErr("sms", ["name", "phone", "msg"]);
|
||||
clearErr("email", ["name", "email", "msg"]);
|
||||
formStartTs = Date.now();
|
||||
});
|
||||
|
||||
/* Badge */
|
||||
setTimeout(function () {
|
||||
if (!isOpen) $badge.style.display = "flex";
|
||||
}, 4000);
|
||||
};
|
||||
|
||||
/* Register & auto-mount */
|
||||
if (!w.customElements.get("support-widget")) {
|
||||
w.customElements.define("support-widget", SupportWidget);
|
||||
}
|
||||
function mount() {
|
||||
if (!d.querySelector("support-widget"))
|
||||
d.body.appendChild(d.createElement("support-widget"));
|
||||
}
|
||||
if (d.readyState === "loading") d.addEventListener("DOMContentLoaded", mount);
|
||||
else mount();
|
||||
})(window, document);
|
||||
|
|
@ -2,6 +2,17 @@ window.PROLOGY_CONFIG = {
|
|||
API_URL: "https://prologyms.nswteam.net/chat-plugin/api/support",
|
||||
LOGO_SRC: "assets/Prology_logo.png",
|
||||
|
||||
// ── Chat / Support widget ──
|
||||
CHAT_API_URL: "https://prologyms.nswteam.net/chat-plugin/api/support",
|
||||
CHAT_WHATSAPP_NUMBER: "84901234567",
|
||||
CHAT_ACCENT_COLOR: "#4f46e5",
|
||||
CHAT_POSITION: "right",
|
||||
CHAT_RATE_LIMIT_MAX: 3,
|
||||
CHAT_RATE_LIMIT_WINDOW_MIN: 10,
|
||||
CHAT_MIN_FILL_MS: 3000,
|
||||
CHAT_FAB_IMAGE_URL:
|
||||
"https://prology.nswteam.net/media/wysiwyg/image_2026-04-02_15-43-14.png",
|
||||
|
||||
// ── Store picker (modal) ──
|
||||
STORE_AU_URL: "https://prology.net/au",
|
||||
STORE_US_URL: "https://prology.net/us",
|
||||
|
|
|
|||
|
|
@ -1093,5 +1093,6 @@
|
|||
|
||||
<footer class="page-footer" role="contentinfo"></footer>
|
||||
<script src="assets/js/footer.js" defer></script>
|
||||
<script src="assets/js/chat.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue