1(function () { 2 'use strict'; 3 4 const scriptTag = document.currentScript; 5 6 const scriptSrc = (scriptTag && scriptTag.src) || ''; 7 const baseUrl = scriptSrc.substring(0, scriptSrc.lastIndexOf('/')); 8 9 const widgetOrigin = (scriptTag && scriptTag.getAttribute('data-origin')) || baseUrl; 10 const theme = (scriptTag && scriptTag.getAttribute('data-theme')) || 'light'; 11 12 const hasDataMode = scriptTag && scriptTag.hasAttribute('data-mode'); 13 const dataModeRaw = hasDataMode ? (scriptTag.getAttribute('data-mode') || '') : null; 14 const pageSourceRaw = (scriptTag && scriptTag.getAttribute('data-page-source')) || ''; 15 16 const APP_PREFIXES = ['app']; 17 function isAppMode(val) { 18 return APP_PREFIXES.some(function (p) { 19 return val === p || val.startsWith(p + '/'); 20 }); 21 } 22 const embedMode = (!hasDataMode || isAppMode(dataModeRaw)) 23 ? 'app' 24 : 'site'; 25 const pageSource = pageSourceRaw || (hasDataMode && !isAppMode(dataModeRaw) 26 ? (dataModeRaw || '/') 27 : 'app'); 28 29 const BRAND_REGISTRY_URL = widgetOrigin + '/config/brand-registry.json'; 30 const DEFAULT_BRAND_ID = 'nsc'; 31 const parentOrigin = window.location.origin; 32 let brandId = DEFAULT_BRAND_ID; 33 let brandConfig = null; 34 35 let isOpen = false; 36 let iframeLoaded = false; 37 let sizeReady = false; 38 let pendingOpen = false; 39 40 const DEFAULT_WIDGET_WIDTH = 380; 41 const DEFAULT_WIDGET_HEIGHT = 560; 42 const DEFAULT_TOGGLE_SIZE = 60; 43 const DEFAULT_TOGGLE_COLOR = '#1a365d'; 44 const DEFAULT_SMALL_BUBBLE_MESSAGE = ''; 45 46 const style = document.createElement('style'); 47 style.textContent = ` 48 :root { 49 --nsc-widget-width: ${DEFAULT_WIDGET_WIDTH}px; 50 --nsc-widget-height: ${DEFAULT_WIDGET_HEIGHT}px; 51 --nsc-toggle-size: ${DEFAULT_TOGGLE_SIZE}px; 52 --nsc-toggle-icon-size: ${DEFAULT_TOGGLE_SIZE}px; 53 --nsc-toggle-color: ${DEFAULT_TOGGLE_COLOR}; 54 } 55 .nsc-chat-toggle { 56 position: fixed; 57 bottom: 24px; 58 right: 24px; 59 width: var(--nsc-toggle-size); 60 height: var(--nsc-toggle-size); 61 border-radius: 50%; 62 background: transparent; 63 color: var(--nsc-toggle-color); 64 border: none; 65 cursor: pointer; 66 box-shadow: none; 67 opacity: 0; 68 pointer-events: none; 69 font-size: 28px; 70 z-index: 99998; 71 display: flex; 72 align-items: center; 73 justify-content: center; 74 transition: transform 0.2s ease, box-shadow 0.2s ease; 75 line-height: 1; 76 } 77 .nsc-chat-toggle:hover { 78 transform: scale(1.08); 79 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25); 80 } 81 .nsc-chat-toggle-icon { 82 width: var(--nsc-toggle-icon-size); 83 height: var(--nsc-toggle-icon-size); 84 object-fit: contain; 85 border-radius: 50%; 86 pointer-events: none; 87 } 88 .nsc-chat-toggle .nsc-close-icon { 89 font-size: 22px; 90 font-weight: bold; 91 font-style: normal; 92 } 93 .nsc-chat-container { 94 position: fixed; 95 bottom: 96px; 96 right: 24px; 97 width: var(--nsc-widget-width); 98 height: var(--nsc-widget-height); 99 max-height: calc(100vh - 48px); 100 max-width: calc(100vw - 48px); 101 border: none; 102 border-radius: 16px; 103 overflow: hidden; 104 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18); 105 z-index: 99999; 106 opacity: 0; 107 transform: translateY(16px) scale(0.95); 108 pointer-events: none; 109 transition: opacity 0.25s ease, transform 0.25s ease; 110 } 111 .nsc-chat-container.nsc-open { 112 opacity: 1; 113 transform: translateY(0) scale(1); 114 pointer-events: auto; 115 } 116 .nsc-chat-iframe { 117 width: 100%; 118 height: 100%; 119 border: none; 120 } 121 .nsc-chat-resize-handle { 122 position: absolute; 123 top: 0; 124 left: 0; 125 width: 6px; 126 height: 100%; 127 cursor: ew-resize; 128 z-index: 1; 129 border-radius: 16px 0 0 16px; 130 transition: background 0.15s; 131 } 132 .nsc-chat-resize-handle:hover { 133 background: rgba(0, 0, 0, 0.08); 134 } 135 .nsc-chat-resize-handle-bottom { 136 position: absolute; 137 top: 0; 138 left: 16px; 139 right: 0; 140 height: 6px; 141 cursor: ns-resize; 142 z-index: 1; 143 border-radius: 16px 16px 0 0; 144 transition: background 0.15s; 145 } 146 .nsc-chat-resize-handle-bottom:hover { 147 background: rgba(0, 0, 0, 0.08); 148 } 149 .nsc-chat-resize-handle-corner { 150 position: absolute; 151 top: 0; 152 left: 0; 153 width: 16px; 154 height: 16px; 155 cursor: nwse-resize; 156 z-index: 2; 157 border-radius: 16px 0 0 0; 158 transition: background 0.15s; 159 } 160 .nsc-chat-resize-handle-corner:hover { 161 background: rgba(0, 0, 0, 0.12); 162 } 163 .nsc-chat-bubble { 164 position: fixed; 165 bottom: 92px; 166 right: 24px; 167 background: #fff; 168 color: #333; 169 padding: 10px 32px 10px 14px; 170 border-radius: 12px; 171 box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); 172 border: 1.5px solid var(--nsc-bubble-border-color, #1a365d); 173 font-size: 14px; 174 font-family: sans-serif; 175 z-index: 99998; 176 white-space: nowrap; 177 pointer-events: auto; 178 animation: nsc-bubble-in 0.3s ease; 179 } 180 .nsc-chat-bubble::before { 181 content: ''; 182 position: absolute; 183 bottom: -10px; 184 right: 27px; 185 width: 0; 186 height: 0; 187 border-left: 9px solid transparent; 188 border-right: 9px solid transparent; 189 border-top: 9px solid var(--nsc-bubble-border-color, #1a365d); 190 } 191 .nsc-chat-bubble::after { 192 content: ''; 193 position: absolute; 194 bottom: -7px; 195 right: 28px; 196 width: 0; 197 height: 0; 198 border-left: 8px solid transparent; 199 border-right: 8px solid transparent; 200 border-top: 8px solid #fff; 201 } 202 .nsc-chat-bubble-close { 203 position: absolute; 204 top: -10px; 205 right: -10px; 206 background: #fff; 207 border: 1.5px solid var(--nsc-bubble-border-color, #1a365d); 208 border-radius: 50%; 209 cursor: pointer; 210 font-size: 10px; 211 color: var(--nsc-bubble-border-color, #1a365d); 212 line-height: 1; 213 width: 22px; 214 height: 22px; 215 display: flex; 216 align-items: center; 217 justify-content: center; 218 padding: 0; 219 } 220 .nsc-chat-bubble-close:hover { 221 background: #f0f0f0; 222 } 223 .nsc-chat-bubble.nsc-hidden { 224 display: none; 225 } 226 .nsc-chat-bubble.nsc-bubble-collapsed { 227 width: 8em; 228 height: 20px; 229 padding: 0; 230 border-radius: 10px; 231 font-size: 14px; 232 line-height: 18px; 233 text-align: center; 234 } 235 .nsc-chat-bubble.nsc-bubble-collapsed::before { 236 display: block; 237 bottom: -7px; 238 right: 4px; 239 border-left-width: 7px; 240 border-right-width: 7px; 241 border-top-width: 7px; 242 } 243 .nsc-chat-bubble.nsc-bubble-collapsed::after { 244 display: block; 245 bottom: -5px; 246 right: 5px; 247 border-left-width: 6px; 248 border-right-width: 6px; 249 border-top-width: 6px; 250 }
251 @keyframes nsc-bubble-in { 252 from { opacity: 0; transform: translateY(8px); } 253 to { opacity: 1; transform: translateY(0); } 254 } 255 @media (max-width: 480px) { 256 .nsc-chat-container { 257 bottom: 0; 258 right: 0; 259 width: 100vw; 260 height: ${embedMode === 'site' ? 'calc(100vh - 140px)' : '100vh'}; 261 max-height: ${embedMode === 'site' ? 'calc(100vh - 140px)' : '100vh'}; 262 max-width: 100vw; 263 border-radius: 0; 264 ${embedMode === 'site' ? 'top: auto;' : ''} 265 } 266 .nsc-chat-toggle { 267 bottom: 16px; 268 right: 16px; 269 width: 52px; 270 height: 52px; 271 font-size: 24px; 272 } 273 .nsc-chat-bubble { 274 bottom: 76px; 275 right: 16px; 276 } 277 .nsc-chat-bubble::before { 278 right: 17px; 279 } 280 .nsc-chat-bubble::after { 281 right: 18px; 282 } 283 .nsc-chat-bubble.nsc-bubble-collapsed::before { 284 right: 4px; 285 } 286 .nsc-chat-bubble.nsc-bubble-collapsed::after { 287 right: 5px; 288 } 289 }`; 290 document.head.appendChild(style); 291 292 let savedBorderEnabled = false; 293 294 function applySizeConfig(cfg) { 295 var root = document.documentElement; 296 var w = parseInt(cfg.widgetWidth) || DEFAULT_WIDGET_WIDTH; 297 var h = parseInt(cfg.widgetHeight) || DEFAULT_WIDGET_HEIGHT; 298 var t = parseInt(cfg.toggleSize) || DEFAULT_TOGGLE_SIZE; 299 var c = cfg.toggleColor || DEFAULT_TOGGLE_COLOR; 300 if (cfg.toggleBorderEnabled !== undefined) { 301 savedBorderEnabled = cfg.toggleBorderEnabled !== false; 302 } 303 var borderOn = savedBorderEnabled; 304 root.style.setProperty('--nsc-widget-width', w + 'px'); 305 root.style.setProperty('--nsc-widget-height', h + 'px'); 306 root.style.setProperty('--nsc-toggle-size', t + 'px'); 307 root.style.setProperty('--nsc-toggle-icon-size', borderOn ? Math.round(t * 58 / 60) + 'px' : t + 'px'); 308 root.style.setProperty('--nsc-toggle-color', c); 309 var toggle = document.querySelector('.nsc-chat-toggle'); 310 if (toggle) { 311 toggle.style.background = borderOn ? '#fff' : 'transparent'; 312 toggle.style.border = borderOn ? '2.5px solid ' + c : 'none'; 313 toggle.style.boxShadow = borderOn ? '0 4px 16px rgba(0, 0, 0, 0.15)' : 'none'; 314 toggle.style.opacity = '1'; 315 toggle.style.pointerEvents = 'auto'; 316 } 317 } 318 319 let toggleIconUrl = ''; 320 const fallbackToggleIconUrl = widgetOrigin + '/config/ChatToggleIcon.png'; 321 let savedBubbleMessage = ''; 322 let savedSmallBubbleMessage = ''; 323 let savedBubbleSizeMode = 'large'; 324 let savedSmallBubbleClickAction = 'openChat'; 325 let savedBubbleBoldFirstLine = true; 326 let savedBubbleFirstLineColor = '#1a365d'; 327 328 const toggleBtn = document.createElement('button'); 329 toggleBtn.className = 'nsc-chat-toggle'; 330 toggleBtn.setAttribute('aria-label', 'ãã£ãããéã'); 331 332 const toggleImg = document.createElement('img'); 333 toggleImg.alt = 'ãã£ãã'; 334 toggleImg.className = 'nsc-chat-toggle-icon'; 335 toggleImg.style.display = 'none'; 336 toggleBtn.appendChild(toggleImg); 337 338 const bubble = document.createElement('div'); 339 bubble.className = 'nsc-chat-bubble nsc-bubble-collapsed'; 340 bubble.innerHTML = DEFAULT_SMALL_BUBBLE_MESSAGE; 341 const bubbleClose = document.createElement('button'); 342 bubbleClose.className = 'nsc-chat-bubble-close'; 343 bubbleClose.innerHTML = '\u2715'; 344 bubbleClose.setAttribute('aria-label', 'å¹ãåºããéãã'); 345 bubbleClose.addEventListener('click', function (e) { 346 e.stopPropagation(); 347 bubble.classList.add('nsc-hidden'); 348 }); 349 bubble.addEventListener('click', function () { 350 if (savedSmallBubbleClickAction === 'showLargeBubble' 351 && bubble.classList.contains('nsc-bubble-collapsed')) { 352 bubble.classList.remove('nsc-bubble-collapsed'); 353 renderBubbleMessage(savedBubbleMessage); 354 return; 355 } 356 openChat(); 357 }); 358 bubble.style.cursor = 'pointer'; 359 360 const container = document.createElement('div'); 361 container.className = 'nsc-chat-container'; 362 363 const iframe = document.createElement('iframe'); 364 iframe.className = 'nsc-chat-iframe'; 365 iframe.title = 'AIã³ã³ã·ã§ã«ã¸ã¥ãã£ããããã'; 366 iframe.setAttribute( 367 'sandbox', 368 'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox' 369 ); 370 iframe.setAttribute('allow', 'clipboard-write; microphone'); 371 iframe.setAttribute('referrerpolicy', 'no-referrer'); 372 373 container.appendChild(iframe); 374 375 const resizeHandle = document.createElement('div'); 376 resizeHandle.className = 'nsc-chat-resize-handle'; 377 container.appendChild(resizeHandle); 378 379 const resizeHandleBottom = document.createElement('div'); 380 resizeHandleBottom.className = 'nsc-chat-resize-handle-bottom'; 381 container.appendChild(resizeHandleBottom); 382 383 const resizeHandleCorner = document.createElement('div'); 384 resizeHandleCorner.className = 'nsc-chat-resize-handle-corner';
385 container.appendChild(resizeHandleCorner); 386 387 document.body.appendChild(container); 388 document.body.appendChild(bubble); 389 document.body.appendChild(toggleBtn); 390 391 function fetchJson(url) { 392 return fetch(url).then(function (res) { 393 return res.ok ? res.json() : Promise.reject(new Error(String(res.status))); 394 }); 395 } 396 397 function resolveBrandId(registry) { 398 if (!registry || !Array.isArray(registry.brands)) return DEFAULT_BRAND_ID; 399 if (!hasDataMode || isAppMode(dataModeRaw)) return registry.defaultBrandId || DEFAULT_BRAND_ID; 400 401 const raw = String(dataModeRaw || '').toLowerCase(); 402 const matched = registry.brands.find(function (brand) { 403 return brand && brand.enabled !== false && Array.isArray(brand.domains) && brand.domains.some(function (domain) { 404 const normalizedDomain = String(domain || '').trim().toLowerCase(); 405 return normalizedDomain && raw.includes(normalizedDomain); 406 }); 407 }); 408 return matched && matched.brandId ? matched.brandId : (registry.defaultBrandId || DEFAULT_BRAND_ID); 409 } 410 411 function findBrandConfig(registry, id) { 412 if (!registry || !Array.isArray(registry.brands)) return null; 413 return registry.brands.find(function (brand) { 414 return brand && brand.brandId === id; 415 }) || null; 416 } 417 418 function setToggleIcon(url) { 419 if (!url) return; 420 toggleIconUrl = url; 421 toggleImg.onerror = function () { 422 toggleImg.onerror = null; 423 if (toggleImg.src !== fallbackToggleIconUrl) { 424 toggleIconUrl = fallbackToggleIconUrl; 425 toggleImg.src = fallbackToggleIconUrl; 426 } else { 427 toggleIconUrl = ''; 428 toggleImg.style.display = 'none'; 429 } 430 }; 431 toggleImg.onload = function () { 432 toggleImg.style.display = ''; 433 }; 434 toggleImg.src = url; 435 } 436 437 function renderBubbleMessage(msg) { 438 bubble.style.width = ''; 439 bubble.innerHTML = ''; 440 if (msg) { 441 var lines = msg.split('\n'); 442 var firstStyle = (savedBubbleBoldFirstLine ? 'font-weight:bold;' : '') 443 + 'color:' + savedBubbleFirstLineColor + ';'; 444 if (lines.length > 1) { 445 bubble.innerHTML = '<span style="' + firstStyle + '">' + lines[0] + '</span><br>' + lines.slice(1).join('<br>'); 446 } else { 447 bubble.innerHTML = '<span style="' + firstStyle + '">' + lines[0] + '</span>'; 448 } 449 } 450 if (msg) { 451 bubble.appendChild(bubbleClose); 452 } 453 } 454 455 function getFirstLine(msg) { 456 return String(msg || '').split(/\r?\n/, 1)[0]; 457 } 458 459 function renderCollapsedBubble() { 460 var configuredMessage = getFirstLine(savedSmallBubbleMessage); 461 var fallbackMessage = getFirstLine(savedBubbleMessage); 462 var message = configuredMessage.trim() 463 ? configuredMessage 464 : (fallbackMessage.trim() ? fallbackMessage : DEFAULT_SMALL_BUBBLE_MESSAGE); 465 bubble.innerHTML = ''; 466 bubble.textContent = message; 467 bubble.style.width = (Array.from(message).length + 1) + 'em'; 468 } 469 470 function applyWidgetSettings(data, registryBrand) { 471 applySizeConfig({ 472 widgetWidth: data.widgetWidth || null, 473 widgetHeight: data.widgetHeight || null, 474 toggleSize: data.toggleSize || null, 475 toggleColor: data.toggleColor || null, 476 toggleBorderEnabled: data.toggleBorderEnabled 477 }); 478 479 savedBubbleMessage = typeof data.bubbleMessage === 'string' ? data.bubbleMessage : ''; 480 savedSmallBubbleMessage = typeof data.smallBubbleMessage === 'string' ? data.smallBubbleMessage : ''; 481 savedBubbleSizeMode = data.bubbleSizeMode === 'large' || data.bubbleSizeMode === 'small' 482 ? data.bubbleSizeMode 483 : (brandId === DEFAULT_BRAND_ID ? 'large' : 'small'); 484 savedSmallBubbleClickAction = data.smallBubbleClickAction === 'showLargeBubble' 485 || data.smallBubbleClickAction === 'openChat' 486 ? data.smallBubbleClickAction 487 : (brandId === DEFAULT_BRAND_ID ? 'openChat' : 'showLargeBubble'); 488 489 const assetBasePath = (registryBrand && registryBrand.assetBasePath) || 'config/'; 490 const iconPath = (registryBrand && registryBrand.toggleIcon) || 'ChatToggleIcon.png'; 491 setToggleIcon(widgetOrigin + '/' + assetBasePath.replace(/\/$/, '') + '/' + iconPath.replace(/^\//, '')); 492 493 if (data.bubbleEnabled === false && bubble) { 494 bubble.classList.add('nsc-hidden'); 495 } else if (bubble) { 496 if (data.bubbleBorderColor) { 497 document.documentElement.style.setProperty('--nsc-bubble-border-color', data.bubbleBorderColor); 498 } 499 savedBubbleBoldFirstLine = data.bubbleBoldFirstLine !== false; 500 savedBubbleFirstLineColor = data.bubbleFirstLineColor || '#1a365d'; 501 if (savedBubbleSizeMode === 'large') { 502 bubble.classList.remove('nsc-bubble-collapsed'); 503 renderBubbleMessage(savedBubbleMessage); 504 } else { 505 bubble.classList.add('nsc-bubble-collapsed'); 506 renderCollapsedBubble(); 507 } 508 } 509 } 510 511 fetchJson(BRAND_REGISTRY_URL) 512 .then(function (registry) { 513 brandId = resolveBrandId(registry); 514 brandConfig = findBrandConfig(registry, brandId); 515 const configPath = (brandConfig && brandConfig.configPath) || 'config/messages.json'; 516 return fetchJson(widgetOrigin + '/' + configPath.replace(/^\//, '')) 517 .then(function (data) { 518 applyWidgetSettings(data, brandConfig); 519 }); 520 }) 521 .catch(function () {
522 brandId = DEFAULT_BRAND_ID; 523 brandConfig = null; 524 return fetchJson(widgetOrigin + '/config/messages.json') 525 .then(function (data) { 526 applyWidgetSettings(data, null); 527 }) 528 .catch(function () { 529 setToggleIcon(fallbackToggleIconUrl); 530 }); 531 }) 532 .finally(function () { 533 sizeReady = true; 534 if (pendingOpen) { 535 pendingOpen = false; 536 container.classList.add('nsc-open'); 537 } 538 }); 539 540 function openChat() { 541 542 if (!iframeLoaded) { 543 const iframeParams = '?theme=' + encodeURIComponent(theme) 544 + '&mode=' + encodeURIComponent(embedMode) 545 + '&pageSource=' + encodeURIComponent(pageSource) 546 + '&brandId=' + encodeURIComponent(brandId) 547 + '&parentOrigin=' + encodeURIComponent(parentOrigin); 548 iframe.src = widgetOrigin + '/index.html' + iframeParams; 549 iframeLoaded = true; 550 } 551 isOpen = true; 552 bubble.classList.add('nsc-hidden'); 553 554 var hideToggle = embedMode === 'site' && window.innerWidth <= 480; 555 if (hideToggle) { 556 toggleBtn.style.display = 'none'; 557 } else { 558 toggleBtn.innerHTML = '<span class="nsc-close-icon">\u2715</span>'; 559 toggleBtn.setAttribute('aria-label', 'ãã£ãããéãã'); 560 } 561 562 if (sizeReady) { 563 container.classList.add('nsc-open'); 564 } else { 565 pendingOpen = true; 566 567 setTimeout(function () { 568 if (pendingOpen) { 569 pendingOpen = false; 570 sizeReady = true; 571 container.classList.add('nsc-open'); 572 } 573 }, 2000); 574 } 575 } 576 577 function closeChat() { 578 isOpen = false; 579 container.classList.remove('nsc-open'); 580 581 toggleBtn.style.display = ''; 582 toggleBtn.textContent = ''; 583 const img = document.createElement('img'); 584 if (toggleIconUrl) img.src = toggleIconUrl; 585 img.onerror = function () { 586 img.onerror = null; 587 if (img.src !== fallbackToggleIconUrl) { 588 toggleIconUrl = fallbackToggleIconUrl; 589 img.src = fallbackToggleIconUrl; 590 } else { 591 toggleIconUrl = ''; 592 img.style.display = 'none'; 593 } 594 }; 595 img.alt = 'ãã£ãã'; 596 img.className = 'nsc-chat-toggle-icon'; 597 img.style.display = toggleIconUrl ? '' : 'none'; 598 toggleBtn.appendChild(img); 599 toggleBtn.setAttribute('aria-label', 'ãã£ãããéã'); 600 } 601 602 toggleBtn.addEventListener('click', function () { 603 if (isOpen) { 604 closeChat(); 605 } else { 606 openChat(); 607 } 608 }); 609 610 const MIN_WIDTH = 280; 611 const MIN_HEIGHT = 200; 612 613 let isResizing = false; 614 let resizeDir = ''; 615 let resizeStartX = 0; 616 let resizeStartY = 0; 617 let resizeStartWidth = 0; 618 let resizeStartHeight = 0; 619 620 function startResize(e, dir) { 621 isResizing = true; 622 resizeDir = dir; 623 resizeStartX = e.clientX; 624 resizeStartY = e.clientY; 625 resizeStartWidth = container.offsetWidth; 626 resizeStartHeight = container.offsetHeight; 627 document.body.style.userSelect = 'none'; 628 629 iframe.style.pointerEvents = 'none'; 630 e.preventDefault(); 631 } 632 633 resizeHandle.addEventListener('mousedown', function (e) { startResize(e, 'left'); }); 634 resizeHandleBottom.addEventListener('mousedown', function (e) { startResize(e, 'bottom'); }); 635 resizeHandleCorner.addEventListener('mousedown', function (e) { startResize(e, 'corner'); }); 636 637 document.addEventListener('mousemove', function (e) { 638 if (!isResizing) return; 639 if (resizeDir === 'left' || resizeDir === 'corner') { 640 641 const dx = resizeStartX - e.clientX; 642 const maxW = window.innerWidth; 643 const newWidth = Math.min(Math.max(resizeStartWidth + dx, MIN_WIDTH), maxW); 644 container.style.width = newWidth + 'px'; 645 } 646 if (resizeDir === 'bottom' || resizeDir === 'corner') { 647 648 const dy = resizeStartY - e.clientY; 649 const maxHeight = window.innerHeight - 24; 650 const newHeight = Math.min(Math.max(resizeStartHeight + dy, MIN_HEIGHT), maxHeight); 651 container.style.height = newHeight + 'px'; 652 container.style.maxHeight = newHeight + 'px'; 653 } 654 }); 655 656 document.addEventListener('mouseup', function () { 657 if (isResizing) { 658 isResizing = false;
659 resizeDir = ''; 660 document.body.style.userSelect = ''; 661 iframe.style.pointerEvents = ''; 662 } 663 }); 664 665 container.addEventListener('wheel', function (e) { 666 if (isOpen) { 667 e.preventDefault(); 668 } 669 }, { passive: false }); 670 671 window.addEventListener('message', function (event) { 672 673 if (widgetOrigin && event.origin !== widgetOrigin) { 674 return; 675 } 676 677 if (event.data && event.data.type === 'nsc-chat-close') { 678 closeChat(); 679 } 680 681 if (event.data && event.data.type === 'nsc-chat-open-url') { 682 if (event.source !== iframe.contentWindow) { 683 return; 684 } 685 686 const rawUrl = event.data.url; 687 if (typeof rawUrl !== 'string' || !/^https?:\/\//i.test(rawUrl)) { 688 return; 689 } 690 691 let url; 692 try { 693 url = new URL(rawUrl); 694 } catch (_) { 695 return; 696 } 697 698 if (!['http:', 'https:'].includes(url.protocol)) { 699 return; 700 } 701 702 const newWindow = window.open(url.href, '_blank'); 703 if (!newWindow) { 704 window.location.href = url.href; 705 } 706 } 707 708 if (event.data && event.data.type === 'nsc-widget-size') { 709 applySizeConfig(event.data); 710 sizeReady = true; 711 712 if (pendingOpen) { 713 pendingOpen = false; 714 container.classList.add('nsc-open'); 715 } 716 } 717 }); 718})();
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.