in comments.)
try {
var phrases = [
'Or for same-origin deployment',
'Gracie Chat Widget',
'Prevent double-loading',
'Get API base URL',
'gracieWidgetLoaded',
'apiBase',
'chatEndpoint',
'Gracie is thinking'
];
var pattern = /(\*+\s*)?Or for same-origin deployment|Gracie Chat Widget|gracieWidgetLoaded|chatEndpoint|apiBase/i;
var root = document.documentElement || document.body;
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
var toRemove = [];
while (walker.nextNode()) {
var node = walker.currentNode;
var text = (node.nodeValue || '').trim();
if (!text) continue;
var matched = false;
for (var i = 0; i < phrases.length; i++) {
if (text.indexOf(phrases[i]) !== -1) {
matched = true;
break;
}
}
if (!matched && pattern.test(text)) matched = true;
if (matched) toRemove.push(node);
}
// Also scrub any element nodes that contain the leaked script text
var elements = (root || document).querySelectorAll ? (root || document).querySelectorAll('pre, code, div, p, span') : [];
for (var j = 0; j < elements.length; j++) {
var el = elements[j];
var t = (el && el.textContent) ? el.textContent : '';
if (t && pattern.test(t)) {
el.textContent = '';
}
}
toRemove.forEach(function(n) { if (n && n.parentNode) n.parentNode.removeChild(n); });
// If leaked text is outside , try to remove from document child nodes.
var topNodes = document.childNodes || [];
for (var k = 0; k < topNodes.length; k++) {
var tn = topNodes[k];
if (tn && tn.nodeType === 3 && pattern.test(String(tn.nodeValue || ''))) {
document.removeChild(tn);
}
}
} catch (e) {
// non-fatal
}
// Create widget HTML
var widget = document.createElement('div');
widget.id = 'gracie-widget';
widget.innerHTML = `
`;
document.body.appendChild(widget);
// Get elements
var toggleBtn = document.getElementById('gracie-toggle');
var closeBtn = document.getElementById('gracie-close');
var messagesDiv = document.getElementById('gracie-messages');
var inputField = document.getElementById('gracie-input');
var sendBtn = document.getElementById('gracie-send');
// FIX 2026-02-11: Generate persistent session ID
// This ensures conversation state is maintained across messages
function getSessionId() {
var stored = sessionStorage.getItem('gracie_session_id');
if (stored) return stored;
// Generate new session ID (timestamp + random for uniqueness)
var newId = 'widget_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
sessionStorage.setItem('gracie_session_id', newId);
console.log('[Gracie Widget] New session:', newId);
return newId;
}
var sessionId = getSessionId();
// Open/close handlers
toggleBtn.onclick = function() {
widget.classList.add('open');
inputField.focus();
};
closeBtn.onclick = function() {
widget.classList.remove('open');
};
// Add message to chat
function addMessage(text, type) {
var msg = document.createElement('div');
msg.className = 'gracie-msg ' + type;
msg.textContent = text;
messagesDiv.appendChild(msg);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
return msg;
}
function addTypingIndicator() {
var msg = document.createElement('div');
msg.className = 'gracie-msg agent typing';
msg.innerHTML = 'Gracie is thinking ';
messagesDiv.appendChild(msg);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
return msg;
}
// Send message
var isSending = false;
async function sendMessage() {
var text = inputField.value.trim();
if (!text || isSending) return;
isSending = true;
sendBtn.disabled = true;
inputField.value = '';
// Add user message
addMessage(text, 'user');
// Add typing indicator
var typingMsg = addTypingIndicator();
var typingStart = Date.now();
var minTypingMs = 350;
try {
var response = await fetch(chatEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': 'true'
},
body: JSON.stringify({
message: text,
user: sessionId, // FIX 2026-02-11: Use persistent session ID
source: window.location.hostname || 'widget'
})
});
if (!response.ok) {
throw new Error('Server returned ' + response.status);
}
var data = await response.json();
// Remove typing indicator
var elapsed = Date.now() - typingStart;
var waitMs = Math.max(0, minTypingMs - elapsed);
setTimeout(function() {
typingMsg.remove();
}, waitMs);
// Add agent response
var agentText = data.response || 'Sorry, I could not process that. Please try again.';
addMessage(agentText, 'agent');
} catch (error) {
console.error('[Gracie Widget] Error:', error);
// Remove typing indicator
var elapsed = Date.now() - typingStart;
var waitMs = Math.max(0, minTypingMs - elapsed);
setTimeout(function() {
typingMsg.remove();
}, waitMs);
// Show error message
addMessage('Connection error. Please call us at (972) 850-6982', 'agent');
}
isSending = false;
sendBtn.disabled = false;
inputField.focus();
}
// Event listeners
sendBtn.onclick = sendMessage;
inputField.onkeydown = function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
};
console.log('[Gracie Widget] Loaded successfully');
})();
Just Doing Business LLC
Hi there! Welcome to Just Doing Business. I'm Gracie - how can I help you today?
