1/** 2 * NSWS Chat Embedding Script 3 * This script can be included in any project to embed the NSWS Chatbot via an iframe 4 * 5 * Usage: 6 * <script src="https://chatbot.nsws.gov.in/chat-embedding.js"></script> 7 */ 8 9(function () { 10 "use strict"; 11 12 // Configuration 13 const MESSAGE_ORIGIN = "https://chatbot.nsws.gov.in"; 14 // const MESSAGE_ORIGIN = "http://localhost:3001"; // Use localhost for development and testing 15 const EMBEDDED_PAGE_URL = `${MESSAGE_ORIGIN}/embedded`; 16 17 // Styles 18 const STYLES = ` 19 .nsws-chat-widget { 20 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 21 "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 22 sans-serif; 23 -webkit-font-smoothing: antialiased; 24 -moz-osx-font-smoothing: grayscale; 25 position: fixed; 26 top: 0; 27 left: 0; 28 width: 0; 29 height: 0; 30 pointer-events: none; 31 z-index: 9998; 32 } 33 34 .nsws-chat-logo-button { 35 position: fixed; 36 bottom: 24px; 37 right: 24px; 38 z-index: 9998; 39 width: 60px; 40 height: 60px; 41 border: none; 42 border-radius: 50%; 43 background: transparent; 44 cursor: pointer; 45 display: flex; 46 align-items: center; 47 justify-content: center; 48 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 49 transition: all 0.3s ease; 50 padding: 0; 51 overflow: hidden; 52 pointer-events: auto; 53 } 54 55 .nsws-chat-logo-button:hover { 56 transform: scale(1.1); 57 box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); 58 } 59 60 .nsws-chat-logo-button img { 61 width: 100%; 62 height: 100%; 63 object-fit: contain; 64 } 65 66 .nsws-chat-notification-badge { 67 position: absolute; 68 bottom: 0; 69 left: 0; 70 width: 12px; 71 height: 12px; 72 border-radius: 50%; 73 background-color: #00d084; 74 border: 2px solid white; 75 animation: pulse 1.5s ease-in-out infinite; 76 } 77 78 @keyframes pulse { 79 0%, 100% { 80 transform: scale(1); 81 } 82 50% { 83 transform: scale(1.1); 84 } 85 } 86 87 .nsws-chat-container { 88 position: fixed; 89 bottom: 24px; 90 right: 24px; 91 z-index: 9999; 92 width: 512px; 93 max-height: calc(100vh - 80px); 94 height: 90vh; 95 background: white; 96 border-radius: 16px; 97 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); 98 display: flex; 99 flex-direction: column; 100 overflow: hidden; 101 opacity: 0; 102 visibility: hidden; 103 transform: scale(0.95) translateY(40px); 104 transition: all 0.3s ease; 105 pointer-events: none; 106 } 107 108 .nsws-chat-container.open { 109 opacity: 1; 110 visibility: visible; 111 transform: scale(1) translateY(0); 112 pointer-events: auto; 113 } 114 115 .nsws-chat-container.fullscreen { 116 position: fixed; 117 inset: 0; 118 width: 100%; 119 height: 100%; 120 border-radius: 0; 121 max-height: 100vh; 122 bottom: auto; 123 right: auto; 124 } 125 126 .nsws-chat-logo-button.hidden { 127 display: none !important; 128 } 129 130 .nsws-chat-controls { 131 position: absolute; 132 top: 1rem; 133 right: 1rem; 134 display: flex; 135 align-items: center; 136 gap: 0; 137 z-index: 9998; 138 pointer-events: auto; 139 } 140 141 .nsws-chat-icon-button { 142 background: rgba(255, 255, 255, 0.1); 143 border: none; 144 color: white; 145 cursor: pointer; 146 padding: 4px; 147 display: flex; 148 align-items: center; 149 justify-content: center; 150 font-size: 20px; 151 transition: all 0.2s ease; 152 border-radius: 4px; 153 margin: 0 4px; 154 } 155 156 .nsws-chat-icon-button:hover { 157 background-color: rgba(255, 255, 255, 0.2); 158 transform: scale(1.05); 159 } 160 161 .nsws-chat-iframe { 162 flex: 1; 163 border: none; 164 background: white; 165 pointer-events: auto; 166 overflow: hidden; 167 } 168 169 .nsws-chat-loader { 170 position: absolute; 171 top: 0; 172 left: 0; 173 right: 0; 174 bottom: 0; 175 display: flex; 176 align-items: center; 177 justify-content: center; 178 background: #EAF3FF; 179 z-index: 10000; 180 transition: opacity 0.3s ease, visibility 0.3s ease; 181 opacity: 1; 182 visibility: visible; 183 } 184 185 .nsws-chat-loader.hidden { 186 opacity: 0; 187 visibility: hidden; 188 pointer-events: none; 189 } 190 191 .nsws-loader-dots { 192 display: flex; 193 align-items: center; 194 justify-content: center; 195 gap: 8px; 196 } 197 198 .nsws-loader-dot { 199 width: 12px; 200 height: 12px; 201 border-radius: 50%; 202 animation: nsws-dot-bounce 1.4s infinite ease-in-out both; 203 } 204 205 .nsws-loader-dot:nth-child(1) { 206 background: #011C4D; 207 animation-delay: -0.32s; 208 } 209 210 .nsws-loader-dot:nth-child(2) { 211 background: #F89B46; 212 animation-delay: -0.16s; 213 } 214 215 .nsws-loader-dot:nth-child(3) { 216 background: #011C4D; 217 animation-delay: 0s; 218 } 219
220 @keyframes nsws-dot-bounce { 221 0%, 80%, 100% { 222 transform: scale(0.8); 223 opacity: 0.6; 224 } 225 40% { 226 transform: scale(1.2); 227 opacity: 1; 228 } 229 } 230 231 /* Animation for chat opening */ 232 @media (max-width: 640px) { 233 .nsws-chat-container { 234 width: 100%; 235 height: 100%; 236 bottom: 0; 237 right: 0; 238 border-radius: 0; 239 } 240 241 .nsws-chat-logo-button { 242 width: 56px; 243 height: 56px; 244 } 245 } 246 247 /* Utility classes */ 248 .nsws-hidden { 249 display: none !important; 250 } 251 252 .nsws-visible { 253 display: flex !important; 254 } 255 `; 256 257 /** 258 * Initialize the chat widget 259 */ 260 function initializeChatWidget() { 261 // Create style element 262 const styleElement = document.createElement("style"); 263 styleElement.textContent = STYLES; 264 document.head.appendChild(styleElement); 265 266 // Create widget container 267 const widgetContainer = document.createElement("div"); 268 widgetContainer.className = "nsws-chat-widget"; 269 widgetContainer.id = "nsws-chat-widget"; 270 271 // Create logo button 272 const logoButton = document.createElement("button"); 273 logoButton.className = "nsws-chat-logo-button"; 274 logoButton.id = "nsws-chat-logo"; 275 logoButton.title = "Chat with BharatAssist"; 276 logoButton.innerHTML = ` 277 <img src="https://chatbot-cdn.nsws.gov.in/images/botIcon.png" alt="BharatAssist" /> 278 <div class="nsws-chat-notification-badge" id="nsws-notification-badge" style="display: none;"></div> 279 `; 280 281 // Create chat container 282 const chatContainer = document.createElement("div"); 283 chatContainer.className = "nsws-chat-container"; 284 chatContainer.id = "nsws-chat-container"; 285 286 // Create control buttons overlay 287 const controlsContainer = document.createElement("div"); 288 controlsContainer.className = "nsws-chat-controls"; 289 controlsContainer.id = "nsws-chat-controls"; 290 controlsContainer.innerHTML = ` 291 <button class="nsws-chat-icon-button" id="nsws-minimize-btn" title="Minimize Chat" aria-label="Minimize"> 292 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"> 293 <line x1="5" y1="12" x2="19" y2="12"></line> 294 </svg> 295 </button> 296 <button class="nsws-chat-icon-button" id="nsws-expand-btn" title="Toggle Fullscreen" aria-label="Expand"> 297 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"> 298 <polyline points="15 3 21 3 21 9"></polyline> 299 <polyline points="9 21 3 21 3 15"></polyline> 300 <line x1="21" y1="3" x2="3" y2="21"></line> 301 </svg> 302 </button> 303 <button class="nsws-chat-icon-button" id="nsws-close-btn" title="Close Chat" aria-label="Close"> 304 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"> 305 <line x1="18" y1="6" x2="6" y2="18"></line> 306 <line x1="6" y1="6" x2="18" y2="18"></line> 307 </svg> 308 </button> 309 `; 310 311 // Create iframe 312 const iframe = document.createElement("iframe"); 313 iframe.className = "nsws-chat-iframe"; 314 iframe.id = "nsws-chat-iframe"; 315 iframe.src = EMBEDDED_PAGE_URL; 316 iframe.allow = "clipboard-write"; 317 318 // Create loader 319 const loader = document.createElement("div"); 320 loader.className = "nsws-chat-loader"; 321 loader.id = "nsws-chat-loader"; 322 loader.innerHTML = ` 323 <div class="nsws-loader-dots"> 324 <div class="nsws-loader-dot"></div> 325 <div class="nsws-loader-dot"></div> 326 <div class="nsws-loader-dot"></div> 327 </div> 328 `; 329 330 // Build chat container 331 chatContainer.appendChild(iframe); 332 chatContainer.appendChild(loader); 333 chatContainer.appendChild(controlsContainer); 334 335 // Add to widget container 336 widgetContainer.appendChild(logoButton); 337 widgetContainer.appendChild(chatContainer); 338 339 // Add widget to page 340 document.body.appendChild(widgetContainer); 341 342 // Set up event listeners 343 setupEventListeners(logoButton, chatContainer, iframe); 344 } 345 346 /** 347 * Setup event listeners for the chat widget 348 */ 349 function setupEventListeners(logoButton, chatContainer, iframe) { 350 let isOpen = false;
351 let isFullscreen = false; 352 let hasMessages = false; 353 const notificationBadge = document.getElementById( 354 "nsws-notification-badge", 355 ); 356 const loader = document.getElementById("nsws-chat-loader"); 357 358 // Hide loader when iframe finishes loading 359 iframe.addEventListener("load", () => { 360 if (loader) { 361 loader.classList.add("hidden"); 362 } 363 }); 364 365 // Show loader if iframe fails to load after timeout 366 const loaderTimeout = setTimeout(() => { 367 if (loader && !loader.classList.contains("hidden")) { 368 loader.classList.add("hidden"); 369 } 370 }, 10000); // 10 second timeout 371 372 // Clear timeout when iframe loads 373 iframe.addEventListener("load", () => { 374 clearTimeout(loaderTimeout); 375 }); 376 377 // Logo button click 378 logoButton.addEventListener("click", () => { 379 isOpen = !isOpen; 380 if (isOpen) { 381 chatContainer.classList.add("open"); 382 logoButton.classList.add("hidden"); 383 notificationBadge.style.display = "none"; 384 // Send message to iframe to show chat 385 iframe.contentWindow.postMessage( 386 { type: "SHOW_CHAT", isEmbedded: true }, 387 MESSAGE_ORIGIN, 388 ); 389 } else { 390 chatContainer.classList.remove("open"); 391 logoButton.classList.remove("hidden"); 392 if (hasMessages) { 393 notificationBadge.style.display = "block"; 394 } 395 // Send message to iframe to hide chat 396 iframe.contentWindow.postMessage( 397 { type: "HIDE_CHAT", isEmbedded: true }, 398 MESSAGE_ORIGIN, 399 ); 400 } 401 }); 402 403 // Minimize button 404 document 405 .getElementById("nsws-minimize-btn") 406 .addEventListener("click", () => { 407 isOpen = false; 408 chatContainer.classList.remove("open"); 409 logoButton.classList.remove("hidden"); 410 if (hasMessages) { 411 notificationBadge.style.display = "block"; 412 } 413 if (isFullscreen) { 414 isFullscreen = false; 415 chatContainer.classList.remove("fullscreen"); 416 } 417 }); 418 419 // Expand button 420 document.getElementById("nsws-expand-btn").addEventListener("click", () => { 421 isFullscreen = !isFullscreen; 422 if (isFullscreen) { 423 chatContainer.classList.add("fullscreen"); 424 logoButton.classList.add("hidden"); 425 // Notify iframe about fullscreen state 426 iframe.contentWindow.postMessage( 427 { type: "TOGGLE_FULLSCREEN", isFullscreen: true }, 428 MESSAGE_ORIGIN, 429 ); 430 } else { 431 chatContainer.classList.remove("fullscreen"); 432 logoButton.classList.remove("hidden"); 433 // Notify iframe about fullscreen state 434 iframe.contentWindow.postMessage( 435 { type: "TOGGLE_FULLSCREEN", isFullscreen: false }, 436 MESSAGE_ORIGIN, 437 ); 438 } 439 }); 440 441 // Close button 442 document.getElementById("nsws-close-btn").addEventListener("click", () => { 443 isOpen = false; 444 chatContainer.classList.remove("open"); 445 logoButton.classList.remove("hidden"); 446 if (isFullscreen) { 447 isFullscreen = false; 448 chatContainer.classList.remove("fullscreen"); 449 } 450 // Reset the conversation in iframe 451 iframe.contentWindow.postMessage( 452 { type: "RESET_CHAT", isEmbedded: true }, 453 MESSAGE_ORIGIN, 454 ); 455 }); 456 457 // Listen for messages from iframe 458 window.addEventListener("message", (event) => { 459 // Verify the message origin 460 if (event.origin !== MESSAGE_ORIGIN) return; 461 462 const { type, data } = event.data; 463 464 if (type === "CHAT_HAS_MESSAGES") { 465 hasMessages = true; 466 // Show notification badge only if chat is not open 467 if (!isOpen) { 468 notificationBadge.style.display = "block"; 469 } 470 } 471 472 if (type === "CHAT_CLOSED") { 473 isOpen = false; 474 isFullscreen = false; 475 chatContainer.classList.remove("open"); 476 chatContainer.classList.remove("fullscreen"); 477 logoButton.style.display = "flex"; 478 } 479 }); 480 481 // Handle resize to ensure proper display 482 window.addEventListener("resize", () => { 483 if (isFullscreen && chatContainer.classList.contains("fullscreen")) { 484 // Fullscreen handles itself via CSS 485 } 486 }); 487 } 488 489 // Initialize when DOM is ready 490 if (document.readyState === "loading") { 491 document.addEventListener("DOMContentLoaded", initializeChatWidget); 492 } else { 493 initializeChatWidget(); 494 } 495})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.