1/** 2 * Welcome Screen Manager for NiafamChat 3 * Ù Ø¯ÛØ±Ûت ØµÙØÙ Ø®ÙØ´Ø§Ù دگÙÛÛ Ù Ø§ÙØªØ®Ø§Ø¨ ÙÙØ¹ Ú¯ÙØªÚ¯Ù 4 */ 5 6/** 7 * NiafamChat Welcome Screen System 8 * =============================== 9 * 10 * RECENT CHANGES (Project Restructure): 11 * - Moved from root to client/js/ directory for better organization 12 * - Updated all file paths and references for new structure 13 * - Maintained integration with FAQ and user-info systems 14 * 15 * FEATURES: 16 * - Progressive user journey management 17 * - Interactive welcome options with statistics 18 * - Smart navigation based on user history 19 * - Integration with FAQ, user-info, and main chat 20 * - Persian RTL design with animations 21 * 22 * Author: NiafamChat Development Team 23 * Last Updated: Project restructure for better organization 24 */ 25class WelcomeScreen { 26 constructor() { 27 this.isVisible = false; 28 this.animationDuration = 300; 29 30 this.init(); 31 } 32 33 init() { 34 this.bindEvents(); 35 console.log('ð Welcome Screen initialized'); 36 } 37 38 /** 39 * ÙÙ Ø§ÛØ´ ØµÙØÙ Ø®ÙØ´Ø§Ù دگÙÛÛ 40 */ 41 show() { 42 if (this.isVisible) return; 43 44 // Ø§ÛØ¬Ø§Ø¯ Ù ØØªÙØ§Û welcome screen 45 this.createWelcomeContent(); 46 47 // Ù Ø®ÙÛ Ú©Ø±Ø¯Ù Ø¨Ø®Ø´âÙØ§Û ÚØª 48 this.hideChatElements(); 49 50 // ÙÙ Ø§ÛØ´ welcome screen 51 this.showWelcomeElements(); 52 53 this.isVisible = true; 54 55 console.log('ð± Welcome screen shown'); 56 } 57 58 /** 59 * Ù Ø®ÙÛ Ú©Ø±Ø¯Ù ØµÙØÙ Ø®ÙØ´Ø§Ù دگÙÛÛ Ù ÙÙ Ø§ÛØ´ ÚØª 60 */ 61 hide() { 62 if (!this.isVisible) return; 63 64 const welcomeContainer = document.getElementById('welcomeContainer'); 65 if (welcomeContainer) { 66 welcomeContainer.style.opacity = '0'; 67 welcomeContainer.style.transform = 'translateY(20px)'; 68 69 setTimeout(() => { 70 welcomeContainer.remove(); 71 this.showChatElements(); 72 this.isVisible = false; 73 console.log('ð¬ Switched to chat mode'); 74 }, this.animationDuration); 75 } 76 } 77 78 /** 79 * Ø§ÛØ¬Ø§Ø¯ Ù ØØªÙØ§Û ØµÙØÙ Ø®ÙØ´Ø§Ù دگÙÛÛ 80 */ 81 createWelcomeContent() { 82 const chatContainer = document.getElementById('chatContainer'); 83 84 // Load configuration 85 const config = this.getWelcomeConfig(); 86 87 const welcomeHTML = ` 88 <div id="welcomeContainer" class="welcome-screen"> 89 <!-- Close Button - دک٠٠بست٠--> 90 <button id="closeWelcome" class="welcome-close-btn" title="بستÙ"> 91 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 92 <line x1="18" y1="6" x2="6" y2="18"></line> 93 <line x1="6" y1="6" x2="18" y2="18"></line> 94 </svg> 95 </button> 96 97 <!-- Welcome Header --> 98 <div class="welcome-header"> 99 <div class="welcome-logo"> 100 <i data-lucide="${config.header.icon}"></i> 101 </div> 102 <h1 class="welcome-title">${config.header.title}</h1> 103 <p class="welcome-subtitle">${config.header.subtitle}</p> 104 </div> 105 106 <!-- Welcome Content --> 107 <div class="welcome-content"> 108 <div class="welcome-options"> 109 ${this.generateOptions(config.options)} 110 </div> 111 </div> 112 113 <!-- Welcome Footer --> 114 <div class="welcome-footer"> 115 <div class="welcome-links"> 116 ${this.generateFooterLinks(config.footer.links)} 117 </div> 118 </div> 119 </div> 120 `; 121 122 chatContainer.insertAdjacentHTML('afterbegin', welcomeHTML); 123 124 // Initialize icons for the new content 125 if (window.initializeLucideIcons) { 126 window.initializeLucideIcons(); 127 } 128 129 // Bind close button event 130 const closeBtn = document.getElementById('closeWelcome'); 131 if (closeBtn) { 132 closeBtn.addEventListener('click', () => { 133 this.closeChat(); 134 }); 135 } 136 137 // Animate in 138 setTimeout(() => { 139 const welcomeContainer = document.getElementById('welcomeContainer'); 140 if (welcomeContainer) { 141 welcomeContainer.style.opacity = '1'; 142 welcomeContainer.style.transform = 'translateY(0)'; 143 } 144 }, 50); 145 } 146 147 /** 148 * Ø¨Ø³ØªÙ ÚØª ٠برگشت Ø¨Ù ØØ§Ùت اÙÙÛÙ 149 * Ù¾ÛØ´ از Ù Ø®ÙÛ Ú©Ø±Ø¯Ù UIØ ÙØ± سشÙÛ Ú©Ù Ø¯Ø± Ø¨Ú©âØ§ÙØ¯ Ø¨Ø±Ø§Û Ú©Ø§Ø±Ø¨Ø± باز است بست٠٠ÛâØ´ÙØ¯ 150 * Ù state Ú©ÙØ§ÛÙØª (userDataØ messagesØ localStorage) Ú©Ø§Ù ÙØ§Ù پاک Ù ÛâØ´ÙØ¯. 151 * 152 * اگر Ú¯ÙØªÚ¯ÙÛ Ø¢ÙÙØ§ÛÙ ÙØ¹Ø§Ù Ø¨Ø§Ø´Ø¯Ø Ú©Ø§Ø±Øª تأÛÛØ¯ درÙÙ Ù¾ÙÙ ÚØª ÙÙ Ø§ÛØ´ داد٠٠ÛâØ´ÙØ¯ 153 * (Ø¨Ù Ø¬Ø§Û popup Ù Ø±ÙØ±Ú¯Ø±) Ù Ø¨Ø³ØªÙ ÙØ§ÙØ¹Û Ù¾Ø³ از تأÛÛØ¯ کاربر Ø§ØªÙØ§Ù Ù ÛâØ§ÙØªØ¯. 154 */ 155 closeChat() { 156 // modal تأÛÛØ¯ را رÙÛ Ù ØÙظÙâÛ ÙØ¹ÙÛ (welcome / user-info / chat) ÙÙ Ø§ÛØ´ بد٠157 try { 158 if (window.niafamChat && 159 typeof window.niafamChat.hasActiveLiveSession === 'function' && 160 window.niafamChat.hasActiveLiveSession() && 161 typeof window.niafamChat.showCloseConfirmationInChat === 'function') { 162 window.niafamChat.showCloseConfirmationInChat(() => this._performCloseChat()); 163 return; 164 } 165 } catch (e) { 166 // ignore â Ø¨Ù Ù Ø³ÛØ± ٠ع٠ÙÙÛ Ø¨Ø±Ù 167 } 168 169 this._performCloseChat(); 170 } 171 172 _performCloseChat() { 173 // بست٠ÙÙ ÙâÛ Ø³Ø´ÙâÙØ§Û باز در Ø¨Ú©âØ§ÙØ¯ + Ù¾Ø§Ú©âØ³Ø§Ø²Û Ú©Ø§Ù Ù state Ú©ÙØ§ÛÙØª 174 try { 175 if (window.niafamChat && typeof window.niafamChat.closeAllBackendSessions === 'function') { 176 window.niafamChat.closeAllBackendSessions(); 177 } 178 } catch (e) { 179 // ignore 180 } 181 182 const chatContainer = document.getElementById('chatContainer'); 183 const chatToggle = document.getElementById('chatToggle'); 184 const speechBubble = document.getElementById('chatSpeechBubble'); 185 186 // Remove active class from chat container 187 if (chatContainer) { 188 chatContainer.classList.remove('active'); 189 } 190 191 // Show chat toggle button 192 if (chatToggle) { 193 chatToggle.style.display = 'flex'; 194 } 195 196 // Show speech bubble
197 if (speechBubble) { 198 speechBubble.style.display = ''; 199 } 200 201 // Restore body scroll for mobile 202 document.body.style.overflow = ''; 203 document.body.style.position = ''; 204 document.body.style.width = ''; 205 206 console.log('ðª Chat closed from welcome screen'); 207 } 208 209 /** 210 * Ø¯Ø±ÛØ§Ùت ØªÙØ¸Û٠ات Welcome Screen از config 211 */ 212 getWelcomeConfig() { 213 const defaultConfig = { 214 header: { 215 title: "ÙÛØ§ÙØ§Ù ÚØª", 216 subtitle: "Ø¯Ø³ØªÛØ§Ø± ÙÙØ´Ù ÙØ¯ ش٠ا Ø¨Ø±Ø§Û Ù¾Ø§Ø³Ø®Ú¯ÙÛÛ Ø³Ø±ÛØ¹ ٠دÙÛÙ", 217 icon: "bot" 218 }, 219 options: { 220 smartChat: { 221 enabled: true, 222 title: "ÚØª ÙÙØ´Ù ÙØ¯", 223 description: "Ú¯ÙØªÚ¯Ù با Ø¯Ø³ØªÛØ§Ø± ÙÙØ´ ٠صÙÙØ¹Û", 224 icon: "bot" 225 }, 226 faq: { 227 enabled: true, 228 title: "Ø¯Ø§ÙØ´Ùا٠Ù", 229 description: "Ù Ø´Ø§ÙØ¯Ù پاسخ Ø³Ø±ÛØ¹ Ø¨Ù Ø³ÙØ§Ùات Ø±Ø§ÛØ¬ ٠پرتکرار", 230 icon: "help-circle" 231 } 232 }, 233 footer: { 234 links: [ 235 { text: "ØØ±ÛÙ Ø®ØµÙØµÛ", url: "#privacy" }, 236 { text: "Ø´Ø±Ø§ÛØ· Ø§Ø³ØªÙØ§Ø¯Ù", url: "#terms" }, 237 { text: "Ù¾Ø´ØªÛØ¨Ø§ÙÛ", url: "#support" } 238 ] 239 }, 240 chatGreeting: { 241 withName: "Ø³ÙØ§Ù {name} Ø¹Ø²ÛØ²! Ù Ù Ø¯Ø³ØªÛØ§Ø± ÙÙØ´Ù ÙØ¯ {botName} ÙØ³ØªÙ . ÚØ·Ùر Ù ÛâØªÙÙ٠ک٠کتÙÙ Ú©ÙÙ Ø", 242 withoutName: "Ø³ÙØ§Ù ! Ù Ù Ø¯Ø³ØªÛØ§Ø± ÙÙØ´Ù ÙØ¯ {botName} ÙØ³ØªÙ . ÚØ·Ùر Ù ÛâØªÙÙ٠ک٠کتÙÙ Ú©ÙÙ Ø ð" 243 } 244 }; 245 246 // Try to get from loaded config 247 if (window.LOADED_CONFIG && window.LOADED_CONFIG.welcomeScreen) { 248 return { ...defaultConfig, ...window.LOADED_CONFIG.welcomeScreen }; 249 } 250 251 return defaultConfig; 252 } 253 254 /** 255 * تÙÙÛØ¯ HTML گزÛÙÙâÙØ§ 256 */ 257 generateOptions(options) { 258 let html = ''; 259 260 // Ú¯ÙØªÚ¯ÙÛ Ø¢ÙÙØ§ÛÙ (Live Chat) 261 if (options.liveChat && options.liveChat.enabled) { 262 html += ` 263 <div class="option-card primary" data-option="live-chat" data-chat-mode="${options.liveChat.chatMode || 'live'}"> 264 <div class="option-header"> 265 <div class="option-icon"> 266 <i data-lucide="${options.liveChat.icon}"></i> 267 </div> 268 <div class="option-info"> 269 <h3 class="option-title">${options.liveChat.title}</h3> 270 <p class="option-description">${options.liveChat.description}</p> 271 </div> 272 <div class="option-arrow">â</div> 273 </div> 274 </div> 275 `; 276 } 277 278 // Ú¯Ù¾ ÙÙØ´Ù ÙØ¯ (AI Assistant) 279 if (options.aiAssistant && options.aiAssistant.enabled) { 280 html += ` 281 <div class="option-card" data-option="ai-assistant" data-chat-mode="${options.aiAssistant.chatMode || 'ai'}"> 282 <div class="option-header"> 283 <div class="option-icon"> 284 <i data-lucide="${options.aiAssistant.icon}"></i> 285 </div> 286 <div class="option-info"> 287 <h3 class="option-title">${options.aiAssistant.title}</h3> 288 <p class="option-description">${options.aiAssistant.description}</p> 289 </div> 290 <div class="option-arrow">â</div> 291 </div> 292 </div> 293 `; 294 } 295 296 // Ø¯Ø§ÙØ´Ùا٠٠(FAQ) 297 if (options.faq && options.faq.enabled) { 298 html += ` 299 <div class="option-card" data-option="faq"> 300 <div class="option-header"> 301 <div class="option-icon"> 302 <i data-lucide="${options.faq.icon}"></i> 303 </div> 304 <div class="option-info"> 305 <h3 class="option-title">${options.faq.title}</h3> 306 <p class="option-description">${options.faq.description}</p> 307 </div> 308 <div class="option-arrow">â</div> 309 </div> 310 </div> 311 `; 312 } 313 314 return html; 315 } 316 317 /** 318 * تÙÙÛØ¯ HTML ÙÛÙÚ©âÙØ§Û Footer 319 */ 320 generateFooterLinks(links) { 321 if (!links || !Array.isArray(links)) return ''; 322 323 return links 324 .map(link => `<a href="${link.url}" class="welcome-link">${link.text}</a>`) 325 .join('\n'); 326 } 327 328 /** 329 * Ù Ø®ÙÛ Ú©Ø±Ø¯Ù Ø¹ÙØ§ØµØ± ÚØª 330 */ 331 hideChatElements() { 332 const elementsToHide = [ 333 'searchContainer',
334 'savedMessagesPanel', 335 'searchResultsPanel', 336 'chatMessages', 337 'typingIndicator', 338 'suggestedReplies', 339 'chat-input' 340 ]; 341 342 elementsToHide.forEach(id => { 343 const element = document.getElementById(id) || document.querySelector(`.${id}`); 344 if (element) { 345 element.style.display = 'none'; 346 } 347 }); 348 349 // Ù Ø®ÙÛ Ú©Ø±Ø¯Ù header ÚØª 350 const chatHeader = document.querySelector('.chat-header'); 351 if (chatHeader) { 352 chatHeader.style.display = 'none'; 353 } 354 } 355 356 /** 357 * ÙÙ Ø§ÛØ´ Ø¹ÙØ§ØµØ± ÚØª 358 */ 359 showChatElements() { 360 const elementsToShow = [ 361 'chatMessages', 362 'typingIndicator', 363 'suggestedReplies' 364 ]; 365 366 elementsToShow.forEach(id => { 367 const element = document.getElementById(id) || document.querySelector(`.${id}`); 368 if (element) { 369 element.style.display = ''; 370 } 371 }); 372 373 // ÙÙ Ø§ÛØ´ header ÚØª 374 const chatHeader = document.querySelector('.chat-header'); 375 if (chatHeader) { 376 chatHeader.style.display = ''; 377 } 378 379 // ÙÙ Ø§ÛØ´ input ÚØª 380 const chatInput = document.querySelector('.chat-input'); 381 if (chatInput) { 382 chatInput.style.display = ''; 383 } 384 } 385 386 /** 387 * ÙÙ Ø§ÛØ´ Ø¹ÙØ§ØµØ± welcome 388 */ 389 showWelcomeElements() { 390 const welcomeContainer = document.getElementById('welcomeContainer'); 391 if (welcomeContainer) { 392 welcomeContainer.style.opacity = '0'; 393 welcomeContainer.style.transform = 'translateY(20px)'; 394 welcomeContainer.style.transition = `all ${this.animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1)`; 395 } 396 } 397 398 /** 399 * Ø´Ø±ÙØ¹ ÚØª (Live ÛØ§ AI) 400 * @param {string} chatMode - 'live' or 'ai' 401 */ 402 startChat(chatMode = 'live') { 403 console.log(`ð Starting chat with mode: ${chatMode}`); 404 405 // ØªÙØ¸ÛÙ chatMode در chatInterface 406 if (window.niafamChat && window.niafamChat.switchChatMode) { 407 window.niafamChat.switchChatMode(chatMode); 408 } 409 410 // Ø¨Ø±Ø±Ø³Û ØªÙØ¸Û٠ات Ø§ØØ±Ø§Ø² ÙÙÛØª 411 const config = window.LOADED_CONFIG || window.configLoader?.config || window.CHAT_BOX_CONFIG || {}; 412 const clientAuth = config.clientAuthentication; 413 414 // Ø®ÙØ§ÙØ¯Ù ØªÙØ¸Û٠ات authentication Ù guest 415 const authenticationEnabled = clientAuth?.authentication?.enabled ?? clientAuth?.modes?.authenticationEnabled ?? false; 416 const guestEnabled = clientAuth?.guest?.enabled ?? clientAuth?.modes?.guestEnabled ?? false; 417 418 console.log('ð Authentication settings:', { 419 authenticationEnabled, 420 guestEnabled, 421 clientAuth 422 }); 423 424 // اگر ÙØ± Ø¯Ù ØºÛØ±ÙØ¹Ø§Ù Ø¨Ø§Ø´ÙØ¯Ø Ù Ø³ÛØ± ÙØ±ÙØ¯Û ÙØ¬Ùد ÙØ¯Ø§Ø±Ø¯ 425 if (authenticationEnabled === false && guestEnabled === false) { 426 console.log('âï¸ Both authentication and guest disabled - skipping user info form'); 427 this.hide(); 428 // â ï¸ Ù¾ÛØ§Ù Ø®ÙØ´âآ٠دگÙÛÛ Ø¯Ø± script.js ÙÙ Ø§ÛØ´ داد٠٠ÛâØ´ÙØ¯ 429 return; 430 } 431 432 // تص٠ÛÙ ÙÙ Ø§ÛØ´ ÙØ±Ù را ب٠userInfoForm.shouldShowForm() Ù ÛâØ³Ù¾Ø§Ø±ÛÙ . 433 // اÛ٠٠تد ÙÙ ÙÙ Ø´Ø±Ø§ÛØ· را Ø¨Ø±Ø±Ø³Û Ù ÛâÚ©ÙØ¯: ÙÛØ§Ø² Ø¨Ù Ø§ÙØªØ®Ø§Ø¨ دپارت٠ا٠434 // در Live ChatØ ÙÛÙØ¯ÙØ§Û ÙØ§Ø¨Ù ÙÙ Ø§ÛØ´ بر اساس کاÙÙÛÚ¯Ø Ø§Ø·ÙØ§Ø¹Ø§Øª 435 // session کاربر authenticated Ù ØºÛØ±Ù. 436 // 437 // اگر ÙØ±Ù ÙØ§Ø²Ù باشد show() آ٠را ÙÙ Ø§ÛØ´ Ù ÛâØ¯ÙØ¯Ø اگر ÙÙØ Ø®ÙØ¯Ø´ 438 // proceedToChat() را صدا Ù ÛâØ²ÙØ¯ ٠کاربر ٠ستÙÛÙ Ø§Ù ÙØ§Ø±Ø¯ ÚØª Ù ÛâØ´ÙØ¯. 439 this.hide(); 440 setTimeout(() => { 441 if (window.userInfoForm && typeof window.userInfoForm.show === 'function') { 442 window.userInfoForm.show(); 443 } 444 }, this.animationDuration); 445 } 446 447 /** 448 * Ø´Ø±ÙØ¹ ÚØª ÙÙØ´Ù ÙØ¯ (Ø¨Ø±Ø§Û Ø³Ø§Ø²Ú¯Ø§Ø±Û Ø¨Ø§ کد ÙØ¯ÛÙ Û) 449 * @deprecated Ø§Ø³ØªÙØ§Ø¯Ù از startChat() ØªÙØµÛÙ Ù ÛâØ´ÙØ¯ 450 */ 451 startSmartChat() { 452 this.startChat('ai'); 453 } 454 455 /** 456 * Ø¯Ø±ÛØ§Ùت Ù¾ÛØ§Ù Ø®ÙØ´Ø§Ù دگÙÛÛ Ø¨Ø§ جاÛگزÛÙÛ placeholder ÙØ§ 457 */ 458 getWelcomeMessage() { 459 const config = this.getWelcomeConfig(); 460 const userData = window.userInfoForm ? window.userInfoForm.getUserData() : {}; 461 const firstName = userData.firstName; 462 463 // Get bot name from company branding 464 const botName = window.LOADED_CONFIG?.companyBranding?.botName || 'ÙÛØ§Ùا٠'; 465 466 // Choose template based on whether we have user name 467 let template = firstName 468 ? config.chatGreeting.withName 469 : config.chatGreeting.withoutName; 470 471 // Replace placeholders 472 template = template.replace('{name}', firstName || ''); 473 template = template.replace('{botName}', botName); 474 475 return template; 476 } 477 478 /** 479 * ÙÙ Ø§ÛØ´ ØµÙØÙ Ø¯Ø§ÙØ´Ùا٠٠480 */ 481 showFAQ() { 482 if (window.faqManager) { 483 this.hide(); 484 setTimeout(() => { 485 window.faqManager.show(); 486 }, this.animationDuration); 487 } else { 488 console.warn('FAQ Manager not loaded'); 489 alert('Ø³ÛØ³ØªÙ Ø¯Ø§ÙØ´Ùا٠٠در ØØ§Ù Ø¨Ø§Ø±Ú¯Ø°Ø§Ø±Û Ø§Ø³Øª...'); 490 } 491 } 492 493 /** 494 * اتصا٠رÙÛØ¯Ø§Ø¯Ùا 495 */ 496 bindEvents() { 497 // Ø§Ø³ØªÙØ§Ø¯Ù از event delegation Ø¨Ø±Ø§Û Ú©ÙÛÚ© رÙÛ Ú¯Ø²ÛÙÙâÙØ§ 498 document.addEventListener('click', (e) => { 499 const optionCard = e.target.closest('.option-card'); 500 if (optionCard) { 501 const option = optionCard.dataset.option; 502 const chatMode = optionCard.dataset.chatMode; 503 504 // اÙÛÙ ÛØ´Ù Ú©ÙÛÚ© 505 optionCard.style.transform = 'translateY(-2px) scale(0.98)'; 506 setTimeout(() => { 507 optionCard.style.transform = ''; 508 }, 150); 509 510 // Ø§Ø¬Ø±Ø§Û Ø¹Ù Ù Ù Ø±Ø¨ÙØ·Ù 511 switch (option) { 512 case 'live-chat': 513 this.startChat(chatMode || 'live'); 514 break; 515 case 'ai-assistant': 516 this.startChat(chatMode || 'ai'); 517 break; 518 case 'faq':
519 this.showFAQ(); 520 break; 521 } 522 523 return; 524 } 525 526 }); 527 } 528 529 /** 530 * Ø¨Ø±Ø±Ø³Û Ø§ÛÙÚ©Ù Ø¢ÛØ§ Ø¨Ø§ÛØ¯ welcome screen ÙÙ Ø§ÛØ´ Ø¯Ø§Ø¯Ù Ø´ÙØ¯ 531 */ 532 shouldShow() { 533 // اگر ÙØ¨ÙØ§Ù ÚØª Ø§Ø³ØªÙØ§Ø¯Ù Ø´Ø¯ÙØ welcome screen ÙÙ Ø§ÛØ´ Ø¯Ø§Ø¯Ù ÙØ´Ùد 534 const hasUsedChat = localStorage.getItem('niafam-chat-used'); 535 return !hasUsedChat; 536 } 537 538 /** 539 * Ø¹ÙØ§Ù تâÚ¯Ø°Ø§Ø±Û Ø§ÛÙÚ©Ù ÚØª Ø§Ø³ØªÙØ§Ø¯Ù شد٠540 */ 541 markChatAsUsed() { 542 localStorage.setItem('niafam-chat-used', 'true'); 543 } 544} 545 546// Ø§ÛØ¬Ø§Ø¯ instance global 547window.welcomeScreen = new WelcomeScreen(); 548 549// Export Ø¨Ø±Ø§Û Ø§Ø³ØªÙØ§Ø¯Ù در Ø³Ø§ÛØ± ÙØ§ÛÙâÙØ§ 550if (typeof module !== 'undefined' && module.exports) { 551 module.exports = WelcomeScreen; 552}
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.