1document.addEventListener('DOMContentLoaded', function() { 2 (function($) { 3 4 var chatbox = $('#chatbox'); 5 var chatboxButton = $('#chatbox-button'); 6 var chatboxButtonImg = chatboxButton.find('img'); 7 var chatboxContent = $('#chatbox-content'); 8 var chatboxInput = $('#chatbox-input'); 9 var chatboxSend = $('#chatbox-send'); 10 var chatboxStop = $('#chatbox-stop'); 11 var chatboxNew = $('#chatbox-new'); 12 var unreadBadge = $('#chatbox-unread-badge'); 13 var proactiveBubble = $('#chatbot-proactive-bubble'); 14 15 var conversationHistory = []; 16 var currentConversationId = null; 17 var userInfo = null; 18 var isChatboxOpen = false; 19 var isAnimating = false; 20 var isStreaming = false; 21 var currentAbortController = null; 22 var lastUserMessage = ''; 23 24 const historyKey = chatbot_params.site_id + '_chatbotHistory'; 25 const convIdKey = chatbot_params.site_id + '_chatbotConvId'; 26 const unreadKey = chatbot_params.site_id + '_chatbotUnread'; 27 const userInfoKey = chatbot_params.site_id + '_chatbotUserInfo'; 28 29 var unreadCount = 0; 30 31 function loadUserInfo() { 32 try { 33 var stored = sessionStorage.getItem(userInfoKey); 34 if (stored) { 35 var parsed = JSON.parse(stored); 36 if (parsed && !parsed.sent) { 37 userInfo = parsed; 38 } 39 } 40 } catch (e) {} 41 } 42 43 function saveUserInfo() { 44 if (userInfo) { 45 try { sessionStorage.setItem(userInfoKey, JSON.stringify(userInfo)); } catch (e) {} 46 } 47 } 48 49 function loadUnreadCount() { 50 try { unreadCount = parseInt(sessionStorage.getItem(unreadKey), 10) || 0; } catch (e) {} 51 } 52 53 function saveUnreadCount() { 54 try { sessionStorage.setItem(unreadKey, String(unreadCount)); } catch (e) {} 55 } 56 57 function updateUnreadBadge() { 58 if (unreadCount > 0 && !isChatboxOpen) { 59 unreadBadge.text(unreadCount > 99 ? '99+' : unreadCount).show(); 60 } else { 61 unreadBadge.hide(); 62 } 63 } 64 65 function incrementUnread() { 66 if (!isChatboxOpen) { 67 unreadCount++; 68 saveUnreadCount(); 69 updateUnreadBadge(); 70 } 71 } 72 73 function clearUnread() { 74 unreadCount = 0; 75 saveUnreadCount(); 76 updateUnreadBadge(); 77 } 78 79 function saveHistoryToSessionStorage() { 80 try { 81 sessionStorage.setItem(historyKey, JSON.stringify(conversationHistory)); 82 } catch (e) {} 83 } 84 85 function loadHistoryFromSessionStorage() { 86 try { 87 var storedHistory = sessionStorage.getItem(historyKey); 88 if (storedHistory) { 89 conversationHistory = JSON.parse(storedHistory); 90 rebuildChatFromHistory(); 91 } 92 } catch (e) { 93 conversationHistory = []; 94 } 95 } 96 97 function rebuildChatFromHistory() { 98 chatboxContent.empty();
99 conversationHistory.forEach(function(message) { 100 if (message.role === 'user') { 101 appendUserMessage(message.content); 102 } else if (message.role === 'assistant') { 103 appendBotMessage(message.content); 104 } 105 }); 106 } 107 108 function getConversationId() { 109 if (currentConversationId) return currentConversationId; 110 try { 111 currentConversationId = sessionStorage.getItem(convIdKey); 112 } catch (e) {} 113 return currentConversationId; 114 } 115 116 function autoResizeTextarea() { 117 chatboxInput.css('height', 'auto'); 118 var scrollHeight = chatboxInput[0].scrollHeight; 119 chatboxInput.css('height', Math.min(scrollHeight, 120) + 'px'); 120 } 121 122 function sendMessage() { 123 if (isStreaming) return; 124 125 var message = chatboxInput.val().trim(); 126 if (message === '') return; 127 128 lastUserMessage = message; 129 130 appendUserMessage(message); 131 chatboxInput.val(''); 132 autoResizeTextarea(); 133 134 conversationHistory.push({ role: 'user', content: message }); 135 saveHistoryToSessionStorage(); 136 137 streamMessageToServer(message); 138 } 139 140 function showStreamingUI(streaming) { 141 if (streaming) { 142 chatboxStop.show(); 143 chatboxSend.hide(); 144 } else { 145 chatboxStop.hide(); 146 chatboxSend.show(); 147 } 148 } 149 150 function startNewConversation() { 151 if (isStreaming && currentAbortController) { 152 currentAbortController.abort(); 153 } 154 155 conversationHistory = []; 156 currentConversationId = null; 157 lastUserMessage = ''; 158 159 try { 160 sessionStorage.removeItem(historyKey); 161 sessionStorage.removeItem(convIdKey); 162 } catch (e) {} 163 164 chatboxContent.empty(); 165 clearUnread(); 166 sendWelcomeMessage(); 167 } 168 169 function appendErrorWithRetry(messageText) { 170 var avatarImg = $('<img>').attr('src', chatbot_params.chatbot_bot_avatar_url).attr('alt', 'bot'); 171 var bubble = $('<div>').addClass('chat-bubble error'); 172 bubble.append($('<div>').addClass('error-text').text(messageText)); 173 var retryBtn = $('<button>').addClass('chat-retry-button').text(chatbot_i18n.retry || 'Retry'); 174 bubble.append(retryBtn); 175 176 var newMessage = $('<div>').addClass('chat-message bot').append(avatarImg).append(bubble); 177 chatboxContent.append(newMessage); 178 chatboxContent.stop().animate({ scrollTop: chatboxContent[0].scrollHeight }, 200); 179 180 retryBtn.on('click', function() { 181 newMessage.remove(); 182 if (lastUserMessage) { 183 streamMessageToServer(lastUserMessage); 184 } 185 }); 186 } 187 188 async function streamMessageToServer(message) { 189 var conversationId = getConversationId(); 190 var formData = new FormData(); 191 192 formData.append('action', 'newcodebyte_chatbot_stream_message'); 193 formData.append('message', message); 194 formData.append('conversation_id', conversationId || '0'); 195 formData.append('history', JSON.stringify(conversationHistory)); 196 formData.append('newcodebyte_chatbot_message_nonce', chatbot_ajax.newcodebyte_chatbot_message_nonce); 197 198 if (userInfo) { 199 formData.append('name', userInfo.name); 200 formData.append('email', userInfo.email); 201 formData.append('is_first_message', '1'); 202 userInfo.sent = true; 203 saveUserInfo(); 204 userInfo = null; 205 } 206 207 var botMessageEl = null; 208 var botContent = ''; 209 var doneReceived = false; 210 var isAborted = false; 211 var watchdogFired = false;
212 var watchdogTimer = null; 213 214 function startWatchdog() { 215 stopWatchdog(); 216 watchdogTimer = setTimeout(function() { 217 watchdogFired = true; 218 if (currentAbortController) { 219 currentAbortController.abort(); 220 } 221 }, 60000); 222 } 223 224 function resetWatchdog() { 225 if (watchdogTimer) { 226 clearTimeout(watchdogTimer); 227 watchdogTimer = setTimeout(function() { 228 watchdogFired = true; 229 if (currentAbortController) { 230 currentAbortController.abort(); 231 } 232 }, 60000); 233 } 234 } 235 236 function stopWatchdog() { 237 if (watchdogTimer) { 238 clearTimeout(watchdogTimer); 239 watchdogTimer = null; 240 } 241 } 242 243 isStreaming = true; 244 showStreamingUI(true); 245 246 try { 247 startWatchdog(); 248 249 if (!window.fetch || !window.ReadableStream || !window.AbortController) { 250 throw new Error('no-stream-support'); 251 } 252 253 currentAbortController = new AbortController(); 254 255 var response = await fetch(chatbot_ajax.ajaxurl, { 256 method: 'POST', 257 body: formData, 258 signal: currentAbortController.signal 259 }); 260 261 if (!response.ok) throw new Error('http-error'); 262 if (!response.body) throw new Error('no-stream-support'); 263 264 $('.chat-message.loading').remove(); 265 266 botMessageEl = createBotMessagePlaceholder(); 267 268 var reader = response.body.getReader(); 269 var decoder = new TextDecoder(); 270 var buffer = ''; 271 272 while (true) { 273 var result; 274 try { 275 result = await reader.read(); 276 } catch (readError) { 277 if (readError && readError.name === 'AbortError') { 278 isAborted = true; 279 break; 280 } 281 throw readError; 282 } 283 if (result.done) break; 284 285 buffer += decoder.decode(result.value, { stream: true }); 286 287 var parts = buffer.split('\n\n'); 288 buffer = parts.pop(); 289 var hadToken = false; 290 291 for (var p = 0; p < parts.length; p++) { 292 var lines = parts[p].split('\n'); 293 var eventType = ''; 294 var eventData = ''; 295 296 for (var l = 0; l < lines.length; l++) { 297 var line = lines[l]; 298 if (line.indexOf('event: ') === 0) eventType = line.substring(7); 299 else if (line.indexOf('data: ') === 0) eventData = line.substring(6); 300 } 301 302 if (eventType === 'token' && eventData) { 303 try { 304 var parsed = JSON.parse(eventData); 305 if (parsed.token) { 306 botContent += parsed.token; 307 updateBotMessageText(botMessageEl, botContent); 308 hadToken = true; 309 } 310 } catch (e) {} 311 } else if (eventType === 'done' && eventData) { 312 doneReceived = true; 313 try { 314 var parsed = JSON.parse(eventData); 315 if (parsed.conversation_id && !currentConversationId) { 316 currentConversationId = parsed.conversation_id; 317 try { sessionStorage.setItem(convIdKey, currentConversationId); } catch (e) {} 318 } 319 } catch (e) {} 320 } else if (eventType === 'error' && eventData) { 321 try { 322 var parsed = JSON.parse(eventData); 323 $('.chat-message.loading').remove(); 324 if (botMessageEl && botMessageEl.parent().length) { 325 botMessageEl.closest('.chat-message').remove(); 326 } 327 appendErrorWithRetry(parsed.message || chatbot_i18n.there_was_an_error); 328 doneReceived = true; 329 } catch (e) {} 330 } 331 } 332 333 if (hadToken) { 334 await new Promise(function(resolve) { setTimeout(resolve, 0); }); 335 } 336 337 resetWatchdog(); 338 } 339 340 stopWatchdog(); 341 342 if (botContent && botMessageEl && botMessageEl.parent().length) { 343 botContent = cleanBotContent(botContent); 344 finalizeBotMessage(botMessageEl, botContent); 345 conversationHistory.push({ role: 'assistant', content: botContent }); 346 saveHistoryToSessionStorage(); 347 if (!isAborted && !watchdogFired) { 348 incrementUnread(); 349 } 350 } else if (!doneReceived) { 351 $('.chat-message.loading').remove(); 352 if (botMessageEl && botMessageEl.parent().length) { 353 botMessageEl.closest('.chat-message').remove(); 354 } 355 appendErrorWithRetry(chatbot_i18n.there_was_an_error); 356 } 357 358 } catch (error) { 359 $('.chat-message.loading').remove(); 360 stopWatchdog(); 361
362 if (error && error.name === 'AbortError') { 363 if (botMessageEl && botMessageEl.parent().length && botContent) { 364 botContent = cleanBotContent(botContent); 365 finalizeBotMessage(botMessageEl, botContent); 366 conversationHistory.push({ role: 'assistant', content: botContent }); 367 saveHistoryToSessionStorage(); 368 } else { 369 if (botMessageEl && botMessageEl.parent().length) { 370 botMessageEl.closest('.chat-message').remove(); 371 } 372 appendErrorWithRetry(watchdogFired ? chatbot_i18n.timeout_error : chatbot_i18n.there_was_an_error); 373 } 374 } else { 375 fallbackSendMessage(message); 376 } 377 } finally { 378 stopWatchdog(); 379 currentAbortController = null; 380 isStreaming = false; 381 showStreamingUI(false); 382 } 383 } 384 385 function fallbackSendMessage(message) { 386 var conversationId = getConversationId(); 387 var ajaxData = { 388 action: 'newcodebyte_chatbot_send_message', 389 message: message, 390 conversation_id: conversationId, 391 history: JSON.stringify(conversationHistory), 392 newcodebyte_chatbot_message_nonce: chatbot_ajax.newcodebyte_chatbot_message_nonce 393 }; 394 395 if (userInfo) { 396 ajaxData.name = userInfo.name; 397 ajaxData.email = userInfo.email; 398 ajaxData.is_first_message = '1'; 399 userInfo.sent = true; 400 saveUserInfo(); 401 userInfo = null; 402 } 403 404 isStreaming = true; 405 showStreamingUI(true); 406 407 var loadingMessage = null; 408 if (!$('.chat-message.loading').length) { 409 var avatarImg = $('<img>').attr('src', chatbot_params.chatbot_bot_avatar_url).attr('alt', 'bot'); 410 var typingIndicator = $('<span>').addClass('typing-indicator').text(chatbot_i18n.typing_indicator || 'Typingâ¦'); 411 var bubble = $('<div>').addClass('chat-bubble').append(typingIndicator); 412 loadingMessage = $('<div>').addClass('chat-message loading bot').append(avatarImg).append(bubble); 413 chatboxContent.append(loadingMessage); 414 chatboxContent.stop().animate({ scrollTop: chatboxContent[0].scrollHeight }, 200); 415 } 416 417 $.ajax({ 418 url: chatbot_ajax.ajaxurl, 419 type: 'POST', 420 data: ajaxData, 421 timeout: 60000, 422 success: handleServerResponse, 423 error: handleServerError, 424 complete: function() { 425 isStreaming = false; 426 showStreamingUI(false); 427 } 428 }); 429 } 430 431 function handleServerResponse(response) { 432 $('.chat-message.loading').remove(); 433 434 if (response.success) { 435 var botMessageRaw = cleanBotContent(response.data.message); 436 437 conversationHistory.push({ role: 'assistant', content: botMessageRaw }); 438 saveHistoryToSessionStorage(); 439 440 appendBotMessage(botMessageRaw); 441 442 if (response.data.conversation_id && !currentConversationId) { 443 currentConversationId = response.data.conversation_id; 444 try { sessionStorage.setItem(convIdKey, currentConversationId); } catch (e) {} 445 } 446 447 incrementUnread(); 448 } else { 449 appendErrorWithRetry(response.data.message || chatbot_i18n.there_was_an_error); 450 } 451 } 452 453 function handleServerError(xhr) { 454 $('.chat-message.loading').remove(); 455 var errorMessage = chatbot_i18n.there_was_an_error; 456 if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) { 457 errorMessage = xhr.responseJSON.data.message; 458 } 459 appendErrorWithRetry(errorMessage); 460 } 461 462 function createBotMessagePlaceholder() { 463 var avatarImg = $('<img>').attr('src', chatbot_params.chatbot_bot_avatar_url).attr('alt', 'bot'); 464 var typingIndicator = $('<span>').addClass('typing-indicator').text(chatbot_i18n.typing_indicator || 'Typingâ¦'); 465 var messageBubble = $('<div>').addClass('chat-bubble streaming').append(typingIndicator); 466 var newMessage = $('<div>').addClass('chat-message bot streaming').append(avatarImg).append(messageBubble); 467 chatboxContent.append(newMessage); 468 chatboxContent.stop().animate({ scrollTop: chatboxContent[0].scrollHeight }, 200); 469 return messageBubble; 470 } 471 472 function removeTypingIndicator($bubble) { 473 $bubble.find('.typing-indicator').remove(); 474 } 475 476 function updateBotMessageText($bubble, text) { 477 removeTypingIndicator($bubble); 478 if (typeof marked !== 'undefined' && typeof DOMPurify !== 'undefined') { 479 $bubble.html(DOMPurify.sanitize(marked.parse(text).trim())); 480 } else { 481 $bubble.text(cleanBotContent(text)); 482 } 483 chatboxContent.stop().animate({ scrollTop: chatboxContent[0].scrollHeight }, 50); 484 } 485 486 function cleanBotContent(text) { 487 return String(text || '').replace(/\s+$/, ''); 488 } 489 490 function finalizeBotMessage($bubble, text) { 491 if (!$bubble || !$bubble.parent().length) return; 492 493 $bubble.removeClass('streaming'); 494 $bubble.closest('.chat-message').removeClass('streaming'); 495 removeTypingIndicator($bubble); 496 497 if (typeof marked !== 'undefined' && typeof DOMPurify !== 'undefined') { 498 $bubble.html(DOMPurify.sanitize(marked.parse(text).trim())); 499 } else { 500 $bubble.text(cleanBotContent(text)); 501 } 502 chatboxContent.stop().animate({ scrollTop: chatboxContent[0].scrollHeight }, 200); 503 } 504 505 function sendWelcomeMessage() { 506 var welcomeMsg = cleanBotContent(chatbot_i18n.welcome_message || 'Hello!'); 507 if (conversationHistory.length === 0) { 508 conversationHistory.push({ role: 'assistant', content: welcomeMsg }); 509 saveHistoryToSessionStorage(); 510 appendBotMessage(welcomeMsg); 511 } 512 } 513 514 function showPreChatForm() { 515 if (chatboxContent.find('#prechat-form').length) return; 516 chatboxContent.empty(); 517 $('#chatbox-controls').hide(); 518 519 var formHtml = '<div id="prechat-form">' + 520 '<h3>' + (chatbot_i18n.prechat_title || 'Before we start chatting') + '</h3>' + 521 '<p>' + (chatbot_i18n.prechat_description || 'Please provide the following information:') + '</p>' + 522 '<div class="form-field">' + 523 '<label for="prechat-name">' + (chatbot_i18n.prechat_name_label || 'Name') + ' *</label>' + 524 '<input type="text" id="prechat-name" required />' + 525 '</div>' + 526 '<div class="form-field">' + 527 '<label for="prechat-email">' + (chatbot_i18n.prechat_email_label || 'Email') + '</label>' + 528 '<input type="email" id="prechat-email" />' + 529 '</div>' + 530 '<button id="prechat-start-btn">' + (chatbot_i18n.prechat_button_text || 'Start Chat') + '</button>' + 531 '<div id="prechat-error" style="color:#c00;margin-top:8px;display:none;"></div>' + 532 '</div>'; 533 534 chatboxContent.append(formHtml); 535 536 $('#prechat-start-btn').on('click', function() { 537 var name = $('#prechat-name').val().trim(); 538 if (!name) { 539 $('#prechat-error').text(chatbot_i18n.prechat_error_message || 'Please fill in all required fields.').show(); 540 return; 541 } 542 $('#prechat-error').hide(); 543 userInfo = { name: name, email: $('#prechat-email').val().trim() }; 544 saveUserInfo(); 545 $('#prechat-form').remove(); 546 $('#chatbox-controls').show(); 547 // Riparte da zero: utente nuovo, cronologia fresca. 548 conversationHistory = []; 549 currentConversationId = null; 550 try { 551 sessionStorage.removeItem(historyKey); 552 sessionStorage.removeItem(convIdKey); 553 } catch (e) {} 554 sendWelcomeMessage(); 555 }); 556 } 557 558 function appendChatMessage(type, avatar, messageContent) { 559 var avatarImg = $('<img>').attr('src', avatar).attr('alt', type); 560 var messageBubble = $('<div>').addClass('chat-bubble'); 561 562 if (type === 'user') {
563 messageBubble.text(messageContent); 564 } else { 565 if (typeof marked !== 'undefined' && typeof DOMPurify !== 'undefined') { 566 messageBubble.html(DOMPurify.sanitize(marked.parse(messageContent).trim())); 567 } else { 568 messageBubble.text(cleanBotContent(messageContent)); 569 } 570 } 571 572 var newMessage = $('<div>').addClass('chat-message ' + type).append(avatarImg).append(messageBubble); 573 chatboxContent.append(newMessage); 574 chatboxContent.stop().animate({ scrollTop: chatboxContent[0].scrollHeight }, 200); 575 } 576 577 function appendUserMessage(message) { appendChatMessage('user', chatbot_params.chatbot_user_avatar_url, message); } 578 function appendBotMessage(message) { appendChatMessage('bot', chatbot_params.chatbot_bot_avatar_url, message); } 579 580 function toggleChatbox() { 581 if (isAnimating) return; 582 isAnimating = true; 583 584 if (proactiveBubble.hasClass('visible')) { 585 proactiveBubble.removeClass('visible').addClass('hidden'); 586 try { sessionStorage.setItem('newcodebyte_proactive_bubble_closed', 'true'); } catch(e) {} 587 } 588 589 var isMobile = window.innerWidth <= 768; 590 var newPosition = isChatboxOpen ? (isMobile ? '-100vh' : '-800px') : (isMobile ? '60px' : '85px'); 591 var newIcon = isChatboxOpen ? chatbot_params.chatbot_icon_closed_url : chatbot_params.chatbot_icon_open_url; 592 593 chatbox.animate({ bottom: newPosition }, 300, function() { 594 isAnimating = false; 595 isChatboxOpen = !isChatboxOpen; 596 if (isChatboxOpen) { 597 clearUnread(); 598 } 599 updateUnreadBadge(); 600 }); 601 602 chatboxButtonImg.attr('src', newIcon); 603 604 if (!isChatboxOpen) { 605 playNotificationSound(); 606 if (conversationHistory.length === 0) { 607 if (chatbot_params.enable_prechat_form === '1' && !userInfo) { 608 showPreChatForm(); 609 } else { 610 sendWelcomeMessage(); 611 } 612 } 613 } 614 } 615 616 function playNotificationSound() { 617 var audioPath = chatbot_params.notification_sound_url; 618 if (audioPath) { 619 try { 620 var audio = new Audio(audioPath); 621 audio.play().catch(function() {}); 622 } catch (e) {} 623 } 624 } 625 626 loadHistoryFromSessionStorage(); 627 getConversationId(); 628 loadUnreadCount(); 629 loadUserInfo(); 630 updateUnreadBadge(); 631 autoResizeTextarea(); 632 633 if ($('.chatbot-shortcode-wrapper').length === 0) { 634 chatboxButton.on('click', toggleChatbox); 635 $('#chatbox-close').on('click', toggleChatbox); 636 } 637 638 chatboxSend.on('click', sendMessage); 639 640 chatboxStop.on('click', function() { 641 if (currentAbortController) { 642 currentAbortController.abort(); 643 } 644 }); 645 646 chatboxNew.on('click', function() { 647 if (!confirm(chatbot_i18n.confirm_new_conversation || 'Start a new conversation?')) return; 648 startNewConversation(); 649 }); 650 651 // Supporto tastiera per lo span #chatbox-new (role="button"):: Enter/Spazio 652 chatboxNew.on('keydown', function(e) { 653 if (e.key === 'Enter' || e.key === ' ') { 654 e.preventDefault(); 655 chatboxNew.trigger('click'); 656 } 657 }); 658 659 chatboxInput.on('input', autoResizeTextarea); 660 chatboxInput.on('keydown', function(e) { 661 if (e.key === 'Enter' && !e.shiftKey) { 662 e.preventDefault(); 663 sendMessage(); 664 } 665 }); 666 667 $(document).on('chatbot:ready', function() { 668 if (conversationHistory.length === 0) { 669 if (chatbot_params.enable_prechat_form === '1' && !userInfo) { 670 showPreChatForm(); 671 } else { 672 sendWelcomeMessage(); 673 } 674 } 675 }); 676 677 if ($('.chatbot-shortcode-wrapper').length === 0 && chatbot_params.enable_proactive === '1' && proactiveBubble.length > 0) { 678 proactiveBubble.find('.chatbot-proactive-text').text(chatbot_params.proactive_text || ''); 679 680 proactiveBubble.find('.chatbot-proactive-close').on('click', function(e) { 681 e.stopPropagation(); 682 proactiveBubble.removeClass('visible').addClass('hidden'); 683 try { sessionStorage.setItem('newcodebyte_proactive_bubble_closed', 'true'); } catch (e) {} 684 }); 685 686 proactiveBubble.on('click', function(e) { 687 if ($(e.target).closest('.chatbot-proactive-close').length) return; 688 if (!isChatboxOpen) toggleChatbox(); 689 }); 690
691 var proactiveDelay = parseInt(chatbot_params.proactive_delay, 10) || 5; 692 setTimeout(function() { 693 if (!isChatboxOpen && !proactiveBubble.hasClass('visible')) { 694 var closed = false; 695 try { closed = sessionStorage.getItem('newcodebyte_proactive_bubble_closed') === 'true'; } catch (e) {} 696 if (!closed) { 697 proactiveBubble.removeClass('hidden').addClass('visible'); 698 } 699 } 700 }, proactiveDelay * 1000); 701 } 702 703 })(jQuery); 704});
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.