1var CART_COUNT, CART_TOTAL, components, googleTokenClient, HEADER_STICKY_HEIGHT; 2window['COMMON_LANG'] = TRANSLATES['common']; 3window['BLOCK_JS'] = {}; 4window['SNIPPET_JS'] = {}; 5window['components'] = {}; 6window['HEADER_STICKY_HEIGHT'] = 0; 7window['googleTokenClient'] = null; 8window['DEFAULT_PRODUCT_IMAGE'] = 'https://public.tsoftstatic.com/Data/EditorFiles/urun-gorseli-hazirlaniyor.svg'; 9 10function getLocale() { 11 const lang = LANGUAGE || 'tr'; 12 const localeMap = { 13 ar: 'ar', 14 fa: 'fa', 15 ku: 'ku', 16 kg: 'ky-KG', 17 ua: 'uk-UA', 18 az: 'az', 19 en: 'en-US', 20 pt: 'pt-PT', 21 cs: 'cs-CZ', 22 el: 'el-GR', 23 sl: 'sl-SI', 24 ka: 'ka-GE', 25 bs: 'bs-BA', 26 }; 27 if (localeMap[lang]) return localeMap[lang]; 28 return lang + '-' + lang.toUpperCase(); 29} 30 31const LoginPageTracking = { 32 Callback: {}, 33 callbackArray: [] 34}; 35const SignPageTracking = { 36 Callback: () => {} 37}; 38const QuickViewObj = { 39 callback: { 40 open: [] 41 } 42}; 43const OrderCallback = { 44 address: [], 45 payment: [], 46 paymentChange: [], 47 approve: [], 48} 49const productLoaderCallback = []; 50 51async function request(method = null, url = null, data = null, accept = null, signal = null) { 52 if (url == null) { 53 console.warn('request warn: url null'); 54 return; 55 } 56 57 if (method == null) method = 'GET'; 58 method = method.toUpperCase(); 59 60 const isStorefrontV3 = url.includes('/api/v3/'); 61 62 const headers = { 63 'Accept-Language': LANGUAGE || 'tr', 64 }; 65 66 if (accept == 'JSON' || isStorefrontV3) { 67 headers['Accept'] = 'application/json'; 68 } 69 70 const options = { 71 method: method, 72 headers: headers, 73 body: data, 74 ...(signal ? { signal } : {}), 75 }; 76 77 return fetch(url, options) 78 .then(async response => { 79 const contentType = response.headers.get('content-type'); 80 const isJson = contentType && contentType.indexOf('application/json') !== -1; 81 82 const payload = isJson 83 ? await response.json() 84 : await response.text(); 85 86 if (!response.ok) { 87 const error = new Error( 88 payload?.message || payload?.statusText || 'Bir hata oluÅtu' 89 ); 90 91 error.status = response.status; 92 error.response = payload; 93 error.retryAfter = Number(response.headers.get('Retry-After')) || null; 94 throw error; 95 } 96 97 return payload; 98 }) 99 .then(text => { 100 try { 101 return JSON.parse(text); 102 } catch { 103 return text; 104 } 105 }) 106 .catch(error => { 107 throw error; 108 }); 109} 110 111const requestError = (error, endpoint, form = null, msgType = 'notify') => { 112 const message = error.response?.message || error.response?.statusText || error.message; 113 114 const showMessage = (msg) => { 115 if (msgType == 'notify') { 116 notify({ 117 text: msg || message, 118 className: 'danger', 119 duration: 5000, 120 stopOnFocus: true 121 }); 122 } else { 123 modal({ 124 html: msg || message, 125 width: '480px', 126 alert: true, 127 class: 'danger', 128 }); 129 } 130 } 131 132 if (error.response?.errorCode === 401) { 133 loginPopup(); 134 showMessage(message); 135 return; 136 } 137 138 if (error.status === 429 || error.response?.throttle?.retryAfter) { 139 const retryAfter = error.response?.throttle?.retryAfter || error.retryAfter || 60; 140 const errorMessage = COMMON_LANG?.too_many_requests?.replace('%RETRY_AFTER%', retryAfter) || message; 141 showMessage(errorMessage); 142 return; 143 } 144 145 if (error.response?.errors?.length && form) { 146 const errors = error.response.errors; 147 for (let key of errors) { 148 const selector = form.querySelector(`#${key}`) || form.querySelector('[name="email"]'); 149 const errorMessage = errors[key] || COMMON_LANG?.form_required; 150 if (selector) { 151 popoverAlert({ 152 selector: selector, 153 message: errorMessage, 154 }); 155 } else { 156 showMessage(errorMessage); 157 } 158 } 159 return; 160 } 161 162 showMessage(message); 163 getCaptcha(form, error.response); 164} 165 166const LocalApi = { 167 get: (key, def = false) => { 168 try { 169 const obj = JSON.parse(localStorage.getItem(key)); 170 let now = new Date().getTime(); 171 if(Number.isNaN(obj.timeout) || obj.timeout < 1000000000) { 172 obj.timeout = 1000000000; 173 } else if (obj.timeout < 9000000000) { 174 obj.timeout = obj.timeout * 1000; 175 } 176 if (obj.timeout && obj.timeout < now) { 177 return def; 178 } 179 return typeof obj.value !== 'undefined' ? obj.value : def; 180 } catch (ex) { 181 return def; 182 } 183 }, 184 set: (key, val, timeout) => { 185 try { 186 timeout = Number.parseInt(timeout) > 0 ? timeout : 365 * 24 * 3600; 187 let obj = { value: val, timeout: new Date().getTime() + (1000 * timeout) }; 188 localStorage.setItem(key, JSON.stringify(obj)); 189 return true; 190 } catch (ex) { 191 return false;
192 } 193 }, 194 remove: key => { 195 try { 196 localStorage.removeItem(key); 197 return true; 198 } catch (ex) { 199 return false; 200 } 201 } 202} 203 204const scrollBehavior = function(href = null, height, trigger = false) { 205 const top = height != null && typeof height == 'number' ? height : 0; 206 const element = typeof href == 'object' && href != null ? href : (href != null ? document.querySelector(href) : null); 207 if (element == null) { 208 console.warn('scrollBehavior warn: element null'); 209 return; 210 }; 211 212 if (trigger && !element.classList.contains('active')) element.click(); 213 214 let elementRect = element.getBoundingClientRect(); 215 let bodyRect = document.body.getBoundingClientRect(); 216 217 let topRelativeToBody = elementRect.top - bodyRect.top - top; 218 219 scroll({ 220 top: topRelativeToBody, 221 behavior: "smooth" 222 }); 223} 224 225const loader = function(status = false) { 226 if (status == true) { 227 document.body.classList.add('is-loading'); 228 } else { 229 document.body.classList.remove('is-loading'); 230 } 231} 232 233const addClass = function(element, cls) { 234 cls = cls.split(' '); 235 for (let i = 0; i < element.length; i++) { 236 element[i].classList.add(...cls); 237 } 238} 239 240const removeClass = function(element, cls) { 241 cls = cls.split(' '); 242 for (let i = 0; i < element.length; i++) { 243 element[i].classList.remove(...cls); 244 } 245} 246 247const toggleClass = function(element, cls) { 248 cls = cls.split(' '); 249 for (let i = 0; i < element.length; i++) { 250 cls.map(c => element[i].classList.toggle(c)); 251 } 252} 253 254const getUrlParam = function(key = '', url = document.location) { 255 if (key == '') { 256 console.warn('getUrlParam warn: key undefined'); 257 return; 258 }; 259 260 url = new URL(url); 261 const params = url.searchParams; 262 return params.get(key); 263} 264 265const setUrlParam = function(key = '', value = '' , url = document.location) { 266 if (key == '') { 267 console.warn('setUrlParam warn: key undefined'); 268 return; 269 }; 270 271 url = new URL(url); 272 const params = url.searchParams; 273 params.set(key, value); 274 return url; 275} 276 277const deleteUrlParam = function(key = '', url = document.location) { 278 if (key == '') { 279 console.warn('deleteUrlParam warn: key undefined'); 280 return; 281 }; 282 283 url = new URL(url); 284 const params = url.searchParams; 285 params.delete(key); 286 return url; 287} 288 289const setCookie = function(cookieName, cookieValue, ExDays) { 290 let d = new Date(); 291 d.setTime(d.getTime() + (ExDays * 24 * 60 * 60 * 1000)); 292 let expires = 'expires=' + d.toUTCString(); 293 document.cookie = cookieName + '=' + cookieValue + ';' + expires + ';path=/'; 294} 295 296const getCookie = function(cookieName) { 297 let name = cookieName + '='; 298 let decodedCookie = decodeURIComponent(document.cookie); 299 let ca = decodedCookie.split(';'); 300 for (let i = 0; i < ca.length; i++) { 301 let c = ca[i]; 302 while (c.charAt(0) == ' ') { 303 c = c.substring(1); 304 } 305 if (c.indexOf(name) == 0) { 306 return c.substring(name.length, c.length); 307 } 308 } 309 return ''; 310} 311 312const browser = function() { 313 const browserItem = navigator.userAgent; 314 if (browserItem.indexOf('Firefox') > -1) { 315 return 'Mozilla Firefox'; 316 } else if (browserItem.indexOf('Opera') > -1 || browserItem.indexOf('OPR') > -1) { 317 return 'Opera'; 318 } else if (browserItem.indexOf('Trident') > -1) { 319 return 'Microsoft Internet Explorer'; 320 } else if (browserItem.indexOf('Edge') > -1) { 321 return 'Microsoft Edge'; 322 } else if (browserItem.indexOf('Chrome') > -1) { 323 return 'Google Chrome'; 324 } else if (browserItem.indexOf('Safari') > -1) { 325 return 'Apple Safari'; 326 } else { 327 return 'unknown'; 328 } 329} 330 331const isIOSDevice = function() { 332 return /iPhone|iPad|iPod/i.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); 333} 334if (isIOSDevice()) { 335 document.documentElement.classList.add('ios'); 336} 337 338const mobileDeviceRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|W
338indows Phone|tsoftmobile/i; 339const mobileQueryCache = new Map(); 340 341const getMobileQuery = function(size) { 342 const breakpoint = Math.max(0, Number(size) - 1); 343 if (!mobileQueryCache.has(breakpoint)) { 344 mobileQueryCache.set(breakpoint, window.matchMedia(`(max-width: ${breakpoint}px)`)); 345 } 346 return mobileQueryCache.get(breakpoint); 347} 348 349const isMobile = function(size = null) { 350 if (size == null) size = 1024; 351 if (!mobileDeviceRegex.test(navigator.userAgent)) return false; 352 if (typeof window.matchMedia == 'function') return getMobileQuery(size).matches; 353 return window.innerWidth < size; 354} 355 356const isMobileApp = function() { 357 return /tsoftmobile-(ios|android)/i.test(navigator.userAgent); 358} 359 360const getLink = function(param, value, link) { 361 let re = new RegExp('[\?\&]' + param + '=', 'g'); 362 let url = link || window.location.href; 363 if (re.test(url)) { 364 re = new RegExp('([\?\&]' + param + '=)(.*?)&', 'g'); 365 if (re.test(url)) { 366 url = url.replace(re, '$1' + value + '&'); 367 } else { 368 re = new RegExp('([\?\&]' + param + '=)(.*?)$', 'g'); 369 url = url.replace(re, '$1' + value); 370 } 371 } else if (/\?/g.test(url)) { 372 url += '&' + param + '=' + value; 373 } else { 374 url += '?' + param + '=' + value; 375 } 376 if (value === '') { 377 re = new RegExp('([\?\&])' + param + '=[^?&]*&?', 'g'); 378 url = url.replace(re, '$1'); 379 } 380 url = url.replaceAll(/[?&]$/g, ''); 381 if (param !== 'pg') url = url.replaceAll(/(\?|\&)pg=\d+/ig, "$1pg=1"); 382 if (param !== 'ps') url = url.replaceAll(/(\?|\&)ps=\d+/ig, "$1ps=1"); 383 return url; 384} 385 386const evalScripts = async function(content) { 387 const mainRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi; 388 let match; 389 let callbackArr = []; 390 391 while ((match = mainRegex.exec(content)) !== null) { 392 callbackArr.push(match[1]); 393 } 394 395 for (let i = 0; i < callbackArr.length; i++) { 396 try { 397 eval(callbackArr[i]); 398 } catch (ex) { 399 console.log(ex); 400 } 401 } 402} 403 404const evalScriptsAdd = function(content, id) { 405 content = content.replaceAll(/\s+/g, ' '); 406 let startRegex = "<\s*script.*?>"; 407 let finishRegex = "<\s*\/\s*script\s*>"; 408 let mainRegex = new RegExp(startRegex + ".*?" + finishRegex, "ig"); 409 let res = content.match(mainRegex) || []; 410 411 for (let i=0; i<res.length; i++) { 412 if (document.querySelector(`#${id}-${i}`)) document.querySelector(`#${id}-${i}`).remove(); 413 try{ 414 const parser = new DOMParser(); 415 const doc = parser.parseFromString(res[i], 'text/html'); 416 const src = doc.querySelector('script').getAttribute('src'); 417 418 const script = document.createElement('script'); 419 script.id = `${id}-${i}`; 420 if (src != undefined) script.src = src; 421 script.innerHTML = doc.querySelector('script').innerHTML || ''; 422 document.body.appendChild(script); 423 }catch(ex){ 424 console.log(ex); 425 } 426 } 427 if (res.length > 0) return true; 428} 429 430const scriptAdd = async function(src = null, onload = () => {}, onerror = () => {}, integrity = null) { 431 if (src == null) return; 432 433 const beforeScript = document.querySelector(`[src="${src}"]`); 434 if (beforeScript) beforeScript.remove(); 435 436 const script = document.createElement('script'); 437 script.src = src; 438 script.async = true; 439 script.defer = true; 440 if (integrity != null) { 441 script.setAttribute('integrity', integrity); 442 script.setAttribute('crossorigin', 'anonymous'); 443 } 444 script.onload = onload; 445 script.onerror = onerror; 446 document.body.appendChild(script); 447} 448 449const setLanguage = async function(value = null, callback = null) { 450 if (value == null) { 451 console.warn('setLanguage warn: value null'); 452 return; 453 }; 454 455 const linkType = document.querySelector('#link_type').value; 456 const linkTableId = document.querySelector('#link_table_id').value; 457 458 let link = `/${linkType}/${linkTableId}`; 459 try { 460 await request('GET', getEndpoint('SET_LANGUAGE', `${value}${link}`)).then(response => { 461 if (response.success) { 462 if (typeof callback === 'function') { 463 try { 464 callback(response); 465 } catch (error) {} 466 } else { 467 if (response.link == '') { 468 (PAGE_ID == 1) ? window.location.href = '/' : window.location.reload(); 469 } else { 470 window.location.href = '/' + response.link; 471 } 472 } 473 } else { 474 console.warn(`SET_LANGUAGE error response => ${response}`); 475 } 476 }); 477 } catch (error) { 478 console.warn(`SET_LANGUAGE error => ${error}`); 479 } 480} 481 482const setCurrency = async function(value = null, callback = null) { 483 if (value == null) { 484 console.warn('setCurrency warn: value null'); 485 return; 486 }; 487 488 try { 489 await request('GET', getEndpoint('SET_CURRENCY', `${value}`)).then(response => { 490 if (response == 1) { 491 if (typeof callback === 'function') { 492 try { 493 callback(response); 494 } catch (error) {} 495 } else { 496 window.location.reload(); 497 } 498 } else { 499 console.warn(`SET_CURRENCY error response => ${response}`); 500 } 501 }); 502 } catch (error) { 503 console.warn(`SET_CURRENCY error => ${error}`); 504 } 505} 506 507const setExportCountry = async function(value = null, languageValue = null, currencyValue = null) { 508 if (value == null) { 509 console.warn('setCountry warn: value null'); 510 return; 511 }; 512 513 if (languageValue == null) languageValue = value == 'TR' ? 'tr' : 'en'; 514 if (currencyValue == null) { 515 currencyValue = 'USD'; 516 if (value == 'TR' || value == 'ECN') { 517 currencyValue = 'TL'; 518 } else if (['IT','DE','AL','AT','BE','CZ','DK','FI','FR','NL','ES','SE','CH','IS','ME','LU','HU','NO','PL','PT','RO','SK','GR','BG'].indexOf(value) > -1) { 519 currencyValue = 'EUR'; 520 } else if (['GB','ZI','IE','EN'].indexOf(value) > -1) { 521 currencyValue = 'GBP'; 522 } 523 } 524 525 try { 526 const callbackfn = function() {}; 527 const response = await request('GET', getEndpoint('SET_COUNTRY', `${value}`)); 528 if (response === 1) { 529 if (languageValue) await setLanguage(languageValue, callbackfn); 530 if (currencyValue) await setCurrency(currencyValue, callbackfn); 531 532 window.location.reload(); 533 } 534 } catch (error) { 535 console.warn(`SET_COUNTRY error => ${error}`); 536 } 537} 538 539const copyText = function(text = null, msg = null) { 540 if (text == null) { 541 console.warn('copyText warn: text null'); 542 return; 543 }; 544 545 let message = msg == null ? COMMON_LANG?.copied : msg; 546 if (message) { 547 notify({ 548 text: message, 549 }); 550 } 551 navigator.clipboard.writeText(text); 552} 553 554function awaitTimeOut(ms) { 555 return new Promise(resolve => { 556 setTimeout(resolve, ms); 557 }); 558} 559 560const modalClose = function(modal = null, modalObj = null) { 561 if (modal != null) { 562 if (typeof modal == 'string') modal = document.querySelector(modal); 563 if (window.modals) modalObj ? window.modals = window.modals.filter(x => x.id != modalObj.id) : window.modals.pop(); 564 if (modal) modal.remove(); 565 } else if (window.modals?.length > 0) { 566 const modalObj = window.modals[window.modals.length - 1]; 567 if (!modalObj) { 568 console.warn('modalClose warn: modalObj undefined'); 569 return; 570 }; 571 572 const modal = document.querySelector(`#${modalObj.selector}`); 573 if (modal) { 574 modal.remove(); 575 window.modals.pop(); 576 } 577 } 578 579 if (window.modals?.length == 0) document.querySelector('body').classList.remove('overflow-hidden', 'modal-active'); 580} 581 582const modal = async function(options = null) { 583 if (options == null) { 584 console.warn('modal warn: options null'); 585 return; 586 }; 587 588 window['modals'] = window.modals || []; 589 590 const defaults = { 591 id: '', 592 class: '', /* success danger info */ 593 width: '580px', 594 height: '', 595 title: '', 596 html: '', 597 timeout: null, 598 close: true, 599 disabledClose: false, 600 iconHtml: '', 601 alert: false, 602 buttons: [ 603 /* {text: '', href: '', target: '', class: '', event: function() {}, }, */ 604 ], 605 callbacks: { 606 open: function() {}, 607 close: function() {}, 608 }, 609 } 610 611 const opt = { ...defaults, ...options }; 612 if (opt.html == '') { 613 console.warn('modal warn: opt.html undefined'); 614 return; 615 }; 616 617 document.querySelector('body').classList.add('overflow-hidden', 'modal-active'); 618 619 const modal = document.createElement('div'); 620 modal.counter = window.modals.length + 1; 621 modal.id = opt.id ? opt.id : `modal-${modal.counter}`;
622 modal.className = `fixed flex inset-0 ${!opt.alert ? 'p-4' : ''} overflow-y-auto overflow-x-hidden bg-black/50 z-[999999996] opacity-0 pointer-events-none scroll-smooth transition-opacity duration-300 ease-out delay-100 ${opt.class} modal`; 623 624 const beforeModal = document.getElementById(modal.id); 625 if (beforeModal) modalClose(`#${modal.id}`); 626 627 setTimeout(() => { 628 modal.classList.add('opacity-100', 'pointer-events-auto'); 629 }, 1); 630 631 if (opt.alert && opt.width == '580px') { 632 opt.width = '360px'; 633 } 634 635 const modalWrapper = document.createElement('div'); 636 modalWrapper.className = 'bg-white h-max m-auto max-w-full rounded-custom shadow-xl relative opacity-0 -translate-y-12 transition-transform duration-300 ease-out modal-wrapper'; 637 modalWrapper.style.width = opt.width.indexOf('px') > 0 ? opt.width : `${opt.width}px`; 638 modal.append(modalWrapper); 639 640 if (opt.alert && window.innerWidth < 768) { 641 modalWrapper.classList.add('!w-full', 'rounded-b-none', 'mb-0', 'pb-4'); 642 } 643 644 const modalContainer = document.createElement('div'); 645 modalContainer.className = `w-full px-6 py-8 overflow-x-auto modal-container`; 646 if (opt.height) modalContainer.style.height = opt.height.indexOf('px') > 0 ? opt.height : `${opt.height}px`; 647 modalWrapper.append(modalContainer); 648 649 if (!opt.disabledClose && opt.close) { 650 const modalCloseBtn = document.createElement('span'); 651 modalCloseBtn.className = 'absolute end-0 top-0 text-gray-500 z-10 size-8 flex items-center justify-center rounded-custom cursor-pointer hover:text-gray-900 duration-300 modal-close'; 652 modalCloseBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" viewBox="0 0 16 16"><path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708"/></svg>`; 653 modalWrapper.append(modalCloseBtn); 654 modalCloseBtn.addEventListener('click', removeModal); 655 } 656 657 const modalContent = document.createElement('div'); 658 modalContent.className = `w-full flex items-center gap-4 modal-content ${opt.alert ? 'flex-wrap text-center' : ''}`; 659 modalContainer.append(modalContent); 660 661 if (opt.alert) { 662 modal.classList.add('modal-alert'); 663 if (opt.iconHtml) { 664 opt.iconHtml = `<div class="size-14 flex items-center justify-center rounded-full ${opt.class.includes('success') ? 'bg-green-100 text-green-500' : 665 opt.class.includes('danger') ? 'bg-red-100 text-red-500' : 666 opt.class.includes('info') ? 'bg-blue-100 text-blue-500' : 667 'bg-light text-primary' 668 }">${opt.iconHtml}</div>`; 669 } else { 670 if (opt.class.includes('success')) { 671 opt.iconHtml = ` 672 <div class="size-14 flex items-center justify-center rounded-full bg-green-100 text-green-500"> 673 <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> 674 <circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/> 675 </svg> 676 </div>`; 677 } else if (opt.class.includes('danger')) { 678 opt.iconHtml = ` 679 <div class="size-14 flex items-center justify-center rounded-full bg-red-100 text-red-500"> 680 <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/> 681 <path d="m15 9-6 6"/><path d="m9 9 6 6"/> 682 </svg> 683 </div>`; 684 } else if (opt.class.includes('info')) { 685 opt.iconHtml = ` 686 <div class="size-14 flex items-center justify-center rounded-full bg-blue-100 text-blue-500"> 687 <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> 688 <circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="8" y2="12"/>
688<line x1="12" x2="12.01" y1="16" y2="16"/> 689 </svg> 690 </div>`; 691 } 692 } 693 } 694 695 if (opt.iconHtml) { 696 const modalIcon = document.createElement('div'); 697 if (opt.alert) { 698 modalIcon.className = 'w-full flex items-center justify-center modal-icon'; 699 modalIcon.innerHTML = opt.iconHtml; 700 } else { 701 modalIcon.className = `flex shrink-0 size-10 items-center justify-center rounded-full ${ 702 opt.class.includes('success') ? 'bg-green-500 text-green-100' : 703 opt.class.includes('danger') ? 'bg-red-500 text-red-100' : 704 opt.class.includes('info') ? 'bg-blue-500 text-blue-100' : 705 'bg-light text-primary' 706 } modal-icon`; 707 modalIcon.innerHTML = opt.iconHtml; 708 } 709 710 modalContent.append(modalIcon); 711 } 712 713 const modalHtml = document.createElement('div'); 714 modalHtml.className = 'w-full modal-html'; 715 716 if (opt.title) { 717 const modalTitle = document.createElement('div'); 718 modalTitle.className = 'text-base font-semibold leading-6 text-gray-900 mb-2 modal-title'; 719 modalTitle.innerHTML = opt.title; 720 modalHtml.append(modalTitle); 721 } 722 723 const modalText = document.createElement('div'); 724 modalText.className = 'text-sm modal-text'; 725 modalText.innerHTML = opt.html; 726 727 const iframe = modalText.querySelector('iframe'); 728 if (iframe?.src.includes('youtube')) { 729 let src = iframe.src; 730 const params = { 731 'autoplay': '1', 732 'mute': '1', 733 'controls': '1', 734 'loop': '1', 735 'showinfo': '0', 736 'rel': '0' 737 }; 738 739 Object.keys(params).forEach(key => { 740 if (!src.includes(key + '=')) { 741 const separator = src.includes('?') ? '&' : '?'; 742 src += separator + key + '=' + params[key]; 743 } 744 }); 745 746 iframe.src = src; 747 } 748 749 if (iframe?.src.includes('vimeo')) { 750 let src = iframe.src; 751 const params = { 752 'autoplay': '1', 753 'muted': '1', 754 'loop': '1', 755 'controls': '0', 756 'background': '1' 757 }; 758 759 Object.keys(params).forEach(key => { 760 if (!src.includes(key + '=')) { 761 const separator = src.includes('?') ? '&' : '?'; 762 src += separator + key + '=' + params[key]; 763 } 764 }); 765 766 iframe.src = src; 767 } 768 769 const videoElement = modalText.querySelector('video'); 770 if (videoElement) { 771 videoElement.autoplay = true; 772 videoElement.muted = true; 773 videoElement.loop = true; 774 if (!videoElement.hasAttribute('controls')) { 775 videoElement.controls = false; 776 } 777 } 778 modalHtml.append(modalText); 779 modalContent.append(modalHtml); 780 781 if (opt.buttons.length > 0) { 782 const modalButtons = document.createElement('div'); 783 modalButtons.className = `flex flex-wrap ${opt.alert ? 'justify-center' : 'justify-end'} gap-4 ${opt.alert ? 'mt-6' : 'mt-4'} modal-buttons`; 784 opt.buttons.forEach(btn => { 785 const button = document.createElement('a'); 786 button.className = btn.class || ''; 787 button.innerHTML = btn.text || ''; 788 button.href = btn.href || 'javascript:void(0)'; 789 button.target = btn.target || ''; 790 if (btn.event) { 791 button.addEventListener('click', (e) => { 792 try { 793 btn.event(e); 794 } catch (error) { 795 console.error('Button event error:', error); 796 } 797 }); 798 } 799 modalButtons.append(button); 800 }); 801 modalContainer.append(modalButtons); 802 } 803 804 document.body.append(modal); 805 const selectors = modal.querySelectorAll('[data-selector="country"], [data-selector="city"], [data-selector="town"], [data-selector="district"]'); 806 if (selectors.length > 0) { 807 modalContainer.classList.remove('overflow-x-auto'); 808 } 809 await evalScripts(modal.innerHTML); 810 await awaitTimeOut(300); 811 await components.init(); 812 813 modal.addEventListener('mousedown', e => { 814 if ((e.button === 0 || e.button === undefined) && !modalWrapper.contains(e.target)) { 815 removeModal(); 816 } 817 }); 818 819 const modalObj = { 820 id: modal.counter, 821 selector: modal.id 822 }; 823 824 window.modals.push(modalObj); 825 826 function removeModal() { 827 if (opt.disabledClose) return; 828 try { 829 modalClose(modal, modalObj); 830 opt.callbacks.close?.(); 831 } catch (error) { 832 console.error('Modal close error:', error); 833 } 834 } 835 836 if (opt.timeout) { 837 setTimeout(removeModal, opt.timeout); 838 } 839 840 try { 841 opt.callbacks.open?.(); 842 } catch (error) { 843 console.error('Modal open callback error:', error); 844 } 845 846 modalWrapper.classList.remove('opacity-0', '-translate-y-12'); 847} 848 849const notify = function(options = null) { 850 if (!options || typeof options !== 'object') { 851 console.warn('notify warn: options null'); 852 return; 853 } 854 855 const defaults = { 856 title: '', 857 text: '', 858 duration: 3500, 859 gravity: 'top', /* top || bottom */ 860 position: 'right', /* left || right */ 861 class: 'success', /* success || danger || info || notr */ 862 className: '', 863 iconHtml: '', 864 buttons: [ 865 /* {text: '', href: '', target: '', class: '', event: function() {}, }, */ 866 ], 867 callback: function(notify) {}, 868 }; 869 870 const opt = { ...defaults, ...options }; 871 const variantInput = `${opt.class || ''} ${opt.className || ''}`.trim(); 872 const isRTL = document.dir === 'rtl'; 873 874 if (!opt.text && !opt.title) { 875 console.warn('notify warn: opt.text or opt.title undefined'); 876 return; 877 } 878 879 if (!Array.isArray(window.notifies)) { 880 window.notifies = []; 881 } 882 883 const normalizeGravity = (gravity) => { 884 return String(gravity).toLowerCase() === 'bottom' ? 'bottom' : 'top'; 885 }; 886 887 const normalizePosition = (position) => { 888 return String(position).toLowerCase() === 'left' ? 'left' : 'right'; 889 }; 890 891 const normalizeVariant = (className = '') => {
892 const value = String(className).toLowerCase(); 893 if (value.includes('danger') || value.includes('error')) return 'danger'; 894 if (value.includes('warning') || value.includes('warn')) return 'warning'; 895 if (value.includes('info')) return 'info'; 896 if (value.includes('success')) return 'success'; 897 if (value.includes('notr')) return 'notr'; 898 return 'success'; 899 }; 900 901 const setHtml = (element, value) => { 902 element.innerHTML = value == null ? '' : `${value}`; 903 }; 904 905 const getDefaultIcon = (variant) => { 906 const icons = { 907 success: ` 908 <svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> 909 <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.707a1 1 0 00-1.414-1.414L9 10.172 7.707 8.879a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path> 910 </svg> 911 `, 912 danger: ` 913 <svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> 914 <path fill-rule="evenodd" d="M18 10A8 8 0 112 10a8 8 0 0116 0zM9 6a1 1 0 012 0v4a1 1 0 11-2 0V6zm1 8a1.25 1.25 0 100-2.5A1.25 1.25 0 0010 14z" clip-rule="evenodd"></path> 915 </svg> 916 `, 917 info: ` 918 <svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> 919 <path fill-rule="evenodd" d="M18 10A8 8 0 112 10a8 8 0 0116 0zM9 8a1 1 0 112 0v5a1 1 0 11-2 0V8zm1-3a1.25 1.25 0 100 2.5A1.25 1.25 0 0010 5z" clip-rule="evenodd"></path> 920 </svg> 921 `, 922 warning: ` 923 <svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> 924 <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.72-1.36 3.485 0l6.518 11.593C19.01 16.022 18.047 18 16.518 18H3.482c-1.53 0-2.492-1.978-1.742-3.308L8.257 3.1zM9 8a1 1 0 112 0v3a1 1 0 11-2 0V8zm1 7a1.25 1.25 0 100-2.5A1.25 1.25 0 0010 15z" clip-rule="evenodd"></path> 925 </svg> 926 `, 927 notr: ` 928 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> 929 <path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/> 930 </svg>`, 931 }; 932 return icons[variant] || icons.success; 933 }; 934 935 const gravity = normalizeGravity(opt.gravity); 936 const logicalPosition = normalizePosition(opt.position); 937 const visualPosition = isRTL ? (logicalPosition === 'left' ? 'right' : 'left') : logicalPosition; 938 const variant = normalizeVariant(variantInput); 939 const translateClass = visualPosition === 'left' ? '-translate-x-6' : 'translate-x-6'; 940 941 const wrapperId = `notify-${gravity}-${logicalPosition}`; 942 let wrapper = document.querySelector(`#${wrapperId}`); 943 if (!wrapper) { 944 wrapper = document.createElement('div'); 945 wrapper.id = wrapperId; 946 947 const positions = { 948 'top-right': isRTL ? 'top-4 left-4' : 'top-4 right-4', 949 'top-left': isRTL ? 'top-4 right-4' : 'top-4 left-4', 950 'bottom-right': isRTL ? 'bottom-4 left-4' : 'bottom-4 right-4', 951 'bottom-left': isRTL ? 'bottom-4 right-4' : 'bottom-4 left-4', 952 }; 953 954 const stackPosition = positions[`${gravity}-${logicalPosition}`] || (isRTL ? positions['top-left'] : positions['top-right']); 955 wrapper.className = `notify fixed ${stackPosition} z-[999999999] flex w-[92vw] max-w-[380px] flex-col gap-2 pointer-events-none`; 956 document.body.appendChild(wrapper); 957 } 958 959 const variantMap = { 960 success: { 961 item: 'border-emerald-200/90 bg-white', 962 iconWrap: 'bg-emerald-50 text-emerald-600', 963 action: 'text-emerald-600 hover:text-emerald-700', 964 progress: 'bg-emerald-400/45', 965 }, 966 danger: { 967 item: 'border-red-200/90 bg-white', 968 iconWrap: 'bg-red-50 text-red-600', 969 action: 'text-red-600 hover:text-red-700', 970 progress: 'bg-red-400/45', 971 }, 972 info: { 973 item: 'border-blue-200/90 bg-white', 974 iconWrap: 'bg-blue-50 text-blue-600', 975 action: 'text-blue-600 hover:text-blue-700', 976 progress: 'bg-blue-400/45', 977 }, 978 warning: { 979 item: 'border-amber-200/90 bg-white', 980 iconWrap: 'bg-amber-50 text-amber-600', 981 action: 'text-amber-600 hover:text-amber-700', 982 progress: 'bg-amber-400/45', 983 }, 984 notr: { 985 item: 'border-gray-200/90 bg-white', 986 iconWrap: 'bg-gray-50 text-gray-600', 987 action: 'text-gray-600 hover:text-gray-700', 988 progress: 'bg-gray-400/45', 989 }, 990 }; 991 992 const theme = variantMap[variant] || variantMap.success; 993 994 const notify = document.createElement('div'); 995 notify.id = `notify-item-${Date.now()}-${Math.random().toString(16).slice(2)}`; 996 notify.className = [ 997 'notify-item pointer-events-auto relative flex items-start gap-3 overflow-hidden rounded-xl border px-3 py-3.5 shadow-xl', 998 'opacity-0 scale-95 transform transition-all duration-300 ease-out', 999 translateClass, 1000 theme.item, 1001 ].join(' '); 1002 notify.setAttribute('role', variant === 'danger' ? 'alert' : 'status'); 1003 notify.setAttribute('aria-live', variant === 'danger' ? 'assertive' : 'polite'); 1004 1005 const icon = document.createElement('span'); 1006 icon.className = `inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full ${theme.iconWrap}`; 1007 setHtml(icon, opt.iconHtml || getDefaultIcon(variant)); 1008 1009 const content = document.createElement('div'); 1010 content.className = 'min-w-0 flex-1'; 1011 1012 const header = document.createElement('div'); 1013 header.className = 'flex items-start justify-between gap-2'; 1014 1015 const heading = document.createElement('div'); 1016 heading.className = 'min-w-0 flex-1'; 1017 1018 const hasTitle = opt.title !== false && `${opt.title || ''}`.trim() !== ''; 1019 const hasText = `${opt.text || ''}`.trim() !== ''; 1020 1021 if (hasTitle) { 1022 const title = document.createElement('p'); 1023 title.className = 'notify-title m-0 text-sm font-semibold leading-5'; 1024 setHtml(title, opt.title); 1025 heading.appendChild(title); 1026 } 1027 1028 if (hasText) { 1029 const text = document.createElement('p'); 1030 text.className = `notify-text m-0 ${hasTitle ? 'mt-0.5' : ''} text-sm leading-5 text-gray-600`; 1031 setHtml(text, opt.text); 1032 heading.appendChild(text); 1033 } 1034 1035 const closeButton = document.createElement('button'); 1036 closeButton.type = 'button'; 1037 closeButton.className = 'notify-close inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-gray-400 transition-colors duration-150 hover:bg-gray-100 hover:text-gray-700'; 1038 closeButton.setAttribute('aria-label', 'Close notification'); 1039 closeButton.innerHTML = `
1040 <svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> 1041 <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path> 1042 </svg> 1043 `; 1044 1045 header.appendChild(heading); 1046 header.appendChild(closeButton); 1047 content.appendChild(header); 1048 1049 if (Array.isArray(opt.buttons) && opt.buttons.length > 0) { 1050 const notifyButtons = document.createElement('div'); 1051 notifyButtons.className = 'notify-buttons mt-1.5 flex flex-wrap items-center gap-2'; 1052 1053 opt.buttons.forEach((btn) => { 1054 if (!btn || typeof btn !== 'object') return; 1055 1056 const button = document.createElement('a'); 1057 const fallbackClass = `notify-action inline-flex items-center text-sm font-semibold transition-colors duration-150 ${theme.action} hover:underline`; 1058 button.className = btn.class || fallbackClass; 1059 setHtml(button, btn.text || ''); 1060 button.href = btn.href || 'javascript:void(0)'; 1061 button.target = btn.target || ''; 1062 1063 if (button.target === '_blank') { 1064 button.rel = 'noopener noreferrer'; 1065 } 1066 1067 if (typeof btn.event === 'function') { 1068 button.addEventListener('click', () => { 1069 try { 1070 btn.event(); 1071 } catch (error) { 1072 console.error('Notify button event error:', error); 1073 } 1074 }); 1075 } 1076 1077 notifyButtons.appendChild(button); 1078 }); 1079 1080 content.appendChild(notifyButtons); 1081 } 1082 1083 notify.appendChild(icon); 1084 notify.appendChild(content); 1085 1086 let progress = null; 1087 let progressScale = 1; 1088 1089 const duration = Number(opt.duration) > 0 ? Number(opt.duration) : 0; 1090 if (duration > 0) { 1091 progress = document.createElement('span'); 1092 progress.className = `pointer-events-none absolute inset-x-0 bottom-0 h-0.5 origin-left transform ${theme.progress}`; 1093 progress.style.transform = 'scaleX(1)'; 1094 notify.appendChild(progress); 1095 } 1096 1097 if (gravity === 'top') { 1098 wrapper.prepend(notify); 1099 } else { 1100 wrapper.appendChild(notify); 1101 } 1102 1103 window.notifies.push({ id: notify.id }); 1104 1105 const nativeRemove = notify.remove.bind(notify); 1106 let removed = false; 1107 let hideTimeout = null; 1108 let timeoutId = null; 1109 let startedAt = 0; 1110 let remaining = duration; 1111 1112 const removeFromStore = () => { 1113 window.notifies = window.notifies.filter((item) => item.id !== notify.id); 1114 if (wrapper.childElementCount === 0) { 1115 wrapper.remove(); 1116 } 1117 }; 1118 1119 const removeNotify = (immediate = false) => { 1120 if (removed) return; 1121 removed = true; 1122 1123 if (timeoutId) { 1124 clearTimeout(timeoutId); 1125 timeoutId = null; 1126 } 1127 1128 if (hideTimeout) { 1129 clearTimeout(hideTimeout); 1130 hideTimeout = null; 1131 } 1132 1133 const finalize = () => { 1134 nativeRemove(); 1135 removeFromStore(); 1136 }; 1137 1138 if (immediate) { 1139 finalize(); 1140 return; 1141 } 1142 1143 notify.classList.remove('translate-x-0', 'opacity-100', 'scale-100'); 1144 notify.classList.add(translateClass, 'opacity-0', 'scale-95'); 1145 hideTimeout = setTimeout(finalize, 260); 1146 }; 1147 1148 const getScaleX = (element) => { 1149 const matrix = window.getComputedStyle(element).transform; 1150 if (!matrix || matrix === 'none') return 1; 1151 const value = matrix.match(/matrix\(([^)]+)\)/); 1152 if (!value) return progressScale; 1153 const first = parseFloat(value[1].split(',')[0]); 1154 return Number.isFinite(first) ? Math.max(0, Math.min(1, first)) : progressScale; 1155 }; 1156 1157 const runProgress = () => { 1158 if (!progress || remaining <= 0) return; 1159 progress.style.transition = 'none'; 1160 progress.style.transform = `scaleX(${progressScale})`; 1161 requestAnimationFrame(() => { 1162 progress.style.transition = `transform ${remaining}ms linear`; 1163 progress.style.transform = 'scaleX(0)'; 1164 }); 1165 }; 1166 1167 const startTimer = () => { 1168 if (duration <= 0 || removed || remaining <= 0) { 1169 if (duration > 0 && remaining <= 0) removeNotify(); 1170 return; 1171 } 1172 startedAt = Date.now(); 1173 timeoutId = setTimeout(removeNotify, remaining); 1174 runProgress(); 1175 }; 1176 1177 const pauseTimer = () => { 1178 if (!timeoutId || removed) return; 1179 clearTimeout(timeoutId); 1180 timeoutId = null; 1181 remaining -= Date.now() - startedAt; 1182 if (remaining < 0) remaining = 0; 1183 if (progress) { 1184 progressScale = getScaleX(progress);
1185 progress.style.transition = 'none'; 1186 progress.style.transform = `scaleX(${progressScale})`; 1187 } 1188 }; 1189 1190 closeButton.addEventListener('click', (event) => { 1191 event.preventDefault(); 1192 removeNotify(); 1193 }); 1194 1195 notify.dismiss = removeNotify; 1196 notify.remove = function() { 1197 removeNotify(true); 1198 }; 1199 1200 requestAnimationFrame(() => { 1201 requestAnimationFrame(() => { 1202 notify.classList.remove(translateClass, 'opacity-0', 'scale-95'); 1203 notify.classList.add('translate-x-0', 'opacity-100', 'scale-100'); 1204 }); 1205 }); 1206 1207 if (duration > 0) { 1208 notify.addEventListener('mouseenter', pauseTimer); 1209 notify.addEventListener('mouseleave', startTimer); 1210 startTimer(); 1211 } 1212 1213 if (typeof opt.callback === 'function') { 1214 setTimeout(() => { 1215 try { 1216 opt.callback(notify); 1217 } catch (error) { 1218 console.error('Notify callback error:', error); 1219 } 1220 }, 0); 1221 } 1222}; 1223 1224const drawer = async function(options = null) { 1225 if (options == null) { 1226 console.warn('modal warn: options null'); 1227 return; 1228 }; 1229 1230 window['drawers'] = window.drawers || []; 1231 1232 const defaults = { 1233 id: '', 1234 html: '', 1235 position: 'right', /* left || right */ 1236 close: false, 1237 title: '' 1238 } 1239 1240 const opt = { ...defaults, ...options }; 1241 if (opt.html == '') { 1242 console.warn('modal warn: opt.html undefined'); 1243 return; 1244 }; 1245 1246 const drawer = document.createElement('div'); 1247 drawer.counter = window.drawers.length + 1; 1248 drawer.id = opt.id ? opt.id : `drawer-${drawer.counter}`; 1249 drawer.className = 'drawer'; 1250 drawer.dataset.position = opt.position; 1251 drawer.createDrawer = 1; 1252 1253 const drawerWrapper = document.createElement('div'); 1254 drawerWrapper.className = 'drawer-wrapper'; 1255 drawer.append(drawerWrapper); 1256 1257 const drawerContainer = document.createElement('div'); 1258 drawerContainer.className = 'drawer-container'; 1259 drawerWrapper.append(drawerContainer); 1260 1261 drawerContainer.innerHTML = opt.html; 1262 if (opt.title || opt.close) { 1263 const drawerHeader = document.createElement('div'); 1264 drawerHeader.className = 'w-full flex justify-between font-semibold border-b p-4 drawer-header'; 1265 1266 if (opt.title) { 1267 const drawerTitle = document.createElement('span'); 1268 drawerTitle.className = 'text-lg drawer-title'; 1269 drawerTitle.innerHTML = opt.title; 1270 drawerHeader.append(drawerTitle); 1271 } 1272 1273 if (opt.close) { 1274 const drawerCloseBtn = document.createElement('span'); 1275 drawerCloseBtn.className = 'flex items-center justify-center drawer-close'; 1276 drawerCloseBtn.setAttribute('data-drawer-close', ''); 1277 drawerCloseBtn.innerHTML = ` 1278 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg> 1279 `; 1280 drawerHeader.append(drawerCloseBtn); 1281 } 1282 1283 drawerContainer.prepend(drawerHeader); 1284 } 1285 1286 document.body.append(drawer); 1287 1288 const removeDrawer = (element) => { 1289 document.body.classList.remove('drawer-active'); 1290 element.remove(); 1291 } 1292 1293 drawer.addEventListener('click', e => { 1294 if (e.target == drawer && drawer.contains(e.target)) { 1295 removeDrawer(drawer); 1296 } 1297 }); 1298 1299 const drawerCloseBtns = drawer.querySelectorAll('[data-drawer-close]'); 1300 if (drawerCloseBtns.length > 0) { 1301 Array.from(drawerCloseBtns).forEach(item => { 1302 item.addEventListener('click', e => { 1303 e.preventDefault(); 1304 removeDrawer(drawer); 1305 }); 1306 }); 1307 } 1308 1309 await evalScripts(drawer.innerHTML); 1310 setTimeout(() => { 1311 document.querySelector('body').classList.add('drawer-active'); 1312 drawer.classList.add('active'); 1313 }, 1); 1314 1315 await awaitTimeOut(300); 1316 await components.init(); 1317} 1318 1319const drawerClose = function() { 1320 const drawers = document.querySelectorAll('.drawer.active');
1321 Array.from(drawers).forEach(drawer => { 1322 drawer.click(); 1323 }); 1324} 1325 1326const charClassPattern = function(chars) { 1327 return new RegExp(`[${chars.replace(/[\\\]^-]/g, '\\$&')}]`, 'g'); 1328} 1329 1330const mask = function(element = null, mask = null, regex = null, max = null) { 1331 if (element == null || element.mask == 1 || (element.nodeName != 'INPUT' && element.nodeName != 'TEXTAREA')) return; 1332 1333 if (mask == null) { 1334 mask = element.dataset.mask || element.type || null; 1335 if (regex != null) mask = 'custom'; 1336 } 1337 1338 if (max == null) max = element.dataset.max || null; 1339 1340 function run(event) { 1341 let re; 1342 switch (mask) { 1343 case 'string': 1344 re = /[^\p{L}\s\-]/gu; 1345 break; 1346 case 'text': 1347 re = /[^\p{L}\s\-]/gu; 1348 break; 1349 case 'number': 1350 re = /[^0-9]/g; 1351 break; 1352 case 'price': 1353 re = /[^0-9.,]/g; 1354 break; 1355 case 'password': 1356 re = /^\s+|\s+$/; 1357 break; 1358 case 'email': 1359 re = /^\s+|(?:^|\s|,|;|!|:|-|\.|\?)[ÄçÅüöıÄÃÅÃÃİ?!]/g; 1360 break; 1361 case 'block': 1362 if (regex) re = charClassPattern(regex); 1363 break; 1364 case 'custom': 1365 if (regex) { 1366 try { 1367 re = new RegExp(regex, 'g'); 1368 } catch (e) { 1369 re = charClassPattern(regex); 1370 } 1371 } 1372 break; 1373 } 1374 1375 if (event.type == 'paste') { 1376 setTimeout(() => { 1377 const input = event?.target; 1378 const value = input?.value || null; 1379 if (!input || value == null) return; 1380 1381 let sanitizedValue = String(value); 1382 if (re) sanitizedValue = sanitizedValue.replace(re, ''); 1383 1384 if (max != '' && max != null) { 1385 const maxLength = Number.parseInt(max, 10); 1386 if (!Number.isNaN(maxLength) && sanitizedValue.length > maxLength) { 1387 sanitizedValue = sanitizedValue.slice(0, maxLength); 1388 } 1389 } 1390 1391 if (sanitizedValue !== input.value) input.value = sanitizedValue; 1392 }, 10); 1393 } else if (event.type == 'keypress') { 1394 let value = event.target.value; 1395 if (event.target.dataset.toggle == "flag") value = event.target.value.replaceAll(/\D+/g, ''); 1396 const hasSelection = event.target.selectionStart !== event.target.selectionEnd; 1397 if (max != '' && max != null && value.length >= max && !hasSelection) event.preventDefault(); 1398 if (re?.test(String(event.key))) event.preventDefault(); 1399 } 1400 } 1401 1402 element.addEventListener('paste', event => { 1403 run(event); 1404 }); 1405 1406 element.addEventListener('keypress', event => { 1407 run(event); 1408 }); 1409 1410 element.mask = 1; 1411} 1412 1413const currencyDecimals = function(currency) { 1414 const places = function(value, fallback) { 1415 return value === null || value === '' || !Number.isFinite(Number(value)) ? fallback : Math.abs(Number(value)); 1416 }; 1417 const globalMin = places(typeof DECIMAL_LENGTH !== 'undefined' ? DECIMAL_LENGTH : null, 2); 1418 const globalMax = places(typeof DECIMAL_MAX !== 'undefined' ? DECIMAL_MAX : null, globalMin); 1419 1420 const code = typeof currency === 'string' ? currency.trim().toUpperCase() : ''; 1421 const range = code !== '' && typeof CURRENCY_DECIMALS !== 'undefined' && CURRENCY_DECIMALS ? CURRENCY_DECIMALS[code] : null; 1422 1423 if (!range) { 1424 return { min: globalMin, max: Math.max(globalMin, globalMax) }; 1425 } 1426 1427 const min = places(range.min, globalMin); 1428 return { min: min, max: Math.max(min, places(range.max, min)) }; 1429} 1430 1431const format = function(p, currency) { 1432 const decimals = currencyDecimals(currency); 1433 const minPrecision = decimals.min; 1434 const maxPrecision = decimals.max; 1435 let n = !Number.isFinite(Number(+p)) ? 0 : +p, 1436 sep = (typeof SEP_THO === 'undefined') ? '.' : SEP_THO, 1437 dec = (typeof SEP_DEC === 'undefined') ? ',' : SEP_DEC, 1438 toFixedFix = function(n, prec) {
1439 const k = Math.pow(10, prec); 1440 let scaled = n * k; 1441 const absScaled = Math.abs(scaled); 1442 const nearestHalf = Math.floor(absScaled) + 0.5; 1443 if (absScaled < 1e14 && Math.abs(absScaled - nearestHalf) < 1e-6) { 1444 scaled = Number(scaled.toPrecision(15)); 1445 } 1446 const rounded = scaled < 0 ? -Math.round(-scaled) : Math.round(scaled); 1447 let formatted = '' + rounded / k; 1448 if (formatted.indexOf('e') !== -1) { 1449 formatted = (rounded / k).toFixed(prec); 1450 } 1451 return formatted; 1452 }; 1453 let s = toFixedFix(n, maxPrecision).split('.'); 1454 if (s[0].length > 3) { 1455 s[0] = s[0].replaceAll(/\B(?=(?:\d{3})+(?!\d))/g, sep); 1456 } 1457 s[1] = s[1] || ''; 1458 if (maxPrecision > minPrecision) { 1459 s[1] = s[1].replace(/0+$/, ''); 1460 } 1461 while (s[1].length < minPrecision) { 1462 s[1] += '0'; 1463 } 1464 return s[1] === '' ? s[0] : s[0] + dec + s[1]; 1465} 1466 1467const vat = function(p, vat, currency) { 1468 if (p > 0 && p <= 0.0000001) { 1469 return 0; 1470 } 1471 let priceParam = Number.isNaN(Number(p)) ? 0.0 : Number.parseFloat(p); 1472 let vatParam = Number.isNaN(Number(vat)) > 0 ? 0 : Number.parseInt(vat); 1473 priceParam = priceParam * (100 + vatParam) / 100; 1474 return format(priceParam, currency); 1475} 1476 1477const priceToFloat = function(p) { 1478 if (!p) return false; 1479 p = String(p); 1480 let price = ''; 1481 if (SEP_THO === ',') { 1482 price = Number.parseFloat(p.replaceAll(',', '')); 1483 } else if (SEP_DEC == ",") { 1484 price = Number.parseFloat(p.replaceAll('.', '').replace(',', '.')); 1485 } else { 1486 price = Number.parseFloat(p); 1487 } 1488 return price; 1489} 1490 1491const triggerEvent = function(element = null, event = null) { 1492 if (element == null || event == null) { 1493 console.warn('triggerEvent warn: element or event null'); 1494 return; 1495 }; 1496 1497 let newEvent; 1498 if (event == 'mouseenter') { 1499 newEvent = new MouseEvent(event, { 1500 bubbles: true, 1501 cancelable: true, 1502 view: window 1503 }); 1504 } else { 1505 newEvent = new Event(event); 1506 } 1507 1508 element.dispatchEvent(newEvent); 1509} 1510 1511const outsideClick = function(eventElement, element, onCallback = () => {}) { 1512 if (eventElement?.outsideEvent == 1 || element?.outsideEvent == 1) return; 1513 1514 const event = function(e) { 1515 const target = e.target; 1516 if (!eventElement?.contains(target) && !element?.contains(target)) onCallback(); 1517 } 1518 1519 document.addEventListener('click', event); 1520 1521 if (eventElement) eventElement.outsideEvent = 1; 1522 if (element) element.outsideEvent = 1; 1523}; 1524 1525const timeConverter = function(UNIX_timestamp = 0, format = 'd.m.y') { 1526 let time = Number.parseInt(UNIX_timestamp); 1527 if(Number.isNaN(time) || time < 1000000000) { 1528 time = 1000000000; 1529 } else if (time < 9000000000) { 1530 time = time * 1000; 1531 } 1532 1533 const pad = s => (s < 10) ? '0' + s : s; 1534 const date = new Date(time); 1535 1536 format = format.replaceAll(/y+/ig, date.getFullYear()); 1537 format = format.replaceAll(/d+/ig, pad(date.getDate())); 1538 format = format.replaceAll(/m+/ig, pad(date.getMonth() + 1)); 1539 format = format.replaceAll(/h+/ig, pad(date.getHours())); 1540 format = format.replaceAll(/i+/ig, pad(date.getMinutes())); 1541 format = format.replaceAll(/s+/ig, pad(date.getSeconds())); 1542 1543 return format; 1544} 1545 1546const taxLoader = async function(options = null) { 1547 let opt = { 1548 selector: '', 1549 limit: 15, 1550 start: 0, 1551 } 1552 1553 if (options != null) opt = { ...opt, ...options }; 1554 1555 const element = typeof opt.selector == 'object' ? opt.selector : document.querySelector(opt.selector); 1556 if (!element) { 1557 console.warn('taxLoader warn: element undefined'); 1558 return; 1559 }; 1560 1561 let taxHtml, taxUl, taxSearchWrapper, taxSearch; 1562 1563 const loadTax = (taxData, taxUl, limit) => { 1564 for (let i=opt.start; i<limit; i++) { 1565 const taxItem = document.createElement('li'); 1566 taxItem.id = `tax-item-${taxData[i]}`; 1567 taxItem.className = 'selection-li'; 1568 taxItem.dataset.value = taxData[i]; 1569 taxItem.innerText = taxData[i]; 1570 1571 taxItem.addEventListener('click', () => { 1572 element.value = taxItem.dataset.value; 1573 taxHtml.classList.remove('show'); 1574 taxHtml.style.display = 'none'; 1575 }); 1576 taxUl.append(taxItem); 1577 } 1578 opt.start = limit; 1579 }; 1580 1581 try { 1582 await request('GET', getEndpoint('TAX_OFFICE_LIST')).then(response => { 1583 const data = response; 1584 if ((data !== null || typeof data !== 'undefined')) { 1585 const taxData = data.toString().split("\n"), 1586 collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); 1587 taxData.sort(collator.compare); 1588 1589 element.setAttribute('readonly', 'readonly'); 1590 element.classList.add('tax-form'); 1591 element.parentNode.classList.add('selection-dropdown', 'tax-office-dropdown'); 1592 1593 taxHtml = document.createElement('div'); 1594 taxHtml.className = 'selection-list'; 1595 taxHtml.style.display = 'none'; 1596 1597 taxUl = document.createElement('ul'); 1598 taxUl.className = 'selection-ul'; 1599
1600 taxSearchWrapper = document.createElement('div'); 1601 taxSearchWrapper.className = 'selection-search'; 1602 1603 taxSearch = document.createElement('input'); 1604 taxSearch.type = 'search'; 1605 taxSearch.className = 'form-control'; 1606 taxSearch.placeholder = COMMON_LANG?.search; 1607 1608 taxSearch.addEventListener('keyup', event => { 1609 const value = event.target.value, 1610 searchString = String(value).toLocaleUpperCase(getLocale()), 1611 items = taxUl.querySelectorAll('li'); 1612 1613 if (searchString != '' && searchString.length >= 2) { 1614 opt.limit = taxData.length; 1615 loadTax(taxData, taxUl, opt.limit); 1616 Array.from(items).forEach(li => { 1617 if(String(li.dataset.value).toLocaleUpperCase(getLocale()).indexOf(searchString) > -1) { 1618 li.classList.remove('!hidden'); 1619 } else { 1620 li.classList.add('!hidden'); 1621 } 1622 }); 1623 } else { 1624 Array.from(items).forEach(li => { 1625 li.classList.remove('!hidden'); 1626 }); 1627 } 1628 }); 1629 1630 let count = 1; 1631 taxUl.addEventListener('scroll', () => { 1632 if (taxUl.scrollTop + taxUl.clientHeight >= taxUl.scrollHeight - 1) { 1633 count ++; 1634 if (count * opt.limit >= taxData.length) { 1635 opt.limit = taxData.length; 1636 loadTax(taxData, taxUl, opt.limit); 1637 return; 1638 } else { 1639 loadTax(taxData, taxUl, (count * opt.limit)); 1640 } 1641 } 1642 }); 1643 loadTax(taxData, taxUl, opt.limit); 1644 1645 taxSearchWrapper.append(taxSearch); 1646 taxHtml.append(taxSearchWrapper); 1647 taxHtml.append(taxUl); 1648 element.parentNode.insertBefore(taxHtml, element); 1649 1650 element.addEventListener('focus', () => { 1651 taxHtml.classList.add('show'); 1652 taxHtml.style.display = 'block'; 1653 taxSearch.value = ''; 1654 triggerEvent(taxSearch, 'keyup'); 1655 if (!element.value) taxSearch.focus(); 1656 }); 1657 } 1658 }); 1659 } catch (error) { 1660 console.warn(`TAX_OFFICE_LIST error => ${error}`); 1661 } 1662 1663 outsideClick(element, taxHtml, function() { 1664 taxHtml.classList.remove('show'); 1665 taxHtml.style.display = 'none'; 1666 }); 1667} 1668 1669const stringToObject = function(inputString) { 1670 let keyValuePairs = inputString.split('&'); 1671 let result = {}; 1672 keyValuePairs.forEach(function(keyValue) { 1673 let pair = keyValue.split('='); 1674 result[pair[0]] = pair[1]; 1675 }); 1676 return result; 1677} 1678 1679const snippetUri = function(snippet = null, params = {}) { 1680 if (snippet == null) { 1681 console.warn('snippetUri warn: snippet undefined'); 1682 return; 1683 } 1684 1685 let arrParams = []; 1686 for(let key in params){ 1687 arrParams.push(`${key}=${params[key]}`); 1688 } 1689 1690 let urlParams = ''; 1691 if (arrParams.length) urlParams = `?${arrParams.join('&')}`; 1692 let snippetUrl = getEndpoint('SNIPPET', `${snippet}${urlParams}`); 1693 1694 return snippetUrl; 1695} 1696 1697const loadSnippet = async function(options){ 1698 if(!options.snippet) { 1699 console.warn('loadSnippet warn: options.snippet undefined'); 1700 return; 1701 }; 1702 1703 const defOptions = { 1704 snippet: options.snippet || "", 1705 params: options.params || {}, 1706 success: options.success || function(){} 1707 } 1708 1709 try { 1710 await request('GET', snippetUri(defOptions.snippet, defOptions.params)).then(async response => { 1711 await defOptions.success(response); 1712 }) 1713 } catch (error) { 1714 console.warn(`SNIPPET(loadSnippet) ${defOptions.snippet} ${JSON.stringify(defOptions.params)} error => ${error}`); 1715 } 1716} 1717 1718const getOffsetTop = function(element) { 1719 let offsetTop = 0; 1720 while (element) { 1721 offsetTop += element.offsetTop; 1722 element = element.offsetParent; 1723 } 1724 1725 return offsetTop; 1726} 1727 1728const checkValidity = function(form = null) { 1729 const formElements = form?.elements || null; 1730 if (formElements == null) return; 1731 1732 form.errorElements = []; 1733 let status = true; 1734 1735 const validateMessage = function(el, message) { 1736 const formItem = el.closest('.form-item'); 1737 if (!formItem) return; 1738 1739 let elError = formItem.querySelector('.form-error'); 1740 1741 if (!elError) { 1742 formItem.classList.add('error'); 1743 elError = document.createElement('div'); 1744 elError.className = 'form-error'; 1745 formItem.append(elError); 1746 } 1747 if (elError) { 1748 elError.innerHTML = message; 1749 form.errorElements.push(el); 1750 } 1751 }; 1752 1753 const validate = function(el) { 1754 let message = ''; 1755 const elTrim = el.value.trim().replaceAll(/\s/g, ""); 1756 1757 if (elTrim == '') { 1758 message = COMMON_LANG?.form_required; 1759 } else if (el.type == 'email' && validateEmail(el)) { 1760 message = COMMON_LANG?.not_email_format; 1761 } else if (el.type == 'tel' && validatePhone(el)) { 1762 message = COMMON_LANG?.not_phone_format; 1763 } else if ((el.type == 'checkbox' || el.type == 'radio') && validateCheckbox(el)) { 1764 message = COMMON_LANG?.form_required; 1765 } 1766 1767 if (message) { 1768 status = false;
1769 validateMessage(el, message); 1770 } else { 1771 validateRemove(el); 1772 } 1773 }; 1774 1775 const validateEmail = function(email) { 1776 const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 1777 if (!re.test(String(email.value).toLowerCase())) return true; 1778 }; 1779 1780 const validatePhone = function(tel) { 1781 const re = /^[0-9]{9,15}$/; 1782 const value = tel.value.replaceAll(/\D+/g, ''); 1783 if (!re.test(String(value))) return true; 1784 }; 1785 1786 const validateCheckbox = function(input) { 1787 if (input.type == 'radio') { 1788 let status = true; 1789 1790 const name = input.name; 1791 if (name) { 1792 const radios = input.form.querySelectorAll(`input[type="radio"][name="${name}"]`); 1793 Array.from(radios).forEach(radio => { 1794 if (radio.checked) status = false; 1795 1796 }); 1797 } else if (input.checked) { 1798 status = false; 1799 } 1800 return status; 1801 } else if (input.type == 'checkbox' && !input.checked) { 1802 return true; 1803 } 1804 }; 1805 1806 const validateRemove = function(el) { 1807 const formItem = el.closest('.form-item'); 1808 if (!formItem) return; 1809 1810 formItem.classList.remove('error'); 1811 1812 let elError = formItem.querySelector('.form-error'); 1813 if (elError) { 1814 elError.remove(); 1815 form.errorElements = form.errorElements.filter(x => x != el); 1816 } 1817 } 1818 1819 const validateHandle = function(el) { 1820 const element = el.target; 1821 element.validate = 1; 1822 if (element.classList.contains('required')) { 1823 if (element.type == 'radio') { 1824 const name = element.name; 1825 if (name) { 1826 const radios = element.form.querySelectorAll(`input[type="radio"][name="${name}"]`); 1827 Array.from(radios).forEach(radio => { 1828 validate(radio); 1829 1830 }); 1831 } else { 1832 validate(element); 1833 } 1834 } else { 1835 validate(element); 1836 } 1837 } 1838 return; 1839 } 1840 1841 Array.from(formElements).forEach(el => { 1842 if (el.classList.contains('required')) validate(el); 1843 if (el.validate != 1) el.addEventListener('change', validateHandle); 1844 }); 1845 1846 if (form.errorElements.length) { 1847 const errorElement = form.errorElements[0]; 1848 const formItem = errorElement.closest('.form-item'); 1849 if (!formItem) return; 1850 1851 const offsetTop = getOffsetTop(formItem); 1852 if (offsetTop == 0) return; 1853 1854 let el = formItem; 1855 while (el && el !== document.body) { 1856 if (el.classList && Array.from(el.classList).some(c => c.includes('sticky')) && !isMobile()) return; 1857 el = el.parentElement; 1858 } 1859 1860 const modal = formItem.closest('.modal'); 1861 if (modal) { 1862 modal.scrollTo(0, offsetTop - 50); 1863 } else { 1864 const header = document.querySelector('.folder-header'); 1865 let scrollHeight = 180; 1866 if (header) scrollHeight = header.offsetHeight || 0; 1867 window.scrollTo(0, offsetTop - scrollHeight); 1868 } 1869 } 1870 1871 return status; 1872} 1873 1874const popoverAlert = function(options = null) { 1875 if (options == null) { 1876 console.warn('popoverAlert warn: options null'); 1877 return; 1878 }; 1879 1880 const defaults = { 1881 selector: '', 1882 message: '', 1883 scroll: true, 1884 } 1885 1886 const opt = { ...defaults, ...options }; 1887 1888 const element = typeof opt.selector == 'object' ? opt.selector : document.querySelector(opt.selector); 1889 if (!element) { 1890 console.warn('popoverAlert warn: element undefined'); 1891 return; 1892 }; 1893 1894 let message = null; 1895 const formItem = element.closest('.form-item'); 1896 if (formItem) { 1897 formItem.classList.add('error'); 1898 message = formItem.querySelector('.form-error'); 1899 if (!message) { 1900 message = document.createElement('div'); 1901 message.className = 'form-error'; 1902 formItem.append(message); 1903 } 1904 } else { 1905 message = element.nextElementSibling.contains('.form-error') ? element.nextElementSibling : null; 1906 if (!message) { 1907 message = document.createElement('div'); 1908 message.className = 'form-error'; 1909 element.parentNode.insertBefore(message, element.nextSibling); 1910 } 1911 } 1912 1913 message.innerHTML = opt.message; 1914 if (opt.scroll) { 1915 const offsetTop = getOffsetTop(formItem); 1916 if (offsetTop == 0) return; 1917 1918 let el = formItem; 1919 while (el && el !== document.body) { 1920 if (el.classList && Array.from(el.classList).some(c => c.includes('sticky')) && !isMobile()) return; 1921 el = el.parentElement; 1922 } 1923 1924 const modal = formItem.closest('.modal'); 1925 if (modal) { 1926 modal.scrollTo(0, offsetTop - 50); 1927 } else { 1928 const header = document.querySelector('.folder-header'); 1929 let scrollHeight = 180; 1930 if (header) scrollHeight = header.offsetHeight || 0; 1931 window.scrollTo(0, offsetTop - scrollHeight - 50); 1932 } 1933 } 1934 1935 const inputHandle = function() { 1936 popoverAlertHide(formItem); 1937 formItem.classList.remove('error'); 1938 element.removeEventListener('change', inputHandle); 1939 }; 1940 1941 element.addEventListener('change', inputHandle); 1942} 1943 1944const popoverAlertHide = function(parent = null) { 1945 if (parent == null) parent = document.body; 1946 1947 const errorItem = parent.closest('.error'); 1948 if (errorItem) { 1949 errorItem.classList.remove('error'); 1950 const formErrorItem = errorItem.querySelector('.form-error'); 1951 if (formErrorItem) formErrorItem.remove(); 1952 return; 1953 } 1954 1955 const allPopoverAlert = parent.querySelectorAll('.form-error'); 1956 Array.from(allPopoverAlert).forEach(item => { 1957 item.remove(); 1958 1959 }); 1960 1961 const allPopoverAlertClass = parent.querySelectorAll('.error');
1962 Array.from(allPopoverAlertClass).forEach(item => { 1963 item.classList.remove('error'); 1964 1965 }); 1966} 1967 1968const passwordToggle = function(event = null, input = null) { 1969 if (event == null || input == null) { 1970 console.warn('passwordToggle warn: event or input null'); 1971 return; 1972 }; 1973 1974 const eye = ` 1975 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> 1976 <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8M1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/> 1977 <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0"/> 1978 </svg> 1979 `; 1980 const eyeSlash = ` 1981 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> 1982 <path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486z"/> 1983 <path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829"/> 1984 <path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12-.708.708"/> 1985 </svg> 1986 `; 1987 1988 try { 1989 event.classList.toggle('active'); 1990 event.classList.toggle('text-primary'); 1991 event.innerHTML = event.classList.contains('active') ? eye : eyeSlash; 1992 input.type = event.classList.contains('active') ? 'text' : 'password'; 1993 } catch (error) {} 1994} 1995 1996const messageCount = async function(){ 1997 const messages = document.querySelectorAll('[data-toggle="message-count"]'); 1998 if (messages.length > 0 && MEMBER_INFO.ID > 0) { 1999 let count = 0; 2000 try { 2001 await request('GET', getEndpoint('GET_MESSAGE')).then(response => { 2002 count = response; 2003 return; 2004 }) 2005 } catch (error) { 2006 console.warn(`GET_MESSAGE error => ${error}`); 2007 } 2008 2009 Array.from(messages).forEach(message => { 2010 if (message.componentMessageCount == 1) return; 2011 2012 message.innerText = count; 2013 if (Number.parseFloat(count) == 0) message.style.display = 'none'; 2014 2015 message.componentMessageCount = 1; 2016 2017 }); 2018 } 2019} 2020 2021const formLoader = async function(options) { 2022 const opt = { 2023 selector: 'form:last', 2024 url: '', 2025 data: null, 2026 callback: async function() {} 2027 }; 2028 2029 for (let i in options) { 2030 opt[i] = options[i]; 2031 } 2032 2033 const fillFormFields = async (result) => { 2034 const form = document.querySelector(opt.selector); 2035 for (const key in result) { 2036 const value = result[key]; 2037 2038 if (value == null) continue; 2039 if (form == null) return; 2040 2041 const input = form.querySelector(`[name="${key}"]`); 2042 2043 if(input != null) { 2044 if (input.type === 'radio' || input.type === 'checkbox') { 2045 const inputsRadio = form.querySelectorAll(`[name="${key}"]`); 2046 Array.from(inputsRadio).forEach(item => { 2047 (value == item.value || Number.parseInt(value) === 1) ? item.checked = value : ''; 2048 2049 }); 2050 } else if (input.type == 'tel' && value.length < 4) { 2051 input.value = ''; 2052 } else if(input.value === '' || typeof input.value === 'undefined' || input.value === '-1') { 2053 input.value = value; 2054 } 2055 } 2056 } 2057 2058 await opt.callback(result); 2059 return; 2060 }; 2061 2062 if (String(opt.url).trim() == '' && opt.data !== null) { 2063 await fillFormFields(opt.data); 2064 return; 2065 } 2066 2067 if (String(opt.url).trim() == '') return; 2068 2069 try { 2070 await request('GET', opt.url).then(async response => { 2071 const result = typeof response == 'object' && response != '' ? response : response; 2072 await fillFormFields(result); 2073 }) 2074 } catch (error) { 2075 console.warn(`FormLoader ${opt.url} error => ${error}`); 2076 } 2077} 2078 2079const mapGeocoder = { 2080 map: null, 2081 marker: null, 2082 circle: null, 2083 geocoder: null, 2084 loader: null, 2085 updateRequestId: 0,
2086 isUserLocked: false, 2087 allowManualPinMove: false, 2088 circleRadius: 320, 2089 apiKey: SETTINGS.GOOGLE_MAPS_API_KEY || '', 2090 coordinates: { 2091 x: '', 2092 y: '', 2093 }, 2094 hideMapElement: function() { 2095 const mapElement = document.querySelector('[data-toggle="map-geocoder"]'); 2096 if (mapElement) { 2097 mapElement.style.display = 'none'; 2098 mapElement.setAttribute('hidden', 'hidden'); 2099 } 2100 }, 2101 getInitialLocation: function(mapElement = null) { 2102 const parseNumber = function(value) { 2103 const parsed = Number.parseFloat(value); 2104 return Number.isFinite(parsed) ? parsed : null; 2105 }; 2106 2107 const mapX = parseNumber(mapElement?.dataset?.coordinateX); 2108 const mapY = parseNumber(mapElement?.dataset?.coordinateY); 2109 if (mapX !== null && mapY !== null) { 2110 return { lat: mapY, lng: mapX }; 2111 } 2112 2113 const form = document.querySelector('[data-toggle="address-popup-form"]'); 2114 const inputX = parseNumber(form?.querySelector('[name="coordinate_x"]')?.value); 2115 const inputY = parseNumber(form?.querySelector('[name="coordinate_y"]')?.value); 2116 if (inputX !== null && inputY !== null) { 2117 return { lat: inputY, lng: inputX }; 2118 } 2119 2120 return null; 2121 }, 2122 setCoordinates: function(location = null, source = 'system') { 2123 if (!location) return; 2124 2125 const lat = typeof location.lat === 'function' ? location.lat() : location.lat; 2126 const lng = typeof location.lng === 'function' ? location.lng() : location.lng; 2127 if (typeof lat !== 'number' || typeof lng !== 'number') return; 2128 2129 const x = Number(lng.toFixed(6)); 2130 const y = Number(lat.toFixed(6)); 2131 2132 mapGeocoder.coordinates.x = x; 2133 mapGeocoder.coordinates.y = y; 2134 if (source === 'map') mapGeocoder.isUserLocked = true; 2135 2136 const mapElement = document.querySelector('[data-toggle="map-geocoder"]'); 2137 if (mapElement) { 2138 mapElement.dataset.coordinateX = String(x); 2139 mapElement.dataset.coordinateY = String(y); 2140 } 2141 2142 window.dispatchEvent(new CustomEvent('mapGeocoder:change', { 2143 detail: { 2144 x, 2145 y, 2146 lat: y, 2147 lng: x, 2148 source: source, 2149 locked: mapGeocoder.isUserLocked, 2150 } 2151 })); 2152 }, 2153 setMarkerPosition: function(location = null) { 2154 if (!mapGeocoder.marker || !location) return; 2155 if (typeof mapGeocoder.marker.setPosition === "function") { 2156 mapGeocoder.marker.setPosition(location); 2157 } else { 2158 mapGeocoder.marker.position = location; 2159 } 2160 mapGeocoder.setCircleCenter(location); 2161 }, 2162 setCircleCenter: function(location = null) { 2163 if (!mapGeocoder.circle || !location) return; 2164 if (typeof mapGeocoder.circle.setCenter === 'function') { 2165 mapGeocoder.circle.setCenter(location); 2166 } 2167 }, 2168 updateMap: function(value = '', options = {}) { 2169 if (!value) return; 2170 const source = options?.source || 'auto'; 2171 const force = !!options?.force; 2172 const requestId = ++mapGeocoder.updateRequestId; 2173 if (mapGeocoder.isUserLocked && !force && source !== 'map') return; 2174 if (!mapGeocoder.geocoder) return; 2175 if (!mapGeocoder.map) return; 2176 if (!mapGeocoder.marker) return; 2177 2178 const geocodeRequest = { 2179 address: value, 2180 componentRestrictions: { country: "TR" } 2181 }; 2182 2183 mapGeocoder.geocoder.geocode(geocodeRequest, 2184 function(results, status) { 2185 if (status === "OK") { 2186 if (requestId !== mapGeocoder.updateRequestId) return; 2187 if (mapGeocoder.isUserLocked && !force && source !== 'map') return; 2188 const location = results[0].geometry.location; 2189 2190 mapGeocoder.map.setCenter(location); 2191 mapGeocoder.map.setZoom(15); 2192 2193 mapGeocoder.setMarkerPosition(location); 2194 mapGeocoder.setCoordinates(location, source); 2195 } 2196 } 2197 ); 2198 }, 2199 getGoogleMapsScriptUrl: function() { 2200 if (!mapGeocoder.apiKey) return ''; 2201
2202 return `https://maps.googleapis.com/maps/api/js?key=${encodeURIComponent(mapGeocoder.apiKey)}&loading=async&libraries=marker`; 2203 }, 2204 initMap: function() { 2205 const mapElement = document.querySelector('[data-toggle="map-geocoder"]'); 2206 const googleMaps = window.google?.maps; 2207 if (!mapElement || !googleMaps) { 2208 mapGeocoder.hideMapElement(); 2209 return false; 2210 } 2211 mapGeocoder.isUserLocked = false; 2212 2213 const mapId = SETTINGS.GOOGLE_MAP_ID || ""; 2214 const mapOptions = { 2215 center: { lat: 39, lng: 35 }, 2216 zoom: 6 2217 }; 2218 2219 const canUseAdvancedMarker = !!(mapId && googleMaps.marker?.AdvancedMarkerElement); 2220 if (canUseAdvancedMarker) mapOptions.mapId = mapId; 2221 mapGeocoder.map = new googleMaps.Map(mapElement, mapOptions); 2222 2223 if (canUseAdvancedMarker) { 2224 mapGeocoder.marker = new googleMaps.marker.AdvancedMarkerElement({ 2225 map: mapGeocoder.map, 2226 position: { lat: 39, lng: 35 }, 2227 gmpDraggable: mapGeocoder.allowManualPinMove, 2228 }); 2229 if (mapGeocoder.allowManualPinMove && typeof mapGeocoder.marker.addListener === 'function') { 2230 mapGeocoder.marker.addListener('dragend', function(event) { 2231 const location = event?.latLng || mapGeocoder.marker.position; 2232 mapGeocoder.setCircleCenter(location); 2233 mapGeocoder.setCoordinates(location, 'map'); 2234 }); 2235 } 2236 } else if (googleMaps.Marker) { 2237 mapGeocoder.marker = new googleMaps.Marker({ 2238 map: mapGeocoder.map, 2239 position: { lat: 39, lng: 35 }, 2240 draggable: mapGeocoder.allowManualPinMove, 2241 }); 2242 if (mapGeocoder.allowManualPinMove && typeof mapGeocoder.marker.addListener === 'function') { 2243 mapGeocoder.marker.addListener('dragend', function(event) { 2244 mapGeocoder.setCircleCenter(event?.latLng); 2245 mapGeocoder.setCoordinates(event?.latLng, 'map'); 2246 }); 2247 } 2248 } else { 2249 mapGeocoder.marker = null; 2250 } 2251 2252 if (googleMaps.Circle) { 2253 mapGeocoder.circle = new googleMaps.Circle({ 2254 map: mapGeocoder.map, 2255 center: { lat: 39, lng: 35 }, 2256 radius: mapGeocoder.circleRadius, 2257 strokeColor: '#2A65F5', 2258 strokeOpacity: 0.45, 2259 strokeWeight: 1, 2260 fillColor: '#2A65F5', 2261 fillOpacity: 0.2, 2262 clickable: false, 2263 }); 2264 } else { 2265 mapGeocoder.circle = null; 2266 } 2267 2268 if (mapGeocoder.allowManualPinMove && mapGeocoder.map?.addListener) { 2269 mapGeocoder.map.addListener('click', function(event) { 2270 if (!event?.latLng) return; 2271 mapGeocoder.setMarkerPosition(event.latLng); 2272 mapGeocoder.setCoordinates(event.latLng, 'map'); 2273 }); 2274 } 2275 2276 mapGeocoder.geocoder = new googleMaps.Geocoder(); 2277 2278 const selectedLocation = mapGeocoder.getInitialLocation(mapElement); 2279 const initialLocation = selectedLocation || { lat: 39, lng: 35 }; 2280 mapGeocoder.setMarkerPosition(initialLocation); 2281 mapGeocoder.map.setCenter(initialLocation); 2282 if (selectedLocation) { 2283 mapGeocoder.map.setZoom(15); 2284 } 2285 mapGeocoder.setCoordinates(initialLocation, 'form'); 2286 return true; 2287 }, 2288 init: function() { 2289 if (!mapGeocoder.apiKey) { 2290 mapGeocoder.hideMapElement(); 2291 return Promise.resolve(false); 2292 } 2293 2294 window.gm_authFailure = function() { 2295 mapGeocoder.loader = null; 2296 mapGeocoder.hideMapElement(); 2297 }; 2298 2299 if (window.google?.maps) { 2300 return Promise.resolve(mapGeocoder.initMap()); 2301 } 2302 if (mapGeocoder.loader) { 2303 return mapGeocoder.loader.then(function() { 2304 return mapGeocoder.initMap(); 2305 }); 2306 } 2307 2308 const scriptUrl = mapGeocoder.getGoogleMapsScriptUrl(); 2309 if (!scriptUrl) return Promise.resolve(false); 2310 2311 mapGeocoder.loader = new Promise((resolve, reject) => { 2312 window.mapGeocoderLoaded = function () { 2313 resolve(true); 2314 }; 2315 2316 const url = new URL(scriptUrl, window.location.origin); 2317 url.searchParams.set('loading', 'async'); 2318 url.searchParams.set('libraries', 'marker'); 2319 url.searchParams.set('callback', 'mapGeocoderLoaded'); 2320 2321 const script = document.createElement('script'); 2322 script.src = url.toString(); 2323 script.async = true; 2324 script.defer = true; 2325 script.onerror = function () { 2326 mapGeocoder.loader = null; 2327 mapGeocoder.hideMapElement(); 2328 reject(new Error('Google Maps API could not be loaded')); 2329 }; 2330 2331 document.head.appendChild(script); 2332 }); 2333 2334 return mapGeocoder.loader.then(function() { 2335 return mapGeocoder.initMap(); 2336 }).catch(function() { 2337 mapGeocoder.hideMapElement(); 2338 return false;
2339 }); 2340 }, 2341} 2342 2343const tsRegion = async function(options) { 2344 const opt = { 2345 container: '.regions', 2346 element: null, 2347 countryLimit: 1, 2348 storeLimit: 0, 2349 regionLimit: null, 2350 postalcodeInput: null, 2351 country: { 2352 selector: '[data-selector="country"]', 2353 code: '', 2354 data: [], 2355 element: null, 2356 type: 'U', 2357 default: 'TR', 2358 callbacks: [], 2359 }, 2360 state: { 2361 selector: '[data-selector="state"]', 2362 value: '', 2363 code: '', 2364 data: [], 2365 element: null, 2366 type: 'E', 2367 callbacks: [], 2368 }, 2369 city: { 2370 selector: '[data-selector="city"]', 2371 value: '', 2372 code: '', 2373 data: [], 2374 element: null, 2375 type: 'S', 2376 callbacks: [], 2377 }, 2378 town: { 2379 selector: '[data-selector="town"]', 2380 value: '', 2381 code: '', 2382 data: [], 2383 element: null, 2384 type: 'I', 2385 callbacks: [], 2386 }, 2387 district: { 2388 selector: '[data-selector="district"]', 2389 value: '', 2390 code: '', 2391 data: [], 2392 element: null, 2393 type: 'M', 2394 callbacks: [], 2395 }, 2396 geocoder: 0, 2397 }; 2398 2399 if (typeof options == "object") { 2400 for (let i in options) { 2401 if (typeof options[i] == "object") { 2402 for (let j in options[i]) { 2403 opt[i][j] = options[i][j]; 2404 } 2405 } else { 2406 opt[i] = options[i]; 2407 } 2408 } 2409 } 2410 2411 opt.element = document.querySelector(opt.container); 2412 if (!opt.element) { 2413 console.warn('tsRegion warn: opt element undefined'); 2414 return; 2415 }; 2416 opt.postalcodeInput = opt.element.querySelector('[name="post_code"]'); 2417 2418 const regionCallback = function(region, value = '') { 2419 const regionCallbacks = opt[region].callbacks; 2420 if (value) { 2421 if (regionCallbacks.length > 0) { 2422 for (let i = 0; i < regionCallbacks.length; i++) { 2423 if (typeof regionCallbacks[i] === 'function') { 2424 try { 2425 regionCallbacks[i](value); 2426 } catch (error) { 2427 console.warn(`REGION ${region} callback error => ${error}`); 2428 } 2429 } 2430 } 2431 } 2432 if (opt.geocoder) { 2433 mapGeocoder.updateMap(value); 2434 } 2435 2436 const element = opt[region].element; 2437 if (element) { 2438 const formItem = element.closest('.form-item'); 2439 if (formItem) { 2440 formItem.classList.remove('error'); 2441 const elError = formItem.querySelector('.form-error'); 2442 if (elError) elError.remove(); 2443 } 2444 } 2445 } 2446 } 2447 2448 const setElements = function() { 2449 const regions = ['country', 'state', 'city', 'town', 'district']; 2450 Array.from(regions).forEach(region => { 2451 if (opt[region].element == null && opt[region].selector) { 2452 opt[region].element = opt.element.querySelector(opt[region].selector); 2453 } 2454 }); 2455 } 2456 2457 const regionStateControl = function(region = '') { 2458 const element = opt[region].element; 2459 if (element == null) return true; 2460 2461 const elementWrapper = element.closest('.form-item') || element.closest('.form-group') || element.closest('.region-dropdown'); 2462 if (elementWrapper == null) return true; 2463 2464 const country = getCountry(opt.country.code) || {}; 2465 if (country.has_state) { 2466 elementWrapper.style.display = 'block'; 2467 return false; 2468 } else { 2469 elementWrapper.style.display = 'none'; 2470 element.value = ''; 2471 return true; 2472 } 2473 } 2474 2475 const getCountry = (code) => { 2476 const isItem = Array.from(opt.country.data).find(item => item.code == code); 2477 if (isItem) { 2478 isItem.has_state = isItem.has_state == 1; 2479 return isItem; 2480 } else { 2481 return {}; 2482 } 2483 } 2484 2485 const getParent = function(region = '') { 2486 const country = getCountry(opt.country.code) || {}; 2487 2488 if (region == '') return ''; 2489 if (region == 'state') return opt.country.code; 2490 if (region == 'city') return country.has_state ? opt.state.code : opt.country.code; 2491 if (region == 'town') return opt.city.code; 2492 if (region == 'district') return opt.town.code; 2493 return ''; 2494 } 2495 2496 const getFormData = function(region = '') { 2497 const formData = new FormData(); 2498 if (region == '') return formData; 2499 2500 switch (region) { 2501 case 'state': 2502 formData.append('U', opt.country.code); 2503 break; 2504 case 'city': 2505 formData.append('U', opt.country.code); 2506 formData.append('E', opt.state.code || ''); 2507 break; 2508 case 'town': 2509 formData.append('U', opt.country.code); 2510 formData.append('E', opt.state.code || ''); 2511 formData.append('S', opt.city.code || ''); 2512 break; 2513 case 'district': 2514 formData.append('U', opt.country.code); 2515 formData.append('E', opt.state.code || ''); 2516 formData.append('S', opt.city.code || ''); 2517 formData.append('I', opt.town.code || ''); 2518 break; 2519 } 2520 return formData; 2521 } 2522 2523 const regionHtml = function(region = '', element = null) { 2524 if (element == null || element.regionHtml == 1) return; 2525 2526 if (opt[region].value) { 2527 element.value = opt[region].value; 2528 regionCallback(region, opt[region].value); 2529 } 2530 2531 const elementParent = element.parentNode; 2532 2533 const codeElement = document.createElement('input'); 2534 codeElement.type = 'hidden'; 2535 codeElement.name = element.dataset.target ? element.dataset.target : `${element.name}_code`;
2536 codeElement.id = `${element.id.replace(element.name, codeElement.name)}`; 2537 if (opt[region].code) codeElement.value = opt[region].code; 2538 opt[region].codeElement = codeElement; 2539 elementParent.insertBefore(codeElement, element); 2540 2541 const selectionList = document.createElement('div'); 2542 selectionList.className = 'selection-list'; 2543 selectionList.dataset.qa = `${region}-list`; 2544 selectionList.style.display = 'none'; 2545 2546 const searchWrapper = document.createElement('div'); 2547 searchWrapper.className = 'selection-search'; 2548 const searchInput = document.createElement('input'); 2549 searchInput.type = 'text'; 2550 searchInput.className = 'form-control region-search'; 2551 searchInput.placeholder = COMMON_LANG.search; 2552 searchInput.dataset.qa = `${region}-search`; 2553 searchInput.addEventListener('input', e => { 2554 const value = e.target.value; 2555 const ul = opt[region].selectionList.querySelector('ul'); 2556 if (!ul) return; 2557 2558 const items = ul.querySelectorAll('li'); 2559 items.forEach(item => { 2560 if (item.innerHTML.toLocaleUpperCase(getLocale()).includes(value.toLocaleUpperCase(getLocale()))) { 2561 item.style.display = 'block'; 2562 } else { 2563 item.style.display = 'none'; 2564 } 2565 }); 2566 }); 2567 searchWrapper.appendChild(searchInput); 2568 selectionList.appendChild(searchWrapper); 2569 2570 const ul = document.createElement('ul'); 2571 ul.className = 'selection-ul'; 2572 ul.dataset.qa = `${region}-ul`; 2573 selectionList.appendChild(ul); 2574 2575 opt[region].selectionList = selectionList; 2576 2577 const wrapper = document.createElement('div'); 2578 wrapper.className = 'relative selection-dropdown region-dropdown'; 2579 2580 const elementParentNode = elementParent?.parentNode; 2581 if (elementParentNode) elementParentNode.insertBefore(wrapper, elementParent); 2582 2583 wrapper.appendChild(elementParent); 2584 wrapper.appendChild(opt[region].selectionList); 2585 2586 element.regionHtml = 1; 2587 } 2588 2589 const regionSelectionList = function(region) { 2590 const element = opt[region].element; 2591 if (element == null) return; 2592 2593 const codeElement = opt[region].codeElement; 2594 const selectionList = opt[region].selectionList; 2595 2596 const ul = selectionList.querySelector('ul'); 2597 if (!ul) return; 2598 2599 ul.innerHTML = ''; 2600 opt[region].data.forEach((item, index) => { 2601 const li = document.createElement('li'); 2602 li.className = 'selection-li'; 2603 li.dataset.code = item.code; 2604 li.dataset.index = index; 2605 li.dataset.qa = `${region}-li`; 2606 if (item.postalcode) li.dataset.postalcode = item.postalcode; 2607 li.innerHTML = item.title; 2608 if (item.code == opt[region].code) { 2609 element.value = item.title; 2610 regionCallback(region, item.title); 2611 } 2612 li.addEventListener('click', e => { 2613 opt[region].value = item.title; 2614 opt[region].code = item.code; 2615 element.value = item.title; 2616 regionCallback(region, item.title); 2617 if (codeElement) codeElement.value = item.code; 2618 selectionList.style.display = 'none'; 2619 if (opt.postalcodeInput) { 2620 opt.postalcodeInput.value = region == 'district' ? item.postalcode : ''; 2621 } 2622 regionReset(region); 2623 regionNext(region); 2624 }); 2625 ul.appendChild(li); 2626 }); 2627 2628 if (element.componentRegion == 1) return; 2629 element.componentRegion = 1; 2630 2631 element.addEventListener('click', () => { 2632 if (opt[region].data.length == 0) return; 2633 2634 selectionList.style.display = 'block'; 2635 selectionList.querySelector('input').value = ''; 2636 selectionList.querySelector('input').dispatchEvent(new Event('input')); 2637 selectionList.querySelector('input').focus(); 2638 selectionList.querySelector('input').select(); 2639 }); 2640 2641 if (!opt._regionGlobalClickBound) { 2642 opt._regionGlobalClickBound = 1; 2643 document.addEventListener('click', e => { 2644 const regions = ['country', 'state', 'city', 'town', 'district']; 2645 regions.forEach(r => { 2646 const rElement = opt[r]?.element; 2647 const rSelectionList = opt[r]?.selectionList; 2648 if (!rElement || !rSelectionList) return; 2649 if (rSelectionList.style.display != 'block' || opt[r].data.length == 0) return; 2650 if (rElement.contains(e.target) || rSelectionList.contains(e.target)) return; 2651 rSelectionList.style.display = 'none'; 2652 }); 2653 }); 2654 } 2655 } 2656 2657 const regionReset = function(region = '') { 2658 if (region == '') return; 2659 2660 let regions = [] 2661 if (region == 'country') regions = ['state', 'city', 'town', 'district']; 2662 if (region == 'state') regions = ['city', 'town', 'district']; 2663 if (region == 'city') regions = ['town', 'district'];
2664 if (region == 'town') regions = ['district']; 2665 if (region == 'district') regions = []; 2666 2667 regions.forEach(r => { 2668 opt[r].code = ''; 2669 opt[r].value = ''; 2670 opt[r].data = []; 2671 2672 const element = opt[r].element; 2673 const codeElement = opt[r].codeElement; 2674 2675 if (codeElement) codeElement.value = ''; 2676 if (element) { 2677 element.value = ''; 2678 element.readOnly = false; 2679 element.disabled = false; 2680 const regionDropdown = element.closest('.region-dropdown'); 2681 if (regionDropdown) regionDropdown.classList.remove('active'); 2682 } 2683 }); 2684 } 2685 2686 const regionNext = function(region = '') { 2687 if (region == '' || region == 'district') return; 2688 2689 let nextRegion = ''; 2690 if (region == 'country') { 2691 const phoneInputs = opt.element.querySelectorAll('[data-toggle="flag"]'); 2692 phoneInputs.forEach(phoneInput => { 2693 if (phoneInput.dataset.key != opt.country.code) FLAG_MASK.changePhoneMask(phoneInput, opt.country.code); 2694 }); 2695 nextRegion = 'state'; 2696 } 2697 if (region == 'state') nextRegion = 'city'; 2698 if (region == 'city') nextRegion = 'town'; 2699 if (region == 'town') nextRegion = 'district'; 2700 2701 if (nextRegion == '') return; 2702 regionLoad(nextRegion); 2703 } 2704 2705 const regionLoad = async function(region = '') { 2706 if (region == '') return; 2707 2708 if (region == 'state' && regionStateControl(region)) { 2709 regionNext(region); 2710 return; 2711 }; 2712 2713 const element = opt[region].element; 2714 if (element == null) return; 2715 2716 regionHtml(region, element); 2717 2718 try { 2719 element.disabled = true; 2720 const type = opt[region].type; 2721 const parent = getParent(region); 2722 const limit = opt.regionLimit != null ? opt.regionLimit : (opt.countryLimit ? '1' : '0'); 2723 const formData = getFormData(region); 2724 if (shouldUnlockRegion(region, formData)) { 2725 element.readOnly = false; 2726 return; 2727 } 2728 2729 const query = new URLSearchParams(formData).toString(); 2730 const response = await request('GET', getEndpoint('REGION', `${type}/${parent}/${limit}?${query}`)); 2731 opt[region].data = response.data || []; 2732 2733 if (opt[region].selectionList && opt[region].data?.length) { 2734 element.readOnly = true; 2735 element.disabled = false; 2736 element.closest('.region-dropdown').classList.add('active'); 2737 regionSelectionList(region); 2738 regionNext(region); 2739 } else { 2740 element.readOnly = false; 2741 element.closest('.region-dropdown').classList.remove('active'); 2742 } 2743 } catch (error) { 2744 console.warn(`REGION error => ${error}`); 2745 if (element) { 2746 element.readOnly = false; 2747 element.closest('.region-dropdown').classList.remove('active'); 2748 } 2749 } finally { 2750 updateRegionDisabled(); 2751 } 2752 } 2753 2754 const updateRegionDisabled = function() { 2755 const country = getCountry(opt.country.code) || {}; 2756 2757 const order = country.has_state ? ['state', 'city', 'town', 'district'] : ['city', 'town', 'district']; 2758 const setDisabled = (region, value) => { 2759 const el = opt[region]?.element; 2760 if (el) el.disabled = value; 2761 }; 2762 2763 let prevHasData = Array.isArray(opt.country.data) && opt.country.data.length > 0; 2764 let prevSelected = !!opt.country.code; 2765 2766 for (let i = 0; i < order.length; i++) { 2767 const region = order[i]; 2768 if (!opt[region]) continue; 2769 2770 if (!prevHasData) { 2771 for (let j = i; j < order.length; j++) { 2772 setDisabled(order[j], false); 2773 } 2774 return; 2775 } 2776 2777 if (prevHasData && !prevSelected) { 2778 for (let j = i; j < order.length; j++) { 2779 setDisabled(order[j], true); 2780 } 2781 return; 2782 } 2783 2784 setDisabled(region, false); 2785 prevHasData = Array.isArray(opt[region].data) && opt[region].data.length > 0; 2786 prevSelected = !!opt[region].code; 2787 } 2788 } 2789 2790 const shouldUnlockRegion = function(region, formData) { 2791 const country = getCountry(opt.country.code) || {}; 2792 2793 if (region == 'state') return formData.get('U') == ''; 2794 if (region == 'city') return (formData.get('E') == '' && country.has_state) || formData.get('U') == ''; 2795 if (region == 'town') return formData.get('S') == ''; 2796 if (region == 'district') return formData.get('I') == ''; 2797 return false;
2798 } 2799 2800 const regionCountryLoad = async function() { 2801 const element = opt.country.element; 2802 2803 try { 2804 const response = await request('GET', getEndpoint('COUNTRY', '1?lang='+LANGUAGE)); 2805 opt.country.data = response.countries || []; 2806 if (!opt.country.data?.length) { 2807 if (element) element.value = opt.country.default; 2808 return; 2809 }; 2810 2811 if (opt.countryLimit) { 2812 opt.country.data = Array.from(opt.country.data).filter(x => !MEMBER_INFO.E_COUNTRY || x.code == MEMBER_INFO.E_COUNTRY); 2813 } 2814 2815 let cExists = false; 2816 if (element) { 2817 Array.from(opt.country.data).forEach(item => { 2818 const option = document.createElement('option'); 2819 option.value = item.code; 2820 option.innerHTML = item.title; 2821 element.appendChild(option); 2822 if (opt.country.code && (item.code == opt.country.code)) cExists = true; 2823 }); 2824 } 2825 2826 if (!opt.country.code && !cExists) { 2827 if (opt.country.data.length == 1) { 2828 opt.country.code = opt.country.data[0].code; 2829 } else if (response.selected && getCountry(response.selected)) { 2830 opt.country.code = response.selected; 2831 } else if (getCountry('TR')) { 2832 opt.country.code = 'TR'; 2833 } else if (opt.country.data.length > 0) { 2834 opt.country.code = opt.country.data[0].code; 2835 } 2836 } 2837 2838 if (element && (element.value != opt.country.code)) element.value = opt.country.code; 2839 if (opt.element) opt.element.dataset.country = opt.country.code; 2840 } catch (error) { 2841 console.warn(`COUNTRY error => ${error}`); 2842 if (element) element.value = opt.country.default; 2843 } finally { 2844 if (element) { 2845 element.addEventListener('change', () => { 2846 opt.country.code = element.value; 2847 if (opt.element) opt.element.dataset.country = opt.country.code; 2848 regionReset('country');
2849 regionNext('country'); 2850 }); 2851 } 2852 regionNext('country'); 2853 } 2854 }; 2855 2856 setElements(); 2857 regionCountryLoad(); 2858 if (opt.geocoder) { 2859 mapGeocoder.init(); 2860 } 2861} 2862 2863const queryControl = async function() { 2864 const activationModal = function(activation = null) { 2865 if (activation == null) { 2866 console.warn('activationModal warn: activation null'); 2867 return; 2868 }; 2869 loadSnippet({ 2870 snippet: '_customer-activation-popup', 2871 params: { 2872 include: 'customer-register', 2873 activation: activation, 2874 }, 2875 success: function(loadRes){ 2876 modal({ 2877 id: 'modal-customer-activation-popup', 2878 width: '480px', 2879 html: loadRes, 2880 }); 2881 } 2882 }); 2883 }; 2884 2885 const activationType = getUrlParam('activation'); 2886 if (activationType) { 2887 switch (activationType) { 2888 case 'newsletter': 2889 let result = {}; 2890 const token = getUrlParam('token'); 2891 const newsletterActivateData = new FormData(); 2892 newsletterActivateData.append('token', token); 2893 try { 2894 await request('POST', getEndpoint('NEWSLETTER_ACTIVATE'), newsletterActivateData).then(response => { 2895 result = response; 2896 if (response.status) { 2897 modal({ 2898 html: response.data.message || response.message || response.statusText || COMMON_LANG?.send_success, 2899 width: '480px', 2900 alert: true, 2901 class: 'success', 2902 }); 2903 } 2904 }) 2905 } catch (error) { 2906 // V5-12313: Token tuketildikten ya da suresi doldugunda uc 422 2907 // donuyor, kullaniciya ise hicbir sey gosterilmiyordu. Servisin 2908 // mesaji zaten cevrilmis ve musteriye gosterilebilir bir cumle; 2909 // ham error.message'a DUSULMEZ, o teknik metin tasiyabilir. 2910 modal({ 2911 html: error.response?.message || COMMON_LANG?.error_try_again, 2912 width: '480px', 2913 alert: true, 2914 class: 'danger', 2915 }); 2916 console.warn(`NEWSLETTER_ACTIVATE error => ${error}`); 2917 } 2918 2919 // V5-12313: Token tek kullanimlik ve onaylandiginda siliniyor. Adres 2920 // cubugunda kalirsa yenileme, geri tusu ve mail tarayicilarinin 2921 // on-getirmesi ayni tokeni tekrar POST edip 422 uretiyor. 2922 const activationUrl = new URL(window.location.href); 2923 activationUrl.searchParams.delete('activation'); 2924 activationUrl.searchParams.delete('token'); 2925 window.history.replaceState({}, '', activationUrl); 2926 2927 setTimeout(() => { 2928 for (let i=0; i < callbacks.customer.subscribe.length; i++){ 2929 if (typeof callbacks.customer.subscribe[i] === 'function'){ 2930 try { 2931 callbacks.customer.subscribe[i]?.(result); 2932 } catch (error) { console.warn(`Customer Other Callback Error => ${error}`); } 2933 } 2934 } 2935 }, 1500); 2936 break; 2937 case 'membership': 2938 const memberId = getUrlParam('id'), 2939 memberToken = getUrlParam('token'); 2940 try { 2941 await request('POST', getEndpoint('ACTIVATE_MEMBERSHIP', `${memberId}/${memberToken}`)).then(response => { 2942 if (response.status) { 2943 notify({ 2944 text: COMMON_LANG?.membership_activated, 2945 }); 2946 loginPopup(); 2947 history.pushState('', '', '/'); 2948 } 2949 }); 2950 } catch (error) { 2951 console.warn(`ACTIVATE_MEMBERSHIP error => ${error}`); 2952 } 2953 break; 2954 case 'facebook': 2955 activationModal('facebook-email'); 2956 break; 2957 case 'facebookphone': 2958 activationModal('facebook-phone'); 2959 break;
2960 case 'googlephone': 2961 activationModal('google-phone'); 2962 break; 2963 case 'social': 2964 activationModal('social'); 2965 break; 2966 } 2967 } 2968 2969 const errorType = getUrlParam('error'); 2970 if (errorType && PAGE_ID == '5' && MEMBER_INFO.ID < 1) { 2971 const errorMessages = { 2972 mail_already_registered: { 2973 message: COMMON_LANG.mail_already_registered || 'Bu e-posta adresiyle kayıt yapılmıÅtır. Lütfen giriÅ yapınız.' 2974 }, 2975 phone_already_registered: { 2976 message: COMMON_LANG.phone_already_registered || 'Bu telefon numarasıyla kayıt yapılmıÅtır. Lütfen giriÅ yapınız.' 2977 } 2978 }; 2979 2980 if (errorMessages[errorType]) { 2981 modal({ 2982 html: errorMessages[errorType].message, 2983 alert: true, 2984 class: 'danger' 2985 }); 2986 2987 const url = new URL(window.location.href); 2988 url.searchParams.delete('error'); 2989 window.history.replaceState({}, '', url); 2990 } 2991 } 2992 2993 const multiShoppingToken = getUrlParam('TmultiShoppingCart'); 2994 if (multiShoppingToken) { 2995 const parts = multiShoppingToken.split('.'); 2996 if (parts.length === 3) { 2997 const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/'); 2998 const padded = base64 + '='.repeat((4 - base64.length % 4) % 4); 2999 const payload = JSON.parse(atob(padded)); 3000 components.multiShoppingCart.get(payload); 3001 } 3002 } 3003 3004 const newsletterToken = getUrlParam('token'); 3005 if (PAGE_ID == '95' && newsletterToken) { 3006 const formData = new FormData(); 3007 formData.append('token', newsletterToken); 3008 3009 try { 3010 const response = await request('POST', getEndpoint('NEWSLETTER_CONFIRM_UNSUBSCRIBE'), formData); 3011 if (response.status) { 3012 modal({ 3013 html: response.data.message || response.message || response.statusText || COMMON_LANG?.send_success, 3014 width: '480px', 3015 alert: true, 3016 class: 'success', 3017 iconHtml: ` 3018 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16"> 3019 <path d="M15.964.686a.5.5 0 0 0-.65-.65L.767 5.855a.75.75 0 0 0-.124 1.329l4.995 3.178 1.531 2.406a.5.5 0 0 0 .844-.536L6.637 10.07l7.494-7.494-1.895 4.738a.5.5 0 1 0 .928.372l2.8-7Zm-2.54 1.183L5.93 9.363 1.591 6.602l11.833-4.733Z"/> 3020 <path d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0m-1.993-1.679a.5.5 0 0 0-.686.172l-1.17 1.95-.547-.547a.5.5 0 0 0-.708.708l.774.773a.75.75 0 0 0 1.174-.144l1.335-2.226a.5.5 0 0 0-.172-.686Z"/> 3021 </svg> 3022 `, 3023 }); 3024 } 3025 } catch (error) { 3026 requestError(error, 'NEWSLETTER_CONFIRM_UNSUBSCRIBE', null, 'modal'); 3027 const url = deleteUrlParam('token'); 3028 window.history.replaceState('', '', url); 3029 } 3030 } 3031 3032 if (getUrlParam('review')) { 3033 loadSnippet({ 3034 snippet: '_review-from-email', 3035 success: function(loadRes){ 3036 modal({ 3037 id: 'modal-review-from-email', 3038 width: '480px', 3039 html: loadRes, 3040 }); 3041 } 3042 }); 3043 } 3044} 3045 3046const passwordStrengthControl = async function() { 3047 if (Number(MEMBER_INFO.ID) < 1 || typeof MEMBER_INFO.PASSWORD_STRENGTH == 'undefined' || Number(MEMBER_INFO.PASSWORD_STRENGTH)) return; 3048 3049 loadSnippet({ 3050 snippet: '_customer-password-update', 3051 params: { 3052 include: 'customer-register', 3053 }, 3054 success: function(loadRes){ 3055 modal({ 3056 id: 'modal-customer-password-update', 3057 html: loadRes, 3058 width: '480px', 3059 close: false, 3060 disabledClose: true, 3061 }); 3062 } 3063 }); 3064} 3065 3066const loginPasswordChange = async function(formData) { 3067 await loadSnippet({ 3068 snippet: '_customer-password-change', 3069 params: { 3070 include: 'customer-register', 3071 }, 3072 success: async function(loadRes){ 3073 await modal({
3074 id: 'modal-customer-password-change', 3075 width: '480px', 3076 html: loadRes, 3077 }); 3078 } 3079 }); 3080 3081 while (true) { 3082 if (typeof SNIPPET_JS.CUSTOMER_PASSWORD_CHANGE == 'object') { 3083 SNIPPET_JS.CUSTOMER_PASSWORD_CHANGE['FORM_DATA'] = formData; 3084 break; 3085 } else { 3086 await awaitTimeOut(1000); 3087 } 3088 } 3089} 3090 3091const loginTwoFactor = async function(form, response) { 3092 if (document.querySelector('#modal-login-two-factor-popup') && typeof SNIPPET_JS.LOGIN_TWO_FACTOR_POPUP == 'object') { 3093 SNIPPET_JS.LOGIN_TWO_FACTOR_POPUP.update(response); 3094 return; 3095 } 3096 3097 if (!response.two_factor_sent) { 3098 notify({ 3099 text: response.statusText || COMMON_LANG?.error_try_again, 3100 class: 'danger', 3101 }); 3102 return; 3103 } 3104 3105 delete SNIPPET_JS.LOGIN_TWO_FACTOR_POPUP; 3106 3107 await loadSnippet({ 3108 snippet: '_login-two-factor-popup', 3109 params: { 3110 include: 'customer-login', 3111 channel: response.channel, 3112 ttl: response.ttl, 3113 }, 3114 success: async function(loadRes){ 3115 await modal({ 3116 id: 'modal-login-two-factor-popup', 3117 width: '480px', 3118 html: loadRes, 3119 }); 3120 } 3121 }); 3122 3123 while (true) { 3124 if (typeof SNIPPET_JS.LOGIN_TWO_FACTOR_POPUP == 'object') { 3125 SNIPPET_JS.LOGIN_TWO_FACTOR_POPUP['FORM'] = form; 3126 break; 3127 } else { 3128 await awaitTimeOut(1000); 3129 } 3130 } 3131} 3132 3133const phonePromptControl = async function() { 3134 if (!MEMBER_INFO.PHONE_PROMPT_PENDING || getUrlParam('activation')) return; 3135 if (['30', '73', '91'].includes(String(PAGE_ID))) return; 3136 if (MEMBER_INFO.KVKK != 1 && SETTINGS.KVKK_REQUIRED == 1) return; 3137 if (typeof MEMBER_INFO.PASSWORD_STRENGTH != 'undefined' && !Number(MEMBER_INFO.PASSWORD_STRENGTH)) return; 3138 3139 try { 3140 const response = await request('POST', getEndpoint('PHONE_PROMPT_START')); 3141 if (!response.token) return; 3142 MEMBER_INFO.PHONE_PROMPT = response.token; 3143 } catch (error) { 3144 console.warn(`PHONE_PROMPT_START error => ${error}`); 3145 return; 3146 } 3147 3148 loadSnippet({ 3149 snippet: '_customer-phone-prompt-popup', 3150 params: { 3151 include: 'customer-register', 3152 }, 3153 success: function(loadRes) { 3154 modal({ 3155 id: 'modal-customer-phone-prompt-popup', 3156 width: '480px', 3157 html: loadRes, 3158 }); 3159 } 3160 }); 3161}; 3162 3163const memberKvkkControl = async function() { 3164 if (typeof ADMIN_USER_FULLNAME != 'undefined' && ADMIN_USER_FULLNAME.trim() != '') return; 3165 if (MEMBER_INFO.ID == 0 || MEMBER_INFO.KVKK == 1 || SETTINGS.KVKK_REQUIRED != 1) return; 3166 3167 loadSnippet({ 3168 snippet: '_customer-activation-popup', 3169 params: { 3170 include: 'customer-register', 3171 activation: 'kvkk', 3172 }, 3173 success: function(loadRes){ 3174 modal({ 3175 id: 'modal-customer-activation-popup', 3176 width: '480px', 3177 html: loadRes, 3178 }); 3179 } 3180 }); 3181} 3182 3183const loginPopup = async function(statusText = null) { 3184 if (MEMBER_INFO.ID > 0) return; 3185 3186 await loadSnippet({ 3187 snippet: 'login-popup', 3188 params: { 3189 include: 'customer-login', 3190 }, 3191 success: function(loadRes){ 3192 modal({ 3193 id: 'modal-login-popup', 3194 width: '480px', 3195 html: `${statusText ? `${statusText}` : ''} ${loadRes}`, 3196 }); 3197 } 3198 }); 3199}; 3200 3201const Cart = { 3202 callback: { 3203 update: null, 3204 updateAll: null, 3205 delete: null, 3206 deleteAll: null, 3207 load: [], 3208 add: [], 3209 }, 3210 storageKey : 'Cart', 3211 update: async function(sessIndex, count, callback) { 3212 try { 3213 await request('GET', getEndpoint('CART_UPDATE', `${sessIndex}/${count}`)).then(response => { 3214 if (typeof mobileApp !== 'undefined') { 3215 try {
3216 mobileApp.changedCartCount(response.totalQuantity); 3217 } catch (err) {} 3218 } 3219 if (typeof webkit !== 'undefined') { 3220 try { 3221 webkit.messageHandlers.callbackHandler.postMessage(response.totalQuantity); 3222 } catch (err) {} 3223 } 3224 3225 Cart.setStorage('update',response,sessIndex); 3226 3227 for(let i=0; i < callbacks.cart.update.length; i++){ 3228 if(typeof callbacks.cart.update[i] === 'function'){ 3229 try { 3230 callbacks.cart.update[i]?.(response); 3231 } catch (error) { console.log(`Cart Update Callback Error => ${error}`); } 3232 } 3233 } 3234 3235 if (response.totalQuantity > CART_COUNT) { 3236 for(let i=0; i < callbacks.cart.add.length; i++){ 3237 if(typeof callbacks.cart.add[i] === 'function'){ 3238 try { 3239 callbacks.cart.add[i]?.(response); 3240 } catch (error) { console.log(`Cart Add Callback Error => ${error}`); } 3241 } 3242 } 3243 } else { 3244 for(let i=0; i < callbacks.cart.delete.length; i++){ 3245 if(typeof callbacks.cart.delete[i] === 'function'){ 3246 try { 3247 callbacks.cart.delete[i]?.(response); 3248 } catch (error) { console.log(`Cart Delete Callback Error => ${error}`); } 3249 } 3250 } 3251 } 3252 3253 if (typeof SNIPPET_JS.CART_PREVIEW?.getCart == 'function') { 3254 SNIPPET_JS.CART_PREVIEW.getCart(); 3255 } 3256 3257 if (typeof SNIPPET_JS.CART_DRAWER?.getCart == 'function') { 3258 SNIPPET_JS.CART_DRAWER.getCart(); 3259 } 3260 3261 CART_COUNT = response.totalQuantity; 3262 CART_TOTAL = response.totalPrice; 3263 components.cartCountPrice(); 3264 3265 if (typeof callback === 'function') { 3266 callback(response); 3267 if (typeof Cart.callback.update === 'function') { 3268 Cart.callback.update(response); 3269 } 3270 } else { 3271 modal({ 3272 width: '400px', 3273 html: response.statusText 3274 }); 3275 setTimeout(function() { 3276 window.location.reload(); 3277 }, 2500); 3278 } 3279 }) 3280 } catch (error) { 3281 console.warn(`CART_UPDATE error => ${error}`); 3282 } 3283 }, 3284 updateAll: async function(countList, callback) { 3285 try { 3286 const param = typeof countList === 'object' ? countList.join('-') : countList; 3287 await request('GET', getEndpoint('CART_UPDATE_ALL', `${param}`)).then(response => { 3288 if (typeof mobileApp !== 'undefined') { 3289 try { 3290 mobileApp.changedCartCount(response.totalQuantity); 3291 } catch (err) {} 3292 } 3293 if (typeof webkit !== 'undefined') { 3294 try { 3295 webkit.messageHandlers.callbackHandler.postMessage(response.totalQuantity); 3296 } catch (err) {} 3297 } 3298 3299 for(let i=0; i < callbacks.cart.updateAll.length; i++){ 3300 if(typeof callbacks.cart.updateAll[i] === 'function'){ 3301 try { 3302 callbacks.cart.updateAll[i]?.(response); 3303 } catch (error) { console.log(`Cart UpdateAll Callback Error => ${error}`); } 3304 } 3305 } 3306 3307 if (typeof callback === 'function') { 3308 callback(response); 3309 if (typeof Cart.callback.updateAll === 'function') { 3310 Cart.callback.updateAll(response); 3311 } 3312 } else { 3313 window.location.reload(); 3314 } 3315 }); 3316 } catch (error) { 3317 console.warn(`CART_UPDATE_ALL error => ${error}`); 3318 } 3319 }, 3320 delete: async function(sessIndex, callback) { 3321 try { 3322 await request('GET', getEndpoint('CART_DELETE', `${sessIndex}`)).then(response => {
3323 if (typeof mobileApp !== 'undefined') { 3324 try { 3325 mobileApp.changedCartCount(response.totalQuantity); 3326 } catch (err) {} 3327 } 3328 if (typeof webkit !== 'undefined') { 3329 try { 3330 webkit.messageHandlers.callbackHandler.postMessage(response.totalQuantity); 3331 } catch (err) {} 3332 } 3333 3334 if (typeof SNIPPET_JS.CART_PREVIEW?.getCart == 'function') { 3335 SNIPPET_JS.CART_PREVIEW.getCart(); 3336 } 3337 3338 if (typeof SNIPPET_JS.CART_DRAWER?.getCart == 'function') { 3339 SNIPPET_JS.CART_DRAWER.getCart(); 3340 } 3341 3342 CART_COUNT = response.totalQuantity; 3343 CART_TOTAL = response.totalPrice; 3344 components.cartCountPrice(); 3345 3346 Cart.setStorage('delete',response,sessIndex); 3347 3348 for(let i=0; i < callbacks.cart.delete.length; i++){ 3349 if(typeof callbacks.cart.delete[i] === 'function'){ 3350 try { 3351 callbacks.cart.delete[i]?.(response); 3352 } catch (error) { console.log(`Cart Delete Callback Error => ${error}`); } 3353 } 3354 } 3355 3356 if (typeof callback === 'function') { 3357 callback(response); 3358 if (typeof Cart.callback.delete === 'function') { 3359 Cart.callback.delete(response); 3360 } 3361 } else if (typeof window[callback] === 'function') { 3362 window[callback](response); 3363 if (typeof Cart.callback.delete === 'function') { 3364 Cart.callback.delete(response); 3365 } 3366 } else { 3367 window.location.reload(); 3368 } 3369 }); 3370 } catch (error) { 3371 console.warn(`CART_DELETE error => ${error}`); 3372 } 3373 }, 3374 deleteAll: async function(callback) { 3375 try { 3376 await request('GET', getEndpoint('CART_DELETE_ALL')).then(response => { 3377 if (typeof mobileApp !== 'undefined') { 3378 try { 3379 mobileApp.changedCartCount(0); 3380 } catch (err) {} 3381 } 3382 if (typeof webkit !== 'undefined') { 3383 try { 3384 webkit.messageHandlers.callbackHandler.postMessage(0); 3385 } catch (err) {} 3386 } 3387 3388 CART_COUNT = '0'; 3389 CART_TOTAL = '0,00'; 3390 components.cartCountPrice(); 3391 3392 Cart.setStorage('deleteAll'); 3393 3394 for(let i=0; i < callbacks.cart.deleteAll.length; i++){ 3395 if(typeof callbacks.cart.deleteAll[i] === 'function'){ 3396 try { 3397 callbacks.cart.deleteAll[i]?.(response); 3398 } catch (error) { console.log(`Cart DeleteAll Callback Error => ${error}`); } 3399 } 3400 } 3401 3402 if (typeof Cart.callback.deleteAll === 'function') { 3403 Cart.callback.deleteAll(response); 3404 } 3405 if (typeof callback === 'function') { 3406 callback(response); 3407 } else { 3408 setTimeout(function() { 3409 window.location.reload(); 3410 }, 750); 3411 } 3412 }); 3413 } catch (error) { 3414 console.warn(`CART_DELETE_ALL error => ${error}`); 3415 } 3416 }, 3417 setStorage: async function(action,response,index){ 3418 if(typeof window.localStorage === 'undefined'){ 3419 return false; 3420 } 3421 let storage = window.localStorage.getItem(Cart.storageKey); 3422 if(storage === null){ 3423 storage = { summary : { }, items : [] }; 3424 }else{ 3425 storage = JSON.parse(storage); 3426 } 3427 3428 switch(action) { 3429 case 'update': 3430 case 'add': 3431 if(response.cartProducts === null){ 3432 return false;
3433 } 3434 storage.items = response.cartProducts; 3435 storage.summary = { total : response.totalPrice}; 3436 break; 3437 3438 case 'delete': 3439 if (storage && storage.items) { 3440 if(storage.items.length === 0){ 3441 return false; 3442 } 3443 storage.items.splice(index,1); 3444 storage.summary.total = response.priceCart; 3445 break; 3446 } 3447 3448 case 'deleteAll': 3449 storage.items = []; 3450 storage.summary = { total : 0}; 3451 break; 3452 } 3453 3454 window.localStorage.setItem(Cart.storageKey,JSON.stringify(storage)); 3455 return true; 3456 }, 3457 getStorage: function(){ 3458 let storage = window.localStorage.getItem(Cart.storageKey); 3459 if(storage === null){ 3460 storage = []; 3461 }else{ 3462 storage = JSON.parse(storage); 3463 } 3464 return storage; 3465 } 3466} 3467 3468const addToCart = async function(opt = {}) { 3469 let defaults = { 3470 productId: 0, 3471 variantId: 0, 3472 quantity: 0, 3473 buyNow: 0, 3474 buyRedirect: 'cart', /* cart || order */ 3475 redirect: PAGE_LINK.CART, 3476 relatedProductId: '', 3477 multi: false, 3478 multiItem: null, 3479 personalizationPid: [], 3480 modal: true, 3481 }; 3482 3483 opt = { ...defaults, ...opt }; 3484 3485 if (opt.buyNow == 1 && opt.buyRedirect == 'order') opt.redirect = PAGE_LINK.ORDER; 3486 3487 const formData = new FormData(); 3488 3489 if(Array.isArray(opt.productId) && opt.productId.length > 1) { 3490 opt.productId.forEach(item => { 3491 formData.append('productId[]', item); 3492 }); 3493 } else { 3494 opt.multi = false; 3495 formData.append('productId', opt.productId); 3496 } 3497 3498 if(Array.isArray(opt.variantId) && opt.variantId.length > 1) { 3499 opt.variantId.forEach(item => { 3500 formData.append('variantId[]', item); 3501 }); 3502 } else { 3503 formData.append('variantId', opt.variantId); 3504 } 3505 3506 if(Array.isArray(opt.quantity) && opt.quantity.length > 1) { 3507 opt.quantity.forEach(item => { 3508 formData.append('quantity[]', item); 3509 }); 3510 } else { 3511 formData.append('quantity', opt.quantity); 3512 } 3513 3514 if (opt.relatedProductId) { 3515 opt.relatedProductId = opt.relatedProductId.split('-'); 3516 opt.relatedProductId = opt.relatedProductId.join(':'); 3517 } 3518 3519 if(Array.isArray(opt.relatedProductId) && opt.relatedProductId.length > 0) { 3520 opt.relatedProductId.forEach(item => { 3521 formData.append('relatedProductId[]', item); 3522 }); 3523 } else { 3524 formData.append('relatedProductId', opt.relatedProductId); 3525 } 3526 3527 const productSubscribe = document.querySelector(`[data-toggle="product-subscribe-${opt.productId}"]`); 3528 if (productSubscribe?.value) { 3529 formData.append('subscribe', 1); 3530 formData.append('subscribeFrequency', productSubscribe.value); 3531 } 3532 3533 const productNote = document.querySelector(`[data-toggle="product-note-${opt.productId}"]`); 3534 if (productNote?.value) { 3535 formData.append('orderNotes', productNote.value); 3536 } 3537 3538 if (typeof BLOCK_JS.PERSONALIZATION != 'undefined' && BLOCK_JS.PERSONALIZATION[opt.productId]) { 3539 const appPersonalization = BLOCK_JS.PERSONALIZATION[opt.productId]; 3540 if (!await appPersonalization.setData()) return; 3541 3542 const data = { 3543 form_id: appPersonalization.FORM_DATA.form_id, 3544 product_id: opt.productId, 3545 sub_product_id: opt.variantId, 3546 data: appPersonalization.FORM_DATA.data, 3547 } 3548 formData.append(`formData[${opt.productId}]`, JSON.stringify(data)); 3549 } 3550 3551 formData.append('csrfToken', CART_CSRF_TOKEN); 3552 3553 const variantModal = (response) => { 3554 if (document.querySelector('#modal-cart-popup-variant')) { 3555 notify({ 3556 text: COMMON_LANG?.variant_notify ?? response.statusText, 3557 class: 'danger', 3558 }); 3559 return; 3560 } 3561 3562 if (opt.relatedProductId) { 3563 opt.relatedProductId = opt.relatedProductId.split(':'); 3564 opt.relatedProductId = opt.relatedProductId.join('-'); 3565 } 3566 3567 const modalFnc = (loadRes) => { 3568 modal({
3569 id: 'modal-cart-popup-variant', 3570 width: '380px', 3571 html: loadRes, 3572 }); 3573 } 3574 3575 loadSnippet({ 3576 snippet: 'cart-popup-variant', 3577 params: { 3578 include: 'cart', 3579 PRODUCT_ID: opt.productId, 3580 QUANTITY: opt.quantity, 3581 RELATED_PRODUCT_ID: opt.relatedProductId, 3582 }, 3583 success: function(loadRes){ 3584 if (callbacks.cart.modal?.variant?.length > 0) { 3585 for(let i=0; i < callbacks.cart.modal.variant.length; i++){ 3586 if(typeof callbacks.cart.modal.variant[i] === 'function'){ 3587 try { 3588 callbacks.cart.modal.variant[i]?.(loadRes, opt); 3589 } catch (error) { 3590 modalFnc(loadRes); 3591 } 3592 } 3593 } 3594 } else { 3595 modalFnc(loadRes); 3596 } 3597 } 3598 }); 3599 } 3600 3601 const cartModal = (response) => { 3602 if (opt.modal == false) return; 3603 3604 if (opt.relatedProductId) { 3605 opt.relatedProductId = opt.relatedProductId.split(':'); 3606 opt.relatedProductId = opt.relatedProductId.join('-'); 3607 } 3608 3609 const modalFnc = (loadRes) => { 3610 modalClose('#modal-cart-popup'); 3611 modalClose('#modal-cart-popup-variant'); 3612 modal({ 3613 id: 'modal-cart-popup', 3614 width: '580px', 3615 html: loadRes, 3616 }); 3617 } 3618 3619 loadSnippet({ 3620 snippet: 'cart-popup', 3621 params: { 3622 include: 'cart', 3623 product: (Array.isArray(opt.productId) && opt.productId.length > 1) ? opt.productId.join('-') : opt.productId, 3624 multi: (Array.isArray(opt.productId) && opt.productId.length > 1) ? true : false, 3625 relatedProductId : opt.relatedProductId 3626 }, 3627 success: function(loadRes){ 3628 if (callbacks.cart.modal?.add?.length > 0) { 3629 for(let i=0; i < callbacks.cart.modal.add.length; i++){ 3630 if(typeof callbacks.cart.modal.add[i] === 'function'){ 3631 try { 3632 callbacks.cart.modal.add[i]?.(loadRes, opt); 3633 } catch (error) { 3634 modalFnc(loadRes); 3635 } 3636 } 3637 } 3638 } else { 3639 modalFnc(loadRes); 3640 } 3641 } 3642 }); 3643 if (PAGE_ID == 30 && BLOCK_JS?.CART?.load && typeof BLOCK_JS?.CART?.load == 'function') BLOCK_JS.CART.load(); 3644 }; 3645 3646 try { 3647 await request('POST', getEndpoint('ADD_TO_CART'), formData).then(response => { 3648 if (response?.statusCode == 'EOC0001') { 3649 modal({ 3650 html: ` 3651 <div> 3652 <div>${response.statusText || COMMON_LANG?.cart_change_warning}</div> 3653 <div class="text-xs text-gray-500 mt-2">${COMMON_LANG?.redirect_to_payment_page}</div> 3654 </div> 3655 `, 3656 class: 'info', 3657 alert: true, 3658 buttons: [ 3659 { 3660 text: COMMON_LANG?.go_to_payment, 3661 href: '/' + PAGE_LINK.ORDER, 3662 class: 'btn btn-primary !text-xs !h-10', 3663 } 3664 ], 3665 callbacks: { 3666 close: function() { 3667 window.location.href = '/' + PAGE_LINK.ORDER; 3668 } 3669 } 3670 }); 3671 3672 setTimeout(() => { 3673 window.location.href = '/' + PAGE_LINK.ORDER; 3674 }, 3000); 3675 return; 3676 } 3677 3678 if (response.status > 0 || (Array.isArray(response) && response[0].status > 0)) { 3679 const cartCount = Array.isArray(response) ? response.filter(x => x.totalQuantity) : response; 3680 CART_COUNT = Array.isArray(cartCount) ? (cartCount[cartCount.length - 1]?.totalQuantity ?? CART_COUNT) : cartCount.totalQuantity; 3681 const cartTotal = Array.isArray(response) ? response.filter(x => x.totalPrice) : response; 3682 CART_TOTAL = Array.isArray(cartTotal) ? (cartTotal[cartTotal.length - 1]?.totalPrice ?? CART_TOTAL) : cartTotal.totalPrice; 3683 components.cartCountPrice(); 3684 3685 Cart.setStorage('add', response); 3686 3687 for(let i=0; i < callbacks.cart.add.length; i++){ 3688 if(typeof callbacks.cart.add[i] === 'function'){ 3689 try { 3690 callbacks.cart.add[i]?.(response); 3691 } catch (error) { console.log(`Cart Add Callback Error => ${error}`); } 3692 } 3693 } 3694 3695 for (let i = 0; i < Cart.callback.add.length; i++) { 3696 if (typeof Cart.callback.add[i] === 'function') { 3697 try { 3698 Cart.callback.add[i]?.(response); 3699 } catch (error) { console.warn(`Shopping basket adding Callback error => ${error}`) } 3700 } 3701 } 3702 } 3703 3704 if (opt.multi) { 3705 const errorMessage = []; 3706 for(let i=0; i<response.length; i++) { 3707 const that = response[i]; 3708 if(that.status < 1) { 3709 errorMessage.push(that); 3710 } 3711 else { 3712 opt.multiItem = that; 3713 } 3714 } 3715 3716 if (opt.multiItem != null) { 3717 switch (opt.multiItem.status) { 3718 case 1: 3719 opt.multi = false;
3720 if (opt.buyNow == 0) { 3721 cartModal(response); 3722 } else { 3723 window.location.href = '/' + opt.redirect; 3724 } 3725 break; 3726 case 2: 3727 window.location.href = opt.multiItem.url; 3728 break; 3729 case 3: 3730 opt.multi = false; 3731 if (opt.buyNow == 0) { 3732 cartModal(response); 3733 } else { 3734 window.location.href = '/' + opt.redirect; 3735 } 3736 break; 3737 } 3738 opt.multiItem = null; 3739 } 3740 if (errorMessage.length > 0) { 3741 let errorHtml = ''; 3742 errorMessage.forEach(msg => { 3743 if (msg.url && msg.title) { 3744 errorHtml += `<p>${msg.statusText} : <a href="/${msg.url}" class="underline" target="_blank">${msg.title}</a></p>`; 3745 } else { 3746 errorHtml += `<p>${msg.statusText}</p>`; 3747 } 3748 }); 3749 setTimeout(() => { 3750 notify({ 3751 text: errorHtml, 3752 class: 'danger', 3753 duration: 2400, 3754 }); 3755 }, 500); 3756 } 3757 } else { 3758 switch (response.status) { 3759 case -1: 3760 if (response.url != '') { 3761 if ((PAGE_TYPE !== 'product' || document.getElementById('product-id')?.value != opt.productId) && !document.querySelector('#popup-variant')) { 3762 variantModal(response); 3763 } else if (PAGE_TYPE === 'product' && document.getElementById('product-id')?.value === opt.productId) { 3764 const variantNotify = document.querySelector('[data-toggle="variant-notify"]'); 3765 const productItem = variantNotify.closest('[data-toggle="product"]'); 3766 if (variantNotify) { 3767 variantNotify.classList.remove('hidden'); 3768 if (productItem) productItem.classList.add('variant-notify'); 3769 scrollBehavior(variantNotify, 200); 3770 variantNotify.querySelector('button').addEventListener('click', e => { 3771 variantNotify.classList.add('hidden'); 3772 if (productItem) productItem.classList.remove('variant-notify'); 3773 }); 3774 } else { 3775 notify({ 3776 text: COMMON_LANG?.variant_notify ?? response.statusText, 3777 class: 'danger', 3778 duration: 2400, 3779 }); 3780 } 3781 } else { 3782 window.location.href = '/' + response.url; 3783 } 3784 return false; 3785 } 3786 break; 3787 case 0: 3788 if (response.personalization_error && (PAGE_TYPE !== 'product' || opt.relatedProductId) && response.url) { 3789 window.location.href = '/' + response.url; 3790 return false; 3791 } 3792 notify({ 3793 text: response.statusText || COMMON_LANG?.no_product_added, 3794 class: 'danger', 3795 duration: 2400, 3796 }); 3797 break; 3798 case -2: 3799 loadSnippet({ 3800 snippet: 'customer-address-store', 3801 params: { 3802 include: 'customer-address', 3803 productId: opt.productId, 3804 variantId: opt.variantId, 3805 quantity: opt.quantity, 3806 buyNow : opt.buyNow 3807 }, 3808 success: function(loadRes){ 3809 modal({
3810 id: 'modal-customer-address-store', 3811 html: loadRes, 3812 }); 3813 } 3814 }); 3815 break; 3816 case 1: 3817 if (opt.buyNow == 0) { 3818 modalClose('#modal-cart-popup-variant'); 3819 if (components.cartPreview.element) components.cartPreview.previewShow(true); 3820 if (components.cartDrawer.element) components.cartDrawer.element.click(); 3821 } else { 3822 window.location.href = '/' + opt.redirect; 3823 } 3824 break; 3825 case 2: 3826 window.location.href = response.url; 3827 break; 3828 case 3: 3829 if (opt.buyNow == 0) { 3830 cartModal(response); 3831 } else { 3832 window.location.href = '/' + opt.redirect; 3833 } 3834 break; 3835 case 4: 3836 if (opt.buyNow == 0) { 3837 modalClose('#modal-cart-popup-variant'); 3838 notify({ 3839 text: `<div>${response.statusText}</div><a href="/${PAGE_LINK.CART}" class="text-green-500 font-semibold underline">${COMMON_LANG?.go_to_cart}</a>`, 3840 duration: 5000, 3841 }); 3842 } else { 3843 window.location.href = '/' + opt.redirect; 3844 } 3845 default: 3846 break; 3847 } 3848 } 3849 3850 const products = document.querySelectorAll('input[data-toggle="multi-select"]:checked'); 3851 Array.from(products).map(item => item.checked = false); 3852 }); 3853 } catch (error) { 3854 console.warn(`ADD_TO_CART error => ${error}`); 3855 } 3856} 3857 3858const productLoader = { 3859 element: null, 3860 container: null, 3861 currentPage: Number(getUrlParam('ps') || 1), 3862 firstPage: Number(getUrlParam('ps') || 1), 3863 lastPage: null, 3864 options: { 3865 id: null, 3866 target: null, 3867 product: null, 3868 relationId: null, 3869 scrollUp: false, 3870 }, 3871 state: { 3872 loading: false, 3873 prefetching: false, 3874 canScrollDown: true, 3875 canScrollUp: true, 3876 lastScrollTop: 0, 3877 abortController: null, 3878 }, 3879 upBtn: null, 3880 downBtn: null, 3881 prefetchCache: new Map(), 3882 canPrefetch: function() { 3883 const conn = navigator.connection; 3884 if (!conn) return true; 3885 return !conn.saveData && !['slow-2g', '2g'].includes(conn.effectiveType); 3886 }, 3887 buildEndpoint: function(page) { 3888 let params = getLink('link', window.location.pathname.replaceAll(/^\//ig, '')); 3889 params = getLink('ps', '', params); 3890 params = getLink('pg', page, params); 3891 params = params.split('?')[1] || ''; 3892 return getEndpoint('BLOCK_PAGE', `${this.options.relationId}/products?${params}`); 3893 }, 3894 prefetchPage: function(page, prepend = false) { 3895 if (page < 1 || this.prefetchCache.has(page) || !this.canPrefetch() || this.state.loading) return; 3896 if (!prepend && this.lastPage != null && page > this.lastPage) return; 3897 3898 const already = this.container.querySelectorAll(`${this.options.product}[data-page="${page}"]`); 3899 if (already.length) return; 3900 3901 this.state.prefetching = true; 3902 3903 const controller = new AbortController(); 3904 const promise = request('GET', this.buildEndpoint(page), null, null, controller.signal) 3905 .catch((err) => { 3906 if (err?.name !== 'AbortError') { 3907 this.restoreScroll(prepend); 3908 } 3909 return null; 3910 }) 3911 .finally(() => { 3912 this.state.prefetching = false; 3913 }); 3914
3915 this.prefetchCache.set(page, { promise, controller }); 3916 }, 3917 paginationPushState: function(key, title, url) { 3918 window.pushstateloaded = true; 3919 window.history?.pushState && window.history.replaceState(key, title, url); 3920 }, 3921 addClassProduct: function(products) { 3922 const arr = Array.from(products); 3923 if (!arr.length) return; 3924 3925 arr[0].dataset.page = this.currentPage; 3926 arr[arr.length - 1].dataset.page = this.currentPage; 3927 3928 arr.forEach((product, index) => { 3929 product.dataset.index = `${this.currentPage}-${index}`; 3930 product.addEventListener('click', () => { 3931 setCookie('productIndex', JSON.stringify({ 3932 category: window.location.pathname, 3933 product: product.dataset.index, 3934 page: product.dataset.index?.split('-')[0] ?? getUrlParam('ps') ?? 1 3935 })); 3936 }); 3937 }); 3938 }, 3939 appendProduct: async function(html, prepend = false) { 3940 const parsed = new DOMParser().parseFromString(html, 'text/html'); 3941 const products = parsed.documentElement.querySelectorAll(this.options.product); 3942 if (!products.length) return; 3943 3944 const fragment = document.createDocumentFragment(); 3945 const arr = Array.from(products); 3946 3947 if (prepend) { 3948 arr.reverse().forEach(p => fragment.prepend(p)); 3949 } else { 3950 arr.forEach(p => fragment.append(p)); 3951 } 3952 3953 await awaitTimeOut(200); 3954 if (prepend) { 3955 const anchor = this.container.firstElementChild; 3956 this.container.prepend(fragment); 3957 requestAnimationFrame(() => { 3958 if (anchor) { 3959 window.scrollTo(0, anchor.offsetTop); 3960 } 3961 }); 3962 } else { 3963 this.container.append(fragment); 3964 } 3965 3966 await components.init(); 3967 this._runDynamicLoaderCallbacks(html); 3968 this.addClassProduct(products); 3969 setTimeout(() => { 3970 if (prepend) { 3971 this.state.canScrollUp = true; 3972 } else { 3973 this.state.canScrollDown = true; 3974 } 3975 }, 300); 3976 }, 3977 _runDynamicLoaderCallbacks: function(html) { 3978 const regex = /PRODUCT_DATA\.push\(JSON\.parse\('([^']+)'\)\);/g; 3979 const matches = []; 3980 3981 for (const match of html.matchAll(regex)) { 3982 try { 3983 const jsonStr = eval("'" + match[1] + "'"); 3984 matches.push(JSON.parse(jsonStr)); 3985 PRODUCT_DATA.push(JSON.parse(jsonStr)); 3986 } catch (err) { 3987 console.warn('productLoader: PRODUCT_DATA parse hatası', err); 3988 } 3989 } 3990 3991 if (!callbacks?.product?.dynamicLoader?.length) return; 3992 3993 callbacks.product.dynamicLoader.forEach(cb => { 3994 if (typeof cb === 'function') { 3995 try { cb(matches); } 3996 catch (err) { console.warn('productLoader: dynamicLoader callback hatası', err); } 3997 } 3998 }); 3999 }, 4000 getProduct: async function(page, prepend = false) { 4001 const nextPage = prepend ? Number(page) - 1 : Number(page) + 1; 4002 if (nextPage < 1) return; 4003 if (!prepend && this.lastPage != null && nextPage > this.lastPage) { 4004 this.restoreScroll(prepend); 4005 return; 4006 } 4007 4008 const already = this.container.querySelectorAll(`${this.options.product}[data-page="${nextPage}"]`); 4009 if (already.length) return; 4010 4011 if (this.state.loading) return; 4012 this.state.loading = true; 4013 4014 this.state.abortController?.abort(); 4015 4016 this.prefetchCache.forEach((entry, cachedPage) => { 4017 if (cachedPage !== nextPage) { 4018 entry?.controller?.abort(); 4019 this.prefetchCache.delete(cachedPage); 4020 } 4021 }); 4022 4023 this.state.abortController = new AbortController(); 4024 const { signal } = this.state.abortController; 4025 4026 if (SETTINGS.DYNAMIC_LOADING_SHOW_BUTTON != 1) { 4027 if (prepend) { 4028 this.state.canScrollUp = false;
4029 } else { 4030 this.state.canScrollDown = false; 4031 } 4032 } 4033 4034 loader(true); 4035 4036 try { 4037 const cached = this.prefetchCache.get(nextPage); 4038 this.prefetchCache.delete(nextPage); 4039 4040 let response; 4041 if (cached) { 4042 response = await (cached.promise ?? cached); 4043 } else { 4044 response = await request('GET', this.buildEndpoint(nextPage), null, null, signal); 4045 } 4046 4047 if (signal.aborted) return; 4048 if (response) { 4049 this.currentPage = nextPage; 4050 await this.appendProduct(response, prepend); 4051 4052 if (!prepend && this.lastPage != null && this.currentPage >= this.lastPage) { 4053 this.restoreScroll(false); 4054 } 4055 } else { 4056 this.restoreScroll(prepend); 4057 } 4058 } catch (err) { 4059 if (err?.name === 'AbortError') return; 4060 this.restoreScroll(prepend); 4061 } finally { 4062 loader(); 4063 this.state.loading = false; 4064 } 4065 }, 4066 restoreScroll: function(prepend) { 4067 if (prepend) { 4068 this.state.canScrollUp = false; 4069 } else { 4070 this.state.canScrollDown = false; 4071 if (this.downBtn) this.downBtn.remove(); 4072 } 4073 }, 4074 runScroll: function() { 4075 const _self = productLoader; 4076 const { clientHeight } = document.documentElement; 4077 const pageProducts = _self.container.querySelectorAll(`${_self.options.product}[data-page]`); 4078 if (!pageProducts.length) return; 4079 4080 pageProducts.forEach(product => { 4081 const rect = product.getBoundingClientRect(); 4082 const center = rect.top + rect.height / 2; 4083 if (center >= 0 && center <= window.innerHeight 4084 && (getUrlParam('ps') || '1') != product.dataset.page) { 4085 const url = product.dataset.page == 1 4086 ? getLink('ps', '') 4087 : getLink('ps', product.dataset.page); 4088 _self.paginationPushState('pagination', `Page ${product.dataset.page}`, url); 4089 } 4090 }); 4091 4092 const currentPage = getUrlParam('ps') || 1; 4093 const products = _self.container.querySelectorAll(`${_self.options.product}[data-page="${currentPage}"]`); 4094 if (!products.length) return; 4095 4096 const firstRect = products[0].getBoundingClientRect(); 4097 const lastRect = (products[1] ?? products[0]).getBoundingClientRect(); 4098 const currentST = window.scrollY || document.documentElement.scrollTop; 4099 4100 if (currentST > _self.state.lastScrollTop) { 4101 if (SETTINGS.DYNAMIC_LOADING_SHOW_BUTTON != 1) { 4102 if ((lastRect.bottom - (lastRect.height * 2)) < clientHeight) { 4103 _self.prefetchPage(Number(currentPage) + 1, false); 4104 } 4105 if (lastRect.bottom < clientHeight && _self.state.canScrollDown) { 4106 _self.getProduct(currentPage); 4107 } 4108 } 4109 } else if (currentST < _self.state.lastScrollTop && currentPage > 1) { 4110 if ((firstRect.top + (firstRect.height * 2)) > 0 && _self.options.scrollUp) { 4111 _self.prefetchPage(Number(currentPage) - 1, true); 4112 } 4113 if (firstRect.bottom > 0 && _self.options.scrollUp && _self.state.canScrollUp) { 4114 _self.getProduct(currentPage, true); 4115 } 4116 } 4117 4118 _self.state.lastScrollTop = currentST; 4119 }, 4120 throttle: function(fn, wait) { 4121 let last = 0; 4122 return (...args) => { 4123 const now = Date.now(); 4124 if (now - last >= wait) { 4125 last = now; 4126 fn(...args); 4127 } 4128 }; 4129 }, 4130 loadMoreBtn: function() { 4131 const _self = productLoader; 4132 4133 const psValue = Number(getUrlParam('ps') || 1); 4134 if (Number(psValue) > 1) { 4135 _self.upBtn = document.createElement('div'); 4136 _self.upBtn.className = 'w-full mb-4 lg:mb-6 product-loader-btn'; 4137 _self.upBtn.innerHTML = ` 4138 <button 4139 class="w-full btn" 4140 data-direction="up" 4141 data-qa="load-more-button" 4142 >${COMMON_LANG?.up_load_more_products}</button> 4143 `; 4144 _self.container.before(_self.upBtn); 4145 4146 _self.upBtn.querySelector('button').addEventListener('click', () => { 4147 const psValueUp = Number(getUrlParam('ps') || 1); 4148 document.documentElement.style.scrollBehavior = 'revert'; 4149 _self.upBtn.remove(); 4150 _self.options.scrollUp = true; 4151 _self.getProduct(psValueUp, true); 4152 }); 4153 } 4154 4155 if (SETTINGS.DYNAMIC_LOADING_SHOW_BUTTON == 1 && !(_self.lastPage != null && _self.currentPage >= _self.lastPage)) { 4156 _self.downBtn = document.createElement('div'); 4157 _self.downBtn.className = 'w-full text-center mb-4 lg:mb-6 product-loader-btn'; 4158 _self.downBtn.innerHTML = ` 4159 <button 4160 class="btn btn-primary" 4161 data-direction="down" 4162 data-qa="load-more-button" 4163 >${COMMON_LANG?.load_more_products}</button> 4164 `; 4165 _self.container.after(_self.downBtn); 4166 4167 _self.downBtn.querySelector('button').addEventListener('click', () => { 4168 _self.getProduct(_self.currentPage, false); 4169 }); 4170 } 4171 }, 4172 cookieSetScroll: async function() { 4173 const _self = productLoader; 4174 const psValue = Number(getUrlParam('ps') || 1); 4175 const cookieRaw = getCookie('productIndex');
4176 setCookie('productIndex', ''); 4177 4178 if ('scrollRestoration' in history && psValue > 1 && (_self.firstPage != psValue || SETTINGS.DYNAMIC_LOADING_SHOW_BUTTON)) { 4179 history.scrollRestoration = 'manual'; 4180 } 4181 4182 const scrollToTop = () => { 4183 if (psValue > 1 && _self.firstPage != psValue) { 4184 window.scrollTo(0, 0); 4185 } 4186 } 4187 const navType = performance.getEntriesByType?.('navigation')?.[0]?.type; 4188 if (navType === 'reload' || navType === 'navigate') { 4189 scrollToTop(); 4190 } 4191 4192 const pageshowHandler = async (event) => { 4193 const nav = performance.getEntriesByType?.('navigation')?.[0]; 4194 const isHistoryNavigation = nav?.type === 'back_forward' || event.persisted; 4195 document.documentElement.style.scrollBehavior = 'revert'; 4196 if (isHistoryNavigation && cookieRaw) { 4197 try { 4198 const saved = JSON.parse(cookieRaw) || {}; 4199 if (saved?.category === window.location.pathname && saved?.product) { 4200 document.body.style.overflow = 'hidden'; 4201 4202 const targetPage = saved.page || psValue; 4203 if (targetPage && targetPage !== psValue) { 4204 const direction = saved.page > psValue ? true : false; 4205 await _self.getProduct(targetPage, direction); 4206 } 4207 document.body.style.overflow = 'auto'; 4208 4209 const target = _self.container.querySelector(`${_self.options.product}[data-index="${saved.product}"]`); 4210 const fallback = _self.container.querySelector(`${_self.options.product}`); 4211 const scrollY = target ? target.offsetTop - 100 : (fallback ? fallback.offsetTop - 100 : 0); 4212 window.scrollTo(0, scrollY); 4213 } else { 4214 scrollToTop(); 4215 } 4216 } catch { 4217 scrollToTop(); 4218 } 4219 } 4220 }; 4221 4222 if (typeof _self._pageshowHandler === 'function') { 4223 window.removeEventListener('pageshow', _self._pageshowHandler); 4224 } 4225 _self._pageshowHandler = pageshowHandler; 4226 window.addEventListener('pageshow', pageshowHandler); 4227 4228 document.documentElement.style.scrollBehavior = 'smooth'; 4229 }, 4230 init: function(page = null) { 4231 const _self = productLoader; 4232 4233 if (typeof _self._destroy === 'function') _self._destroy(); 4234 _self._destroy = null; 4235 4236 if (page != null) _self.currentPage = page; 4237 4238 _self.element = document.querySelector('[data-toggle="product-loader"]'); 4239 if (!_self.element) return; 4240 4241 _self.options.id = _self.element.dataset.id ?? null; 4242 _self.options.target = _self.element.dataset.target ?? null; 4243 _self.options.product = _self.element.dataset.item ?? null; 4244 _self.lastPage = _self.element.dataset.lastPage !== undefined && _self.element.dataset.lastPage !== '' ? Number(_self.element.dataset.lastPage) : null; 4245 4246 if (!_self.options.id || !_self.options.target || !_self.options.product) { 4247 console.warn('productLoader: id, target veya product undefined'); 4248 return; 4249 } 4250 4251 _self.container = document.querySelector(_self.options.target); 4252 if (!_self.container) return; 4253 4254 _self.options.relationId = _self.container.closest('[data-block-id]')?.dataset.blockId ?? 0; 4255 if (!_self.options.relationId) return; 4256 4257 _self.state.abortController?.abort(); 4258 _self.state.abortController = null; 4259 _self.prefetchCache.forEach(entry => entry?.controller?.abort()); 4260 _self.prefetchCache.clear(); 4261 _self.state.loading = false; 4262 _self.state.prefetching = false; 4263 _self.state.canScrollDown = true; 4264 _self.state.canScrollUp = true; 4265 _self.state.lastScrollTop = 0; 4266 4267 const throttledScroll = _self.throttle(_self.runScroll, 16); 4268 _self.cookieSetScroll(); 4269 _self.loadMoreBtn(); 4270 4271 const initialProducts = _self.container.querySelectorAll(`${_self.options.product}`); 4272 if (initialProducts.length) _self.addClassProduct(initialProducts); 4273 4274 window.addEventListener('scroll', throttledScroll, { passive: true }); 4275 document.body.addEventListener('touchend', throttledScroll, { passive: true }); 4276 4277 const destroy = function() { 4278 window.removeEventListener('scroll', throttledScroll); 4279 document.body.removeEventListener('touchend', throttledScroll); 4280 4281 if (typeof _self._pageshowHandler === 'function') { 4282 window.removeEventListener('pageshow', _self._pageshowHandler); 4283 _self._pageshowHandler = null; 4284 } 4285 4286 _self.prefetchCache.forEach(entry => { 4287 entry?.controller?.abort(); 4288 }); 4289 _self.prefetchCache.clear(); 4290 4291 _self.state.abortController?.abort(); 4292 _self.state.abortController = null; 4293 }; 4294 _self._destroy = destroy; 4295 4296 return { destroy }; 4297 } 4298} 4299 4300const refererStorage = { 4301 ip: '', 4302 hasLocal: 'localStorage' in window && window['localStorage'] !== null, 4303 shouldAddItem: function(obj) { 4304 let items = obj.items || []; 4305 let last = items[items.length - 1]; 4306 let expired = typeof obj.schedule == "undefined" || Date.now() >= obj.schedule; 4307 let refHost = ''; 4308 if (document.referrer) { 4309 let a = document.createElement("a"); 4310 a.href = document.referrer; 4311 refHost = a.host; 4312 } 4313 if (/[?&](gclid|gbraid|wbraid|fbclid|msclkid|utm_source)=/.test(window.location.search)) { 4314 return !last || last.page != window.location.href; 4315 } 4316 if (refHost != '' && refHost != window.location.host) { 4317 return expired || !last || (last.uri || '').indexOf(refHost) == -1; 4318 } 4319 if (refHost != '') { 4320 return false;
4321 } 4322 return expired; 4323 }, 4324 addItemStorage: async function() { 4325 let cReferrer = localStorage.getItem('cReferrer'); 4326 let items = []; 4327 if (cReferrer !== null) { 4328 let obj = {}; 4329 try { 4330 obj = JSON.parse(cReferrer); 4331 } catch (err) { 4332 } 4333 if (!this.shouldAddItem(obj)) { 4334 return; 4335 } 4336 items = obj.items || []; 4337 } 4338 4339 try { 4340 if (!this.ip) { 4341 this.ip = await request('GET', getEndpoint('GET_IP')); 4342 } 4343 } catch (error) { 4344 console.warn(`GET_IP error => ${error}`); 4345 } 4346 items.push({ time: Number.parseInt(Date.now() / 1000), uri: document.referrer, page: window.location.href, ip: this.ip }); 4347 this.setItems(items); 4348 }, 4349 getObj: function() { 4350 let cReferrer = null; 4351 let cReferrerObj = {}; 4352 if (this.hasLocal) { 4353 cReferrer = localStorage.getItem('cReferrer'); 4354 } else { 4355 try { 4356 cReferrer = getCookie('cReferrer'); 4357 } catch (err) {} 4358 } 4359 try { 4360 cReferrerObj = JSON.parse(cReferrer); 4361 } catch (err) {} 4362 return cReferrerObj; 4363 }, 4364 getItems: function() { 4365 return this.getObj().items || []; 4366 }, 4367 setItems: function(items) { 4368 let obj = { 4369 schedule: Date.now() + (24 * 60 * 60 * 1000), 4370 items: items || [] 4371 }; 4372 this.save(obj); 4373 }, 4374 save: function(sObj) { 4375 if (this.hasLocal) { 4376 this.saveStorage(sObj); 4377 } else { 4378 this.saveCookie(sObj); 4379 } 4380 }, 4381 saveStorage: function(sObj) { 4382 localStorage.setItem('cReferrer', JSON.stringify(sObj)); 4383 }, 4384 saveCookie: function(sObj) { 4385 try { 4386 setCookie('cReferrer', JSON.stringify(sObj), { path: '/', expires: 30 }); 4387 } catch (err) {} 4388 } 4389} 4390 4391const desingMode = async function (mode = 0) { 4392 let newMode = Number.parseInt(mode) ? '0' : '1'; 4393 4394 await request('GET', getEndpoint('MOD_DESIGNER', `${newMode}`)).then(response => { 4395 if (response.status) window.location.reload(); 4396 }).catch(error => { 4397 console.warn(`mod-designer error => ${error}`); 4398 }); 4399} 4400 4401const getPluginsPopup = async function() { 4402 const cookieName = 't-plugins-popup'; 4403 if (PAGE_ID != 1 4404 || typeof SETTINGS.PLUGINS_POPUP == 'undefined' 4405 || SETTINGS.PLUGINS_POPUP?.IS_ACTIVE != 1 4406 || SETTINGS.PLUGINS_POPUP?.CONTENT == '' 4407 || getCookie(cookieName)) return; 4408 4409 setTimeout(() => { 4410 modal({ 4411 id: 'plugins-popup', 4412 html: SETTINGS.PLUGINS_POPUP.CONTENT, 4413 width: 'auto', 4414 callbacks: { 4415 open: function() {}, 4416 close: function() { 4417 setCookie(cookieName, '1', 1); 4418 }, 4419 }, 4420 }); 4421 }, 2000); 4422} 4423 4424const notificationAccept = { 4425 element: null, 4426 accept: function() { 4427 new TsoftWebPush(); 4428 notificationAccept.element.style.display = 'none'; 4429 }, 4430 reject: function() { 4431 notificationAccept.element.style.display = 'none'; 4432 setCookie('webpush', 1, 2); 4433 }, 4434 init: function() { 4435 notificationAccept.element = document.querySelector('[data-toggle="notification-permission"]'); 4436 if (!notificationAccept.element) return; 4437 4438 const webpush = typeof getCookie('webpush') === 'undefined' ? '0' : getCookie('webpush'), 4439 isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/); 4440 4441 if (isSafari === false && webpush !== '1' && ('Notification' in window)) { 4442 setTimeout(function() { 4443 notificationAccept.element.style.display = 'block'; 4444 }, 1000); 4445 } 4446 } 4447} 4448 4449const getInfluencerBar = async () => { 4450 loadSnippet({ 4451 snippet: '_influencer-bar-popup', 4452 params: { 4453 include: '_influencer-bar', 4454 }, 4455 success: function (loadRes) { 4456 modal({ 4457 id: 'influencer-bar-popup', 4458 html: loadRes, 4459 width: '520px', 4460 }); 4461 } 4462 }); 4463} 4464 4465const onPageReady = function() { 4466 if (typeof ON_PAGE_READY == 'undefined') return; 4467 for (let i = 0; i < ON_PAGE_READY.length; i++) { 4468 if (typeof ON_PAGE_READY[i] == 'function') { 4469 try { 4470 ON_PAGE_READY[i](); 4471 } catch (ex) { 4472 console.error(ex); 4473 } 4474 } 4475 } 4476} 4477 4478const lazyLoadedBg = async function(img) { 4479 const loadedBg = function(image) { 4480 const imgPos = image.getBoundingClientRect();
4481 const viewportHeight = window.innerHeight || document.documentElement.clientHeight; 4482 const viewportWidth = window.innerWidth || document.documentElement.clientWidth; 4483 const isOnView = imgPos.bottom >= 0 && imgPos.right >= 0 && 4484 imgPos.top <= viewportHeight && imgPos.left <= viewportWidth; 4485 4486 const style = image.dataset.style; 4487 if (isOnView && style) { 4488 image.setAttribute('style', style); 4489 image.classList.remove('style-load'); 4490 image.classList.add('style-loaded'); 4491 image.removeAttribute('data-style'); 4492 image.componentLoadedBg = 1; 4493 } 4494 return; 4495 } 4496 4497 if (img.dataset?.style) { 4498 loadedBg(img); 4499 return; 4500 } 4501 4502 const images = document.querySelectorAll('[data-style]'); 4503 Array.from(images).forEach(img => { 4504 if (img.componentLoadedBg == 1) return; 4505 loadedBg(img); 4506 }); 4507} 4508 4509const getCaptcha = async function(form = null, response = null) { 4510 if (form.captcha == 1) return; 4511 if (form == null || response == null) { 4512 console.warn('getCaptcha warn: form or response null'); 4513 return; 4514 }; 4515 4516 const field = response.field || response.key || ''; 4517 let isCaptcha = false; 4518 isCaptcha = field == 'security_code' || field == 'g-recaptcha-response'; 4519 4520 if (!isCaptcha) { 4521 return; 4522 }; 4523 4524 const formCaptcha = form.querySelector('[data-toggle="captcha"]'); 4525 if (!formCaptcha) { 4526 console.warn('getCaptcha warn: [data-toggle="captcha"] undefined'); 4527 return; 4528 }; 4529 4530 const key = formCaptcha.dataset.key; 4531 if (!key) { 4532 console.warn('getCaptcha warn: key or sitekey undefined'); 4533 return; 4534 }; 4535 4536 const url = getEndpoint('CAPTCHA', `${key}?size=small`); 4537 try { 4538 const response = await request('GET', url); 4539 if (!response?.includes('name="security_code"')) return; 4540 4541 formCaptcha.innerHTML = response; 4542 formCaptcha.parentNode.insertBefore(formCaptcha.content.cloneNode(true), formCaptcha); 4543 if (formCaptcha.dataset.size || formCaptcha.dataset.class) { 4544 const captchaElement = form.querySelector('[data-toggle="captcha-container"]'); 4545 if (captchaElement) { 4546 if (formCaptcha.dataset.size) captchaElement.classList.add(formCaptcha.dataset.size); 4547 if (formCaptcha.dataset.class) captchaElement.classList.add(formCaptcha.dataset.class); 4548 } 4549 } 4550 formCaptcha.remove(); 4551 4552 if (typeof grecaptcha == 'undefined' && field == 'g-recaptcha-response') { 4553 await new Promise(resolve => { 4554 const script = document.createElement('script'); 4555 script.src = 'https://www.google.com/recaptcha/api.js'; 4556 script.async = true; 4557 script.defer = true; 4558 document.body.appendChild(script); 4559 script.onload = () => { 4560 const checkRecaptcha = () => { 4561 if (typeof grecaptcha !== 'undefined' && grecaptcha.render) { 4562 resolve(); 4563 } else { 4564 setTimeout(checkRecaptcha, 100); 4565 } 4566 }; 4567 checkRecaptcha(); 4568 }; 4569 }); 4570 } 4571 } catch (error) { 4572 console.warn(`CAPTCHA(getCaptcha) ${url} error => ${error}`); 4573 } finally { 4574 form.captcha = 1; 4575 const recaptcha = form.querySelector('[data-toggle="google-recaptcha"]'); 4576 if (recaptcha && typeof grecaptcha !== 'undefined' && grecaptcha.render) { 4577 const widgetId = grecaptcha.render(recaptcha, {'sitekey': recaptcha.dataset.sitekey}); 4578 recaptcha.dataset.captchaId = widgetId; 4579 } 4580 4581 if (field == 'security_code') { 4582 const captcha = form.querySelector('[name="security_code"]'); 4583 if (captcha) captcha.focus(); 4584 } 4585 components.init(); 4586 } 4587} 4588 4589const captchaReset = async function(form = null) { 4590 if (form == null) { 4591 console.warn('captchaReset warn: element null'); 4592 return; 4593 }; 4594 4595 try { 4596 const captchaToggle = form.querySelector('[data-toggle="captcha-toggle"]'); 4597 if (captchaToggle) captchaToggle.click(); 4598 4599 const captcha = form.querySelector('[name="security_code"]'); 4600 if (captcha) captcha.value = ''; 4601 4602 const recaptcha = form.querySelector('[data-toggle="google-recaptcha"]'); 4603 if (recaptcha && typeof grecaptcha != 'undefined') { 4604 const widgetId = recaptcha.dataset.captchaId; 4605 if (widgetId) { 4606 grecaptcha.reset(Number.parseInt(widgetId)); 4607 } else { 4608 grecaptcha.reset(); 4609 } 4610 } 4611 } catch (error) {} 4612} 4613 4614const moneyExchange = function(price, from, to) { 4615 let k = from + '_TO_' + to; 4616 4617 if (typeof RATE === 'object' && typeof RATE[k] !== 'undefined') { 4618 return price * RATE[k]; 4619 } else { 4620 return price; 4621 } 4622} 4623 4624const fastClone = function(obj) { 4625 if (typeof obj !== 'object' || obj === null) return obj; 4626 if (obj instanceof Array) return obj.map(item => fastClone(item)); 4627 4628 const cloned = {}; 4629 for (const key in obj) { 4630 if (obj.hasOwnProperty(key)) { 4631 cloned[key] = fastClone(obj[key]); 4632 } 4633 } 4634 return cloned; 4635} 4636 4637const productItemScope = function(productItem, selectors, multiple = false) { 4638 if (!productItem || !selectors) return null; 4639 4640 const selectorList = selectors 4641 .split(',') 4642 .map(s => s.trim()) 4643 .filter(Boolean) 4644 .map(s => `${s}:not(:scope [data-toggle="product"] ${s})`) 4645 .join(', '); 4646 4647 if (!selectorList) return null; 4648 4649 return multiple 4650 ? productItem.querySelectorAll(`:scope ${selectorList}`) 4651 : productItem.querySelector(`:scope ${selectorList}`); 4652}; 4653 4654const getNestedValue = (obj, path) => { 4655 return path.split('.').reduce((acc, key) => acc?.[key], obj); 4656} 4657 4658const createSlider = function(element, index) { 4659 const id = element.dataset.id || element.closest('[data-block-id]')?.dataset.blockId || index; 4660 const BLOCK_SETTING = Array.from(BLOCKS).find(x => x.ID == id) || {}; 4661 const slider = element.querySelector('[data-swiper]'); 4662 const thumbSlider = element.querySelector('[data-swiper-thumb]'); 4663 const callback = element.dataset.callback; 4664 4665 const defaultSetting = { 4666 slider: { perview: {}, margin: {}, grid: {} }, 4667 thumbSlider: { perview: {}, margin: {}, grid: {} }, 4668 }; 4669 4670 function applyDatasetConfig(element, type, target) { 4671 const datasetValue = element?.dataset[type]; 4672 if (!datasetValue) return; 4673 4674 datasetValue.replaceAll(/\s+/g, "").split(",").forEach(item => { 4675 const [key, value] = item.split(":"); 4676 if (key && value) { 4677 target[type][key.trim()] = Number(value); 4678 } 4679 }); 4680 } 4681 4682 function defaultSettingFn(type) { 4683 applyDatasetConfig(slider, type, defaultSetting.slider); 4684 applyDatasetConfig(thumbSlider, type, defaultSetting.thumbSlider); 4685 } 4686 4687 ['perview', 'margin', 'grid'].forEach(defaultSettingFn); 4688 const breakpointsConfig = {0: 'xs',640: 'sm',768: 'md',1024: 'lg',1280: 'xl',1440: 'xxl'}; 4689 4690 function resolveValue(settingKey, size, fallbackSizes, defaultVal, target, name) { 4691 const blockVal = BLOCK_SETTING?.SETTING?.[settingKey]?.[size.toUpperCase()]; 4692 if (blockVal && blockVal != '0' && name == 'slider') return Number.parseFloat(blockVal); 4693 4694 for (const fb of fallbackSizes) { 4695 const val = target?.[settingKey.toLowerCase()]?.[fb]; 4696 if (val && val != '0') return Number.parseFloat(val); 4697 } 4698 4699 return defaultVal; 4700 } 4701 4702 function buildBreakpoints(target, name) { 4703 const breakpoints = {}; 4704 4705 for (const [bp, size] of Object.entries(breakpointsConfig)) { 4706 const fallbackSizes = Object.values(breakpointsConfig) 4707 .slice(0, Object.values(breakpointsConfig).indexOf(size) + 1) 4708 .reverse(); 4709 4710 breakpoints[bp] = { 4711 slidesPerView: resolveValue('PERVIEW', size, fallbackSizes, 'auto', target, name), 4712 slidesPerGroup: (BLOCK_SETTING?.SETTING?.LOOP || slider?.dataset?.loop) ? 1 : Math.floor(resolveValue('PERVIEW', size, fallbackSizes, 1, target, name)), 4713 spaceBetween: resolveValue('MARGIN', size, fallbackSizes, 0, target, name), 4714 grid: { 4715 rows: resolveValue('GRID', size, fallbackSizes, 1, target, name), 4716 }, 4717 }; 4718 } 4719 4720 return breakpoints; 4721 } 4722
4723 const sliderBreakpoints = buildBreakpoints(defaultSetting.slider, 'slider'); 4724 const thumbSliderBreakpoints = buildBreakpoints(defaultSetting.thumbSlider, 'thumbSlider'); 4725 4726 const swiperNoScope = function(selectors) { 4727 if (!selectors) return null; 4728 4729 const selectorList = selectors 4730 .split(',') 4731 .map(s => s.trim()) 4732 .filter(Boolean) 4733 .map(s => `${s}:not(:scope [data-toggle="slider"] ${s})`) 4734 .join(', '); 4735 4736 if (!selectorList) return null; 4737 4738 return element.querySelector(`:scope ${selectorList}`); 4739 }; 4740 4741 const nextEls = swiperNoScope('.swiper-button-next'); 4742 const prevEls = swiperNoScope('.swiper-button-prev'); 4743 const pagEls = swiperNoScope('.swiper-pagination'); 4744 const scrollbarEls = swiperNoScope('.swiper-scrollbar'); 4745 4746 if (BLOCK_SETTING?.SETTING?.PAGINATION_TYPE == 'progress') BLOCK_SETTING.SETTING.PAGINATION_TYPE = 'progressbar'; 4747 4748 let sliderDatasetDirection = slider?.dataset?.direction; 4749 if (slider?.dataset?.directionMobile && isMobile(768)) sliderDatasetDirection = slider?.dataset?.directionMobile; 4750 4751 let thumbSliderDatasetDirection = thumbSlider?.dataset?.direction; 4752 if (thumbSlider?.dataset?.directionMobile && isMobile(768)) thumbSliderDatasetDirection = thumbSlider?.dataset?.directionMobile; 4753 4754 BLOCK_JS[`SLIDER-${id}`] = { 4755 options: { 4756 direction: BLOCK_SETTING?.SETTING?.DIRECTION || sliderDatasetDirection || 'horizontal', 4757 loop: BLOCK_SETTING?.SETTING?.LOOP || slider?.dataset?.loop || false, 4758 initialSlide: Number.parseFloat(BLOCK_SETTING?.SETTING?.INITIAL_SLIDE) || Number.parseFloat(slider?.dataset?.initialSlide) || 0, 4759 centeredSlides: BLOCK_SETTING?.SETTING?.CENTER || slider?.dataset?.center || false, 4760 centerInsufficientSlides: slider?.dataset?.centerInsufficient || false, 4761 speed: Number.parseFloat(BLOCK_SETTING?.SETTING?.SPEED) || Number.parseFloat(slider?.dataset?.speed) || 500, 4762 effect: BLOCK_SETTING?.SETTING?.EFFECT || slider?.dataset?.effect || 'slide', 4763 creativeEffect: { 4764 prev: { 4765 shadow: true, 4766 translate: [0, 0, -400], 4767 }, 4768 next: { 4769 translate: ['100%', 0, 0], 4770 }, 4771 }, 4772 navigation: { 4773 nextEl: nextEls, 4774 prevEl: prevEls, 4775 }, 4776 pagination: { 4777 el: pagEls, 4778 type: BLOCK_SETTING?.SETTING?.PAGINATION_TYPE || 'bullets', 4779 clickable: true, 4780 dynamicBullets: BLOCK_SETTING?.SETTING?.PAGINATION_TYPE == 'dynamic' || slider?.dataset?.paginationDynamic || false, 4781 }, 4782 thumbs: { 4783 swiper: thumbSlider ?? '', 4784 }, 4785 scrollbar: { 4786 el: scrollbarEls, 4787 hide: true 4788 }, 4789 grid:{ 4790 fill: 'row', 4791 }, 4792 breakpoints: sliderBreakpoints, 4793 lazy: true, 4794 }, 4795 thumbOptions: { 4796 direction: thumbSliderDatasetDirection || 'horizontal', 4797 centerInsufficientSlides: thumbSlider?.dataset?.centerInsufficient || false, 4798 watchSlidesProgress: true, 4799 lazy: { 4800 loadPrevNext: true 4801 }, 4802 breakpoints: thumbSliderBreakpoints, 4803 }, 4804 init: function() { 4805 const self = this; 4806 4807 if ((BLOCK_SETTING?.SETTING?.AUTOPLAY && BLOCK_SETTING?.SETTING?.AUTOPLAY != 0) || (slider?.dataset?.autoplay && slider?.dataset?.autoplay != 0)) { 4808 self.options['autoplay'] = { 4809 delay: (BLOCK_SETTING?.SETTING?.AUTOPLAY || slider?.dataset?.autoplay) + '000' 4810 } 4811 } 4812 4813 if (thumbSlider) { 4814 thumbSlider.swiper?.destroy(); 4815 new Swiper(thumbSlider, { 4816 ...self.thumbOptions 4817 }); 4818 } else { 4819 self.thumbOptions = {}; 4820 } 4821 4822 slider.swiper?.destroy(); 4823 new Swiper(slider, { 4824 ...self.options 4825 }); 4826 if (!slider.closest('.folder-slider')) slider.swiper.slideTo(0, false,false); 4827 } 4828 } 4829 4830 BLOCK_JS[`SLIDER-${id}`]['element'] = element; 4831 BLOCK_JS[`SLIDER-${id}`]['slider'] = slider; 4832 BLOCK_JS[`SLIDER-${id}`]['thumbSlider'] = thumbSlider; 4833 BLOCK_JS[`SLIDER-${id}`]['blockId'] = id; 4834 4835 try { 4836 BLOCK_JS[`SLIDER-${id}`].init(); 4837 } catch (error) {} 4838 4839 if(typeof window[callback] == 'function'){ 4840 try { 4841 window[callback](BLOCK_JS[`SLIDER-${id}`]); 4842 console.log('%c ð Callback Hatırlatma ', 'background: #ff9800; color: white; font-size: 12px; font-weight: bold; padding: 4px 8px; border-radius: 3px;', 4843 `${callback} fonksiyonu çalıÅtı! â ï¸ Options veya thumbOptions deÄiÅikliklerinden sonra init(
4843) çaÄrılmalı`); 4844 } catch (error) {} 4845 } 4846 4847}; 4848 4849const createPicker = function(root, options = {}) { 4850 if (!root) return; 4851 4852 const defaultOptions = { 4853 minDate: null, 4854 maxDate: null, 4855 offDates: [], 4856 offDayIndexes: [], 4857 }; 4858 4859 let parsedFromDom = {}; 4860 try { 4861 const raw = root.dataset.dateProperties || root.dataset.datePickerProperties; 4862 if (raw && String(raw).trim()) { 4863 parsedFromDom = JSON.parse(String(raw)); 4864 } 4865 } catch (e) { 4866 console.warn('date-picker: data-date-properties JSON parse error', e); 4867 } 4868 4869 const normalizePickerOpts = function(o) { 4870 const minD = o.minDate != null && String(o.minDate).trim() !== '' && String(o.minDate) !== 'null' ? String(o.minDate) : null; 4871 const maxD = o.maxDate != null && String(o.maxDate).trim() !== '' && String(o.maxDate) !== 'null' ? String(o.maxDate) : null; 4872 return { 4873 minDate: minD, 4874 maxDate: maxD, 4875 offDates: Array.isArray(o.offDates) ? o.offDates.map(String) : [], 4876 offDayIndexes: Array.isArray(o.offDayIndexes) 4877 ? o.offDayIndexes.map(Number).filter(n => !Number.isNaN(n) && n >= 0 && n <= 6) 4878 : [], 4879 }; 4880 }; 4881 4882 const pickerOpts = normalizePickerOpts({ ...defaultOptions, ...parsedFromDom, ...options }); 4883 4884 const monthNames = [COMMON_LANG.january, COMMON_LANG.february, COMMON_LANG.march, COMMON_LANG.april, COMMON_LANG.may, COMMON_LANG.june, COMMON_LANG.july, COMMON_LANG.august, COMMON_LANG.september, COMMON_LANG.october, COMMON_LANG.november, COMMON_LANG.december]; 4885 const weekdayLangKeys = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']; 4886 const weekdayShortFallback = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; 4887 function weekdayShortLabelMonday0(index) { 4888 const key = weekdayLangKeys[index]; 4889 const v = COMMON_LANG && COMMON_LANG[key]; 4890 return (v != null && String(v).trim() !== '') ? String(v) : weekdayShortFallback[index]; 4891 } 4892 const presetActiveClasses = ['text-primary', 'bg-gray-100']; 4893 const dayBaseClasses = ['cursor-pointer', 'text-xs', 'text-gray-500', 'aspect-square', 'flex', 'flex-col', 'gap-0.5', 'items-center', 'justify-center', 'rounded']; 4894 const dayOutsideClasses = ['text-gray-500']; 4895 const dayTodayClasses = ['font-semibold']; 4896 const dayInRangeClasses = ['bg-primary/10', 'text-primary']; 4897 const dayEdgeClasses = ['bg-primary', 'text-white', 'rounded']; 4898 const dayDisabledClasses = ['opacity-40', 'cursor-not-allowed', 'pointer-events-none']; 4899 const dayHoverClasses = ['transition-colors', 'hover:bg-gray-100']; 4900 const dayEdgeHoverClasses = ['transition-opacity', 'hover:opacity-90']; 4901 4902 const state = { 4903 startDate: null, 4904 endDate: null, 4905 viewMonth: firstDayOfMonth(new Date()), 4906 activePreset: '' 4907 }; 4908 4909 const dateInputHidden = root.querySelector('[data-toggle="date-calendar-hidden"]'); 4910 const startInput = root.querySelector('[data-toggle="date-picker-start"]'); 4911 const endInput = root.querySelector('[data-toggle="date-picker-end"]'); 4912 const startHidden = root.querySelector('[data-toggle="date-picker-start-hidden"]'); 4913 const endHidden = root.querySelector('[data-toggle="date-picker-end-hidden"]');
4914 const month = root.querySelector('[data-toggle="date-calendar-month"]'); 4915 const month0 = root.querySelector('[data-toggle="date-picker-month-0"]'); 4916 const month1 = root.querySelector('[data-toggle="date-picker-month-1"]'); 4917 const days0 = root.querySelector('[data-toggle="date-picker-days-0"]'); 4918 const days1 = root.querySelector('[data-toggle="date-picker-days-1"]'); 4919 const days = root.querySelector('[data-toggle="date-calendar-days"]'); 4920 const calendarCallback = root.dataset.callback; 4921 4922 function addClasses(el, classes) { 4923 for (let i = 0; i < classes.length; i++) { 4924 el.classList.add(classes[i]); 4925 } 4926 } 4927 4928 function removeClasses(el, classes) { 4929 for (let i = 0; i < classes.length; i++) { 4930 el.classList.remove(classes[i]); 4931 } 4932 } 4933 4934 function pad(value) { 4935 return value < 10 ? '0' + value : String(value); 4936 } 4937 4938 function firstDayOfMonth(date) { 4939 return new Date(date.getFullYear(), date.getMonth(), 1); 4940 } 4941 4942 function addMonths(date, offset) { 4943 return new Date(date.getFullYear(), date.getMonth() + offset, 1); 4944 } 4945 4946 function dayStart(date) { 4947 return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0); 4948 } 4949 4950 function dayEnd(date) { 4951 return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999); 4952 } 4953 4954 function dayKey(date) { 4955 return date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate(); 4956 } 4957 4958 function formatDateInput(date) { 4959 if (!date) { 4960 return ''; 4961 } 4962 4963 return [ 4964 pad(date.getDate()), 4965 pad(date.getMonth() + 1), 4966 date.getFullYear() 4967 ].join('.'); 4968 } 4969 4970 function formatDateHidden(date) { 4971 if (!date) { 4972 return ''; 4973 } 4974 4975 return [ 4976 date.getFullYear(), 4977 pad(date.getMonth() + 1), 4978 pad(date.getDate()) 4979 ].join('-'); 4980 } 4981 4982 function isPickDisabled(cellDate) { 4983 const iso = [ 4984 cellDate.getFullYear(), 4985 pad(cellDate.getMonth() + 1), 4986 pad(cellDate.getDate()) 4987 ].join('-'); 4988 if (pickerOpts.minDate && iso < pickerOpts.minDate) return true; 4989 if (pickerOpts.maxDate && iso > pickerOpts.maxDate) return true; 4990 if (pickerOpts.offDates.includes(iso)) return true; 4991 const dowMonday0 = (cellDate.getDay() + 6) % 7; 4992 if (pickerOpts.offDayIndexes.includes(dowMonday0)) return true; 4993 return false; 4994 } 4995 4996 function parseInput(value) { 4997 const match = String(value || '').trim().match(/^(\d{2})\.(\d{2})\.(\d{4})$/); 4998 if (!match) { 4999 return null; 5000 } 5001 5002 const day = Number(match[1]);
5003 const month = Number(match[2]); 5004 const year = Number(match[3]); 5005 5006 const parsed = new Date(year, month - 1, day, 0, 0, 0, 0); 5007 if ( 5008 parsed.getFullYear() !== year || 5009 parsed.getMonth() !== month - 1 || 5010 parsed.getDate() !== day 5011 ) { 5012 return null; 5013 } 5014 5015 return parsed; 5016 } 5017 5018 function setPreset(preset) { 5019 const now = new Date(); 5020 let start; 5021 let end; 5022 5023 if (preset === 'today') { 5024 start = dayStart(now); 5025 end = dayEnd(now); 5026 } else if (preset === 'yesterday') { 5027 const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); 5028 start = dayStart(yesterday); 5029 end = dayEnd(yesterday); 5030 } else if (preset === 'last7') { 5031 start = dayStart(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6)); 5032 end = dayEnd(now); 5033 } else if (preset === 'last30') { 5034 start = dayStart(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 29)); 5035 end = dayEnd(now); 5036 } else if (preset === 'last90') { 5037 start = dayStart(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 89)); 5038 end = dayEnd(now); 5039 } else if (preset === 'last365') { 5040 start = dayStart(new Date(now.getFullYear(), now.getMonth(), now.getDate() - 364)); 5041 end = dayEnd(now); 5042 } else { 5043 return; 5044 } 5045 5046 state.startDate = start; 5047 state.endDate = end; 5048 state.viewMonth = firstDayOfMonth(start); 5049 state.activePreset = preset; 5050 render(); 5051 } 5052 5053 function selectDateFromCalendar(date, isCalendar = false) { 5054 const picked = new Date(date.getFullYear(), date.getMonth(), date.getDate()); 5055 5056 if (isPickDisabled(picked)) { 5057 return; 5058 } 5059 5060 if (isCalendar) { 5061 const pickedStart = dayStart(picked); 5062 state.startDate = pickedStart; 5063 if(typeof window[calendarCallback] == 'function'){ 5064 try { 5065 window[calendarCallback](formatDateHidden(state.startDate)); 5066 } catch (error) {} 5067 } 5068 render(); 5069 return; 5070 } 5071 5072 if (!state.startDate || (state.startDate && state.endDate)) { 5073 state.startDate = dayStart(picked); 5074 state.endDate = null; 5075 state.activePreset = ''; 5076 render(); 5077 return; 5078 } 5079 5080 if (!state.endDate) { 5081 const pickedStart = dayStart(picked); 5082 const pickedEnd = dayEnd(picked); 5083 5084 if (dayKey(picked) < dayKey(state.startDate)) { 5085 state.endDate = dayEnd(new Date(state.startDate)); 5086 state.startDate = pickedStart; 5087 } else { 5088 state.endDate = pickedEnd; 5089 } 5090 5091 state.activePreset = ''; 5092 render(); 5093 } 5094 } 5095 5096 function renderCalendar(monthDate, container, monthLabel) { 5097 container.innerHTML = ''; 5098 monthLabel.textContent = monthNames[monthDate.getMonth()] + ' ' + monthDate.getFullYear(); 5099 5100 const firstDay = new Date(monthDate.getFullYear(), monthDate.getMonth(), 1); 5101 const weekOffset = (firstDay.getDay() + 6) % 7; 5102 const gridStart = new Date(monthDate.getFullYear(), monthDate.getMonth(), 1 - weekOffset); 5103 5104 const startKey = state.startDate ? dayKey(state.startDate) : null; 5105 const endKey = state.endDate ? dayKey(state.endDate) : null; 5106 const todayKey = dayKey(new Date()); 5107 5108 for (let i = 0; i < 42; i++) { 5109 const cellDate = new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i); 5110 const key = dayKey(cellDate); 5111 const dayBtn = document.createElement('span'); 5112 let toggle = 'date-picker-day'; 5113 if (root.dataset.toggle == 'date-calendar' || options.type == 'calendar') toggle = 'date-calendar-day'; 5114 dayBtn.dataset.toggle = toggle; 5115 const dowMonday0 = (cellDate.getDay() + 6) % 7; 5116 const wdLabel = weekdayShortLabelMonday0(dowMonday0); 5117 dayBtn.innerHTML = '<div class="leading-none font-medium">' + String(cellDate.getDate()) + '</div><div class="leading-none text-[9px] opacity-75 hidden day-label">' + wdLabel + '</div>'; 5118 dayBtn.dataset.time = String(cellDate.getTime()); 5119 addClasses(dayBtn, dayBaseClasses); 5120 5121 if (cellDate.getMonth() !== monthDate.getMonth()) { 5122 addClasses(dayBtn, dayOutsideClasses); 5123 } 5124 5125 if (key === todayKey) { 5126 addClasses(dayBtn, dayTodayClasses); 5127 } 5128 5129 if (startKey !== null && endKey !== null && key > startKey && key < endKey) { 5130 addClasses(dayBtn, dayInRangeClasses); 5131 } 5132 5133 if ((startKey !== null && key === startKey) || (endKey !== null && key === endKey)) { 5134 addClasses(dayBtn, dayEdgeClasses); 5135 } 5136 5137 if (isPickDisabled(cellDate)) { 5138 dayBtn.dataset.disabled = '1'; 5139 addClasses(dayBtn, dayDisabledClasses); 5140 } else { 5141 const isEdge = (startKey !== null && key === startKey) || (endKey !== null && key === endKey); 5142 addClasses(dayBtn, isEdge ? dayEdgeHoverClasses : dayH
5142overClasses); 5143 } 5144 5145 container.appendChild(dayBtn); 5146 } 5147 } 5148 5149 function updatePresetButtons() { 5150 const presetButtons = root.querySelectorAll('[data-preset]'); 5151 for (let i = 0; i < presetButtons.length; i++) { 5152 const button = presetButtons[i]; 5153 const isActive = button.dataset.preset === state.activePreset; 5154 5155 if (isActive) { 5156 addClasses(button, presetActiveClasses); 5157 } else { 5158 removeClasses(button, presetActiveClasses); 5159 } 5160 } 5161 } 5162 5163 function updateInputs() { 5164 if (startInput) startInput.value = formatDateInput(state.startDate); 5165 if (endInput) endInput.value = formatDateInput(state.endDate); 5166 if (startHidden) startHidden.value = formatDateHidden(state.startDate); 5167 if (endHidden) endHidden.value = formatDateHidden(state.endDate); 5168 if (dateInputHidden) dateInputHidden.value = formatDateHidden(state.startDate); 5169 } 5170 5171 function render() { 5172 if (days0 && month0) renderCalendar(state.viewMonth, days0, month0); 5173 if (days1 && month1) renderCalendar(addMonths(state.viewMonth, 1), days1, month1); 5174 if (days && month) renderCalendar(state.viewMonth, days, month); 5175 updateInputs(); 5176 updatePresetButtons(); 5177 } 5178 5179 function clearRange() { 5180 state.startDate = null; 5181 state.endDate = null; 5182 state.activePreset = ''; 5183 state.viewMonth = firstDayOfMonth(new Date()); 5184 render(); 5185 5186 root.dispatchEvent(new CustomEvent('date-picker:reset', { 5187 bubbles: true 5188 })); 5189 } 5190 5191 function applyRange() { 5192 const payload = { 5193 start: formatDateHidden(state.startDate), 5194 end: formatDateHidden(state.endDate), 5195 preset: state.activePreset 5196 }; 5197 5198 root.dispatchEvent(new CustomEvent('date-picker:apply', { 5199 detail: payload, 5200 bubbles: true 5201 })); 5202 } 5203 5204 root.addEventListener('click', function (event) { 5205 const target = event.target; 5206 if (!(target instanceof Element)) { 5207 return; 5208 } 5209 5210 const preset = target.closest('[data-preset]'); 5211 if (preset) { 5212 setPreset(preset.dataset.preset); 5213 return; 5214 } 5215 5216 if (target.closest('[data-toggle="date-picker-prev-month"], [data-toggle="date-calendar-prev-month"]')) { 5217 state.viewMonth = addMonths(state.viewMonth, -1); 5218 render(); 5219 return; 5220 } 5221 5222 if (target.closest('[data-toggle="date-picker-next-month"], [data-toggle="date-picker-next-month-mobile"], [data-toggle="date-calendar-next-month"]')) { 5223 state.viewMonth = addMonths(state.viewMonth, 1); 5224 render(); 5225 return; 5226 } 5227 5228 if (target.closest('[data-toggle="date-picker-reset"]')) { 5229 clearRange(); 5230 return; 5231 } 5232 5233 if (target.closest('[data-toggle="date-picker-apply"]')) { 5234 applyRange(); 5235 return; 5236 } 5237 5238 let day = target.closest('[data-toggle="date-picker-day"]'); 5239 if (!day) day = target.closest('[data-toggle="date-calendar-day"]'); 5240 if (day && day.dataset.time) { 5241 if (day.dataset.disabled === '1') { 5242 return; 5243 } 5244 selectDateFromCalendar(new Date(Number(day.dataset.time)), root.dataset.toggle == 'date-calendar' || options.type == 'calendar'); 5245 } 5246 }); 5247 5248 if (startInput) { 5249 startInput.addEventListener('blur', function () { 5250 const parsed = parseInput(startInput.value); 5251 if (!parsed) { 5252 startInput.value = formatDateInput(state.startDate); 5253 return; 5254 } 5255 5256 const candidate = dayStart(parsed); 5257 if (isPickDisabled(candidate)) { 5258 startInput.value = formatDateInput(state.startDate); 5259 return; 5260 } 5261 5262 state.startDate = candidate; 5263 if (state.endDate && state.endDate.getTime() < state.startDate.getTime()) { 5264 state.endDate = dayEnd(new Date(state.startDate)); 5265 } 5266 5267 state.viewMonth = firstDayOfMonth(state.startDate); 5268 state.activePreset = ''; 5269 render(); 5270 }); 5271 } 5272 5273 if (endInput) { 5274 endInput.addEventListener('blur', function () { 5275 const parsed = parseInput(endInput.value); 5276 if (!parsed) { 5277 endInput.value = formatDateInput(state.endDate); 5278 return; 5279 } 5280 5281 const candidateEnd = dayEnd(parsed); 5282 if (isPickDisabled(candidateEnd)) { 5283 endInput.value = formatDateInput(state.endDate); 5284 return; 5285 } 5286 5287 state.endDate = candidateEnd; 5288 if (state.startDate && state.endDate.getTime() < state.startDate.getTime()) { 5289 state.startDate = dayStart(new Date(state.endDate)); 5290 } 5291 5292 state.viewMonth = firstDayOfMonth(state.startDate || state.endDate); 5293 state.activePreset = ''; 5294 render(); 5295 }); 5296 } 5297 5298 if (startInput) { 5299 startInput.addEventListener('keydown', function (event) { 5300 if (event.key === 'Enter') { 5301 startInput.blur(); 5302 } 5303 }); 5304 } 5305 5306 if (endInput) { 5307 endInput.addEventListener('keydown', function (event) { 5308 if (event.key === 'Enter') { 5309 endInput.blur(); 5310 } 5311 }); 5312 } 5313 5314 render(); 5315}; 5316 5317const stockBackup = { 5318 parse: function(response) { 5319 if (typeof response !== 'string') return response; 5320 try { 5321 return JSON.parse(response); 5322 } catch (error) { 5323 return null; 5324 } 5325 }, 5326 check: async function(holder, variantId = 0) { 5327 if (!holder || SETTINGS.STOCK_BACKUP != 1 || SETTINGS.NEGATIVE_STOCK == 1) return; 5328 5329 const formData = new FormData(); 5330 formData.append('product_id', holder.dataset.productId); 5331 formData.append('subproduct_id', variantId || 0); 5332 5333 let response = null; 5334 try { 5335 response = stockBackup.parse(await request('POST', getEndpoint('STOCK_ALTERNATIVES'), formData)); 5336 } catch (error) { 5337 console.warn(`STOCK_BACKUP error => ${error}`); 5338 } 5339 stockBackup.render(holder, response?.alternatives?.[0] || null); 5340 }, 5341 render: function(holder, alternative) { 5342 holder.innerHTML = ''; 5343 holder.classList.toggle('hidden', !alternative); 5344 if (!alternative) return; 5345 5346 const t = TRANSLATES['product-detail'] || {}; 5347 const text = document.createElement('div'); 5348 text.className = 'text-sm'; 5349 text.textContent = (t.stock_backup_offer || '').replace('{name}', alternative.name); 5350 5351 const button = document.createElement('button'); 5352 button.type = 'button'; 5353 button.className = 'btn btn-primary w-full h-10 mt-2'; 5354 button.textContent = (t.stock_backup_use || '').replace('{name}', alternative.name); 5355 button.addEventListener('click', () => stockBackup.use(alternative.field)); 5356 5357 holder.append(text, button); 5358 }, 5359 use: async function(field) { 5360 const formData = new FormData(); 5361 formData.append('field', field); 5362 try { 5363 await request('POST', getEndpoint('SET_BACKUP_STOCK'), formData); 5364 } finally { 5365 window.location.reload(); 5366 } 5367 }, 5368}; 5369 5370const showOutStock = async function(element) { 5371 if (element.nodeName != 'TEMPLATE' || element.componentOutStock == 1) return; 5372 element.componentOutStock = 1; 5373 5374 const url = element.dataset.url; 5375 if (!url) return; 5376 5377 try { 5378 await request('GET', url).then(response => { 5379 const elementClasses = Array.from(element.classList); 5380 const responseWrapper = document.createElement('div'); 5381 responseWrapper.innerHTML = response; 5382 responseWrapper.querySelectorAll('script').forEach(script => script.remove()); 5383 responseWrapper.querySelector('[data-toggle="show-out-stock"]').classList.add(...elementClasses); 5384 element.innerHTML = responseWrapper.innerHTML; 5385 element.parentNode.insertBefore(element.content.cloneNode(true), element); 5386 element.remove(); 5387 }); 5388 } catch (error) { 5389 console.warn(`OUT_STOCK error => ${error}`); 5390 } finally { 5391 components.init(); 5392 } 5393} 5394 5395const pointCalculation = function(productItem) { 5396 if (!productItem) return; 5397 5398 const cashPoints = productItem.querySelectorAll('[data-toggle="cash-point"]'); 5399 const cashPointAmounts = productItem.querySelectorAll('[data-toggle="cash-point-amount"]'); 5400 5401 const countId = productItem.dataset.count; 5402 if (!countId) return; 5403 5404 const count = productItem.querySelector(countId)?.value || 1; 5405
5406 cashPoints.forEach(cashPoint => { 5407 if (!cashPoint.dataset.point) cashPoint.dataset.point = cashPoint.innerHTML; 5408 cashPoint.innerText = Number(cashPoint.dataset.point) * Number(count); 5409 }); 5410 5411 cashPointAmounts.forEach(cashPointAmount => { 5412 if (!cashPointAmount.dataset.price) { 5413 const price = cashPointAmount.innerHTML || 0; 5414 cashPointAmount.dataset.price = priceToFloat(price); 5415 } 5416 cashPointAmount.innerText = format(cashPointAmount.dataset.price * Number(count)); 5417 }); 5418} 5419 5420components = { 5421 slider: function() { 5422 const sliders = document.querySelectorAll('[data-toggle="slider"]'); 5423 Array.from(sliders).forEach((element, index) => { 5424 if (element.componentSlider == 1) return; 5425 5426 createSlider(element, index); 5427 5428 element.componentSlider = 1; 5429 }); 5430 }, 5431 productImageSlider: function() { 5432 const productImageSliders = document.querySelectorAll('[data-toggle="product-image-slider"]'); 5433 Array.from(productImageSliders).forEach(sli
5433der => { 5434 if (slider.componentProductImageSlider == 1) return; 5435 if (slider.componentProductImageSliderBound == 1) return; 5436 5437 const isDesktopPointer = window.matchMedia('(hover: hover) and (pointer: fine)').matches; 5438 5439 const initProductImageSlider = () => { 5440 if (slider.componentProductImageSlider == 1) return; 5441 5442 const swiperElement = slider.classList.contains('swiper') ? slider : slider.querySelector('.swiper'); 5443 if (!swiperElement) return; 5444 5445 const paginationEl = slider.parentElement?.querySelector('.product-image-slider-pagination'); 5446 const options = { 5447 effect: 'fade', 5448 slidesPerView: 1, 5449 spaceBetween: 0, 5450 speed: 300, 5451 nested: true, 5452 }; 5453 if (paginationEl) { 5454 options.pagination = { 5455 el: paginationEl, 5456 clickable: true, 5457 }; 5458 } 5459 5460 swiperElement?.swiper?.destroy(); 5461 const sliderBlock = new Swiper(swiperElement, options); 5462 5463 slider.componentProductImageSlider = 1; 5464 5465 if (isDesktopPointer && slider.componentProductImageSliderMouseBound != 1) { 5466 let moveRaf = null; 5467 let lastIndex = -1; 5468 5469 const mouseMoveHandler = (event) => { 5470 if (!sliderBlock?.slides?.length || sliderBlock.destroyed) return; 5471 5472 if (moveRaf) cancelAnimationFrame(moveRaf); 5473 moveRaf = requestAnimationFrame(() => { 5474 const rect = swiperElement.getBoundingClientRect(); 5475 const containerWidth = rect.width; 5476 if (!containerWidth) return; 5477 5478 const mouseX = event.clientX - rect.left; 5479 const progress = Math.min(Math.max(mouseX / containerWidth, 0), 0.999999); 5480 const newIndex = Math.floor(progress * sliderBlock.slides.length); 5481 5482 if (newIndex === lastIndex) return; 5483 lastIndex = newIndex; 5484 sliderBlock.slideTo(newIndex, 0); 5485 }); 5486 }; 5487 5488 const mouseLeaveHandler = () => { 5489 if (moveRaf) { 5490 cancelAnimationFrame(moveRaf); 5491 moveRaf = null; 5492 } 5493 lastIndex = -1; 5494 if (!sliderBlock.destroyed) sliderBlock.slideTo(0, 0); 5495 }; 5496 5497 swiperElement.addEventListener('mousemove', mouseMoveHandler); 5498 swiperElement.addEventListener('mouseleave', mouseLeaveHandler); 5499 slider.componentProductImageSliderMouseBound = 1; 5500 } 5501 }; 5502 5503 slider.addEventListener('mouseenter', initProductImageSlider, { once: true }); 5504 slider.addEventListener('touchstart', initProductImageSlider, { once: true }); 5505 slider.addEventListener('focusin', initProductImageSlider, { once: true }); 5506 5507 if (slider.matches(':hover')) { 5508 initProductImageSlider(); 5509 } 5510 5511 slider.componentProductImageSliderBound = 1; 5512 }); 5513 }, 5514 menuHover: function() { 5515 const element = document.querySelector('[data-toggle="menu"]'); 5516 if (!element) return; 5517 if (element.componentMenuHover == 1) return; 5518 5519 let time; 5520 5521 setTimeout(() => { 5522 if (element.matches(':hover')) { 5523 element.classList.add('active'); 5524 } 5525 }, 250); 5526 5527 element.addEventListener('mouseenter', () => { 5528 time = setTimeout(() => { 5529 if (!element.classList.contains('active')) element.classList.add('active'); 5530 }, 250); 5531 }); 5532 5533 element.addEventListener('mouseleave', () => { 5534 element.classList.remove('active'); 5535 clearTimeout(time); 5536 }); 5537 5538 element.addEventListener('click', () => { 5539 if (element.classList.contains('active')) { 5540 element.classList.remove('active'); 5541 clearTimeout(time); 5542 } 5543 return; 5544 }); 5545 5546 element.componentMenuHover = 1; 5547 }, 5548 liveSearch: function() { 5549 const liveSearchs = document.querySelectorAll('[data-toggle="live-search"]');
5550 Array.from(liveSearchs).forEach(search => { 5551 if (search.componentLiveSearch == 1) return; 5552 5553 const id = search.closest('[data-block-id]')?.dataset.blockId; 5554 if (!id) { 5555 console.warn('liveSearch warn: id (data-block-id) undefined'); 5556 return; 5557 }; 5558 5559 BLOCK_JS[`LIVE_SEARCH`] = Vue.createApp({ 5560 data() { 5561 return { 5562 live_data: {}, 5563 input : PAGE_ID == 12 ? getUrlParam('q') : '', 5564 limit: 3, 5565 licence: search.dataset.licence || 0, 5566 timer: null, 5567 liveSearchRequestId: 0, 5568 categories: [], 5569 recentSearches: [], 5570 recentSearchStorageKey: 'recent-searches', 5571 outsideClick: null, 5572 CURRENCY: CURRENCY, 5573 t: TRANSLATES['header'], 5574 } 5575 }, 5576 methods: { 5577 getRecentSearches() { 5578 try { 5579 const storedSearches = JSON.parse(localStorage.getItem(this.recentSearchStorageKey) || '[]'); 5580 return Array.isArray(storedSearches) ? storedSearches : []; 5581 } catch (error) { 5582 console.warn(`recent search storage read error => ${error}`); 5583 return []; 5584 } 5585 }, 5586 pushRecentSearch(query) { 5587 const self = this; 5588 const normalizedQuery = typeof query === 'string' ? query.trim() : ''; 5589 if (!normalizedQuery) return; 5590 5591 try { 5592 const searches = self.getRecentSearches(); 5593 const alreadyExists = searches.some(item => String(item).trim().toLowerCase() === normalizedQuery.toLowerCase()); 5594 if (alreadyExists) return; 5595 5596 searches.unshift(normalizedQuery); 5597 const updatedSearches = searches.slice(0, 5); 5598 localStorage.setItem(self.recentSearchStorageKey, JSON.stringify(updatedSearches)); 5599 self.recentSearches = updatedSearches; 5600 } catch (error) { 5601 console.warn(`recent search storage error => ${error}`); 5602 } 5603 }, 5604 clearRecentSearches() { 5605 const self = this; 5606 try { 5607 localStorage.removeItem(self.recentSearchStorageKey); 5608 self.recentSearches = []; 5609 } catch (error) { 5610 console.warn(`recent search storage clear error => ${error}`); 5611 } 5612 }, 5613 selectRecentSearch(query) { 5614 const self = this; 5615 if (typeof query !== 'string') return; 5616 self.input = query; 5617 self.update(query); 5618 }, 5619 getCategories() { 5620 const self = this; 5621 5622 const isCategory = search.querySelector('[name="category"]'); 5623 if (!isCategory) return; 5624 5625 try { 5626 request('GET', getEndpoint('CATEGORY')).then(response => { 5627 self.categories = response; 5628 }); 5629 } catch (error) { 5630 console.warn(`CATEGORY error => ${error}`); 5631 } 5632 }, 5633 search(query = null) { 5634 const self = this; 5635 if (query == null) query = self.input; 5636 const requestId = ++self.liveSearchRequestId; 5637 self.timer = setTimeout(() => { 5638 if (self.outsideClick) { 5639 self.outsideClick = null; 5640 return 5641 }; 5642 try { 5643 request('POST', getEndpoint('SEARCH_PRODUCT', encodeURI(query))).then(response => { 5644 if (requestId !== self.liveSearchRequestId) return; 5645 const data = response.data; 5646 if (typeof data != 'object') return; 5647 5648 self.live_data['products'] = response.data; 5649 }); 5650 } catch (error) { 5651 console.warn(`SEARCH_PRODUCT error => ${error}`); 5652 } 5653 }, 350); 5654 }, 5655 searchAll(query = null) { 5656 const self = this; 5657 if (query == null) query = self.input; 5658 const requestId = ++self.liveSearchRequestId; 5659 self.timer = setTimeout(() => { 5660 if (self.outsideClick) { 5661 self.outsideClick = null; 5662 return; 5663 }; 5664 try { 5665 request('GET', getEndpoint('SEARCH_ALL', query)).then(response => { 5666 if (requestId !== self.liveSearchRequestId) return; 5667 const data = response; 5668 if (typeof data != 'object') return; 5669 5670 self.live_data = response; 5671 }); 5672 } catch (error) { 5673 console.warn(`SEARCH_ALL error => ${error}`); 5674 } 5675 }, 350); 5676 }, 5677 update(value = null) { 5678 const self = this; 5679 5680 if (value == null) { 5681 self.outsideClick = null; 5682 value = self.input; 5683 }
5684 if (typeof value !== 'string') value = String(value || ''); 5685 self.input = value; 5686 clearTimeout(self.timer); 5687 if (value.length >= self.limit) { 5688 self.licence == 1 ? self.searchAll(value) : self.search(value); 5689 } else { 5690 self.liveSearchRequestId++; 5691 self.live_data = {}; 5692 } 5693 }, 5694 submitForm() { 5695 const self = this; 5696 if (self.input.length >= self.limit) self.pushRecentSearch(self.input); 5697 if (search.nodeName == 'FORM') search.submit(); 5698 } 5699 }, 5700 watch: { 5701 'input'(value) { 5702 const self = this; 5703 self.update(value); 5704 }, 5705 'live_data'(value) { 5706 const self = this; 5707 const header = document.querySelector('header'); 5708 if (!header) return; 5709 5710 if ((value.categories?.length || value.brands?.length || value.products?.length) && self.input.length > 3) { 5711 header.classList.add('live-search-active'); 5712 } else { 5713 header.classList.remove('live-search-active'); 5714 } 5715 } 5716 }, 5717 mounted() { 5718 const self = this; 5719 self.recentSearches = self.getRecentSearches(); 5720 if (self.input.length >= self.limit) { 5721 self.pushRecentSearch(self.input); 5722 } 5723 5724 outsideClick(search, null, function() { 5725 self.outsideClick = true; 5726 self.live_data = {}; 5727 self.timer = null; 5728 return; 5729 }); 5730 const searchInput = search.querySelector('input[name="q"]'); 5731 if (searchInput && searchInput.componentLiveSearchLegacyInput != 1) { 5732 const onLegacyInput = function(event) { 5733 if (!event || !event.target) return; 5734 self.update(event.target.value); 5735 }; 5736 ['input', 'keyup', 'change', 'search'].forEach(eventName => { 5737 searchInput.addEventListener(eventName, onLegacyInput); 5738 }); 5739 searchInput.componentLiveSearchLegacyInput = 1; 5740 } 5741 self.getCategories(); 5742 }, 5743 }).mount(search); 5744 5745 search.componentLiveSearch = 1; 5746 }); 5747 }, 5748 lightGallery: { 5749 create: function(gallery, options = {}) { 5750 if (!gallery) return; 5751 5752 const lgId = gallery.getAttribute('lg-uid'); 5753 if (lgId && lgData && lgData[lgId]) { 5754 lgData[lgId].prevScrollTop = window.scrollY; 5755 lgData[lgId].destroy(true); 5756 } 5757 5758 const selector = gallery.querySelectorAll('.gallery-selector'); 5759 let defaultOpt = { 5760 actualSize: false, 5761 download: false, 5762 thumbnail: true, 5763 toggleThumb: false, 5764 thumbContHeight: 8, 5765 thumbWidth: 8, 5766 videoMaxWidth: '800px', 5767 zoom: false, 5768 selector: selector.length ? '.gallery-selector' : '', 5769 } 5770 5771 options = { ...defaultOpt, ...options }; 5772 lightGallery(gallery, options); 5773 5774 let galleryItems = gallery.querySelectorAll('a'); 5775 if (selector.length) galleryItems = gallery.querySelectorAll('.gallery-selector'); 5776 Array.from(galleryItems).forEach(item => { 5777 item.addEventListener('click', e => { 5778 e.preventDefault(); 5779 }); 5780 }); 5781 }, 5782 init: function() { 5783 const lightGalleries = document.querySelectorAll('[data-toggle="light-gallery"], [data-toggle="product-gallery"]');
5784 Array.from(lightGalleries).forEach(gallery => { 5785 if (gallery.componentlightGallery == 1) return; 5786 5787 setTimeout(() => { 5788 components.lightGallery.create(gallery); 5789 }, 100); 5790 5791 gallery.componentlightGallery = 1; 5792 }); 5793 } 5794 }, 5795 dropdown: function() { 5796 let button = null, body = null; 5797 5798 const hide = function() { 5799 if (button == null || body == null) return; 5800 5801 if (button && typeof button.dataset.callback === 'string' && typeof window[button.dataset.callback] === 'function') { 5802 try { 5803 window[button.dataset.callback](button, body); 5804 } catch (error) {} 5805 } 5806 5807 button.classList.remove('active'); 5808 body.classList.remove('active'); 5809 document.removeEventListener('click', toggle); 5810 } 5811 5812 const toggle = function(e) { 5813 const event = e.target; 5814 5815 if (body == null) return; 5816 5817 if (['A', 'BUTTON'].includes(event.nodeName)) { 5818 hide(); 5819 } else if (body.contains(event) || event.dataset.toggle == 'date-picker-day' || (event.dataset.toggle == 'date-calendar-day' || event.closest('[data-toggle="date-calendar-day"]'))) { 5820 return; 5821 } 5822 5823 hide(); 5824 return; 5825 } 5826 5827 const dropdowns = document.querySelectorAll('[data-toggle="dropdown"]'); 5828 Array.from(dropdowns).forEach((dropdown, index) => { 5829 if (dropdown.componentDropdown == 1) return; 5830 5831 const event = dropdown.dataset.event; 5832 if (event == 'hover' && !isMobile()) return; 5833 5834 dropdown.addEventListener('click', e => { 5835 if (e.target.href || e.target.closest('a') && event != 'hover') e.preventDefault(); 5836 if (button != dropdown && document.querySelector('[data-toggle="dropdown"].active')) hide(); 5837 5838 button = dropdown; 5839 5840 body = document.querySelector(dropdown.dataset.dropdown) || document.querySelector(`#${dropdown.dataset.dropdown}`) || document.querySelector(`.${dropdown.dataset.dropdown}`); 5841 if (!body) return; 5842 5843 if (button.classList.contains('active')) { 5844 hide(); 5845 return; 5846 } 5847 5848 button.classList.add('active'); 5849 body.classList.add('active'); 5850 5851 setTimeout(() => { 5852 document.addEventListener('click', toggle); 5853 return; 5854 }, 100); 5855 }); 5856 5857 dropdown.componentDropdown = 1; 5858 }); 5859 5860 }, 5861 language: function() { 5862 const languages = document.querySelectorAll('[data-toggle="language"]'); 5863 Array.from(languages).forEach((language) => { 5864 if (language.componentLanguage == 1) return; 5865 5866 let event = 'click'; 5867 if (language.nodeName == 'SELECT') event = 'change'; 5868 5869 language.addEventListener(event, e => { 5870 if (e.target.href || e.target.closest('a')) e.target.preventDefault(); 5871 let value = language.dataset.language; 5872 if (language.nodeName == 'SELECT') value = language.value; 5873 if (!value || value == LANGUAGE) return; 5874 5875 const callback = language.dataset.callback || ''; 5876 5877 setLanguage(value, callback); 5878 return; 5879 }); 5880 5881 language.componentLanguage = 1; 5882 }); 5883 5884 if (typeof CSRF_COOKIE != 'undefined' && CSRF_COOKIE) { 5885 const languageCache = document.querySelectorAll('[data-cache="language"]'); 5886 Array.from(languageCache).forEach(item => { 5887 item.innerText = LANGUAGE; 5888 }); 5889 } 5890 }, 5891 currency: function() { 5892 const currencies = document.querySelectorAll('[data-toggle="currency"]'); 5893 Array.from(currencies).forEach((currency) => { 5894 if (currency.componentCurrency == 1) return; 5895 5896 let event = 'click'; 5897 if (currency.nodeName == 'SELECT') event = 'change'; 5898 5899 currency.addEventListener(event, e => { 5900 if (e.target.href || e.target.closest('a')) e.target.preventDefault(); 5901 5902 let value = currency.dataset.currency; 5903 if (currency.nodeName == 'SELECT') value = currency.value; 5904 if (!value || value == currency) return; 5905 5906 const callback = currency.dataset.callback || ''; 5907 5908 setCurrency(value, callback); 5909 return; 5910 }); 5911 5912 currency.componentCurrency = 1; 5913 }); 5914 5915 if (typeof CSRF_COOKIE != 'undefined' && CSRF_COOKIE) { 5916 const currencyCache = document.querySelectorAll('[data-cache="currency"]'); 5917 Array.from(currencyCache).forEach(item => { 5918 item.innerText = CURRENCY; 5919 }); 5920 } 5921 }, 5922 exportCountry: function() { 5923 const countries = document.querySelectorAll('[data-toggle="export-country"]');
5924 Array.from(countries).forEach((country) => { 5925 if (country.componentExportCountry == 1) return; 5926 5927 let event = 'click'; 5928 if (country.nodeName == 'SELECT') event = 'change'; 5929 5930 country.addEventListener(event, e => { 5931 if (e.target.href || e.target.closest('a')) e.target.preventDefault(); 5932 let value = country.dataset.country; 5933 if (country.nodeName == 'SELECT') value = country.value; 5934 if (!value || country.classList.contains('active')) return; 5935 5936 setExportCountry(value); 5937 return; 5938 }); 5939 5940 country.componentExportCountry = 1; 5941 }); 5942 }, 5943 popup: function() { 5944 if (document.componentPopupDelegated == 1) return; 5945 5946 document.addEventListener('click', async e => { 5947 const popup = e.target.closest('[data-toggle="popup"]'); 5948 if (!popup || popup.classList.contains('disabled')) return; 5949 5950 e.preventDefault(); 5951 5952 popup.classList.add('disabled'); 5953 const isMemberRequired = popup.dataset.memberRequired; 5954 if (isMemberRequired == 1 && MEMBER_INFO.ID <= 0) { 5955 await loginPopup(); 5956 popup.classList.remove('disabled'); 5957 return; 5958 } 5959 5960 const options = { 5961 id: popup.dataset.id, 5962 class: popup.dataset.class, 5963 width: popup.dataset.width, 5964 title: popup.dataset.title, 5965 html: popup.dataset.html, 5966 timeout: popup.dataset.timeout, 5967 disabledClose: popup.dataset.disabledClose, 5968 url: popup.dataset.url, 5969 property: popup.dataset.property, 5970 } 5971 5972 let opt = {}; 5973 for (let key in options) { 5974 if (options[key] != undefined && options[key] != '') opt[key] = options[key]; 5975 } 5976 5977 if (opt.url) { 5978 try { 5979 await request('GET', opt.url).then(response => { 5980 opt.title = opt.title && response[opt.title] ? response[opt.title] : opt.title; 5981 opt.html = opt.property ? getNestedValue(response, opt.property) : response; 5982 }); 5983 } catch (error) { 5984 console.warn(`popup ${opt.url} error => ${error}`); 5985 } 5986 } else if (opt.html) { 5987 try { 5988 const htmlElement = popup.querySelector(opt.html) ?? document.querySelector(opt.html); 5989 if (htmlElement) { 5990 opt.html = htmlElement.value ?? htmlElement.innerHTML ?? ''; 5991 } 5992 } catch (error) { 5993 console.warn(`popup error => ${error}`); 5994 } 5995 } 5996 5997 if (opt.drawer) { 5998 drawer(opt); 5999 } else { 6000 modal(opt); 6001 } 6002 popup.classList.remove('disabled'); 6003 return; 6004 }); 6005 6006 document.componentPopupDelegated = 1; 6007 }, 6008 tabs: function() { 6009 const tabs = document.querySelectorAll('[data-toggle="tab"]'); 6010 Array.from(tabs).forEach((tab, index) => { 6011 const buttons = tab.querySelectorAll('[data-tab-target]'); 6012 Array.from(buttons).forEach((btn, index) => { 6013 if (btn.componentTabs == 1) return; 6014 6015 btn.addEventListener('click', async e => { 6016 const url = btn.dataset.url; 6017 const property = btn.dataset.property; 6018 const callback = btn.dataset.callback; 6019 const group = btn.dataset.tabGroup; 6020 6021 let result = null; 6022 if (url && !btn.classList.contains('loaded')) { 6023 try { 6024 await request('GET', url).then(response => { 6025 result = property ? getNestedValue(response, property) : response; 6026 return; 6027 }); 6028 } catch (error) { 6029 console.warn(`tabs loaded error => ${error}`); 6030 } 6031 } 6032
6033 Array.from(buttons).forEach(item => { 6034 const id = item.dataset.tabTarget; 6035 if (!id) return; 6036 6037 const target = tab.querySelector(id); 6038 if (!target) { 6039 console.warn('tabs warn: target undefined'); 6040 return; 6041 }; 6042 6043 const contentId = item.dataset.tabContent; 6044 const content = tab.querySelector(contentId); 6045 6046 if (group && item.dataset.tabGroup != group) return; 6047 6048 if (item == btn) { 6049 if (result != null) { 6050 if (content) { 6051 content.innerHTML = result; 6052 evalScripts(content.innerHTML); 6053 } else { 6054 target.innerHTML = result; 6055 evalScripts(target.innerHTML); 6056 } 6057 components.init(); 6058 btn.classList.add('loaded'); 6059 6060 if(typeof window[callback] == 'function'){ 6061 try { 6062 window[callback](btn, target, tab); 6063 } catch (error) {} 6064 } 6065 } 6066 item.classList.add('active'); 6067 target.classList.add('active'); 6068 target.classList.remove('hidden'); 6069 setTimeout(() => { 6070 target.classList.add('tab-loaded'); 6071 }, 200); 6072 } else { 6073 item.classList.remove('active'); 6074 target.classList.remove('active'); 6075 target.classList.add('hidden'); 6076 target.classList.remove('tab-loaded'); 6077 } 6078 }); 6079 6080 }); 6081 if (btn.classList.contains('active')) btn.classList.add('tab-loaded'); 6082 if (btn.classList.contains('active') && btn.dataset.url) btn.click(); 6083 6084 btn.componentTabs = 1; 6085 }); 6086 }); 6087 }, 6088 accordion: function () { 6089 const accordions = document.querySelectorAll('[data-toggle="accordion"]');
6090 Array.from(accordions).forEach((accordion, index) => { 6091 const type = accordion.dataset.type; 6092 const buttons = accordion.querySelectorAll('[data-accordion-target]'); 6093 6094 if (type == 'dropdown') { 6095 accordion.activeDropdown = null; 6096 accordion.activeDropdownTarget = null; 6097 6098 document.addEventListener('click', e => { 6099 if (accordion.activeDropdown && accordion.activeDropdownTarget) { 6100 if (!accordion.activeDropdown.contains(e.target) && !accordion.activeDropdownTarget.contains(e.target)) { 6101 accordion.activeDropdown.classList.remove('active'); 6102 accordion.activeDropdownTarget.classList.remove('active'); 6103 accordion.activeDropdownTarget.classList.add('hidden'); 6104 accordion.activeDropdown = null; 6105 accordion.activeDropdownTarget = null; 6106 } 6107 } 6108 return; 6109 }); 6110 } 6111 6112 Array.from(buttons).forEach((btn, index) => { 6113 if (btn.componentAccordion == 1) return; 6114 6115 if (btn.dataset.accordionMobile && isMobile()) { 6116 btn.classList.remove('active'); 6117 const id = btn.dataset.accordionTarget; 6118 const target = accordion.querySelector(id); 6119 if (!target) return; 6120 target.classList.remove('active'); 6121 target.classList.add('hidden'); 6122 } 6123 6124 btn.addEventListener('click', async e => { 6125 e.preventDefault(); 6126 6127 if (btn.dataset.accordionMobile && !btn.dataset.accordionDesktop && !isMobile()) return; 6128 6129 const url = btn.dataset.url; 6130 const property = btn.dataset.property; 6131 const callback = btn.dataset.callback; 6132 6133 let result = null; 6134 if (url && !btn.classList.contains('loaded')) { 6135 try { 6136 await request('GET', url).then(response => { 6137 result = property ? getNestedValue(response, property) : response; 6138 return; 6139 }); 6140 } catch (error) { 6141 console.warn(`accordion loaded error => ${error}`); 6142 } 6143 } 6144 6145 Array.from(buttons).forEach(item => { 6146 const id = item.dataset.accordionTarget; 6147 if (!id) return; 6148 6149 const target = accordion.querySelector(id); 6150 if (!target) return; 6151 6152 const contentId = item.dataset.accordionContent; 6153 const content = accordion.querySelector(contentId); 6154 6155 if (item == btn) { 6156 if (item.classList.contains('active')) { 6157 item.classList.remove('active'); 6158 target.classList.remove('active'); 6159 target.classList.add('hidden'); 6160 } else { 6161 if (result != null) { 6162 if (content) { 6163 content.innerHTML = result; 6164 evalScripts(content.innerHTML); 6165 } else { 6166 target.innerHTML = result; 6167 evalScripts(target.innerHTML); 6168 } 6169 components.init(); 6170 btn.classList.add('loaded'); 6171 6172 if(typeof window[callback] == 'function'){ 6173 try { 6174 window[callback](btn, target, accordion); 6175 } catch (error) {} 6176 } 6177 } 6178 6179 item.classList.add('active'); 6180 target.classList.add('active'); 6181 target.classList.remove('hidden'); 6182 } 6183 6184 if (type == 'dropdown') { 6185 accordion.activeDropdown = btn; 6186 accordion.activeDropdownTarget = target; 6187 } 6188 } else if (type == 'toggle' || type == 'dropdown') { 6189 item.classList.remove('active'); 6190 target.classList.remove('active'); 6191 target.classList.add('hidden'); 6192 } 6193 }); 6194 6195 }); 6196 if (btn.classList.contains('active') && btn.dataset.url) btn.click(); 6197 6198 btn.componentAccordion = 1; 6199 }); 6200 6201 }); 6202 }, 6203 drawer: function() { 6204 const drawers = document.querySelectorAll('[data-toggle="drawer"]');
6205 Array.from(drawers).forEach(btn => { 6206 if (btn.componentDrawer == 1) return; 6207 6208 const selector = btn.dataset.drawerTarget; 6209 if (!selector) return; 6210 6211 const drawer = document.querySelector(selector); 6212 if (!drawer) return; 6213 6214 const drawerCloseBtns = drawer.querySelectorAll('[data-drawer-close]'); 6215 if (drawerCloseBtns.length > 0) { 6216 Array.from(drawerCloseBtns).forEach(item => { 6217 const closeSelector = item.dataset.drawerTarget; 6218 item.addEventListener('click', e => { 6219 6220 e.preventDefault(); 6221 if (closeSelector) { 6222 const drawer = document.querySelector(closeSelector); 6223 if (!drawer) return; 6224 6225 if (!drawer.closest('.drawer')) document.body.classList.remove('drawer-active'); 6226 drawer.classList.remove('active'); 6227 } else { 6228 document.body.classList.remove('drawer-active'); 6229 drawer.classList.remove('active'); 6230 } 6231 }); 6232 }); 6233 } 6234 6235 drawer.addEventListener('click', e => { 6236 if (e.target == drawer && drawer.contains(e.target)) { 6237 document.body.classList.remove('drawer-active'); 6238 const drawers = document.querySelectorAll('.drawer.active'); 6239 drawers.forEach(drawer => { 6240 drawer.classList.remove('active'); 6241 }); 6242 } 6243 return; 6244 }); 6245 6246 const url = btn.dataset.url; 6247 const property = btn.dataset.property; 6248 const callback = btn.dataset.callback; 6249 6250 btn.addEventListener('click', async e => { 6251 e.preventDefault(); 6252 6253 const isMemberRequired = btn.dataset.memberRequired; 6254 if (isMemberRequired == 1 && MEMBER_INFO.ID <= 0) { 6255 await loginPopup(); 6256 return; 6257 } 6258 6259 if (url && !btn.classList.contains('loaded')) { 6260 try { 6261 await request('GET', url).then(response => { 6262 const result = property ? getNestedValue(response, property) : response; 6263 drawer.querySelector('.drawer-container').innerHTML = result; 6264 evalScripts(drawer.innerHTML); 6265 components.init(); 6266 btn.classList.add('loaded'); 6267 }); 6268 } catch (error) { 6269 console.warn(`drawer loaded error => ${error}`); 6270 } 6271 } 6272 6273 document.body.classList.add('drawer-active'); 6274 drawer.classList.add('active'); 6275 6276 if(typeof window[callback] == 'function'){ 6277 try { 6278 window[callback](btn, drawer); 6279 } catch (error) { 6280 console.warn(`drawer callback error => ${error}`); 6281 } 6282 } 6283 }); 6284 6285 btn.componentDrawer = 1; 6286 }); 6287 }, 6288 tooltip: function() { 6289 const tooltips = document.querySelectorAll('[data-toggle="tooltip"]'); 6290 let activeTooltip = null; 6291 6292 Array.from(tooltips).forEach(tooltip => { 6293 if (tooltip.componentTooltip == 1) return; 6294 6295 const tooltipTitle = tooltip.dataset.title; 6296 if (!tooltipTitle) return; 6297 6298 const wrapper = document.createElement('div'); 6299 wrapper.className = 'tooltip-wrapper fixed z-[999999997] max-w-72'; 6300 6301 const element = document.createElement('div'); 6302 element.className = 'tooltip block text-xs bg-black/90 text-white rounded py-1 px-3'; 6303 element.innerHTML = tooltipTitle; 6304 6305 const tooltipPos = tooltip.dataset.position; 6306 6307 const arrow = document.createElement('div'); 6308 const arrowBaseClass = 'absolute size-2 rotate-45'; 6309 arrow.className = `${arrowBaseClass} start-1/2 -translate-x-1/2 rtl:translate-x-1/2`; 6310 element.append(arrow); 6311 wrapper.appendChild(element); 6312 6313 const createTooltip = function(e) { 6314 if (!isMobile()) e.stopPropagation(); 6315 6316 if (activeTooltip && activeTooltip !== wrapper) { 6317 removeTooltip(activeTooltip); 6318 } 6319 6320 if (document.body.contains(wrapper)) return; 6321 6322 document.body.appendChild(wrapper); 6323 activeTooltip = wrapper; 6324 6325 const rect = tooltip.getBoundingClientRect(); 6326 let x_pos = rect.left; 6327 let y_pos = rect.top; 6328 6329 if (tooltipPos == 'bottom') { 6330 arrow.className = `${arrowBaseClass} start-1/2 top-0 -translate-x-1/2 rtl:translate-x-1/2 -translate-y-1/2`; 6331 x_pos = x_pos + (tooltip.offsetWidth / 2) - (element.offsetWidth / 2); 6332 y_pos = rect.bottom + 8; 6333 } else if (tooltipPos == 'right') { 6334 arrow.className = `${arrowBaseClass} start-0 top-1/2 -translate-x-1/2 rtl:translate-x-1/2 -translate-y-1/2`; 6335 x_pos = rect.right + 8; 6336 y_pos = rect.top + (rect.height / 2) - (element.offsetHeight / 2); 6337 } else if (tooltipPos == 'left') { 6338 arrow.className = `${arrowBaseClass} end-0 top-1/2 translate-x-1/2 rtl:-translate-x-1/2 -translate-y-1/2`; 6339 x_pos = rect.left - element.offsetWidth - 8; 6340 y_pos = rect.top + (rect.height / 2) - (element.offsetHeight / 2); 6341 } else { /* top (default) */ 6342 x_pos = x_pos + (tooltip.offsetWidth / 2) - (element.offsetWidth / 2); 6343 y_pos = rect.top - element.offsetHeight - 8; 6344 } 6345 6346 const viewportWidth = window.innerWidth; 6347 const tooltipWidth = element.offsetWidth; 6348 const padding = 8; 6349 6350 if (x_pos + tooltipWidth > viewportWidth - padding) { 6351 x_pos = viewportWidth - tooltipWidth - padding; 6352 } 6353 if (x_pos < padding) { 6354 x_pos = padding; 6355 } 6356 6357 wrapper.style.left = `${x_pos}px`; 6358 wrapper.style.top = `${y_pos}px`; 6359 6360 element.style.opacity = '1'; 6361 element.style.transform = 'scale(1)'; 6362 }; 6363 6364 const removeTooltip = function(tooltipElement) { 6365 if (tooltipElement) { 6366 tooltipElement.remove(); 6367 } 6368 }; 6369 6370 element.style.transition = 'opacity 0.2s ease-in-out, transform 0.2s ease-in-out'; 6371 element.style.opacity = '0'; 6372 element.style.transform = 'scale(0.9)'; 6373 6374 if (isMobile()) { 6375 tooltip.addEventListener('click', createTooltip); 6376 outsideClick(tooltip, null, () => { 6377 setTimeout(() => removeTooltip(wrapper), 100); 6378 }); 6379 } else { 6380 tooltip.addEventListener('mouseenter', createTooltip); 6381 tooltip.addEventListener('mouseleave', () => { 6382 setTimeout(() => removeTooltip(wrapper), 100); 6383 }); 6384 } 6385 6386 document.addEventListener('scroll', () => { 6387 if (activeTooltip === wrapper) { 6388 removeTooltip(wrapper); 6389 } 6390 return; 6391 }, { passive: true }); 6392 6393 tooltip.componentTooltip = 1; 6394 }); 6395 }, 6396 copyText: function() { 6397 const copyTexts = document.querySelectorAll('[data-toggle="copy"]');
6398 Array.from(copyTexts).forEach(copy => { 6399 if (copy.componentCopyText == 1) return; 6400 6401 copy.addEventListener('click', e => { 6402 const text = copy.dataset.copyText; 6403 if (!text) return; 6404 6405 copyText(text, copy.dataset.message || null); 6406 return; 6407 }); 6408 6409 copy.componentCopyText = 1; 6410 }); 6411 }, 6412 share:function(){ 6413 const shareButtons = document.querySelectorAll('[data-toggle="share"]'); 6414 Array.from(shareButtons).forEach(shareBtn => { 6415 if (shareBtn.componentShare == 1 ) return; 6416 shareBtn.addEventListener('click', function (e) { 6417 const title = encodeURIComponent(this.dataset.title); 6418 const image = encodeURIComponent(this.dataset.image); 6419 const link = encodeURIComponent(this.dataset.url); 6420 6421 if (isMobile() && navigator.share) { 6422 navigator.share({ 6423 title: this.dataset.title || 'Share', 6424 text: this.dataset.text || 'share', 6425 url: this.dataset.url || window.location.href, 6426 }) 6427 } else if (title && image && link) { 6428 loadSnippet({ 6429 snippet: '_share-popup', 6430 params: { 6431 include: 'common', 6432 link: link, 6433 image: image, 6434 title: title, 6435 }, 6436 success: function (loadRes) { 6437 modal({ 6438 id: 'modal-share', 6439 html: loadRes, 6440 width: 'auto', 6441 }); 6442 return; 6443 } 6444 }); 6445 } 6446 }); 6447 6448 shareBtn.componentShare = 1; 6449 }); 6450 6451 const shareTargetButtons = document.querySelectorAll('[data-toggle="share-button"]'); 6452 Array.from(shareTargetButtons).forEach(shareTargetBtn => { 6453 if (shareTargetBtn.componentShareTarget == 1) return; 6454 6455 shareTargetBtn.addEventListener('click', function (e) { 6456 e.preventDefault(); 6457 const url = shareTargetBtn.dataset.url; 6458 if (!url) return; 6459 window.open(url, '_blank'); 6460 }); 6461 6462 shareTargetBtn.componentShareTarget = 1; 6463 }); 6464 }, 6465 mask: function() { 6466 const masks = document.querySelectorAll('[data-toggle="mask"], input[type="password"], input[type="email"], input[name="name"], input[name="surname"], input[name="fullname"], input[name="firstname"], input[name="lastname"], [name="identity_number"]'); 6467 6468 Array.from(masks).forEach(element => { 6469 if (element.componentMask == 1) return; 6470 6471 if (element.name == 'identity_number') { 6472 mask(element, 'number', null, 11); 6473 } else { 6474 mask(element); 6475 } 6476 6477 element.componentMask = 1; 6478 }); 6479 }, 6480 sticky: function() { 6481 const stickies = document.querySelectorAll('[data-toggle="sticky"]'); 6482 Array.from(stickies).forEach(sticky => { 6483 if (sticky.componentSticky == 1) return; 6484 6485 const isMobileDevice = isMobile(); 6486 const mobileOnly = sticky.dataset.mobile === 'true'; 6487 const desktopOnly = sticky.dataset.desktop === 'true'; 6488 const device = sticky.dataset.device; 6489 if ((mobileOnly || device === 'mobile') && !isMobileDevice) return; 6490 if ((desktopOnly || device === 'desktop') && isMobileDevice) return; 6491 6492 const positionClass = sticky.dataset.class || sticky.dataset.stickyClass || 'top-0 fixed z-50'; 6493 const classes = `${positionClass} sticky-active`.split(' '); 6494 const downClasses = 'sticky-down'; 6495 const upClasses = 'sticky-up'; 6496 const shipHeight = sticky.hasAttribute('skip-height'); 6497 6498 let stickyH = 0; 6499 let gap = 0; 6500 let direction = 0; 6501 let ticking = false;
6502 let measureTicking = false; 6503 let pendingScrollUpdate = false; 6504 const triggerSelector = sticky.dataset.stickyAfter; 6505 const triggerEl = triggerSelector ? document.querySelector(triggerSelector) : null; 6506 6507 const wrapper = document.createElement('div'); 6508 wrapper.className = 'w-full sticky-wrapper'; 6509 sticky.parentNode.replaceChild(wrapper, sticky); 6510 wrapper.appendChild(sticky); 6511 6512 const measure = () => { 6513 measureTicking = false; 6514 stickyH = sticky.offsetHeight; 6515 if (triggerEl) { 6516 const rect = triggerEl.getBoundingClientRect(); 6517 gap = rect.top + window.scrollY + rect.height; 6518 } else { 6519 gap = wrapper.getBoundingClientRect().top + window.scrollY; 6520 } 6521 6522 if (pendingScrollUpdate) { 6523 pendingScrollUpdate = false; 6524 handleScroll(); 6525 } 6526 }; 6527 6528 const scheduleMeasure = () => { 6529 pendingScrollUpdate = true; 6530 if (measureTicking) return; 6531 measureTicking = true; 6532 requestAnimationFrame(measure); 6533 }; 6534 6535 scheduleMeasure(); 6536 6537 if (document.fonts) { 6538 document.fonts.ready.then(scheduleMeasure); 6539 } 6540 6541 window.addEventListener('resize', scheduleMeasure); 6542 6543 const handleScroll = () => { 6544 const scrollY = window.scrollY; 6545 6546 if (sticky.closest('header')) { 6547 HEADER_STICKY_HEIGHT = stickyH; 6548 } 6549 6550 if (scrollY > gap) { 6551 if (!shipHeight) wrapper.style.height = `${stickyH}px`; 6552 sticky.classList.add(...classes); 6553 } else { 6554 if (!shipHeight) wrapper.style.height = null; 6555 sticky.classList.remove(...classes, downClasses, upClasses); 6556 } 6557 6558 if (scrollY > gap + stickyH) { 6559 if (direction < scrollY) { 6560 sticky.classList.add(downClasses); 6561 sticky.classList.remove(upClasses); 6562 } else if (direction > scrollY) { 6563 sticky.classList.remove(downClasses); 6564 sticky.classList.add(upClasses); 6565 } 6566 direction = scrollY; 6567 } 6568 }; 6569 6570 window.addEventListener('scroll', () => { 6571 if (!ticking) { 6572 requestAnimationFrame(() => { 6573 if (measureTicking) { 6574 pendingScrollUpdate = true; 6575 } else { 6576 handleScroll(); 6577 } 6578 ticking = false; 6579 return; 6580 }); 6581 ticking = true; 6582 } 6583 }); 6584 6585 sticky.componentSticky = 1; 6586 }); 6587 }, 6588 flagMask: async function() { 6589 if (typeof FLAG_MASK === 'undefined') return; 6590 6591 const flags = document.querySelectorAll('[data-toggle="flag"]'); 6592 if (!flags.length) return; 6593 6594 if (!FLAG_MASK.loadPromise) { 6595 FLAG_MASK.loadPromise = (async () => { 6596 try { 6597 const response = await request('GET', getEndpoint('COUNTRY')); 6598 const countries = response.countries || []; 6599 const data = []; 6600 6601 for (i in countries) { 6602 const country = countries[i]; 6603 if (country.code) { 6604 const fItem = { 6605 code: String(country.phone_code), 6606 key: country.code, 6607 name: country.title, 6608 pattern: country.pattern 6609 }; 6610 data.push(fItem); 6611 } 6612 } 6613 6614 FLAG_MASK.flagData = data; 6615 FLAG_MASK.flagSelected = MEMBER_INFO?.COUNTRY || MEMBER_INFO?.E_COUNTRY || response?.selected || 'TR'; 6616 if (FLAG_MASK.flagData?.length == 1) FLAG_MASK.flagSelected = FLAG_MASK.flagData[0].key; 6617 } catch (error) { 6618 console.warn(`Flag Mask Error => ${error}`); 6619 } 6620 })(); 6621 } 6622 6623 await FLAG_MASK.loadPromise; 6624
6625 Array.from(flags).forEach(flag => { 6626 if (flag.componentFlagMask == 1 || flag.closest('[v-cloak]')) return; 6627 6628 try { 6629 FLAG_MASK.init(flag); 6630 } catch (error) { console.log(error) } 6631 6632 flag.componentFlagMask = 1; 6633 }); 6634 }, 6635 taxLoader: function() { 6636 if (LANGUAGE != 'tr') return; 6637 const taxes = document.querySelectorAll('[data-toggle="tax-list"]'); 6638 Array.from(taxes).forEach(tax => { 6639 if (tax.componentTaxLoader == 1) return; 6640 6641 try { 6642 const opt = { 6643 selector: tax, 6644 } 6645 taxLoader(opt); 6646 } catch (error) { console.log(error) } 6647 6648 tax.componentTaxLoader = 1; 6649 }); 6650 }, 6651 passwordDifficulty: function() { 6652 const colors = ['red', 'orange', 'yellow', 'lime', 'green']; 6653 const passwords = document.querySelectorAll('[data-toggle="password-difficulty"]'); 6654 Array.from(passwords).forEach(pass => { 6655 if (pass.componentPasswordDifficulty == 1) return; 6656 6657 try { 6658 const selector = pass.dataset.selector; 6659 const selectorElement = document.querySelector(selector); 6660 if (!selectorElement) return; 6661 6662 const title = selectorElement.querySelector('[data-toggle="password-difficulty-title"]'); 6663 const progressbar = selectorElement.querySelector('[data-toggle="password-difficulty-progressbar"]'); 6664 if (!title || !progressbar) return; 6665 6666 const setDifficulty = (value) => { 6667 let difficulty = 0; 6668 if (value.length) { 6669 if (value.length >= 8) difficulty++; 6670 if (/[a-z]/.test(value)) difficulty++; 6671 if (/[A-Z]/.test(value)) difficulty++; 6672 if (/\d/.test(value)) difficulty++; 6673 if (/[.#?!@$%^&*-]/.test(value)) difficulty++; 6674 } 6675 6676 if (difficulty > 0) { 6677 selectorElement.style.display = ''; 6678 } else { 6679 selectorElement.style.display = 'none'; 6680 } 6681 6682 if (difficulty < 5) { 6683 title.innerHTML = COMMON_LANG.weak_password; 6684 title.classList.remove(`text-green-500`); 6685 title.classList.add(`text-red-500`); 6686 } else { 6687 title.innerHTML = COMMON_LANG.strong_password; 6688 title.classList.remove(`text-red-500`); 6689 title.classList.add(`text-green-500`); 6690 } 6691 6692 colors.forEach(color => { 6693 progressbar.classList.remove(`bg-${color}-500`); 6694 }); 6695 6696 const afterColor = colors[difficulty - 1]; 6697 if (afterColor) { 6698 progressbar.classList.add(`bg-${afterColor}-500`); 6699 } 6700 6701 progressbar.style.width = `${difficulty * 20}%`; 6702 pass.dataset.difficulty = difficulty; 6703 }; 6704 6705 pass.addEventListener('input', e => { 6706 setDifficulty(pass.value); 6707 return; 6708 }); 6709 6710 setDifficulty(pass.value); 6711 } catch (error) { console.log(error) } 6712 6713 pass.componentPasswordDifficulty = 1; 6714 }); 6715 }, 6716 passwordToggle: function() { 6717 const passwords = document.querySelectorAll('[data-toggle="password-toggle"]');
6718 Array.from(passwords).forEach(pass => { 6719 if (pass.componentPasswordToggle == 1) return; 6720 6721 try { 6722 const selector = pass.dataset.selector; 6723 if (!document.querySelector(selector)) return; 6724 6725 pass.addEventListener('click', e => { 6726 e.preventDefault(); 6727 passwordToggle(pass, document.querySelector(selector)); 6728 return; 6729 }); 6730 } catch (error) { console.log(error) } 6731 6732 pass.componentPasswordToggle = 1; 6733 }); 6734 }, 6735 captchaToggle: function() { 6736 const captchas = document.querySelectorAll('[data-toggle="captcha-toggle"]'); 6737 Array.from(captchas).forEach(captcha => { 6738 if (captcha.componentCaptchaToggle == 1) return; 6739 6740 try { 6741 const selector = captcha.dataset.selector; 6742 const element = document.querySelector(selector); 6743 if (!element) return; 6744 6745 captcha.addEventListener('click', e => { 6746 e.preventDefault(); 6747 element.setAttribute('src', `/SecCode.php?${new Date().getTime()}`); 6748 return; 6749 }); 6750 } catch (error) { console.log(error) } 6751 6752 captcha.componentCaptchaToggle = 1; 6753 }); 6754 }, 6755 countDown: function() { 6756 const countDowns = document.querySelectorAll('[data-toggle="count-down"]'); 6757 6758 const timers = Array.from(countDowns) 6759 .map(count => { 6760 if (count.componentCountDown == 1) return null; 6761 6762 const timer = count.dataset.timer; 6763 let targetDate; 6764 6765 if (timer.includes('.')) { 6766 const [day, month, year] = timer.split('.'); 6767 targetDate = new Date(year, month - 1, day, 0, 0, 0); 6768 } else if (timer.includes(',')) { 6769 const [year, month, day, hour, minute, second] = timer.split(','); 6770 targetDate = new Date( 6771 year, 6772 month - 1, 6773 day, 6774 hour || 0, 6775 minute || 0, 6776 second || 0 6777 ); 6778 } else { 6779 console.warn('Invalid date format'); 6780 return null; 6781 } 6782 6783 count.componentCountDown = 1; 6784 6785 return { 6786 element: count, 6787 targetDate 6788 }; 6789 }) 6790 .filter(Boolean); 6791 6792 6793 if (timers.length) { 6794 const interval = setInterval(() => { 6795 const now = Date.now(); 6796 6797 timers.forEach((timer, index) => { 6798 const { element, targetDate } = timer; 6799 const amount = targetDate.getTime() - now; 6800 6801 if (amount <= 0) { 6802 element.remove(); 6803 timers[index] = null; 6804 return; 6805 } 6806 6807 const days = Math.floor(amount / 86400000); 6808 const hours = Math.floor((amount % 86400000) / 3600000); 6809 const minutes = Math.floor((amount % 3600000) / 60000); 6810 const seconds = Math.floor((amount % 60000) / 1000); 6811 6812 const day = element.querySelector('.day'); 6813 const hour = element.querySelector('.hour'); 6814 const minute = element.querySelector('.minute'); 6815 const second = element.querySelector('.second'); 6816 6817 if (day) day.textContent = days; 6818 if (hour) hour.textContent = String(hours).padStart(2, '0'); 6819 if (minute) minute.textContent = String(minutes).padStart(2, '0'); 6820 if (second) second.textContent = String(seconds).padStart(2, '0'); 6821 }); 6822 6823 if (!timers.some(Boolean)) { 6824 clearInterval(interval); 6825 } 6826 }, 1000); 6827 } 6828 }, 6829 formSubmit: function() { 6830 const formSubmits = document.querySelectorAll('[data-toggle="form-submit"],[data-toggle="meta-form-submit"]');
6831 Array.from(formSubmits).forEach(form => { 6832 if (form.tagName.toLowerCase() !== 'form') return; 6833 6834 if (form.componentFormSubmit == 1) return; 6835 6836 form.addEventListener('submit', async e => { 6837 e.stopPropagation(); 6838 e.preventDefault(); 6839 6840 popoverAlertHide(form); 6841 if (!checkValidity(form)) return; 6842 6843 const formData = new FormData(form); 6844 6845 let endpoint = ''; 6846 if (form.dataset.endpoint) { 6847 let splitEndpoint = form.dataset.endpoint.split(','); 6848 if (splitEndpoint.length > 1) { 6849 endpoint = getEndpoint(splitEndpoint[0].trim(), splitEndpoint[1].trim()); 6850 } else { 6851 endpoint = getEndpoint(splitEndpoint[0].trim()); 6852 } 6853 } else if (form.action) { 6854 endpoint = form.action; 6855 } 6856 if (!endpoint) return; 6857 6858 const method = (form.method).toUpperCase() || 'POST'; 6859 6860 const recaptchaResponse = formData.get('g-recaptcha-response'); 6861 if (recaptchaResponse != null && typeof grecaptcha !== 'undefined') formData.delete('security_code'); 6862 6863 const iso = formData.get('iso_code'); 6864 if (iso) { 6865 const phone = form.querySelector('[type="tel"]'); 6866 if (phone?.dataset.key) formData.set('iso_code', phone.dataset.key.toLowerCase()); 6867 } 6868 6869 const statusText = formData.get('status_text'); 6870 if (statusText) formData.delete('status_text'); 6871 6872 const submitButton = e.submitter; 6873 if (submitButton) submitButton.setAttribute('disabled', 'true'); 6874 6875 try { 6876 await request(method, endpoint, formData).then(async response => { 6877 if (endpoint === getEndpoint('FORM')) { 6878 const htmlToText = (html) => { 6879 if (!html || typeof html !== 'string') return html; 6880 const doc = new DOMParser().parseFromString(html, 'text/html'); 6881 doc.querySelectorAll('script, style').forEach(el => el.remove()); 6882 const firstParagraph = doc.querySelector('p')?.textContent.trim(); 6883 if (firstParagraph) return firstParagraph; 6884 return doc.body.textContent.replace(/\s+/g, ' ').trim(); 6885 }; 6886 6887 const message = typeof response === 'string'? htmlToText(response) : (response?.statusText || response?.message || ''); 6888 6889 notify({ 6890 text: message, 6891 class: 'notr', 6892 }); 6893 6894 return; 6895 } 6896 if (form.dataset.toggle == 'meta-form-submit') { 6897 for(let i=0; i < callbacks.metaForm.submit.length; i++){ 6898 if(typeof callbacks.metaForm.submit[i] === 'function'){ 6899 try { 6900 callbacks.metaForm.submit[i]?.(response, formData); 6901 } catch (error) { console.log(`Meta Form Submit Callback Error => ${error}`); } 6902 } 6903 } 6904 } 6905 6906 if (typeof window[form.dataset.callback] === 'function') { 6907 try { 6908 window[form.dataset.callback]?.(response, formData); 6909 } catch (error) {} 6910 6911 await getCaptcha(form, response); 6912 return; 6913 } 6914 6915 if (response.status || response.success) { 6916 modalClose(); 6917 modal({ 6918 html: response.statusText || response.message || statusText || COMMON_LANG?.send_success, 6919 width: '480px', 6920 alert: true, 6921 class: 'success', 6922 }); 6923 form.reset(); 6924 } else { 6925 await getCaptcha(form, response); 6926 6927 const field = response.field || response.key || ''; 6928 let selector = form.querySelector(`[name="${field == 'g-recaptcha-response' ? 'security_code' : field}"]`); 6929 if (selector) { 6930 popoverAlert({ 6931 selector: selector, 6932 message: response.statusText || response.message || COMMON_LANG?.form_required, 6933 }); 6934 } else { 6935 notify({ 6936 text: response.statusText || response.message || COMMON_LANG?.send_error, 6937 class: 'danger', 6938 }); 6939 } 6940 } 6941 }); 6942 } catch (error) { 6943 if (form.dataset.toggle == 'meta-form-submit') { 6944 requestError(error, 'MetaFormSubmit', form); 6945 } else { 6946 console.warn(`formSubmit error => ${error}`); 6947 } 6948 } finally { 6949 await captchaReset(form); 6950 if (submitButton) submitButton.removeAttribute('disabled'); 6951 } 6952 }); 6953 6954 form.componentFormSubmit = 1; 6955 }); 6956 }, 6957 getRelated() { 6958 if (isMobile()) return; 6959 6960 const relateds = document.querySelectorAll('[data-toggle="product-card-related"]');
6961 Array.from(relateds).forEach(related => { 6962 if (related.componentGetRelated == 1) return; 6963 6964 const productItem = related.closest('[data-toggle="product"]'); 6965 if (!productItem) { 6966 console.warn('getRelated warn: productItem undefined'); 6967 return; 6968 }; 6969 6970 const productId = productItem.dataset.id; 6971 if (!productId) { 6972 console.warn('getRelated warn: productId undefined'); 6973 return; 6974 }; 6975 6976 const RELATED_PRODUCT_COUNT = related.dataset?.count || 4; 6977 6978 const relatedId = related.dataset.relatedId || 1; 6979 let hoverTimeout; 6980 6981 related.addEventListener('mouseenter', () => { 6982 if (related.classList.contains('related-loaded')) return; 6983 6984 hoverTimeout = setTimeout(async () => { 6985 productItem.classList.add('opacity-50'); 6986 related.classList.add('pointer-events-none'); 6987 6988 related['relatedApp'] = Vue.createApp({ 6989 data() { 6990 return { 6991 PRODUCTS: [], 6992 PRODUCT_COUNT: RELATED_PRODUCT_COUNT, 6993 t: TRANSLATES['product-list'] || TRANSLATES['showcase'] || TRANSLATES['common'] 6994 } 6995 }, 6996 methods: { 6997 async getProductRelated() { 6998 const self = this; 6999 try { 7000 const response = await request('GET', getEndpoint('PRODUCT_RELATED', `${productId}/${relatedId}`)); 7001 if (!response.PRODUCTS?.length) { 7002 console.warn('getRelated warn: PRODUCT_RELATED PRODUCTS length'); 7003 return; 7004 } 7005 7006 self.PRODUCTS = response.PRODUCTS; 7007 } catch (error) { 7008 console.warn(`PRODUCT_RELATED error => ${error}`); 7009 } finally { 7010 related.classList.add('related-loaded'); 7011 productItem.classList.remove('opacity-50'); 7012 related.classList.remove('pointer-events-none'); 7013 components.init(); 7014 } 7015 } 7016 }, 7017 async mounted() { 7018 const self = this; 7019 7020 await self.getProductRelated(); 7021 related.classList.add('related-first-loaded'); 7022 7023 setTimeout(() => { 7024 related.classList.remove('related-first-loaded'); 7025 }, 1500); 7026 } 7027 }).mount(related); 7028 }, 300); 7029 }); 7030 7031 related.addEventListener('mouseleave', () => { 7032 if (related.classList.contains('related-loaded')) return; 7033 clearTimeout(hoverTimeout); 7034 }); 7035 7036 related.componentGetRelated = 1; 7037 }); 7038 return; 7039 }, 7040 catalog: { 7041 get: function(catalog, productItem, productId, catalogCode, productData = null) { 7042 let interval; 7043 let speed = 250; 7044 7045 function startAutoSlide(swiper, direction) { 7046 interval = setInterval(() => { 7047 if (direction === 'next') { 7048 swiper.slideNext(); 7049 } else if (direction === 'prev') { 7050 swiper.slidePrev(); 7051 } 7052 }, speed); 7053 } 7054 7055 function stopAutoSlide() { 7056 clearInterval(interval); 7057 } 7058 7059 catalog['catalogApp'] = Vue.createApp({ 7060 data() { 7061 return { 7062 ATTRIBUTES: [], 7063 t: TRANSLATES['product-list'] 7064 } 7065 }, 7066 methods: { 7067 async getAttribute(product_id = null, product = true) { 7068 if (product_id == null) return; 7069 const self = this; 7070 7071 if (catalog[`PRODUCT_ATTRIBUTES_${product_id}`]) { 7072 self.ATTRIBUTES = fastClone(catalog[`PRODUCT_ATTRIBUTES_${product_id}`]);
7073 Array.from(self.ATTRIBUTES).forEach(item => { 7074 const selectedOption = item.OPTIONS.find(opt => opt.SELECTED === true); 7075 if (selectedOption) { 7076 item['SELECT_ID'] = selectedOption.ID; 7077 item['SELECT_NAME'] = selectedOption.NAME; 7078 if (self.ATTRIBUTES.CATALOG_VIEW == 'select' || self.ATTRIBUTES.CATALOG_VIEW == '') item['SELECT_SEARCH'] = item['SELECT_NAME']; 7079 } 7080 }); 7081 } else { 7082 const formData = new FormData(); 7083 formData.append('product_id', product_id); 7084 formData.append('with_image', '1'); 7085 7086 try { 7087 await request('POST', getEndpoint('CATALOG_ATTRIBUTES'), formData).then(response => { 7088 if (!response?.length) { 7089 console.warn('productCardCatalog warn: CATALOG_ATTRIBUTES length'); 7090 return; 7091 }; 7092 self.ATTRIBUTES = response; 7093 7094 Array.from(self.ATTRIBUTES).forEach(item => { 7095 item.OPTIONS = item.OPTIONS.filter(opt => opt.PRODUCT_ID != null); 7096 const selectedOption = item.OPTIONS.find(opt => opt.SELECTED === true); 7097 if (selectedOption) { 7098 item['SELECT_ID'] = selectedOption.ID; 7099 item['SELECT_NAME'] = selectedOption.NAME; 7100 if (item.CATALOG_VIEW == 'select' || item.CATALOG_VIEW == '') item['SELECT_SEARCH'] = item['SELECT_NAME']; 7101 } 7102 }); 7103 catalog[`PRODUCT_ATTRIBUTES_${product_id}`] = fastClone(self.ATTRIBUTES); 7104 }); 7105 } catch (error) { 7106 console.warn(`CATALOG_ATTRIBUTES error => ${error}`); 7107 } 7108 } 7109 7110 if (product == null) { 7111 catalog.ATTRIBUTES = fastClone(self.ATTRIBUTES); 7112 self.getProductProperties(); 7113 } 7114 7115 setTimeout(() => { 7116 self.slideAttribute(); 7117 }, 50); 7118 7119 productItem.classList.remove('catalog-opacity'); 7120 catalog.classList.remove('pointer-events-none'); 7121 catalog.classList.add('catalog-loaded'); 7122 return; 7123 }, 7124 setAttribute(attribute = null, option = null) { 7125 const self = this; 7126 7127 if (option.target?.value) option = Array.from(attribute.OPTIONS).find(x => x.ID == option.target.value); 7128 if (attribute == null || option == null) return; 7129 if (attribute['SELECT_ID'] == option.ID) return; 7130 7131 Array.from(attribute.OPTIONS).map(x => x.SELECTED = x.ID == option.ID ? true : false); 7132 attribute['SELECT_ID'] = option.ID; 7133 attribute['SELECT_NAME'] = option.NAME; 7134 if (attribute.CATALOG_VIEW == 'select' || attribute.CATALOG_VIEW == '') { 7135 attribute['SELECT_SEARCH'] = attribute['SELECT_NAME']; 7136 attribute.IS_OPEN = false; 7137 Array.from(attribute.OPTIONS).forEach(item => { 7138 item['HIDDEN'] = false; 7139 }); 7140 } 7141 7142 catalog.ATTRIBUTES = fastClone(self.ATTRIBUTES); 7143 7144 self.getProductProperties(option); 7145 return; 7146 }, 7147 async getProductProperties(option = null) { 7148 const self = this; 7149 let catalogProductId = ''; 7150 7151 const formData = new FormData(); 7152 formData.append('catalog_code', catalogCode);
7153 Array.from(self.ATTRIBUTES).forEach((item, index) => { 7154 if (item.SELECT_ID) { 7155 catalogProductId += `${item.GROUP_ID}_${item.SELECT_ID}`; 7156 formData.append(`attribute[${index}][id]`, item.GROUP_ID); 7157 formData.append(`attribute[${index}][option_id]`, item.SELECT_ID); 7158 } 7159 }); 7160 7161 if (catalog[`PRODUCT_DATA_${catalogProductId}`]) { 7162 const PRODUCT = catalog[`PRODUCT_DATA_${catalogProductId}`]; 7163 await self.setProductData(PRODUCT); 7164 await self.getAttribute(PRODUCT.ID); 7165 return; 7166 } 7167 7168 try { 7169 productItem.classList.add('catalog-opacity'); 7170 catalog.classList.add('pointer-events-none'); 7171 7172 await request('POST', getEndpoint('CATALOG_GET_PRODUCT'), formData).then(async response => { 7173 if (response?.length) { 7174 const PRODUCT = response[0]; 7175 if (PRODUCT?.ID) { 7176 catalog[`PRODUCT_DATA_${catalogProductId}`] = PRODUCT; 7177 await self.setProductData(PRODUCT); 7178 await self.getAttribute(PRODUCT.ID); 7179 } else { 7180 await self.getAttribute(option.PRODUCT_ID, null); 7181 } 7182 } 7183 }); 7184 } catch (error) { 7185 console.warn(`CATALOG_GET_PRODUCT error => ${error}`); 7186 } finally { 7187 productItem.classList.remove('catalog-opacity'); 7188 catalog.classList.remove('pointer-events-none'); 7189 } 7190 }, 7191 async setProductData(P = null) { 7192 if (P == null) return; 7193 productItem.dataset.id = P.ID; 7194 7195 const priceCurrency = P.TARGET_CURRENCY_ORIGINAL || productItem.dataset.priceCurrency; 7196 7197 const productItemData = productItemScope(productItem, ` 7198 [data-toggle="product-title"], 7199 [data-toggle="product-url"], 7200 [data-toggle="price-sell"], 7201 [data-toggle="price-sell-vat"], 7202 [data-toggle="price-not-discounted"], 7203 [data-toggle="price-not-discounted-vat"], 7204 [data-toggle="discount-percent"], 7205 [data-toggle="price-numeric1"]`, true); 7206 7207 Array.from(productItemData).forEach(item => { 7208 if (item.dataset.toggle == 'product-title') item.innerText = P.TITLE; 7209 if (item.dataset.toggle == 'product-url') item.href = P.URL; 7210 7211 if (item.dataset.toggle == 'price-sell') item.innerText = format(P.PRICE_SELL, priceCurrency); 7212 if (item.dataset.toggle == 'price-sell-vat') item.innerText = vat(P.PRICE_SELL, P.VAT, priceCurrency); 7213 if (item.dataset.toggle == 'price-not-discounted') item.innerText = format(P.PRICE_NOT_DISCOUNTED, priceCurrency); 7214 if (item.dataset.toggle == 'price-not-discounted-vat') item.innerText = vat(P.PRICE_NOT_DISCOUNTED, P.VAT, priceCurrency); 7215 if (item.dataset.toggle == 'discount-percent') item.innerText = P.DISCOUNT_PERCENT; 7216 if (item.dataset.toggle == 'price-numeric1' && P.NUMERIC1) item.innerText = vat(P.PRICE_SELL - (P.PRICE_SELL * P.NUMERIC1 / 100), P.VAT, priceCurrency); 7217 }); 7218 7219 const discountItems = productItemScope(productItem, ` 7220 [data-toggle="discount-percent"], 7221 [data-toggle="price-not-discounted"], 7222 [data-toggle="price-not-discounted-vat"], 7223 [data-toggle="price-numeric1"]`, true); 7224
7225 Array.from(discountItems).forEach(item => { 7226 if (P.IS_DISPLAY_DISCOUNTED_ACTIVE && P.DISCOUNT_PERCENT > 0) { 7227 if (item.closest('[data-toggle="price-control"]')) item.closest('[data-toggle="price-control"]').classList.remove('!hidden', 'hidden'); 7228 } else { 7229 if (item.closest('[data-toggle="price-control"]')) item.closest('[data-toggle="price-control"]').classList.add('!hidden', 'hidden'); 7230 } 7231 }); 7232 7233 const productItemImages = productItemScope(productItem, '[data-toggle="product-image"]', true); 7234 if (productItemImages?.length) { 7235 for (let i = 0; i < productItemImages.length; i++ ) { 7236 productItemImages[i].src = P.IMAGE_LIST[i]?.MEDIUM || P.IMAGE_LIST[0]?.MEDIUM || ''; 7237 } 7238 } 7239 return; 7240 }, 7241 async slideAttribute() { 7242 const catalogSliders = catalog.querySelectorAll('.swiper'); 7243 Array.from(catalogSliders).forEach(item => { 7244 if (item.slider == 1) { 7245 item['swiperData'].update(); 7246 return; 7247 } 7248 7249 const nextEl = item.querySelector('.swiper-button-next'); 7250 const prevEl = item.querySelector('.swiper-button-prev'); 7251 7252 if (item.eventCleanup) { 7253 item.eventCleanup(); 7254 } 7255 7256 item['swiperData'] = new Swiper(item, { 7257 slidesPerView: 'auto', 7258 spaceBetween: 4, 7259 navigation: { 7260 nextEl: nextEl, 7261 prevEl: prevEl 7262 }, 7263 }); 7264 7265 const nextDownHandler = () => startAutoSlide(item['swiperData'], 'next'); 7266 const prevDownHandler = () => startAutoSlide(item['swiperData'], 'prev'); 7267 const stopHandler = stopAutoSlide; 7268 7269 nextEl.addEventListener('mousedown', nextDownHandler); 7270 nextEl.addEventListener('mouseup', stopHandler); 7271 nextEl.addEventListener('mouseleave', stopHandler); 7272 7273 prevEl.addEventListener('mousedown', prevDownHandler); 7274 prevEl.addEventListener('mouseup', stopHandler); 7275 prevEl.addEventListener('mouseleave', stopHandler); 7276 7277 item.eventCleanup = () => { 7278 nextEl.removeEventListener('mousedown', nextDownHandler); 7279 nextEl.removeEventListener('mouseup', stopHandler); 7280 nextEl.removeEventListener('mouseleave', stopHandler); 7281 prevEl.removeEventListener('mousedown', prevDownHandler); 7282 prevEl.removeEventListener('mouseup', stopHandler); 7283 prevEl.removeEventListener('mouseleave', stopHandler); 7284 }; 7285 7286 item.slider = 1; 7287 }); 7288 }, 7289 parentClass(element) { 7290 let currentElement = element; 7291 7292 while (currentElement && currentElement.dataset.toggle != 'product') { 7293 currentElement.classList.add('catalog-parent-element'); 7294 currentElement = currentElement.parentElement; 7295 } 7296 } 7297 }, 7298 async mounted() { 7299 const self = this; 7300 self.parentClass(catalog); 7301 7302 catalog.cleanupCatalog = () => { 7303 const swipers = catalog.querySelectorAll('.swiper');
7304 swipers.forEach(item => { 7305 if (item.swiperData) { 7306 item.swiperData.destroy(true, true); 7307 item.swiperData = null; 7308 } 7309 if (item.eventCleanup) { 7310 item.eventCleanup(); 7311 item.eventCleanup = null; 7312 } 7313 item.slider = 0; 7314 }); 7315 7316 if (catalog.catalogApp) { 7317 catalog.catalogApp.unmount(); 7318 catalog.catalogApp = null; 7319 } 7320 7321 delete catalog[`PRODUCT_ATTRIBUTES_${productId}`]; 7322 delete catalog.ATTRIBUTES; 7323 }; 7324 7325 if (productData) { 7326 catalog[`PRODUCT_ATTRIBUTES_${productId}`] = productData.ATTRIBUTES; 7327 } 7328 await self.getAttribute(productId); 7329 if (productData) return; 7330 7331 catalog.classList.add('catalog-first-loaded'); 7332 7333 setTimeout(() => { 7334 catalog.classList.remove('catalog-first-loaded'); 7335 }, 1500); 7336 }, 7337 beforeUnmount() { 7338 if (catalog.cleanupCatalog) { 7339 catalog.cleanupCatalog(); 7340 } 7341 return; 7342 } 7343 }).mount(catalog); 7344 }, 7345 autoLoad: async function() { 7346 const catalogAutoLoads = document.querySelectorAll('[data-toggle="product-card-catalog"][data-auto-load="1"]:not(.catalog-loaded)'); 7347 if (!catalogAutoLoads.length) return; 7348 7349 const catalogAutoLoadIds = []; 7350 Array.from(catalogAutoLoads).forEach(catalog => { 7351 if (catalog.componentCatalog == 1) return; 7352 7353 const productId = catalog.dataset.product; 7354 if (!productId) { 7355 console.warn('productCardCatalog warn: productId undefined'); 7356 return; 7357 }; 7358 7359 catalogAutoLoadIds.push(productId); 7360 catalog.componentCatalog = 1; 7361 }); 7362 7363 if (!catalogAutoLoadIds.length) return; 7364 7365 const formData = new FormData(); 7366 formData.append('product_ids', catalogAutoLoadIds.join(',')); 7367 formData.append('with_image', '1'); 7368 7369 loader(true); 7370 try { 7371 const response = await request('POST', getEndpoint('CATALOG_ATTRIBUTES_BY_PRODUCT_IDS'), formData); 7372 if (!response.length) return; 7373 7374 Array.from(catalogAutoLoads).forEach(catalog => { 7375 const productItem = catalog.closest('[data-toggle="product"]'); 7376 if (!productItem) { 7377 console.warn('productCardCatalog warn: productItem undefined'); 7378 return; 7379 }; 7380 7381 const productId = catalog.dataset.product; 7382 if (!productId) { 7383 console.warn('productCardCatalog warn: productId undefined'); 7384 return; 7385 }; 7386 7387 const catalogCode = catalog.dataset.catalogCode; 7388 if (!catalogCode) { 7389 console.warn('productCardCatalog warn: catalogCode undefined'); 7390 return; 7391 }; 7392 7393 const isCatalogActive = catalog.querySelector('[data-toggle="is-catalog-active"]'); 7394 if (!isCatalogActive?.value) return; 7395 7396 const productData = response.find(item => item.PRODUCT_ID == productId); 7397 if (!productData?.ATTRIBUTES?.length) return; 7398 7399 Array.from(productData.ATTRIBUTES).forEach(item => { 7400 if (!item.OPTIONS?.length) return; 7401 item.OPTIONS = item.OPTIONS.filter(opt => opt.PRODUCT_ID != null); 7402 }); 7403 7404 components.catalog.get(catalog, productItem, productId, catalogCode, productData); 7405 }); 7406 } catch (error) { 7407 console.warn(`CATALOG_ATTRIBUTES_BY_PRODUCT_IDS error => ${error}`); 7408 } finally { 7409 loader(false); 7410 } 7411 return; 7412 }, 7413 init: function() { 7414 components.catalog.autoLoad(); 7415 7416 if (isMobile()) return; 7417 7418 const catalogs = document.querySelectorAll('[data-toggle="product-card-catalog"]:not([data-auto-load="1"])');
7419 Array.from(catalogs).forEach(catalog => { 7420 if (catalog.componentCatalog == 1) return; 7421 7422 const productItem = catalog.closest('[data-toggle="product"]'); 7423 if (!productItem) { 7424 console.warn('productCardCatalog warn: productItem undefined'); 7425 return; 7426 }; 7427 7428 const productId = catalog.dataset.product; 7429 if (!productId) { 7430 console.warn('productCardCatalog warn: productId undefined'); 7431 return; 7432 }; 7433 7434 const catalogCode = catalog.dataset.catalogCode; 7435 if (!catalogCode) { 7436 console.warn('productCardCatalog warn: catalogCode undefined'); 7437 return; 7438 }; 7439 7440 const isCatalogActive = catalog.querySelector('[data-toggle="is-catalog-active"]'); 7441 if (!isCatalogActive?.value) return; 7442 7443 let hoverTimeout; 7444 catalog.addEventListener('mouseenter', () => { 7445 if (catalog.classList.contains('catalog-loaded') || catalog.catalogApp) return; 7446 7447 hoverTimeout = setTimeout(async () => { 7448 productItem.classList.add('catalog-opacity'); 7449 catalog.classList.add('pointer-events-none'); 7450 7451 components.catalog.get(catalog, productItem, productId, catalogCode); 7452 }, 300); 7453 }); 7454 7455 catalog.addEventListener('mouseleave', () => { 7456 if (catalog.classList.contains('catalog-loaded')) return; 7457 clearTimeout(hoverTimeout); 7458 }); 7459 7460 catalog.componentCatalog = 1; 7461 }); 7462 } 7463 }, 7464 variant: { 7465 cheapestInStock: function(elements, variantPrices) { 7466 const items = Array.from(elements); 7467 if (items.length == 0) return null; 7468 7469 const priceOf = item => Number.parseFloat(variantPrices?.[item.dataset.id] ?? item.dataset.price); 7470 7471 return items.reduce((cheapest, item) => { 7472 const price = priceOf(item); 7473 const cheapestPrice = priceOf(cheapest); 7474 if (Number.isNaN(price)) return cheapest; 7475 if (Number.isNaN(cheapestPrice)) return item; 7476 return price < cheapestPrice ? item : cheapest; 7477 }); 7478 }, 7479 set: { 7480 price: function(variantItem, productItem, variantData, variant) { 7481 if (!variantData) return; 7482 7483 if (PAGE_TYPE === 'product' && productItem.id == 'product-detail') { 7484 PRODUCT_DATA[0].subproduct_code = variantData.WS_CODE; 7485 PRODUCT_DATA[0].subproduct_id = variantData.ID; 7486 } 7487 7488 const price = Number.parseFloat(variantData.PRICE); 7489 const priceNotDiscounted = Number.parseFloat(variantData.PRICE_NOT_DISCOUNTED); 7490 let discountPercent = (((priceNotDiscounted - price) / priceNotDiscounted) * 100); 7491 if (!Number.isFinite(discountPercent)) discountPercent = 0; 7492 const moneyOrderPercent = (1 + variantData.MONEY_ORDER_PERCENT / 100) * price; 7493 const priceVat = parseFloat(variantData.VAT); 7494 const priceCurrency = productItem?.dataset?.priceCurrency; 7495 7496 const priceItems = productItemScope(productItem, ` 7497 [data-toggle="price-sell"], 7498 [data-toggle="price-sell-vat"], 7499 [data-toggle="price-not-discounted"], 7500 [data-toggle="price-not-discounted-vat"], 7501 [data-toggle="price-money-order"], 7502 [data-toggle="price-money-order-vat"], 7503 [data-toggle="price-numeric1"], 7504 [data-toggle="cash-point-amount"], 7505 [data-toggle="cash-point"]`, true); 7506 Array.from(priceItems).forEach(item => { 7507 if (item.dataset.toggle == 'price-sell') item.innerText = format(price, priceCurrency); 7508 if (item.dataset.toggle == 'price-sell-vat') item.innerText = vat(price, priceVat, priceCurrency); 7509 if (item.dataset.toggle == 'price-not-discounted') item.innerText = format(priceNotDiscounted, priceCurrency); 7510 if (item.dataset.toggle == 'price-not-discounted-vat') item.innerText = vat(priceNotDiscounted, priceVat, priceCurrency); 7511 if (item.dataset.toggle == 'price-money-order') item.innerText = format(moneyOrderPercent, priceCurrency); 7512 if (item.dataset.toggle == 'price-money-order-vat') item.innerText = vat(moneyOrderPercent, priceVat, priceCurrency); 7513 if (item.dataset.toggle == 'price-numeric1' && item.dataset.percent) item.innerText = vat(price - (price * item.dataset.percent / 100), priceVat, priceCurrency); 7514 if (item.dataset.toggle == 'cash-point-amount' && variantData.CASH_POINT_AMOUNT) { 7515 item.innerHTML = format(variantData.CASH_POINT_AMOUNT); 7516 item.dataset.price = variantData.CASH_POINT_AMOUNT; 7517 return; 7518 } 7519 if (item.dataset.toggle == 'cash-point' && variantData.CASH_POINT) { 7520 item.innerHTML = variantData.CASH_POINT; 7521 item.dataset.point = variantData.CASH_POINT; 7522 return; 7523 } 7524 item.dataset.price = priceToFloat(item.innerText); 7525 }); 7526 7527 pointCalculation(productItem); 7528 7529 const discountItems = productItemScope(productItem, ` 7530 [data-toggle="discount-percent"], 7531 [data-toggle="price-not-discounted"], 7532 [data-toggle="price-not-discounted-vat"]`, true);
7533 Array.from(discountItems).forEach(item => { 7534 if (discountPercent <= 0) { 7535 if (item.closest('[data-toggle="price-control"]')) item.closest('[data-toggle="price-control"]').classList.add('!hidden', 'hidden'); 7536 } else { 7537 if (item.closest('[data-toggle="price-control"]')) item.closest('[data-toggle="price-control"]').classList.remove('!hidden', 'hidden'); 7538 if (item.dataset.toggle == 'discount-percent') item.innerText = discountPercent.toFixed(); 7539 } 7540 }); 7541 7542 const multipleDiscounts = productItemScope(productItem, '[data-toggle="product-multiple"]', true); 7543 Array.from(multipleDiscounts).forEach(item => { 7544 const priceMultiple = item.querySelector('[data-toggle="price-multiple"]'); 7545 if (!priceMultiple) return; 7546 7547 const percent = item.dataset.percent; 7548 const isVat = item.dataset.vat; 7549 const lastPrice = (price - (price * percent/100)); 7550 7551 if (isVat) { 7552 priceMultiple.innerText = vat(lastPrice, priceVat); 7553 } else { 7554 priceMultiple.innerText = format(lastPrice); 7555 } 7556 }); 7557 if (multipleDiscounts.length) components.multipleDiscount.setTotal(); 7558 7559 const priceAlarm = productItemScope(productItem, '[data-toggle="price-alarm"]'); 7560 if (priceAlarm) priceAlarm.dataset.priceSell = vat(price, priceVat); 7561 7562 components.multiSelect.set(variantItem); 7563 7564 if (typeof BLOCK_JS.PERSONALIZATION != 'undefined') { 7565 if (BLOCK_JS.PERSONALIZATION[productItem.dataset.id]) { 7566 const appPersonalization = BLOCK_JS.PERSONALIZATION[productItem.dataset.id] || {}; 7567 if (typeof appPersonalization?.setPrice == 'function') { 7568 appPersonalization.setPrice(); 7569 } 7570 } 7571 } 7572 7573 if (typeof SNIPPET_JS.PRODUCT_INSTALLMENT != 'undefined') { 7574 if (typeof SNIPPET_JS.PRODUCT_INSTALLMENT.reload == 'function') SNIPPET_JS.PRODUCT_INSTALLMENT.reload(); 7575 } 7576 7577 if (variant) { 7578 try { 7579 window[variant.dataset.callback]?.(variantItem, variantData); 7580 } catch (error) { 7581 console.warn(`${variant.dataset.callback} error => ${error}`); 7582 } 7583 } 7584 return; 7585 }, 7586 other: function(variantItem, productItem, variantData) { 7587 if (!variantData) return; 7588 7589 const codeItems = productItemScope(productItem, ` 7590 [data-toggle="product-code"], 7591 [data-toggle="supplier-product-code"], 7592 [data-toggle="barcode"]`, true); 7593 Array.from(codeItems).forEach(item => { 7594 let code = item.dataset.code || ''; 7595 if (item.dataset.toggle == 'product-code') item.innerText = variantData.WS_CODE ? variantData.WS_CODE : code; 7596 if (item.dataset.toggle == 'supplier-product-code') item.innerText = variantData.CODE ? variantData.CODE : code; 7597 if (item.dataset.toggle == 'barcode') item.innerText = variantData.BARCODE ? variantData.BARCODE : code; 7598 }); 7599 7600 let status = variantData.STOCK > 0 ? true : false; 7601 if (typeof SETTINGS.NEGATIVE_STOCK !== 'undefined' && SETTINGS.NEGATIVE_STOCK == 1) status = true; 7602 7603 const stockItems = productItemScope(productItem, ` 7604 [data-toggle="show-in-stock"], 7605 [data-toggle="show-out-stock"]`, true); 7606 Array.from(stockItems).map(async item => { 7607 if (status) { 7608 if (item.dataset.toggle == 'show-in-stock')
7608item.classList.remove('hidden'); 7609 if (item.dataset.toggle == 'show-out-stock') item.classList.add('hidden'); 7610 } else { 7611 if (item.dataset.toggle == 'show-in-stock') item.classList.add('hidden'); 7612 if (item.dataset.toggle == 'show-out-stock') { 7613 showOutStock(item); 7614 item.classList.remove('hidden'); 7615 } 7616 } 7617 return; 7618 }); 7619 7620 const backupHolders = productItemScope(productItem, '[data-toggle="stock-backup"]', true); 7621 Array.from(backupHolders || []).forEach(holder => { 7622 if (status) stockBackup.render(holder, null); 7623 else stockBackup.check(holder, variantData.ID); 7624 }); 7625 7626 const productId = productItem?.dataset?.id ?? 0; 7627 if (PAGE_TYPE === 'product' && document.getElementById('product-id')?.value === productId) { 7628 const relatedProducts = document.querySelectorAll(`[data-toggle="product-related-id"][data-related-id="${productId}"]`); 7629 Array.from(relatedProducts).forEach(related => { 7630 if (related.value == `${productId}:${variantData.ID}`) return; 7631 related.value = `${productId}:${variantData.ID}`; 7632 }); 7633 } 7634 return; 7635 }, 7636 variantDataSet: function(variantItem, productItem, status = true) { 7637 const codeItems = productItemScope(productItem, ` 7638 [data-toggle="variant-one-active"], 7639 [data-toggle="variant-two-active"]`, true); 7640 Array.from(codeItems).forEach(item => { 7641 if (item.dataset.toggle == 'variant-one-active' && variantItem.closest('[data-toggle="variant-one"]')) item.innerText = status ? variantItem.dataset.type ?? '' : ''; 7642 if (item.dataset.toggle == 'variant-two-active' && variantItem.closest('[data-toggle="variant-two"]')) item.innerText = status ? variantItem.dataset.type ?? '' : ''; 7643 }); 7644 }, 7645 images: function(variantItem, productItem) { 7646 const id = variantItem.dataset.id; 7647 if (!id) return; 7648 7649 const subOne = productItemScope(productItem, '[data-toggle="variant-one"]'); 7650 const subTwo = productItemScope(productItem, '[data-toggle="variant-two"]'); 7651 const variantSub = variantItem.closest('[data-toggle="variant-one"]') ? subOne : subTwo; 7652 7653 const productImages = productItemScope(productItem, '[data-toggle="product-image"]', true); 7654 const defaultImages = productItemScope(productItem, `[data-toggle="product-image"][data-id="0"]`, true); 7655 const variantImages = productItemScope(productItem, `[data-toggle="product-image"][data-id="${id}"]`, true); 7656 7657 if (variantSub == subTwo && !variantImages.length) return; 7658 7659 if (productItem.variantStatus == id) return; 7660 productItem.variantStatus = id; 7661 7662 Array.from(productImages).forEach(item => { 7663 const classes = item.classList; 7664 Array.from(classes).forEach(cls => { 7665 if (cls.startsWith('swiper-')) item.classList.remove(cls); 7666 }); 7667 7668 item.classList.add('!hidden'); 7669 item.classList.remove('gallery-selector'); 7670 7671 let variantId = ''; 7672 if (variantImages.length) { 7673 variantId = id; 7674 } else if (defaultImages.length) { 7675 variantId = 0; 7676 } else { 7677 variantId = item.dataset.id; 7678 } 7679 7680 if (item.dataset.id == variantId) { 7681 item.classList.remove('!hidden'); 7682 item.classList.add('gallery-selector'); 7683 if (item.closest('.swiper')?.swiper) item.classList.add('swiper-slide'); 7684 } 7685 }); 7686 7687 setTimeout(() => { 7688 const imageGalleries = document.querySelectorAll('[data-toggle="product-gallery"]'); 7689 if (imageGalleries.length) {
7690 Array.from(imageGalleries).forEach(gallery => { 7691 try { 7692 components.lightGallery.create(gallery); 7693 } catch (error) {} 7694 }); 7695 } 7696 7697 if (BLOCK_JS.PRODUCT_DETAIL?.imageSlider) { 7698 try { 7699 BLOCK_JS.PRODUCT_DETAIL.imageSlider.init(); 7700 } catch (error) {} 7701 } 7702 return; 7703 }, 100); 7704 return; 7705 }, 7706 product: function(productItem, id) { 7707 productItem.dataset.variantId = id || 0; 7708 7709 if (productItem.dataset.variantId == 0) { 7710 const stockItems = productItemScope(productItem, ` 7711 [data-toggle="show-in-stock"], 7712 [data-toggle="show-out-stock"]`, true); 7713 Array.from(stockItems).forEach(item => { 7714 if (item.dataset.toggle == 'show-in-stock') item.classList.remove('hidden'); 7715 if (item.dataset.toggle == 'show-out-stock') item.classList.add('hidden'); 7716 }); 7717 } 7718 /* 7719 const productLinks = productItemScope(productItem, '[data-toggle="product-url"]', true); 7720 Array.from(productLinks).map(p => { 7721 if (p.href) p.href = setUrlParam('variant', productItem.dataset.variantId, p.href); 7722 }); 7723 */ 7724 7725 if (!productItem.variantSet && productItem.dataset.variantId != 0) { 7726 setTimeout(() => { 7727 productItem.variantSet = true; 7728 }, 1000); 7729 return; 7730 }; 7731 if (PAGE_TYPE === 'product' && document.getElementById('product-id')?.value === productItem.dataset.id) { 7732 let url = setUrlParam('variant', productItem.dataset.variantId); 7733 if (productItem.dataset.variantId == 0) url = deleteUrlParam('variant'); 7734 window.history.replaceState('product', null, url); 7735 } 7736 }, 7737 }, 7738 change: async function(variantItem, productItem, variant) { 7739 const self = this; 7740 7741 const data = { 7742 'ID': variantItem.dataset.subproductId, 7743 'TYPE_ID': variantItem.dataset.id, 7744 'TYPE': variantItem.dataset.type, 7745 'CODE': variantItem.dataset.code, 7746 'TYPE_COLOR_CODE': variantItem.dataset.colorCode, 7747 'PRICE': variantItem.dataset.price, 7748 'STOCK': variantItem.dataset.stock, 7749 'IN_STOCK': variantItem.dataset.instock, 7750 'BARCODE': variantItem.dataset.barcode, 7751 'MONEY_ORDER_PERCENT': variantItem.dataset.mop, 7752 'VAT': variantItem.dataset.vat, 7753 'PRICE_NOT_DISCOUNTED': variantItem.dataset.notDiscounted, 7754 'WS_CODE': variantItem.dataset.wscode, 7755 'WEIGHT': variantItem.dataset.weight, 7756 'ADDITIONAL_INFO': variantItem.dataset.additional || '', 7757 'CASH_POINT': variantItem.dataset.cashPoint, 7758 'CASH_POINT_AMOUNT': variantItem.dataset.cashPointAmount, 7759 }; 7760 7761 self.set.price(variantItem, productItem, data, variant); 7762 self.set.other(variantItem, productItem, data); 7763 self.set.product(productItem, data.ID || 0); 7764 }, 7765 trigger: function(variantItem) { 7766 setTimeout(() => { 7767 if (variantItem.nodeName === 'OPTION') { 7768 const select = variantItem.closest('select'); 7769 if (variantItem.value !== '0') { 7770 select.value = variantItem.value; 7771 triggerEvent(select, 'change'); 7772 } 7773 } else { 7774 variantItem.click(); 7775 } 7776 return; 7777 }, 1); 7778 }, 7779 async getVariantData(productItem, variants) { 7780 try { 7781 const variantId = getUrlParam('variant'); 7782 const productId = productItem.dataset.id; 7783 if (!productId || !variantId) return; 7784 7785 const response = await request('GET', getEndpoint('GET_VARIANT_INFO', `${productId}/${variantId}`));
7786 Array.from(variants).forEach(item => { 7787 let subId = ''; 7788 if (item.closest('[data-toggle="variant-one"]')) { 7789 subId = response.TurId1; 7790 } else if (item.closest('[data-toggle="variant-two"]')) { 7791 subId = response.TurId2; 7792 } 7793 7794 if (item.nodeName == 'SELECT') { 7795 item.querySelectorAll('option').forEach(option => { 7796 option.classList.remove('active'); 7797 if (option.dataset.id == subId) option.classList.add('active'); 7798 }); 7799 } else { 7800 item.classList.remove('active'); 7801 if (item.dataset.id == subId) item.classList.add('active'); 7802 } 7803 }); 7804 } catch (error) { 7805 console.warn(`GET_VARIANT_INFO error => ${error}`); 7806 } 7807 }, 7808 init: async function() { 7809 const self = this; 7810 7811 const productItems = document.querySelectorAll('[data-toggle="product"]'); 7812 Array.from(productItems).map(async productItem => { 7813 if (productItem.componentVariant == 1) return; 7814 productItem.componentVariant = 1; 7815 7816 const variants = productItem.querySelectorAll('[data-toggle="variant"]'); 7817 if (!variants.length) return; 7818 7819 const subOne = productItemScope(productItem, '[data-toggle="variant-one"]'); 7820 const subTwo = productItemScope(productItem, '[data-toggle="variant-two"]'); 7821 if (!subOne && !subTwo) return; 7822 7823 const getParamsVariant = getUrlParam('variant'); 7824 if (PAGE_TYPE === 'product' && productItem.id == 'product-detail' && !productItem.getVariantData) { 7825 productItem.getVariantData = true; 7826 await self.getVariantData(productItem, variants); 7827 } 7828 7829 Array.from(variants).map(async variant => { 7830 if (variant.componentVariant == 1) return; 7831 variant.componentVariant = 1; 7832 7833 let event = 'click'; 7834 if(variant.nodeName == 'SELECT') event = 'change'; 7835 7836 const variantItem = variant.nodeName == 'SELECT' ? variant.options[variant.options.selectedIndex] : variant; 7837 7838 variant.addEventListener(event, async e => { 7839 e.preventDefault(); 7840 7841 const variantItem = variant.nodeName == 'SELECT' ? variant.options[variant.options.selectedIndex] : variant; 7842 const variantSub = variantItem.closest('[data-toggle="variant-one"]') ? subOne : subTwo; 7843 const variantOtherSub = variantSub == subOne ? subTwo : subOne; 7844 7845 const variantData = { 7846 subId: variantItem.dataset.id, 7847 proId: variantItem.dataset.pid 7848 } 7849 7850 Array.from(variantSub.querySelectorAll('[data-toggle="variant"]:not(select), select[data-toggle="variant"] option')).forEach(item => { 7851 item.classList.remove('active'); 7852 }); 7853 7854 if (!variantItem.classList.contains('!hidden')) { 7855 variantItem.classList.add('active'); 7856 } 7857 7858 const variantNotify = productItemScope(productItem, '[data-toggle="variant-notify"]'); 7859 if (variantNotify) { 7860 variantNotify.classList.add('hidden'); 7861 productItem.classList.add('variant-notify'); 7862 } 7863 7864 if (variantItem.nodeName == 'OPTION' && (variantItem.value == '0' || variantItem.value == '')) { 7865 productItem.dataset.variantId = '0'; 7866 self.set.variantDataSet(variantItem, productItem, false); 7867 7868 const stockItems = productItemScope(productItem, ` 7869 [data-toggle="show-in-stock"], 7870 [data-toggle="show-out-stock"]`, true);
7871 Array.from(stockItems).forEach(item => { 7872 if (item.dataset.toggle == 'show-in-stock') item.classList.remove('hidden'); 7873 if (item.dataset.toggle == 'show-out-stock') item.classList.add('hidden'); 7874 }); 7875 return; 7876 } 7877 if (!variantData.subId || !variantData.proId) return; 7878 7879 self.set.images(variantItem, productItem); 7880 self.set.variantDataSet(variantItem, productItem); 7881 7882 let variantPrices = null; 7883 7884 if (variantOtherSub) { 7885 const subOneItems = subOne.querySelectorAll('[data-toggle="variant"]:not(select), select[data-toggle="variant"] option[data-id]'); 7886 if (subOneItems?.length == 1 && variantSub.dataset.toggle == "variant-two") await self.change(variantItem, productItem, variant); 7887 if (subOneItems.length > 1) { 7888 const variantTypeList = variantSub.dataset.toggle == "variant-one" ? 'get-variant2-list' : 'get-variant1-list'; 7889 7890 try { 7891 await request('GET', getEndpoint('VARIANT', `${variantTypeList}/${variantData.proId}/${variantData.subId}`)).then(response => { 7892 const data = response.VARIANT2_LIST ?? response.VARIANT1_LIST; 7893 const variantItems = variantOtherSub.querySelectorAll('[data-toggle="variant"]:not(select), select[data-toggle="variant"] option'); 7894 7895 variantPrices = Array.from(data).reduce((prices, row) => { 7896 prices[row.TYPE_ID] = row.PRICE; 7897 return prices; 7898 }, {}); 7899 7900 Array.from(variantItems).forEach(item => { 7901 if(item.nodeName == 'OPTION' && (item.value == '0' || item.value == '')) { 7902 return 7903 }; 7904 7905 if (variantSub.dataset.toggle == "variant-one") { 7906 item.classList.add('!hidden'); 7907 item.classList.add('passive'); 7908 } 7909 7910 const subData = Array.from(data).find(x => x.TYPE_ID == item.dataset.id); 7911 if (subData) { 7912 if (subData.STOCK > 0) { 7913 item.classList.remove('passive'); 7914 if (item.nodeName == 'OPTION' && item.dataset.type) item.innerText = item.dataset.type; 7915 } else { 7916 if (item.nodeName == 'OPTION' && item.dataset.type && variantSub.dataset.toggle == "variant-one") item.innerText = `${item.dataset.type} (${COMMON_LANG.out_of_stock || 'Tükendi'})`; 7917 } 7918 item.classList.remove('!hidden'); 7919 } 7920 }); 7921 7922 const variantSelected = variantOtherSub.querySelector('.active'); 7923 if (variantSelected) { 7924 if (variantSelected.classList.contains('!hidden')) { 7925 variantSelected.classList.remove('active'); 7926 self.set.variantDataSet(variantSelected, productItem, false); 7927 } 7928 7929 const subId = variantSelected.dataset.id; 7930 const newData = Array.from(data).find(x => x.TYPE_ID == subId); 7931 const variantId = newData ? newData.ID : '0'; 7932 7933 if (!newData?.ID) { 7934 const variantSelect = variantOtherSub.querySelector('select[data-toggle="variant"]'); 7935 if (variantSelect) { 7936 variantSelect.value = '0'; 7937 triggerEvent(variantSelect, 'change'); 7938 } 7939 } 7940 7941 self.set.price(variantItem, productItem, newD
7941ata, variant); 7942 self.set.other(variantItem, productItem, newData); 7943 7944 self.set.product(productItem, variantId || 0); 7945 } else { 7946 window[variant.dataset.callback]?.(variantItem, data); 7947 } 7948 }); 7949 } catch (error) { 7950 console.warn(`VARIANT error => ${error}`); 7951 } 7952 } 7953 } else { 7954 await self.change(variantItem, productItem, variant); 7955 } 7956 7957 if (productItem['selectVariant'] || getParamsVariant) return; 7958 const errorClasses = ['passive', '!hidden', 'hidden']; 7959 let subElement = variantSub; 7960 7961 if (((variantSub == subOne) && (variantOtherSub && variantOtherSub['selectVariant'])) || (!variantOtherSub && variantSub['selectVariant'])) { 7962 if ((variantSub == subOne) && (variantOtherSub && variantOtherSub['selectVariant'])) subElement = variantOtherSub; 7963 7964 const otherItems = subElement.querySelectorAll('[data-toggle="variant"]:not(select), select[data-toggle="variant"] option[data-type]'); 7965 const activeItem = Array.from(otherItems).find(item => item.classList.contains('active')); 7966 if (activeItem) { 7967 const hasErrorClass = errorClasses.some(errorClass => activeItem.classList.contains(errorClass)); 7968 7969 if (hasErrorClass) { 7970 const validItem = self.cheapestInStock(Array.from(otherItems).filter(item => 7971 !item.classList.contains('active') && 7972 !errorClasses.some(errorClass => item.classList.contains(errorClass)) 7973 ), subElement == variantOtherSub ? variantPrices : null); 7974 7975 if (validItem) { 7976 activeItem.classList.remove('active'); 7977 validItem.classList.add('active'); 7978 self.trigger(validItem); 7979 } 7980 } 7981 } 7982 productItem['selectVariant'] = true; 7983 } 7984 return; 7985 }); 7986 7987 if (variant.nodeName == 'SELECT') { 7988 variant.querySelectorAll('option').forEach(option => { 7989 if (option.classList.contains('active')) { 7990 variant.value = option.value; 7991 self.trigger(option); 7992 if (subOne) subOne['selectVariant'] = true; 7993 if (subTwo) subTwo['selectVariant'] = true; 7994 } 7995 7996 if (option.dataset.type) { 7997 const stock = Number(option.dataset.stock); 7998 if (stock <= 0 && (!(subOne && subTwo) || (subOne && subTwo && variant.closest('[data-toggle="variant-two"]')))) { 7999 option.innerText = `${option.dataset.type} (${COMMON_LANG.out_of_stock || 'Tükendi'})`; 8000 } else { 8001 option.innerText = option.dataset.type; 8002 } 8003 } 8004 }); 8005 } else { 8006 if (variantItem.classList.contains('active')) { 8007 self.trigger(variant); 8008 if (subOne) subOne['selectVariant'] = true; 8009 if (subTwo) subTwo['selectVariant'] = true; 8010 } 8011 } 8012 }); 8013 }); 8014 } 8015 }, 8016 favourite: { 8017 STORAGE_KEY: "favorite_products", 8018 SESSION_KEY: "favorites_fetched", 8019 SESSION_SERVER: "server_favorites", 8020 LIST_API_URL: "/srv/service/profile/get-shopping-list", 8021 ADD_API_URL: "/srv/service/profile/add-to-fav-list", 8022 REMOVE_API_URL: "/srv/service/profile/delete-shopping-products-by-product-id", 8023 maxChanges: 10, 8024 updateTimeout: null, 8025 debounceTimeout: null, 8026 syncFavorites: false, 8027 async add(ids = null, message = false) { 8028 const self = this; 8029 if (MEMBER_INFO.ID <= 0) { 8030 await loginPopup(); 8031 return; 8032 } 8033 8034 if (ids == null) ids = []; 8035 if (!Array.isArray(ids)) ids = [ids]; 8036 if (ids.length === 0) return; 8037 8038 let favorites = self.getFavorites(); 8039 let changed = false;
8040 const callback = this.callback; 8041 8042 ids.forEach(productId => { 8043 if (!favorites.includes(productId)) { 8044 favorites.push(productId); 8045 callback(productId, 'add'); 8046 changed = true; 8047 } 8048 }); 8049 8050 if (changed) { 8051 await self.setFavorites(favorites); 8052 await self.sendUpdates(message); 8053 self.set(); 8054 } 8055 }, 8056 async remove(ids = null, message = false) { 8057 const self = this; 8058 if (MEMBER_INFO.ID <= 0) { 8059 await loginPopup(); 8060 return; 8061 } 8062 8063 if (ids == null) ids = []; 8064 if (!Array.isArray(ids)) ids = [ids]; 8065 if (ids.length === 0) return; 8066 8067 let favorites = self.getFavorites(); 8068 let changed = false; 8069 const callback = this.callback; 8070 8071 ids.forEach(productId => { 8072 if (favorites.includes(productId)) { 8073 favorites.splice(favorites.indexOf(productId), 1); 8074 callback(productId, 'remove'); 8075 changed = true; 8076 } 8077 }); 8078 8079 if (changed) { 8080 await self.setFavorites(favorites); 8081 await self.sendUpdates(message); 8082 self.set(); 8083 } 8084 }, 8085 getFavorites() { 8086 const self = this; 8087 return JSON.parse(localStorage.getItem(self.STORAGE_KEY)) || []; 8088 }, 8089 async setFavorites(favorites) { 8090 const self = this; 8091 8092 localStorage.setItem(self.STORAGE_KEY, JSON.stringify(favorites)); 8093 8094 const favouriteCount = document.querySelectorAll('[data-toggle="favourite-count"]'); 8095 Array.from(favouriteCount).forEach(item => { 8096 item.innerText = favorites.length || ''; 8097 }); 8098 }, 8099 fetchFavorites() { 8100 const self = this; 8101 8102 if (MEMBER_INFO.ID <= 0) { 8103 localStorage.removeItem(self.STORAGE_KEY); 8104 sessionStorage.removeItem(self.SESSION_KEY); 8105 sessionStorage.removeItem(self.SESSION_SERVER); 8106 return; 8107 } 8108 8109 if (sessionStorage.getItem(self.SESSION_KEY) && localStorage.getItem(self.STORAGE_KEY)) { 8110 self.sendUpdates(); 8111 return; 8112 } 8113 8114 request('GET', self.LIST_API_URL) 8115 .then(response => { 8116 let apiFavorites = response?.CATEGORIES?.[0]?.PRODUCTS?.map(p => p.ID) || []; 8117 8118 self.setFavorites(apiFavorites); 8119 sessionStorage.setItem(self.SESSION_SERVER, JSON.stringify(apiFavorites)); 8120 sessionStorage.setItem(self.SESSION_KEY, "true"); 8121 8122 self.sendUpdates(false, apiFavorites); 8123 self.set(); 8124 }) 8125 .catch(error => { 8126 console.error("API hatası:", error); 8127 return; 8128 }); 8129 }, 8130 async set() { 8131 const self = this; 8132 8133 const favorites = self.getFavorites(); 8134 8135 const favouriteCount = document.querySelectorAll('[data-toggle="favourite-count"]'); 8136 Array.from(favouriteCount).forEach(item => { 8137 item.innerText = favorites.length || ''; 8138 }); 8139 8140 const allFavButtons = document.querySelectorAll('[data-toggle="favourite"]'); 8141 allFavButtons.forEach(btn => { 8142 btn.classList.remove('active'); 8143 }); 8144 8145 favorites.forEach(productId => { 8146 const favButtons = document.querySelectorAll(`[data-toggle="product"][data-id="${productId}"] [data-toggle="favourite"]`); 8147 favButtons.forEach(btn => { 8148 btn.classList.add('active'); 8149 }); 8150 }); 8151 }, 8152 toggleFavorite(productId) { 8153 const self = this; 8154 8155 let favorites = self.getFavorites(); 8156 const index = favorites.indexOf(productId); 8157 const callback = this.callback; 8158 8159 const products = document.querySelectorAll(`[data-toggle="product"][data-id="${productId}"] [data-toggle="favourite"]`); 8160 if (index === -1) { 8161 favorites.push(productId);
8162 products.forEach(fav => { 8163 fav.classList.add('active'); 8164 }); 8165 callback(productId, 'add'); 8166 } else { 8167 favorites.splice(index, 1); 8168 products.forEach(fav => { 8169 fav.classList.remove('active'); 8170 }); 8171 callback(productId, 'remove'); 8172 } 8173 8174 self.setFavorites(favorites); 8175 self.scheduleUpdate(); 8176 }, 8177 debounceToggleFavorite(productId) { 8178 const self = this; 8179 8180 clearTimeout(self.debounceTimeout); 8181 self.debounceTimeout = setTimeout(() => { 8182 8183 if (!localStorage.getItem(self.STORAGE_KEY)) { 8184 self.fetchFavorites().then(() => { 8185 self.toggleFavorite(productId); 8186 }); 8187 return; 8188 } 8189 8190 self.toggleFavorite(productId); 8191 }, 500); 8192 }, 8193 async scheduleUpdate() { 8194 const self = this; 8195 8196 if (self.updateTimeout) clearTimeout(self.updateTimeout); 8197 8198 let storedFavorites = self.getFavorites(); 8199 let serverFavorites = JSON.parse(sessionStorage.getItem(self.SESSION_SERVER)) || []; 8200 8201 let toAdd = storedFavorites.filter(id => !serverFavorites.includes(id)); 8202 let toRemove = serverFavorites.filter(id => !storedFavorites.includes(id)); 8203 8204 if (toAdd.length + toRemove.length > self.maxChanges) { 8205 self.sendUpdates(); 8206 return; 8207 } 8208 8209 self.updateTimeout = setTimeout(() => { 8210 self.sendUpdates(); 8211 }, 10000); 8212 }, 8213 async sendUpdates(message = false, localFavorites = []) { 8214 const self = this; 8215 if (self.syncFavorites) return; 8216 self.syncFavorites = true; 8217 8218 let storedFavorites = localFavorites.length > 0 ? localFavorites : self.getFavorites(); 8219 let serverFavorites = JSON.parse(sessionStorage.getItem(self.SESSION_SERVER)) || []; 8220 8221 let toAdd = storedFavorites.filter(id => !serverFavorites.includes(id)); 8222 let toRemove = serverFavorites.filter(id => !storedFavorites.includes(id)); 8223 8224 let sent = true; 8225 8226 const sendSync = async (url, data, isAdd = true) => { 8227 try { 8228 if (message && isAdd) { 8229 try { 8230 let response = await request('POST', url, data); 8231 if (response.status) { 8232 if (message == true) { 8233 notify({ 8234 text: COMMON_LANG?.added_fav_success 8235 }); 8236 } 8237 } 8238 8239 return response.status; 8240 } catch (error) { 8241 console.error("Veri gönderme hatası:", error); 8242 return false; 8243 } 8244 } else if (navigator.sendBeacon) { 8245 return navigator.sendBeacon(url, data); 8246 } else { 8247 const xhr = new XMLHttpRequest(); 8248 xhr.open('POST', url, false); 8249 try { 8250 xhr.send(data); 8251 return xhr.status === 200; 8252 } catch (error) { 8253 console.error("Veri gönderme hatası:", error); 8254 return false; 8255 } 8256 } 8257 } catch (error) { 8258 console.error("sendSync iÅlemi sırasında hata:", error); 8259 return false; 8260 } 8261 }; 8262 8263 const processUpdates = async () => { 8264 try { 8265 if (toAdd.length > 0) { 8266 const body = new FormData(); 8267 toAdd.forEach(id => { 8268 body.append('ids[]', id + '-0'); 8269 }); 8270 body.append('fetch', true); 8271 body.append('cat_id', 1); 8272 const addResult = await sendSync(self.ADD_API_URL, body, true); 8273 sent = sent && addResult; 8274 } 8275 8276 if (toRemove.length > 0) { 8277 const body = new FormData(); 8278 toRemove.forEach(id => { 8279 body.append('products[]', id); 8280 }); 8281 const removeResult = await sendSync(self.REMOVE_API_URL, body, false); 8282 sent = sent && removeResult; 8283 } 8284 8285 if (sent) { 8286 sessionStorage.setItem(self.SESSION_SERVER, JSON.stringify(storedFavorites)); 8287 if (localFavorites.length > 0) { 8288 self.setFavorites(localFavorites); 8289 self.set(); 8290 } 8291 } 8292 } catch (error) { 8293 console.error("Favori güncellemesi sırasında hata:", error); 8294 } finally { 8295 self.syncFavorites = false;
8296 } 8297 }; 8298 8299 processUpdates(); 8300 }, 8301 handleOnline() { 8302 const self = this; 8303 self.sendUpdates(); 8304 }, 8305 handlePageLeave() { 8306 const self = this; 8307 self.sendUpdates(); 8308 }, 8309 click: function(id) { 8310 const self = this; 8311 8312 if (!id) { 8313 console.log('favourite click id undefined'); 8314 return; 8315 } 8316 self.debounceToggleFavorite(id); 8317 }, 8318 callback: function(id, type) { 8319 const product = Array.from(PRODUCT_DATA).find(p => p.id == id); 8320 if (product) { 8321 const returnData = [{ 8322 data: { 8323 code: product.code, 8324 id: product.id, 8325 name: product.name, 8326 currency: product.currency, 8327 category_id: product.category_id, 8328 category_name: product.category, 8329 category_path: product.category_path, 8330 brand: product.brand, 8331 price: product.total_sale_price 8332 }, 8333 status: 1 8334 }]; 8335 8336 if (type == 'add') { 8337 for(let i=0; i < callbacks.product.wishList.add.length; i++){ 8338 if(typeof callbacks.product.wishList.add[i] === 'function'){ 8339 try { 8340 callbacks.product.wishList.add[i]?.(returnData); 8341 } catch (error) { console.log(`Product Wishlist Add Callback Error => ${error}`); } 8342 } 8343 } 8344 8345 for(let i=0; i < callbacks.product.favourite.add.length; i++){ 8346 if(typeof callbacks.product.favourite.add[i] === 'function'){ 8347 try { 8348 callbacks.product.favourite.add[i]?.(id); 8349 } catch (error) { console.log(`Products Favourite Add Callback Error => ${error}`); } 8350 } 8351 } 8352 } else { 8353 for(let i=0; i < callbacks.product.wishList.remove.length; i++){ 8354 if(typeof callbacks.product.wishList.remove[i] === 'function'){ 8355 try { 8356 callbacks.product.wishList.remove[i]?.(returnData); 8357 } catch (error) { console.log(`Product Wishlist Remove Callback Error => ${error}`); } 8358 } 8359 } 8360 8361 for(let i=0; i < callbacks.product.favourite.remove.length; i++){ 8362 if(typeof callbacks.product.favourite.remove[i] === 'function'){ 8363 try { 8364 callbacks.product.favourite.remove[i]?.(id); 8365 } catch (error) { console.log(`Products Favourite Remove Callback Error => ${error}`); } 8366 } 8367 } 8368 } 8369 } 8370 }, 8371 init() { 8372 const self = this; 8373 8374 self.fetchFavorites(); 8375 self.set(); 8376 8377 if (MEMBER_INFO.ID > 0) { 8378 window.addEventListener("online", () => self.handleOnline()); 8379 8380 const boundHandlePageLeave = self.handlePageLeave.bind(self); 8381 8382 window.addEventListener("beforeunload", boundHandlePageLeave); 8383 window.addEventListener("unload", boundHandlePageLeave); 8384 window.addEventListener("pagehide", boundHandlePageLeave); 8385 window.addEventListener("visibilitychange", () => { 8386 if (document.visibilityState === 'hidden') { 8387 boundHandlePageLeave(); 8388 } 8389 return; 8390 }); 8391 } 8392 8393 const favourites = document.querySelectorAll('[data-toggle="favourite"]'); 8394 Array.from(favourites).forEach(fav => { 8395 if (fav.componentFavourite == 1) return; 8396 8397 fav.addEventListener('click', async e => { 8398 e.preventDefault(); 8399 8400 fav.classList.add('disabled'); 8401 if (MEMBER_INFO.ID <= 0) { 8402 await loginPopup(); 8403 fav.classList.remove('disabled'); 8404 return; 8405 } 8406 8407 const product = fav.closest('[data-toggle="product"]'); 8408 if (!product) { 8409 console.log(`favourite Product undefined`); 8410 return; 8411 } 8412 8413 const productId = product.dataset.id; 8414 if (!productId) { 8415 console.log(`favourite Product Id undefined => data-id=?`); 8416 return; 8417 } 8418 8419 self.click(productId); 8420 fav.classList.remove('disabled'); 8421 return; 8422 }); 8423 8424 fav.componentFavourite = 1; 8425 }); 8426 } 8427 }, 8428 collection: function() { 8429 const collections = document.querySelectorAll('[data-toggle="collection"]');
8430 Array.from(collections).forEach(btn => { 8431 if (btn.componentCollection == 1) return; 8432 8433 btn.addEventListener('click', e => { 8434 if (MEMBER_INFO.ID <= 0) { 8435 loginPopup(); 8436 return; 8437 } 8438 8439 const productItem = btn.closest('[data-toggle="product"]'); 8440 if (!productItem) return; 8441 8442 const productId = productItem.dataset.id || 0; 8443 const variantId = productItem.dataset.variantId || 0; 8444 8445 loadSnippet({ 8446 snippet: '_shopping-list-popup', 8447 params: { 8448 include: 'shopping-list', 8449 PRODUCT_ID: productId, 8450 VARIANT_ID: variantId 8451 }, 8452 success: function(loadRes){ 8453 modal({ 8454 id: `modal-shopping-list-popup`, 8455 width: '480px', 8456 html: loadRes, 8457 }); 8458 } 8459 }); 8460 }); 8461 8462 btn.componentCollection = 1; 8463 }); 8464 }, 8465 compare: { 8466 compareList: [], 8467 add: function(productId) { 8468 const self = this; 8469 8470 let status; 8471 const isActive = Array.from(self.compareList).find(x => x == productId) || false; 8472 8473 if (!isActive && self.compareList.length == 4) { 8474 status = 3; 8475 } else { 8476 if (isActive) { 8477 status = 2; 8478 } else { 8479 self.compareList.push(productId); 8480 LocalApi.set('compare', self.compareList); 8481 const products = document.querySelectorAll(`[data-toggle="product"][data-id="${productId}"]`); 8482 Array.from(products).forEach(item => item.querySelector('[data-toggle="compare"]')?.classList.add('active')); 8483 status = 1; 8484 } 8485 } 8486 8487 if (self.compareList.length == 1) { 8488 notify({ 8489 text: COMMON_LANG?.compare_min_desc, 8490 class: 'info', 8491 }); 8492 return; 8493 } 8494 8495 loadSnippet({ 8496 snippet: '_product-compare', 8497 params: { 8498 include: 'product-list', 8499 PRODUCT_IDS: self.compareList.join('-'), 8500 ADD_PRODUCT_ID: productId, 8501 STATUS: status 8502 }, 8503 success: function(loadRes){ 8504 modal({ 8505 id: 'modal-product-compare', 8506 width: '991px', 8507 html: loadRes, 8508 }); 8509 return; 8510 } 8511 }); 8512 return; 8513 }, 8514 remove: async function(productId) { 8515 const self = this; 8516 self.compareList = self.compareList.filter(x => x != productId); 8517 LocalApi.set('compare', self.compareList); 8518 8519 const products = document.querySelectorAll(`[data-toggle="product"][data-id="${productId}"]`); 8520 Array.from(products).forEach(item => item.querySelector('[data-toggle="compare"]')?.classList.remove('active')); 8521 return; 8522 }, 8523 get: function() { 8524 const self = this; 8525 8526 self.compareList = LocalApi.get('compare') || []; 8527 Array.from(self.compareList).forEach(item => { 8528 const products = document.querySelectorAll(`[data-toggle="product"][data-id="${item}"]`); 8529 Array.from(products).forEach(item => item.querySelector('[data-toggle="compare"]')?.classList.add('active')); 8530 }); 8531 }, 8532 init: function() { 8533 const self = this; 8534 8535 const compares = document.querySelectorAll('[data-toggle="compare"]'); 8536 Array.from(compares).forEach(compare => { 8537 if (compare.componentCompare == 1) return; 8538 8539 compare.addEventListener('click', e => { 8540 e.preventDefault(); 8541 8542 const product = compare.closest('[data-toggle="product"]'); 8543 if (!product) { 8544 console.log(`compare Product undefined`); 8545 return; 8546 } 8547 8548 const productId = product.dataset.id; 8549 if (!productId) { 8550 console.log(`compare Product Id undefined => data-id=?`); 8551 return; 8552 } 8553 8554 if (getCookie('compare') != 1) {
8555 setCookie('compare', 1); 8556 self.compareList = []; 8557 LocalApi.set('compare', self.compareList); 8558 } 8559 self.compareList = LocalApi.get('compare') || []; 8560 const isCompareAdd = self.compareList.find(x => x == productId); 8561 isCompareAdd ? self.remove(productId, compare) : self.add(productId, compare); 8562 return; 8563 }); 8564 8565 compare.componentCompare = 1; 8566 }); 8567 8568 if (getCookie('compare') == 1) self.get(); 8569 } 8570 }, 8571 stockBackup: function() { 8572 const holders = document.querySelectorAll('[data-toggle="stock-backup"]'); 8573 Array.from(holders).forEach(holder => { 8574 if (holder.componentStockBackup == 1) return; 8575 holder.componentStockBackup = 1; 8576 if (holder.dataset.inStock == 1) return; 8577 8578 stockBackup.check(holder); 8579 }); 8580 }, 8581 outStock: function() { 8582 const outStocks = document.querySelectorAll('[data-toggle="show-out-stock"]');
8583 Array.from(outStocks).forEach(outStock => { 8584 const stockStatus = outStock.dataset.stockStatus; 8585 if (stockStatus == 1) return; 8586 8587 showOutStock(outStock); 8588 }); 8589 }, 8590 stockAlarm: function() { 8591 const stockAlarms = document.querySelectorAll('[data-toggle="stock-alarm"]'); 8592 Array.from(stockAlarms).forEach(alarm => { 8593 if (alarm.componentStockAlarm == 1) return; 8594 8595 alarm.addEventListener('click', async e => { 8596 if (MEMBER_INFO.ID <= 0) { 8597 await loginPopup(); 8598 return; 8599 } 8600 8601 const productItem = alarm.closest('[data-toggle="product"]'); 8602 if (!productItem) return; 8603 8604 const productId = productItem.dataset.id; 8605 const variantId = productItem.dataset.variantId || 0; 8606 if (!productId) return; 8607 8608 alarm.classList.add('disabled'); 8609 await modal({ 8610 id: 'modal-stock-alarm-list-adding-warning', 8611 html: COMMON_LANG?.stock_alarm_list_adding_warning, 8612 width: 'auto', 8613 alert: true, 8614 buttons: [ 8615 { 8616 text: COMMON_LANG?.stock_alarm_yes, href: 'javascript:void(0)', class: 'btn btn-primary !h-10', event: async function() { 8617 modalClose(); 8618 const formData = new FormData(); 8619 formData.append('productId', productId); 8620 formData.append('variantId', variantId); 8621 8622 try { 8623 const response = await request('POST', getEndpoint('ADD_TO_ALARM_LIST', 'stock'), formData); 8624 modal({ 8625 id: 'modal-stock-alarm-list-adding-success', 8626 html: response.statusText || COMMON_LANG?.added_stock_alarm_success, 8627 width: '480px', 8628 alert: true, 8629 class: 'success', 8630 }); 8631 8632 for(let i=0; i < callbacks.product.stockAlarm.length; i++){ 8633 if(typeof callbacks.product.stockAlarm[i] === 'function'){ 8634 try { 8635 callbacks.product.stockAlarm[i]?.(response, productId); 8636 } catch (error) { console.log(`Stock Alarm List Add Callback Error => ${error}`); } 8637 } 8638 } 8639 } catch (error) { 8640 requestError(error, 'ADD_TO_ALARM_LIST stock'); 8641 } 8642 }, 8643 }, 8644 ], 8645 }); 8646 alarm.classList.remove('disabled'); 8647 }); 8648 8649 alarm.componentStockAlarm = 1; 8650 }); 8651 }, 8652 priceAlarm: function() { 8653 const priceAlarms = document.querySelectorAll('[data-toggle="price-alarm"]'); 8654 Array.from(priceAlarms).forEach(alarm => { 8655 if (alarm.componentPriceAlarm == 1) return; 8656 8657 alarm.addEventListener('click', async e => { 8658 if (MEMBER_INFO.ID <= 0) { 8659 await loginPopup(); 8660 alarm.classList.remove('disabled'); 8661 return; 8662 } 8663 8664 const productItem = alarm.closest('[data-toggle="product"]'); 8665 if (!productItem) return; 8666 8667 const productId = productItem.dataset.id; 8668 const variantId = productItem.dataset.variantId || 0; 8669 8670 const productData = PRODUCT_DATA.find(x => x.id == productId); 8671 if (!productData) return; 8672 8673 const currency = productData.currency; 8674 const productVat = productData.vat; 8675 let currentPrice = alarm.dataset.priceSell; 8676 if (!currentPrice) currentPrice = vat(productData.price, productVat); 8677 8678 if (!productId || !currentPrice || !currency || productVat == null) return; 8679 8680 const formData = new FormData(); 8681 formData.append('productId', productId); 8682 formData.append('variantId', variantId);
8683 formData.append('currentPrice', priceToFloat(currentPrice)); 8684 formData.append('currency', currency); 8685 formData.append('dipslayVat', productVat); 8686 8687 alarm.classList.add('disabled'); 8688 await modal({ 8689 html: COMMON_LANG?.price_alarm_list_adding_warning, 8690 width: 'auto', 8691 alert: true, 8692 buttons: [ 8693 { 8694 text: COMMON_LANG?.price_alarm_yes, href: 'javascript:void(0)', class: 'btn btn-primary !h-10', event: async function() { 8695 modalClose(); 8696 try { 8697 const response = await request('POST', getEndpoint('ADD_TO_ALARM_LIST', 'price'), formData); 8698 modal({ 8699 html: response.message || response.statusText || COMMON_LANG?.added_price_alarm_success, 8700 width: '480px', 8701 alert: true, 8702 class: 'success', 8703 }); 8704 8705 for(let i=0; i < callbacks.product.priceAlarm.length; i++){ 8706 if(typeof callbacks.product.priceAlarm[i] === 'function'){ 8707 try { 8708 callbacks.product.priceAlarm[i]?.(response, productId); 8709 } catch (error) { console.log(`Price Alarm List Add Callback Error => ${error}`); } 8710 } 8711 } 8712 } catch (error) { 8713 requestError(error, 'ADD_TO_ALARM_LIST price'); 8714 } 8715 }, 8716 }, 8717 ], 8718 }); 8719 alarm.classList.remove('disabled'); 8720 }); 8721 8722 alarm.componentPriceAlarm = 1; 8723 }); 8724 }, 8725 multipleDiscount: { 8726 elements: [], 8727 active: null, 8728 value: null, 8729 productItem: null, 8730 multipleTotals: [], 8731 displayVat: 1, 8732 setTotal: function() { 8733 const multipleDiscount = components.multipleDiscount; 8734 8735 multipleDiscount.multipleTotals = document.querySelectorAll('[data-toggle="product-multiple-total"]'); 8736 Array.from(multipleDiscount.multipleTotals).forEach(item => { 8737 item.classList.add('hidden'); 8738 item.innerHTML = ''; 8739 }); 8740 8741 if (!multipleDiscount.active || !multipleDiscount.value) return; 8742 8743 const priceElement = multipleDiscount.active.querySelector('[data-toggle="price-multiple"]'); 8744 if (!priceElement) return; 8745 8746 const price = priceToFloat(priceElement.innerHTML?.trim()); 8747 if (Number.isNaN(Number(price)) || price <= 0) return; 8748 8749 const totalPrice = price * multipleDiscount.value; 8750 8751 const callback = multipleDiscount.active.dataset?.callback; 8752 if (callback) { 8753 if (typeof window[callback] === 'function') { 8754 try { 8755 window[callback](multipleDiscount.active, multipleDiscount.value, price, totalPrice); 8756 } catch (error) { console.log(`Multiple Discount Set Total Callback Error => ${error}`); } 8757 } 8758 } 8759 8760 Array.from(multipleDiscount.multipleTotals).forEach(item => { 8761 item.classList.remove('hidden'); 8762 item.innerHTML = format(totalPrice) + ' <span>' + (item.dataset?.currency || CURRENCY || 'TL') + '</span>' + (multipleDiscount.displayVat == 0 ? ' + ' + COMMON_LANG.vat : ''); 8763 }); 8764 return; 8765 }, 8766 set: function(input) { 8767 const multipleDiscount = components.multipleDiscount; 8768 if (!multipleDiscount.elements.length) return; 8769 8770 const productItem = input.closest('[data-toggle="product"]'); 8771 if (productItem != multipleDiscount.productItem) return; 8772 8773 multipleDiscount.active = null; 8774
8775 Array.from(multipleDiscount.elements).forEach(multiple => { 8776 multiple.classList.remove('active'); 8777 8778 const value = input.value; 8779 const min = multiple.dataset?.min || 0; 8780 const max = multiple.dataset?.max || 0; 8781 multipleDiscount.displayVat = multiple.dataset?.vat || 0; 8782 8783 if (Number.parseFloat(min) <= Number.parseFloat(value) && Number.parseFloat(max) >= value) { 8784 multipleDiscount.active = multiple; 8785 multiple.classList.add('active'); 8786 multipleDiscount.value = value; 8787 } 8788 }); 8789 8790 components.multipleDiscount.setTotal(); 8791 }, 8792 init: function() { 8793 const multipleDiscount = components.multipleDiscount; 8794 multipleDiscount.elements = document.querySelectorAll('[data-toggle="product-multiple"]'); 8795 8796 Array.from(multipleDiscount.elements).forEach(multiple => { 8797 if (multiple.componentMultipleDiscount == 1) return; 8798 8799 if (!multipleDiscount.productItem) multipleDiscount.productItem = multiple.closest('[data-toggle="product"]'); 8800 if (!multipleDiscount.productItem) return; 8801 8802 multiple.addEventListener('click', e => { 8803 e.preventDefault(); 8804 8805 const productQty = productItemScope(multipleDiscount.productItem, multipleDiscount.productItem.dataset?.count); 8806 if (!productQty) return; 8807 8808 const min = multiple.dataset?.min; 8809 if (min) productQty.value = min; 8810 8811 multipleDiscount.set(productQty); 8812 return; 8813 }); 8814 8815 multiple.componentMultipleDiscount = 1; 8816 }); 8817 } 8818 }, 8819 qty: function() { 8820 const qtys = document.querySelectorAll('[data-toggle="qty"]'); 8821 Array.from(qtys).forEach(qty => { 8822 if (qty.componentQty == 1) return; 8823 8824 const input = qty.querySelector('input[type="number"]'); 8825 if (!input) return; 8826 8827 const decreaseBtn = qty.querySelector('.decrease'); 8828 const increaseBtn = qty.querySelector('.increase'); 8829 8830 const qtyCallback = function(count, oldCount, qty, inc) { 8831 const cb = qty.dataset.callback; 8832 if (typeof cb !== 'undefined' && count != oldCount) { 8833 if (typeof window[cb] === 'function') { 8834 try { 8835 window[cb](count, oldCount, qty, inc); 8836 } catch (error) {} 8837 } else if (typeof cb === 'string') { 8838 let method = cb.replace(' ', ''); 8839 method = cb.split('.'); 8840 if (method.length < 2) return; 8841 8842 let methodApp = window; 8843 for (let i = 0; i < method.length; i++) { 8844 methodApp = methodApp[method[i]]; 8845 if (typeof methodApp === 'undefined') break; 8846 } 8847 8848 if (typeof methodApp == 'function') { 8849 try { 8850 methodApp(count, oldCount, qty, inc); 8851 } catch (error) {} 8852 } 8853 } 8854 } 8855 } 8856 8857 const calculate = function(inc, decimal) { 8858 let oldCount = Number.parseFloat(input.value); 8859 let count = oldCount; 8860 8861 let minCount = input.min || input.dataset.min || 1; 8862 let maxCount = input.max || input.dataset.max; 8863 8864 if (inc != 'INPUT') { 8865 count += inc; 8866 if (Number.parseFloat(minCount) > Number.parseFloat(count) || (maxCount != '' && Number.parseFloat(count) > Number.parseFloat(maxCount))) return; 8867 } else { 8868 if (maxCount != '' && Number.parseFloat(count) > Number.parseFloat(maxCount)) { 8869 count = Number.parseFloat(maxCount); 8870 } 8871 oldCount = qty.inputOldCount; 8872 } 8873 8874 if (!Number.isInteger(count)) count = Number.parseFloat(count.toFixed(decimal)); 8875 if (Number.isNaN(count) || count === undefined) return; 8876 input.value = count; 8877 8878 qtyCallback(count, oldCount, qty, inc); 8879 try { 8880 components.multipleDiscount.set(input); 8881 components.multiSelect.set(qty); 8882 8883 const productItem = qty.closest('[data-toggle="product"]'); 8884 if (productItem) { 8885 pointCalculation(productItem); 8886 8887 if (typeof BLOCK_JS.PERSONALIZATION != 'undefined') { 8888 if (BLOCK_JS.PERSONALIZATION[productItem.dataset.id]) { 8889 const appPersonalization = BLOCK_JS.PERSONALIZATION[productItem.dataset.id] || {}; 8890 if (typeof appPersonalization?.setPrice == 'function') { 8891 appPersonalization.setPrice(); 8892 } 8893 } 8894 } 8895 } 8896 } catch (error) {} 8897 8898 qty.inputLastCount = count; 8899 } 8900 8901 const calculateBlur = function(inc, decimal) { 8902 const value = input.value; 8903 if (Number.isNaN(value) || value === undefined || value == '') input.value = qty.inputLastCount || input.min || input.dataset.min || 1; 8904 if (Number.parseFloat(value) < Number.parseFloat(input.min)) input.value = input.min; 8905 } 8906 8907 const increment = qty.dataset.increment ? qty.dataset.increment : input.step || '1'; 8908 const decimal = increment.indexOf('.') > -1 ? increment.replaceAll(/^.*?\./g, '').trim().length : 0; 8909 8910 if (decreaseBtn) decreaseBtn.addEventListener('click', () => calculate(increment ? Number.parseFloat(`-${increment}`) : -1, decimal)); 8911 if (increaseBtn) increaseBtn.addEventListener('click', () => calculate(increment ? Number.parseFloat(increment) : 1, decimal)); 8912 8913 input.addEventListener('focus', () => { 8914 qty.inputOldCount = Number.parseFloat(input.value); 8915 qty.inputLastCount = qty.inputOldCount; 8916 return; 8917 }); 8918 input.addEventListener('blur', () => calculateBlur('INPUT', decimal)); 8919 input.addEventListener('input', () => calculate('INPUT', decimal)); 8920 8921 components.multipleDiscount.set(input); 8922 8923 qty.componentQty = 1; 8924 }); 8925 }, 8926 addToCart: function() { 8927 const cartButtons = document.querySelectorAll('[data-toggle="add-to-cart"]');
8928 Array.from(cartButtons).forEach(btn => { 8929 if (btn.componentAddToCart == 1) return; 8930 8931 btn.addEventListener('click', async e => { 8932 const productItem = btn.closest('[data-toggle="product"]'); 8933 if (!productItem) return; 8934 8935 btn.classList.add('disabled'); 8936 8937 let productQty = productItemScope(productItem, productItem.dataset?.count); 8938 if (!productQty) productQty = productItemScope(productItem, productItem.dataset?.count); 8939 8940 const opt = { 8941 productId: productItem.dataset.id || 0, 8942 variantId: productItem.dataset.variantId || 0, 8943 quantity: productQty?.value || 1, 8944 buyNow: btn.dataset.buy || 0, 8945 buyRedirect: btn.dataset.url || '' 8946 } 8947 8948 const productRelated = productItemScope(productItem, '[data-toggle="product-related-id"]'); 8949 if (productRelated) { 8950 const productRelatedId = productRelated.dataset.relatedId || 0; 8951 opt['relatedProductId'] = productRelated.value && productRelated.value != 0 ? productRelated.value : `${productRelatedId}:0`; 8952 } 8953 8954 await addToCart(opt); 8955 setTimeout(() => { 8956 btn.classList.remove('disabled'); 8957 }, 1500); 8958 }); 8959 8960 btn.componentAddToCart = 1; 8961 }); 8962 }, 8963 multiShoppingCart: { 8964 get: async function(payload = []) { 8965 if (payload.length == 0) return; 8966 8967 const cartOpt = { 8968 productId: [], 8969 variantId: [], 8970 quantity: [], 8971 multi: true, 8972 buyNow: 1, 8973 redirect: PAGE_LINK.ORDER, 8974 }; 8975 8976 const formData = new FormData(); 8977 Array.from(payload).forEach(product => { 8978 formData.append('ws_codes[]', product.supplierProductCode); 8979 }); 8980 8981 loader(true); 8982 try { 8983 const response = await request('POST', getEndpoint('GET_PRODUCT_BY_WS_CODES'), formData); 8984 const data = response.data; 8985 if (!data?.length) return; 8986 8987 Array.from(data).forEach(item => { 8988 cartOpt.productId.push(item.id); 8989 8990 const product = payload.find(i => i.supplierProductCode == item.ws_code); 8991 if (product) { 8992 cartOpt.quantity.push(product.quantity || 1); 8993 cartOpt.variantId.push(item.variant_id || 0); 8994 } 8995 }); 8996 8997 if (cartOpt.productId.length) { 8998 await addToCart(cartOpt); 8999 } 9000 } catch (error) { 9001 console.warn(`GET_PRODUCT_BY_WS_CODES error => ${error}`); 9002 } finally { 9003 loader(); 9004 } 9005 }, 9006 init: function() { 9007 const multiShoppingCart = document.querySelectorAll('[data-toggle="multi-shopping-cart"]'); 9008 Array.from(multiShoppingCart).forEach(cart => { 9009 if (cart.componentMultiShoppingCart == 1) return; 9010 9011 cart.addEventListener('click', async e => { 9012 e.preventDefault(); 9013 9014 loader(true); 9015 try { 9016 const response = await request('GET', getEndpoint('CART_LOAD')); 9017 const products = response.PRODUCTS; 9018 let supplierLink = ''; 9019 9020 if (products.length > 0) { 9021 const product = products[0]; 9022 if (product.SUPPLIER_NAME) { 9023 supplierLink = product.SUPPLIER_NAME.trim(); 9024 if (!/^https?:\/\//i.test(supplierLink)) { 9025 supplierLink = `https://${supplierLink}`; 9026 } 9027 } 9028 } 9029 9030 if (response.CART_TOKEN && supplierLink) { 9031 let url = setUrlParam('TmultiShoppingCart', response.CART_TOKEN, supplierLink); 9032 url = setUrlParam('utm_source', window.location.hostname, url); 9033 window.open(url.toString(), '_blank'); 9034 } 9035 } catch (error) {
9036 console.warn(`multiShoppingCart error => ${error}`); 9037 } finally { 9038 loader(); 9039 } 9040 }); 9041 9042 cart.componentMultiShoppingCart = 1; 9043 }); 9044 } 9045 }, 9046 cartPreview: { 9047 element: '', 9048 createElement: '', 9049 hoverTimeout: '', 9050 cartCount: CART_COUNT || 0, 9051 create: async function(step = null) { 9052 const cartPreview = components.cartPreview; 9053 9054 if (step == 1) { 9055 cartPreview.createElement = document.createElement('div'); 9056 cartPreview.createElement.id = 'cart-preview-app'; 9057 cartPreview.createElement.classList.add('hidden', 'fixed', 'z-[9999]'); 9058 document.body.append(cartPreview.createElement); 9059 return; 9060 } 9061 9062 if (step == 2) { 9063 if (cartPreview.element.classList.contains('cart-preview-loaded')) return; 9064 const snippetUrl = cartPreview.element.dataset.url || snippetUri('cart-preview', {'include':'cart'}); 9065 try { 9066 await request('GET', snippetUrl).then(async response => { 9067 if (!response) return; 9068 if (response.statusCode == 'EOC0001') { 9069 window.location.href = '/' + PAGE_LINK.CART; 9070 return; 9071 }; 9072 9073 cartPreview.createElement.innerHTML = response; 9074 await evalScripts(cartPreview.createElement.innerHTML); 9075 }); 9076 } catch (error) { 9077 console.warn(`cartPreview warn: ${snippetUrl} => ${error}`); 9078 } finally { 9079 cartPreview.element.classList.add('cart-preview-loaded'); 9080 } 9081 } 9082 }, 9083 setPosition: async function() { 9084 const cartPreview = components.cartPreview; 9085 const getElement = cartPreview.element; 9086 const setElement = cartPreview.createElement; 9087 9088 const elementRect = getElement.getBoundingClientRect(); 9089 const posY = elementRect.top + elementRect.height; 9090 const posX = document.dir == 'rtl' ? elementRect.left : document.body.offsetWidth - elementRect.right; 9091 9092 setElement.style.top = `${posY}px`; 9093 setElement.style.right = `${posX}px`; 9094 if (document.dir == 'rtl') { 9095 setElement.style.right = ''; 9096 setElement.style.left = `${posX}px`; 9097 } 9098 }, 9099 async previewShow(auto = null) { 9100 const cartPreview = components.cartPreview; 9101 if (isMobile()) return; 9102 9103 await cartPreview.setPosition(); 9104 9105 if (typeof SNIPPET_JS.CART_PREVIEW?.getCart == 'function' && cartPreview.cartCount != CART_COUNT) { 9106 SNIPPET_JS.CART_PREVIEW.getCart(); 9107 } 9108 9109 if (auto) { 9110 await cartPreview.create(2); 9111 cartPreview.createElement.classList.add('no-hidden'); 9112 cartPreview.autoHiddenTimeout = setTimeout(() => { 9113 cartPreview.previewHidden(); 9114 }, 2000); 9115 } 9116 9117 cartPreview.cartCount = CART_COUNT; 9118 cartPreview.createElement.classList.remove('hidden'); 9119 }, 9120 previewHidden() { 9121 const cartPreview = components.cartPreview; 9122 if (isMobile()) return; 9123 9124 cartPreview.createElement.classList.remove('no-hidden'); 9125 cartPreview.createElement.classList.add('hidden'); 9126 }, 9127 init: async function() { 9128 const cartPreview = this; 9129 9130 if (!cartPreview.element) cartPreview.element = document.querySelector('[data-toggle="cart-preview"]'); 9131 if (!cartPreview.element || isMobile() || cartPreview.element.componentCartPreview == 1) return; 9132 cartPreview.element.componentCartPreview = 1; 9133 9134 await cartPreview.create(1); 9135 9136 cartPreview.element.addEventListener('mouseenter', async () => { 9137 if (!CART_COUNT) return; 9138 await cartPreview.create(2); 9139 9140 cartPreview.hoverTimeout = setTimeout(async () => { 9141 cartPreview.createElement.classList.remove('no-hidden'); 9142 clearTimeout(cartPreview.autoHiddenTimeout); 9143 cartPreview.previewShow(); 9144 }, 1); 9145 }); 9146 9147 cartPreview.createElement.addEventListener('mouseenter', async () => { 9148 cartPreview.createElement.classList.remove('no-hidden'); 9149 clearTimeout(cartPreview.autoHiddenTimeout); 9150 }); 9151 9152 document.addEventListener('mousemove', e => { 9153 const event = e.target; 9154 if (cartPreview.element.contains(event) || cartPreview.createElement.contains(event) || cartPreview.createElement.classList.contains('no-hidden')) { 9155 return 9156 }; 9157 9158 clearTimeout(cartPreview.hoverTimeout); 9159 cartPreview.previewHidden(); 9160 }); 9161 } 9162 }, 9163 cartDrawer: { 9164 element: '', 9165 createElement: '', 9166 snippetUrl: '', 9167 getCartDrawer: async function(cartDrawer) { 9168 if (cartDrawer.loaded) return; 9169 cartDrawer.loaded = true; 9170 9171 cartDrawer.createElement = document.createElement('div'); 9172 cartDrawer.snippetUrl = cartDrawer.element.dataset.url || snippetUri('cart-drawer', {'include':'cart'}); 9173 cartDrawer.element.removeAttribute('data-url'); 9174 9175 try { 9176 const response = await request('GET', cartDrawer.snippetUrl); 9177 if (!response || response.statusCode == 'EOC0001') { 9178 window.location.href = '/' + PAGE_LINK.CART; 9179 return; 9180 }; 9181 9182 cartDrawer.createElement.id = 'cart-drawer-app'; 9183 cartDrawer.createElement.innerHTML = response; 9184 9185 document.body.append(cartDrawer.createElement); 9186 await evalScripts(cartDrawer.createElement.innerHTML); 9187 } catch (error) { 9188 console.warn(`cartDrawer warn: ${cartDrawer.snippetUrl} => ${error}`); 9189 } finally { 9190 const drawerTarget = cartDrawer.createElement.querySelector('.drawer'); 9191 if (drawerTarget) { 9192 setTimeout(() => { 9193 drawerTarget.classList.add('active');
9194 document.body.classList.add('drawer-active'); 9195 cartDrawer.element.dataset.toggle = 'drawer'; 9196 drawerTarget.id = 'cart-drawer-wrapper'; 9197 cartDrawer.element.dataset.drawerTarget = '#cart-drawer-wrapper'; 9198 components.init(); 9199 }, 1); 9200 } 9201 } 9202 }, 9203 init: function() { 9204 const cartDrawer = this; 9205 9206 if (!cartDrawer.element) cartDrawer.element = document.querySelector('[data-toggle="cart-drawer"]'); 9207 if (!cartDrawer.element || cartDrawer.element.componentCartDrawer == 1) return; 9208 9209 cartDrawer.element.addEventListener('click', async e => { 9210 e.preventDefault(); 9211 9212 await cartDrawer.getCartDrawer(cartDrawer); 9213 9214 if (typeof SNIPPET_JS.CART_DRAWER?.getCart == 'function') { 9215 SNIPPET_JS.CART_DRAWER.getCart(); 9216 return; 9217 } 9218 }); 9219 9220 cartDrawer.element.componentCartDrawer = 1; 9221 } 9222 }, 9223 cartCountPrice: function() { 9224 const counts = document.querySelectorAll('[data-toggle="cart-count"]'); 9225 const prices = document.querySelectorAll('[data-toggle="cart-price"]'); 9226 9227 Array.from(counts).forEach(item => { 9228 if (CART_COUNT > 99) CART_COUNT = '99+'; 9229 item.innerText = CART_COUNT || ''; 9230 }); 9231 Array.from(prices).forEach(item => { 9232 const total = typeof CART_TOTAL === 'string' ? priceToFloat(CART_TOTAL) : CART_TOTAL; 9233 item.innerHTML = total > 0 ? `${format(total)} <span>${CURRENCY}</span>` : ''; 9234 }); 9235 return; 9236 }, 9237 multiAddtoCart: function() { 9238 const cartButtons = document.querySelectorAll('[data-toggle="multi-select-add"]'); 9239 Array.from(cartButtons).forEach(btn => { 9240 if (btn.componentMultiAddtoCart == 1) return; 9241 9242 callbacks.cart.add.push(function(response) { 9243 if (typeof btn.dataset.callback === 'string' && typeof window[btn.dataset.callback] === 'function') { 9244 try { 9245 window[btn.dataset.callback](response); 9246 } catch (error) {} 9247 } 9248 }); 9249 9250 btn.addEventListener('click', async e => { 9251 btn.classList.add('disabled'); 9252 const products = document.querySelectorAll('input[data-toggle="multi-select"]:checked'); 9253 if (products.length == 0) { 9254 notify({ 9255 text: COMMON_LANG?.no_products_selected, 9256 class: 'danger', 9257 }); 9258 btn.classList.remove('disabled'); 9259 return; 9260 } 9261 9262 const pids = [], 9263 variants = [], 9264 quantities = []; 9265 9266 Array.from(products).forEach(item => { 9267 const productItem = item.closest('[data-toggle="product"]'); 9268 if (!productItem) return; 9269 9270 let productQty = productItemScope(productItem, item.dataset?.count); 9271 if (!productQty) productQty = productItemScope(productItem, productItem.dataset?.count); 9272 9273 const id = productItem.dataset.id || 0; 9274 const variant = productItem.dataset.variantId || 0; 9275 const quantity = productQty?.value || 1; 9276 9277 pids.push(id); 9278 variants.push(variant); 9279 quantities.push(quantity); 9280 }); 9281 9282 const opt = { 9283 productId: pids, 9284 variantId: variants, 9285 quantity: quantities, 9286 multi: true, 9287 } 9288 9289 await addToCart(opt); 9290 components.multiSelect.multiSelectedItems = []; 9291 const multiCount = document.querySelector('[data-toggle="multi-select-count"]'); 9292 const multiTotal = document.querySelector('[data-toggle="multi-select-total"]'); 9293 const multiNotDiscountTotal = document.querySelector('[data-toggle="multi-select-not-discount-total"]'); 9294 9295 9296 if (multiCount) multiCount.innerText = format(0); 9297 if (multiTotal) multiTotal.innerText = format(0); 9298 if (multiNotDiscountTotal) multiNotDiscountTotal.innerText = format(0); 9299 btn.classList.remove('disabled'); 9300 return; 9301 }); 9302 9303 btn.componentMultiAddtoCart = 1; 9304 }); 9305 }, 9306 multiSelect: { 9307 multiSelectItems: [], 9308 multiSelectedItems: [], 9309 selectedData: [], 9310 set: function(btn = null) { 9311 if (btn == null) return; 9312 9313 const self = this; 9314 self.selectedData = []; 9315 9316 const productItem = btn.closest('[data-toggle="product"]'); 9317 if (!productItem) return; 9318 9319 if (btn.dataset.toggle == 'multi-select') { 9320 const isCheck = btn.checked; 9321 if (isCheck && !self.multiSelectedItems.includes(productItem)) { 9322 self.multiSelectedItems.push(productItem); 9323 } else if (!isCheck && self.multiSelectedItems.includes(productItem)) { 9324 self.multiSelectedItems = self.multiSelectedItems.filter(x => x != productItem); 9325 } 9326 } 9327 9328 let notDiscountTotal = 0; 9329 let priceTotal = 0; 9330
9331 Array.from(self.multiSelectedItems).forEach(item => { 9332 const priceSell = item.querySelector('[data-toggle="price-sell"]') || item.querySelector('[data-toggle="price-sell-vat"]'); 9333 const count = item.querySelector(item.dataset.count)?.value || 1; 9334 9335 let price = 0; 9336 if (priceSell) price = priceToFloat(priceSell.innerText); 9337 9338 self.selectedData.push({ 9339 id: item.dataset.id, 9340 price: price, 9341 count: count, 9342 }); 9343 priceTotal += price * count; 9344 9345 const priceNotDiscounted = item.querySelector('[data-toggle="price-not-discounted"]') || item.querySelector('[data-toggle="price-not-discounted-vat"]'); 9346 if (priceNotDiscounted && !priceNotDiscounted.closest('[data-toggle="price-control"]')?.classList.contains('hidden')) { 9347 price = priceToFloat(priceNotDiscounted.innerText); 9348 } 9349 9350 notDiscountTotal += price * count; 9351 }); 9352 9353 const multiCount = document.querySelector('[data-toggle="multi-select-count"]'); 9354 const multiTotal = document.querySelector('[data-toggle="multi-select-total"]'); 9355 const multiNotDiscountTotal = document.querySelector('[data-toggle="multi-select-not-discount-total"]'); 9356 9357 9358 if (multiCount) multiCount.innerText = self.multiSelectedItems.length || 0; 9359 if (multiTotal) multiTotal.innerText = format(priceTotal); 9360 if (multiNotDiscountTotal) multiNotDiscountTotal.innerText = format(notDiscountTotal); 9361 9362 if (multiNotDiscountTotal) { 9363 if (priceTotal < notDiscountTotal) { 9364 if (multiNotDiscountTotal.closest('[data-toggle="price-control"]')) multiNotDiscountTotal.closest('[data-toggle="price-control"]').classList.remove('!hidden', 'hidden'); 9365 multiNotDiscountTotal.style.display = 'block'; 9366 } else { 9367 if (multiNotDiscountTotal.closest('[data-toggle="price-control"]')) multiNotDiscountTotal.closest('[data-toggle="price-control"]').classList.add('!hidden', 'hidden'); 9368 multiNotDiscountTotal.style.display = 'none'; 9369 } 9370 } 9371 9372 const callbackBtn = productItemScope(productItem, '[data-toggle="multi-select"]'); 9373 if (callbackBtn) { 9374 if (typeof callbackBtn.dataset.callback === 'string' && typeof window[callbackBtn.dataset.callback] === 'function') { 9375 try { 9376 window[callbackBtn.dataset.callback](priceTotal, notDiscountTotal, btn, self.multiSelectItems, self.multiSelectedItems, self.selectedData); 9377 } catch (error) {} 9378 return; 9379 } 9380 } 9381 return; 9382 }, 9383 init: function() { 9384 const self = this; 9385 9386 self.multiSelectItems = []; 9387 9388 self.multiSelectItems = document.querySelectorAll('[data-toggle="multi-select"]'); 9389 Array.from(self.multiSelectItems).forEach(btn => { 9390 if (btn.componentMultiSelect == 1) return; 9391 9392 const productItem = btn.closest('[data-toggle="product"]'); 9393 if (!productItem) return; 9394 9395 btn.addEventListener('change', e => { 9396 self.set(btn); 9397 return; 9398 }); 9399 9400 self.set(btn); 9401 btn.componentMultiSelect = 1; 9402 }); 9403 } 9404 }, 9405 memberName() { 9406 const members = document.querySelectorAll('[data-toggle="member-name"]'); 9407 if (members.length > 0 && MEMBER_INFO.ID > 0) { 9408 Array.from(members).forEach(member => { 9409 if (member.componentMemberName == 1) return; 9410 9411 member.innerText = `${MEMBER_INFO.FIRST_NAME} ${MEMBER_INFO.LAST_NAME}`; 9412 9413 member.componentMemberName = 1; 9414 }); 9415 } 9416 }, 9417 newsletter: function() { 9418 const newsletters = document.querySelectorAll('[data-toggle="newsletter"],[data-toggle="newsletter-unsubscribe"]');
9419 Array.from(newsletters).forEach(form => { 9420 if (form.tagName.toLowerCase() !== 'form') return; 9421 9422 if (form.componentNewsletter == 1) return; 9423 9424 form.addEventListener('submit', async e => { 9425 e.stopPropagation(); 9426 e.preventDefault(); 9427 9428 popoverAlertHide(form); 9429 if (!checkValidity(form)) return; 9430 9431 const callback = form.dataset.callback; 9432 const formData = new FormData(form); 9433 9434 const submitButton = e.submitter; 9435 if (submitButton) submitButton.setAttribute('disabled', 'true'); 9436 9437 try { 9438 let endpoint = getEndpoint('NEWSLETTER_SUBSCRIBE'); 9439 if (form.dataset.toggle == 'newsletter-unsubscribe') { 9440 endpoint = getEndpoint('NEWSLETTER_UNSUBSCRIBE'); 9441 } 9442 9443 const response = await request('POST', endpoint, formData); 9444 if (typeof callback === 'function') { 9445 try { 9446 callback(response); 9447 return; 9448 } catch (error) {} 9449 } 9450 9451 if (callback) return; 9452 9453 if (response.status) { 9454 modal({ 9455 html: response.data.message || response.message || response.statusText || COMMON_LANG?.send_success, 9456 width: '480px', 9457 alert: true, 9458 class: 'success', 9459 iconHtml: ` 9460 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16"> 9461 <path d="M15.964.686a.5.5 0 0 0-.65-.65L.767 5.855a.75.75 0 0 0-.124 1.329l4.995 3.178 1.531 2.406a.5.5 0 0 0 .844-.536L6.637 10.07l7.494-7.494-1.895 4.738a.5.5 0 1 0 .928.372l2.8-7Zm-2.54 1.183L5.93 9.363 1.591 6.602l11.833-4.733Z"/> 9462 <path d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0m-1.993-1.679a.5.5 0 0 0-.686.172l-1.17 1.95-.547-.547a.5.5 0 0 0-.708.708l.774.773a.75.75 0 0 0 1.174-.144l1.335-2.226a.5.5 0 0 0-.172-.686Z"/> 9463 </svg> 9464 `, 9465 }); 9466 9467 for (let i=0; i < callbacks.customer.subscribe.length; i++){ 9468 if (typeof callbacks.customer.subscribe[i] === 'function'){ 9469 try { 9470 callbacks.customer.subscribe[i]?.(response); 9471 } catch (error) { console.warn(`Customer Other Callback Error => ${error}`); } 9472 } 9473 } 9474 9475 form.reset(); 9476 } 9477 } catch (error) { 9478 requestError(error, 'NEWSLETTER_SUBSCRIBE', form); 9479 } finally { 9480 if (submitButton) submitButton.removeAttribute('disabled'); 9481 } 9482 9483 return; 9484 }); 9485 9486 form.componentNewsletter = 1; 9487 }); 9488 }, 9489 fastRegister: function() { 9490 const fastRegisters = document.querySelectorAll('[data-toggle="fast-register"]'); 9491 Array.from(fastRegisters).forEach(form => { 9492 if (form.componentFastRegister == 1) return; 9493 if (form.tagName.toLowerCase() !== 'form') return; 9494 9495 const notifys = form.querySelector('[name="notifys"]'); 9496 const mail_notify = form.querySelector('[name="mail_notify"]'); 9497 const sms_notify = form.querySelector('[name="sms_notify"]'); 9498 const phone_notify = form.querySelector('[name="phone_notify"]'); 9499 if (notifys) { 9500 notifys.addEventListener('change', e => { 9501 if (mail_notify) mail_notify.checked = notifys.checked; 9502 if (sms_notify) sms_notify.checked = notifys.checked; 9503 if (phone_notify) phone_notify.checked = notifys.checked; 9504 }); 9505 } 9506 9507 form.addEventListener('submit', async e => { 9508 e.stopPropagation(); 9509 e.preventDefault(); 9510 9511 popoverAlertHide(form); 9512 if (!checkValidity(form)) return; 9513 9514 const formData = new FormData(form); 9515 const recaptchaResponse = formData.get('g-recaptcha-response'); 9516 if (recaptchaResponse != null && typeof grecaptcha !== 'undefined') formData.delete('security_code'); 9517 9518 const phone = form.querySelector('[type="tel"]'); 9519 if (phone?.dataset.key) formData.set('iso_code', phone.dataset.key.toLowerCase()); 9520 9521 formData.delete('notifys'); 9522 9523 const submitButton = e.submitter; 9524 if (submitButton) submitButton.setAttribute('disabled', 'true'); 9525 9526 loader(true); 9527 try { 9528 await request('POST', getEndpoint('FAST_REGISTER'), formData).then(async response => { 9529 if (response.status == 1) { 9530 let url = response.url; 9531 window.location.href = url; 9532 } else { 9533 await getCaptcha(form, response); 9534 9535 const field = response.field || response.key || ''; 9536 let selector = form.querySelector(`[name="${field == 'g-recaptcha-response' ? 'security_code' : field}"]`); 9537 if (!response.statusText) return; 9538 if (selector) { 9539 popoverAlert({ 9540 selector: selector, 9541 message: response.statusText, 9542 }); 9543 } else { 9544 notify({ 9545 text: response.statusText, 9546 class: 'danger', 9547 }); 9548 } 9549 } 9550 }); 9551 } catch (error) { 9552 console.warn(`FAST_REGISTER error => ${error}`); 9553 } finally { 9554 await captchaReset(form); 9555 if (submitButton) submitButton.removeAttribute('disabled'); 9556 loader(); 9557 } 9558 return; 9559 }); 9560 9561 form.componentFastRegister = 1; 9562 }); 9563 }, 9564 login: function() { 9565 const logins = document.querySelectorAll('[data-toggle="login"]');
9566 Array.from(logins).forEach(form => { 9567 if (form.componentLogin == 1) return; 9568 if (form.tagName.toLowerCase() !== 'form') return; 9569 9570 form.addEventListener('submit', async e => { 9571 e.stopPropagation(); 9572 e.preventDefault(); 9573 9574 popoverAlertHide(form); 9575 9576 const callback = form.dataset.callback; 9577 const formData = new FormData(form); 9578 9579 let type = formData.get('type'); 9580 9581 const password = formData.get('password'); 9582 let value = ''; 9583 if (type == 'phone') { 9584 value = formData.get('phone'); 9585 value = value.replaceAll(/\D/g, ''); 9586 } else { 9587 type = 'email'; 9588 value = formData.get('email'); 9589 } 9590 9591 if (value?.trim() == '' || password?.trim() == '') { 9592 let selector = null; 9593 if (value?.trim() == '') { 9594 selector = form.querySelector(`[name="${type}"]`); 9595 } else if (password?.trim() == '') { 9596 selector = form.querySelector(`[name="password"]`); 9597 } 9598 9599 if (selector) { 9600 popoverAlert({ 9601 selector: selector, 9602 message: COMMON_LANG?.form_required 9603 }); 9604 } 9605 return; 9606 } 9607 9608 const recaptchaResponse = formData.get('g-recaptcha-response'); 9609 if (recaptchaResponse != null && typeof grecaptcha !== 'undefined') formData.delete('security_code'); 9610 9611 const popupLogin = formData.get('popup'); 9612 9613 formData.delete('email'); 9614 formData.delete('phone'); 9615 formData.delete('type'); 9616 formData.delete('popup'); 9617 9618 const submitButton = e.submitter; 9619 if (submitButton) submitButton.setAttribute('disabled', 'true'); 9620 9621 const passwordData = { 9622 'type': type, 9623 'username': encodeURI(value), 9624 'password_old': formData.get('password') 9625 } 9626 9627 loader(true); 9628 try { 9629 await request('POST', getEndpoint('LOGIN', `${type}/${encodeURI(value)}`), formData).then(async response => { 9630 if (response.password_change) { 9631 await loginPasswordChange(passwordData); 9632 return; 9633 } 9634 9635 if (response.two_factor) { 9636 await loginTwoFactor(form, response); 9637 return; 9638 } 9639 9640 if (response.status == 1) { 9641 if (typeof MEMBER_INFO == 'object') { 9642 MEMBER_INFO.ID = response.customer_id || MEMBER_INFO.ID || 0; 9643 MEMBER_INFO.FIRST_NAME = response.customer_first_name || MEMBER_INFO.FIRST_NAME || ''; 9644 MEMBER_INFO.LAST_NAME = response.customer_last_name || MEMBER_INFO.LAST_NAME || ''; 9645 MEMBER_INFO.MAIL = response.customer_email || MEMBER_INFO.MAIL || ''; 9646 MEMBER_INFO.TRANSACTION_COUNT = response.customer_transaction_count || MEMBER_INFO.TRANSACTION_COUNT || 0; 9647 } 9648 9649 for(let i=0; i < callbacks.customer.login.length; i++){ 9650 if(typeof callbacks.customer.login[i] === 'function'){ 9651 try { 9652 callbacks.customer.login[i]?.(response); 9653 } catch (error) { console.log(`Customer Login Callback Error => ${error}`); } 9654 } 9655 } 9656 9657 if (response.statusText) { 9658 notify({ 9659 text: response.statusText, 9660 }); 9661 } 9662 9663 if (popupLogin == 1 || PAGE_ID == '91') { 9664 if (popupLogin == 1) modalClose('#modal
9664-login-popup'); 9665 location.reload(); 9666 return; 9667 } 9668 9669 let url = response.url; 9670 if (PAGE_ID == '64') url = '/' + PAGE_LINK.ORDER; 9671 if (form.dataset.successUrl) url = form.dataset.successUrl; 9672 await awaitTimeOut(1000); 9673 window.location.href = url; 9674 } else { 9675 await getCaptcha(form, response); 9676 9677 const field = response.field || response.key || ''; 9678 let selector = form.querySelector(`[name="${field == 'g-recaptcha-response' ? 'security_code' : field}"]`); 9679 if (!response.statusText) return; 9680 if (selector) { 9681 popoverAlert({ 9682 selector: selector, 9683 message: response.statusText, 9684 }); 9685 } else { 9686 notify({ 9687 text: response.statusText, 9688 class: 'danger', 9689 }); 9690 } 9691 } 9692 }); 9693 } catch (error) { 9694 if (error.status === 429) requestError(error, 'LOGIN', form); 9695 console.warn(`LOGIN error => ${error}`); 9696 } finally { 9697 await captchaReset(form); 9698 if (submitButton) submitButton.removeAttribute('disabled'); 9699 setTimeout(() => { 9700 loader(); 9701 }, 500); 9702 } 9703 }); 9704 9705 form.componentLogin = 1; 9706 }); 9707 }, 9708 otpLogin: { 9709 isEmail: function() { 9710 return typeof SETTINGS !== 'undefined' && SETTINGS?.OTP_LOGIN_EMAIL == 1; 9711 }, 9712 isSms: function() { 9713 return typeof SETTINGS !== 'undefined' && SETTINGS?.OTP_LOGIN_SMS == 1; 9714 }, 9715 normalizeReceiver: function(value) { 9716 value = value?.trim() ?? ''; 9717 return components.otpLogin.isSms() ? value.replaceAll(/\D/g, '') : value; 9718 }, 9719 loginOtpPopup: async function(receiver, ttl) { 9720 if (MEMBER_INFO.ID > 0) return; 9721 9722 const modalLogin = document.querySelector('#modal-login-popup'); 9723 if (modalLogin) { 9724 modalClose('#modal-login-popup'); 9725 } 9726 9727 await loadSnippet({ 9728 snippet: '_login-otp-popup', 9729 params: { 9730 include: 'customer-login', 9731 receiver: receiver, 9732 ttl: ttl, 9733 }, 9734 success: function(loadRes){ 9735 modal({ 9736 id: 'modal-login-otp-popup', 9737 width: '480px', 9738 html: loadRes, 9739 }); 9740 return; 9741 } 9742 }); 9743 }, 9744 getCode: async function(receiver, formData) { 9745 const response = await request('POST', getEndpoint('OTP_REQUEST', encodeURIComponent(receiver)), formData); 9746 return response; 9747 }, 9748 otpRequest: async function() { 9749 const otpLogins = document.querySelectorAll('[data-toggle="otp-login"]');
9750 Array.from(otpLogins).forEach(form => { 9751 if (form.componentOtpLogin == 1) return; 9752 if (form.tagName.toLowerCase() !== 'form') return; 9753 9754 form.addEventListener('submit', async e => { 9755 e.stopPropagation(); 9756 e.preventDefault(); 9757 9758 popoverAlertHide(form); 9759 9760 const formData = new FormData(form); 9761 9762 const receiver = components.otpLogin.normalizeReceiver(formData.get('otp')); 9763 9764 if (receiver == '') { 9765 const selector = form.querySelector(`[name="otp"]`); 9766 if (selector) { 9767 popoverAlert({ 9768 selector: selector, 9769 message: COMMON_LANG?.form_required 9770 }); 9771 } 9772 return; 9773 } 9774 9775 const recaptchaResponse = formData.get('g-recaptcha-response'); 9776 if (recaptchaResponse != null && typeof grecaptcha !== 'undefined') formData.delete('security_code'); 9777 9778 formData.delete('otp'); 9779 9780 const submitButton = e.submitter; 9781 if (submitButton) submitButton.setAttribute('disabled', 'true'); 9782 loader(true); 9783 9784 try { 9785 const response = await components.otpLogin.getCode(receiver, formData); 9786 if (response.status == 1) { 9787 if (response.statusText) { 9788 notify({ 9789 text: response.statusText, 9790 }); 9791 9792 await components.otpLogin.loginOtpPopup(receiver, response.ttl || 60); 9793 } 9794 } else { 9795 if (response.ttl) { 9796 await components.otpLogin.loginOtpPopup(receiver, response.ttl || 60); 9797 return; 9798 } 9799 9800 await getCaptcha(form, response); 9801 9802 const field = response.field || response.key || ''; 9803 let selector = form.querySelector(`[name="${field == 'g-recaptcha-response' ? 'security_code' : field}"]`); 9804 if (!response.statusText) return; 9805 if (selector) { 9806 popoverAlert({ 9807 selector: selector, 9808 message: response.statusText, 9809 }); 9810 } else { 9811 notify({ 9812 text: response.statusText, 9813 class: 'danger', 9814 }); 9815 } 9816 } 9817 } catch (error) { 9818 console.warn(`OTP_REQUEST error => ${error}`); 9819 } finally { 9820 await captchaReset(form); 9821 if (submitButton) submitButton.removeAttribute('disabled'); 9822 setTimeout(() => { 9823 loader(); 9824 return; 9825 }, 500); 9826 } 9827 }); 9828 9829 form.componentOtpLogin = 1; 9830 }); 9831 }, 9832 otpValidate: async function() { 9833 const otpLogins = document.querySelectorAll('[data-toggle="otp-validate"]');
9834 Array.from(otpLogins).forEach(form => { 9835 if (form.componentOtpLogin == 1) return; 9836 if (form.tagName.toLowerCase() !== 'form') return; 9837 9838 form.addEventListener('submit', async e => { 9839 e.stopPropagation(); 9840 e.preventDefault(); 9841 9842 popoverAlertHide(form); 9843 9844 const formData = new FormData(form); 9845 9846 const receiver = components.otpLogin.normalizeReceiver(formData.get('receiver')); 9847 9848 if (receiver == '') { 9849 const selector = form.querySelector(`[name="receiver"]`); 9850 if (selector) { 9851 popoverAlert({ 9852 selector: selector, 9853 message: COMMON_LANG?.form_required 9854 }); 9855 } 9856 return; 9857 } 9858 9859 const otp = formData.get('otp'); 9860 if (otp?.trim() == '' || otp?.length != 6 || form['otpControl'] == otp) { 9861 return; 9862 } 9863 9864 const recaptchaResponse = formData.get('g-recaptcha-response'); 9865 if (recaptchaResponse != null && typeof grecaptcha !== 'undefined') formData.delete('security_code'); 9866 9867 formData.delete('receiver'); 9868 formData.delete('type'); 9869 9870 const submitButton = e.submitter; 9871 if (submitButton) submitButton.setAttribute('disabled', 'true'); 9872 loader(true); 9873 9874 try { 9875 const response = await request('POST', getEndpoint('OTP_VALIDATE', encodeURIComponent(receiver)), formData); 9876 if (response.status == 1) { 9877 if (PAGE_ID == '64' || PAGE_ID == '91') { 9878 window.location.href = '/' + PAGE_LINK.ORDER; 9879 } else if (PAGE_ID == 5) { 9880 window.location.href = '/'; 9881 } else { 9882 window.location.reload(); 9883 } 9884 } else { 9885 await getCaptcha(form, response); 9886 9887 const field = response.field || response.key || ''; 9888 let isCaptcha = false; 9889 isCaptcha = field == 'security_code' || field == 'g-recaptcha-response'; 9890 if (!isCaptcha) form['otpControl'] = otp; 9891 9892 let selector = form.querySelector(`[name="${field == 'g-recaptcha-response' ? 'security_code' : field}"]`); 9893 if (!response.statusText) return; 9894 if (selector) { 9895 popoverAlert({ 9896 selector: selector, 9897 message: response.statusText, 9898 }); 9899 } else { 9900 notify({ 9901 text: response.statusText, 9902 class: 'danger', 9903 }); 9904 } 9905 } 9906 } catch (error) { 9907 console.warn(`OTP_VALIDATE error => ${error}`); 9908 } finally { 9909 await captchaReset(form); 9910 if (submitButton) submitButton.removeAttribute('disabled'); 9911 setTimeout(() => { 9912 loader(); 9913 }, 500); 9914 } 9915 }); 9916 9917 form.componentOtpLogin = 1; 9918 }); 9919 }, 9920 init: function() { 9921 components.otpLogin.otpRequest(); 9922 components.otpLogin.otpValidate(); 9923 return; 9924 } 9925 }, 9926 nomembership: function() { 9927 const nomemberships = document.querySelectorAll('[data-toggle="nomembership"]');
9928 Array.from(nomemberships).forEach(form => { 9929 if (form.componentNomembership == 1) return; 9930 9931 form.addEventListener('submit', async e => { 9932 e.stopPropagation(); 9933 e.preventDefault(); 9934 9935 popoverAlertHide(form); 9936 9937 const formData = new FormData(form); 9938 9939 const recaptchaResponse = formData.get('g-recaptcha-response'); 9940 if (recaptchaResponse != null && typeof grecaptcha !== 'undefined') formData.delete('security_code'); 9941 9942 const submitButton = e.submitter; 9943 if (submitButton) submitButton.setAttribute('disabled', 'true'); 9944 9945 loader(true); 9946 try { 9947 await request('POST', getEndpoint('LOGIN_SHOPPING'), formData).then(async response => { 9948 if (response.success) { 9949 let url = response.url || '/' + PAGE_LINK.ORDER; 9950 window.location.href = url; 9951 9952 for(let i=0; i < callbacks.customer.login.length; i++){ 9953 if(typeof callbacks.customer.login[i] === 'function'){ 9954 try { 9955 callbacks.customer.login[i]?.(response); 9956 } catch (error) { console.log(`Customer Login Callback Error => ${error}`); } 9957 } 9958 } 9959 if (typeof LoginPageTracking !== 'undefined') { 9960 if(typeof LoginPageTracking.Callback === 'function'){ 9961 try { 9962 LoginPageTracking.Callback(response); 9963 } catch (error) { console.log(`Customer Login LoginPageTracking Callback Error => ${error}`); } 9964 } 9965 for(let i = 0; i < LoginPageTracking.callbackArray.length; i++){ 9966 if(typeof LoginPageTracking.callbackArray[i] === 'function'){ 9967 try { 9968 LoginPageTracking.callbackArray[i]?.(response); 9969 } catch (error) { console.log(`Customer Login LoginPageTracking.callbackArray Callback Error => ${error}`); } 9970 } 9971 } 9972 } 9973 } else if (response.message) { 9974 await getCaptcha(form, response); 9975 9976 notify({ 9977 text: response.message, 9978 class: 'danger', 9979 }); 9980 } 9981 }); 9982 } catch (error) { 9983 console.warn(`LOGIN_SHOPPING error => ${error}`); 9984 } finally { 9985 await captchaReset(form); 9986 if (submitButton) submitButton.removeAttribute('disabled'); 9987 loader(); 9988 } 9989 }); 9990 9991 form.componentNomembership = 1; 9992 }); 9993 }, 9994 socialLogin: { 9995 getGoogleTokenClient: function() { 9996 try { 9997 if (googleTokenClient == null) { 9998 googleTokenClient = google.accounts.oauth2.initTokenClient({ 9999 client_id: GOOGLE_CLIENT_ID, 10000 scope: 'openid email profile', 10001 callback: (response) => { 10002 if (response.access_token) { 10003 location.href = location.origin + `/srv/service/social/google/login?code=${response.access_token}`; 10004 } else { 10005 console.warn('Google Sign-In error:', response); 10006 } 10007 return; 10008 }, 10009 }); 10010 } 10011 googleTokenClient?.requestAccessToken(); 10012 } catch (error) { 10013 console.warn('Google Sign-In error:', error); 10014 } 10015 }, 10016 openTsoft360Popup: function(trigger) { 10017 const startUrl = trigger.getAttribute('href') || '/api/v3/storefront/sso/tsoft360/start'; 10018 const separator = startUrl.includes('?') ? '&' : '?'; 10019 const returnTo = encodeURIComponent(window.location.pathname + window.location.search); 10020 const fullPageUrl = `${startUrl}${separator}return_to=${returnTo}`; 10021 const popupUrl = `${fullPageUrl}&display=popup`; 10022 10023 const width = 480; 10024 const height = 640; 10025 const left = Math.max(0, window.screenX + (window.outerWidth - width) / 2); 10026 const top = Math.max(0, window.screenY + (window.outerHeight - height) / 2); 10027 10028 const popup = window.open(popupUrl, 'tsoft360Sso', `width=${width},height=${height}
10028,left=${left},top=${top}`); 10029 if (!popup) { 10030 window.location.href = fullPageUrl; 10031 return; 10032 } 10033 10034 components.socialLogin.bindTsoft360Listener(); 10035 popup.focus(); 10036 }, 10037 bindTsoft360Listener: function() { 10038 if (window.tsoft360SsoListenerBound == 1) return; 10039 window.tsoft360SsoListenerBound = 1; 10040 10041 window.addEventListener('message', event => { 10042 if (event.origin !== window.location.origin) return; 10043 10044 const data = event.data; 10045 if (!data || data.type !== 'tsoft360-sso') return; 10046 10047 if (data.status == 'success') { 10048 window.location.href = data.returnTo || window.location.href; 10049 } else { 10050 window.location.href = data.returnTo || '/'; 10051 } 10052 }); 10053 }, 10054 init: function() { 10055 const socials = document.querySelectorAll('[data-toggle="social-login"]'); 10056 Array.from(socials).forEach(social => { 10057 if (social.componentSocialLogin == 1) return; 10058 10059 social.addEventListener('click', e => { 10060 e.preventDefault(); 10061 10062 const platform = social.dataset.platform; 10063 if (platform == 'facebook') { 10064 window.location.href = getEndpoint('FACEBOOK_LOGIN'); 10065 return; 10066 } else if (platform == 'google') { 10067 components.socialLogin.getGoogleTokenClient(); 10068 return; 10069 } else if (platform == 'apple') { 10070 window.location.href = getEndpoint('APPLE_LOGIN'); 10071 return; 10072 } else if (platform == 'tsoft360') { 10073 components.socialLogin.openTsoft360Popup(social); 10074 return; 10075 } else { 10076 console.warn('platform undefined'); 10077 } 10078 return; 10079 }); 10080 10081 social.componentSocialLogin = 1; 10082 }); 10083 } 10084 }, 10085 logout: function() { 10086 const logouts = document.querySelectorAll('[data-toggle="logout"]'); 10087 Array.from(logouts).forEach(logout => { 10088 if (logout.componentLogout == 1) return; 10089 10090 logout.addEventListener('click', e => { 10091 e.preventDefault(); 10092 try { 10093 request('GET', getEndpoint('LOGOUT')).then(response => { 10094 window.location.href = '/'; 10095 }); 10096 } catch (error) { 10097 console.warn(`LOGOUT error => ${error}`); 10098 } 10099 return; 10100 }); 10101 10102 logout.componentLogout = 1; 10103 }); 10104 }, 10105 isRequired: function() { 10106 setTimeout(() => { 10107 const requireds = document.querySelectorAll('.required');
10108 Array.from(requireds).forEach(required => { 10109 const formLabel = required.nextElementSibling; 10110 if (required.componentIsRequired == 1 || formLabel?.querySelector('.form-required')) return; 10111 10112 const placeholder = required.getAttribute('placeholder'); 10113 if (placeholder) required.placeholder = placeholder + ' *'; 10114 10115 const marker = formLabel?.querySelector(':scope > .input-checkbox, :scope > .input-radio'); 10116 if (formLabel?.classList.contains('form-label') || formLabel?.classList.contains('form-label-file') || marker) { 10117 const target = marker && formLabel.lastElementChild != marker ? formLabel.lastElementChild : formLabel; 10118 target.innerHTML = `${target.innerHTML} <span class="form-required">*</span>`; 10119 } 10120 required.componentIsRequired = 1; 10121 }); 10122 }, 250); 10123 }, 10124 personalization: {}, 10125 imageMap: function() { 10126 const imageMaps = document.querySelectorAll(`[data-toggle="image-map"]`); 10127 Array.from(imageMaps).forEach(map => { 10128 if (map.componentImageMap == 1) return; 10129 10130 map.addEventListener('click', function (e) { 10131 let xPercent = Math.round((e.offsetX / this.clientWidth) * 100); 10132 let yPercent = Math.round((e.offsetY / this.clientHeight) * 100); 10133 let copyText = `${xPercent}|${yPercent}`; 10134 copyText(copyText, `${copyText} ${COMMON_LANG?.copied}`); 10135 return; 10136 }); 10137 10138 map.componentImageMap = 1; 10139 }); 10140 }, 10141 lazyLoadedBg: function() { 10142 const images = document.querySelectorAll('[data-style]'); 10143 Array.from(images).forEach(img => { 10144 lazyLoadedBg(img); 10145 }); 10146 return; 10147 }, 10148 productLink: function() { 10149 const products = document.querySelectorAll('[data-toggle="product-url"]'); 10150 Array.from(products).forEach((p, index) => { 10151 if (p.componentProductLink == 1) return; 10152 10153 for(let i=0; i < callbacks.product.click.length; i++){ 10154 if(typeof callbacks.product.click[i] === 'function'){ 10155 try { 10156 callbacks.product.click[i]?.(index, p); 10157 } catch (error) { console.warn(`Product Click Callback Error => ${error}`); } 10158 return; 10159 } 10160 } 10161 10162 p.componentProductLink = 1; 10163 }); 10164 }, 10165 addClass: function() { 10166 const addClasses = document.querySelectorAll('[data-toggle="add-class"]'); 10167 Array.from(addClasses).forEach(cls => { 10168 if (cls.componentAddClass == 1) return; 10169 10170 const elements = cls.dataset.elements; 10171 const classNames = cls.dataset.class; 10172 if (!elements || !classNames) return; 10173 10174 const trimElements = elements.trim(); 10175 const arrElements = trimElements.split(/\s*,\s*/); 10176 10177 if (!arrElements.length) return; 10178 10179 cls.addEventListener('click', e => { 10180 Array.from(arrElements).forEach(item => { 10181 item = document.querySelectorAll(item); 10182 addClass(item, classNames); 10183 }); 10184 }); 10185 10186 cls.componentAddClass = 1; 10187 }); 10188 }, 10189 removeClass: function() { 10190 const removeClasses = document.querySelectorAll('[data-toggle="remove-class"]'); 10191 Array.from(removeClasses).forEach(cls => { 10192 if (cls.componentRemoveClass == 1) return; 10193 10194 const elements = cls.dataset.elements; 10195 const classNames = cls.dataset.class; 10196 if (!elements || !classNames) return; 10197 10198 const trimElements = elements.trim(); 10199 const arrElements = trimElements.split(/\s*,\s*/); 10200 10201 if (!arrElements.length) return; 10202 10203 cls.addEventListener('click', e => { 10204 Array.from(arrElements).forEach(item => { 10205 item = document.querySelectorAll(item); 10206 removeClass(item, classNames); 10207 }); 10208 }); 10209 10210 cls.componentRemoveClass = 1; 10211 }); 10212 }, 10213 toggleClass: function() { 10214 const toggleClasses = document.querySelectorAll('[data-toggle="toggle-class"]');
10215 Array.from(toggleClasses).forEach(cls => { 10216 if (cls.componentToggleClass == 1) return; 10217 10218 const elements = cls.dataset.elements; 10219 const classNames = cls.dataset.class; 10220 if (!elements || !classNames) return; 10221 10222 const trimElements = elements.trim(); 10223 const arrElements = trimElements.split(/\s*,\s*/); 10224 10225 if (!arrElements.length) return; 10226 10227 cls.addEventListener('click', e => { 10228 Array.from(arrElements).forEach(item => { 10229 item = document.querySelectorAll(item); 10230 toggleClass(item, classNames); 10231 }); 10232 }); 10233 10234 cls.componentToggleClass = 1; 10235 }); 10236 }, 10237 readMore: function() { 10238 const readMores = document.querySelectorAll('[data-toggle="read-more"]'); 10239 Array.from(readMores).forEach(element => { 10240 if (element.componentReadMore == 1) return; 10241 element.componentReadMore = 1; 10242 10243 const height = Number.parseFloat(element.dataset.height) || 150; 10244 if (element.scrollHeight <= height) return; 10245 10246 const moreText = element.dataset.moreText || COMMON_LANG.read_more; 10247 const lessText = element.dataset.lessText || COMMON_LANG.less_more; 10248 10249 element.style.maxHeight = 'none'; 10250 element.style.height = 'auto'; 10251 10252 const content = document.createElement('div'); 10253 content.style.maxHeight = `${height}px`; 10254 content.style.overflow = 'hidden'; 10255 content.style.position = 'relative'; 10256 content.style.transition = 'max-height 0.3s ease-in-out'; 10257 content.dataset.qa = 'read-more-content'; 10258 content.innerHTML = element.innerHTML; 10259 element.innerHTML = ''; 10260 element.appendChild(content); 10261 10262 const overlay = document.createElement('div'); 10263 overlay.innerHTML = '...'; 10264 overlay.className = 'shadow-[inset_0_-30px_20px_0px_rgba(255,255,255,1)] absolute start-0 end-0 bottom-0 h-14 pointer-events-none flex items-end'; 10265 overlay.dataset.qa = 'read-more-overlay'; 10266 content.appendChild(overlay); 10267 10268 const button = document.createElement('button'); 10269 button.className = 'font-semibold underline mt-4'; 10270 button.textContent = moreText; 10271 button.dataset.qa = 'read-more-button'; 10272 button.addEventListener('click', function() { 10273 element.classList.toggle('active'); 10274 if (element.classList.contains('active')) { 10275 content.style.maxHeight = '5000px'; 10276 overlay.style.display = 'none'; 10277 button.textContent = lessText; 10278 } else { 10279 content.style.maxHeight = `${height}px`; 10280 overlay.style.display = 'flex'; 10281 button.textContent = moreText; 10282 10283 if (element.offsetTop + window.scrollY > window.innerHeight) { 10284 setTimeout(() => { 10285 scroll({ 10286 top: element.offsetTop - HEADER_STICKY_HEIGHT - 100, 10287 behavior: "smooth" 10288 }); 10289 }, 100); 10290 } 10291 } 10292 }); 10293 element.appendChild(button); 10294 }); 10295 }, 10296 setupImageTracking: { 10297 SETUP_IMAGE_TIMEOUT: 7000, 10298 isSvg(src) { 10299 if (!src) return true; 10300 return ( 10301 src.endsWith('.svg') || 10302 src.includes('.svg?') || 10303 src.startsWith('data:image/svg') 10304 ); 10305 }, 10306 triggerFallback(img, src) { 10307 if (img.dataset.fallbackProcessed) return; 10308 img.dataset.fallbackProcessed = 'true'; 10309 10310 const fallbackSrc = src.replace(/(?:https?:)?\/\/[^/]+/i, `${location.protocol}//${location.hostname}`); 10311 10312 if (img.dataset.src) { 10313 img.dataset.src = fallbackSrc; 10314 setTimeout(() => { 10315 if (img.src != fallbackSrc) img.src = fallbackSrc; 10316 }, 100); 10317 } else { 10318 img.src = fallbackSrc; 10319 } 10320 10321 const imgPicture = img.closest('picture'); 10322 if (imgPicture) { 10323 const imgSource = imgPicture.querySelector('source'); 10324 if (imgSource) { 10325 const srcset = imgSource.srcset; 10326 const fallbackSrcset = srcset.replace(/(?:https?:)?\/\/[^/]+/i, `${location.protocol}//${location.hostname}`); 10327 imgSource.srcset = fallbackSrcset; 10328 } 10329 } 10330 }, 10331 trackImage(img, src) { 10332 if (window.location.hostname === 'local.tsoft.biz') return; 10333 if (!src || !src.includes('witcdn.') || components.setupImageTracking.isSvg(sr
10333c)) return; 10334 10335 const timer = setTimeout(() => { 10336 if (!img.complete || img.naturalWidth === 0) { 10337 components.setupImageTracking.triggerFallback(img, src); 10338 } 10339 }, components.setupImageTracking.SETUP_IMAGE_TIMEOUT); 10340 10341 img.addEventListener('load', () => { 10342 if (img.naturalWidth === 0) { 10343 components.setupImageTracking.triggerFallback(img, src); 10344 } 10345 clearTimeout(timer); 10346 }, { once: true }); 10347 10348 img.addEventListener('error', () => { 10349 components.setupImageTracking.triggerFallback(img, src); 10350 clearTimeout(timer); 10351 }, { once: true }); 10352 10353 if (img.complete && img.naturalWidth === 0) { 10354 clearTimeout(timer); 10355 components.setupImageTracking.triggerFallback(img, src); 10356 } 10357 }, 10358 init() { 10359 document.querySelectorAll('img').forEach(img => { 10360 const src = img.src; 10361 const dataSrc = img.dataset.src; 10362 10363 if (dataSrc || img.classList.contains('lazyload')) return; 10364 components.setupImageTracking.trackImage(img, src); 10365 }); 10366 } 10367 }, 10368 sort: function() { 10369 const sorts = document.querySelectorAll('[data-toggle="sort"]'); 10370 Array.from(sorts).forEach(sort => { 10371 if (sort.componentSort == 1) return; 10372 sort.componentSort = 1; 10373 10374 let event = 'click'; 10375 if (sort.tagName == 'SELECT') event = 'change'; 10376 10377 sort.addEventListener(event, e => { 10378 e.preventDefault(); 10379 const sortValue = sort.dataset.sort ?? sort.value ?? '0'; 10380 window.location.href = getLink('sort', sortValue == '0' ? '' : sortValue); 10381 return; 10382 }); 10383 }); 10384 }, 10385 layout: function() { 10386 const layouts = document.querySelectorAll('[data-toggle="layout"]'); 10387 Array.from(layouts).forEach(layout => { 10388 if (layout.componentLayout == 1) return; 10389 layout.componentLayout = 1; 10390 10391 let event = 'click'; 10392 if (layout.tagName == 'SELECT') event = 'change'; 10393 10394 layout.addEventListener(event, e => { 10395 e.preventDefault(); 10396 const layoutValue = layout.dataset.layout ?? layout.value ?? ''; 10397 if (layoutValue == '') return; 10398 10399 setCookie('selected_layout', layoutValue); 10400 location.reload(); 10401 }); 10402 }); 10403 }, 10404 videoPlay: { 10405 play: async function(embed) { 10406 await awaitTimeOut(200); 10407 /* YouTube */ 10408 if (embed.src.includes('youtube.com')) { 10409 if (!embed.src.includes('autoplay=1')) { 10410 let videoId = ''; 10411 try { 10412 const url = new URL(embed.src); 10413 videoId = url.searchParams.get('v') || url.pathname.split('/embed/')[1]?.split('/')[0] || ''; 10414 } catch (e) {} 10415 10416 const playlistParam = !embed.src.includes('playlist=') && videoId ? `&playlist=${videoId}` : ''; 10417 embed.src += (embed.src.includes('?') ? '&' : '?') + `autoplay=1&mute=1&controls=0&loop=1&showinfo=0&rel=0${playlistParam}`; 10418 } 10419 } 10420 /* Vimeo */ 10421 else if (embed.src.includes('vimeo.com')) { 10422 if (!embed.src.includes('autoplay=1')) { 10423 embed.src += (embed.src.includes('?') ? '&' : '?') + 'autoplay=1&muted=1&controls=0&background=1&loop=1'; 10424 } 10425 } 10426 /* HTML5 */ 10427 else if (embed.tagName.toLowerCase() === 'video') { 10428 embed.muted = true; 10429 embed.controls = false; 10430 embed.loop = true; 10431 embed.play(); 10432 } 10433 return; 10434 }, 10435 init: function() { 10436 const videos = document.querySelectorAll('[data-toggle="video-play"]');
10437 Array.from(videos).forEach(video => { 10438 if (video.componentVideoPlay == 1) return; 10439 video.componentVideoPlay = 1; 10440 10441 const targetEmbed = video.dataset.targetEmbed; 10442 if (!targetEmbed) return; 10443 10444 const videoEmbedEl = video.querySelector(targetEmbed); 10445 if (!videoEmbedEl) return; 10446 10447 const embed = videoEmbedEl?.querySelector('iframe') || videoEmbedEl?.querySelector('video'); 10448 if (!embed) return; 10449 10450 const targetButton = video.dataset.targetButton; 10451 if (targetButton) { 10452 const videoButtonEl = video.querySelector(targetButton); 10453 if (videoButtonEl) { 10454 videoButtonEl.addEventListener('click', e => { 10455 e.preventDefault(); 10456 video.classList.add('active'); 10457 components.videoPlay.play(embed); 10458 }); 10459 } 10460 } else { 10461 components.videoPlay.play(embed); 10462 } 10463 }); 10464 } 10465 }, 10466 dateRangePicker: function() { 10467 const dateRangePickers = document.querySelectorAll('[data-toggle="date-picker"], [data-toggle="date-calendar"]'); 10468 Array.from(dateRangePickers).forEach(picker => { 10469 if (picker.componentDatePicker == 1) return; 10470 picker.componentDatePicker = 1; 10471 10472 const snippet = picker.dataset.toggle; 10473 if (snippet != 'date-picker' && snippet != 'date-calendar') return; 10474 10475 loadSnippet({ 10476 snippet: snippet, 10477 params: { 10478 include: 'date-picker', 10479 }, 10480 success: function (loadRes) { 10481 picker.innerHTML = loadRes; 10482 createPicker(picker); 10483 } 10484 }); 10485 }); 10486 }, 10487 appMobile: function() { 10488 const element = document.querySelector('[data-toggle="mobile-app"]'); 10489 if (!element || element.componentAppMobile == 1 || PAGE_ID != 1) return; 10490 10491 element.componentAppMobile = 1; 10492 10493 if (getCookie('mobileApp') == 1 || isMobileApp()) { 10494 element.remove(); 10495 return; 10496 } else { 10497 setTimeout(() => { 10498 element.removeAttribute('hidden'); 10499 }, 4000); 10500 } 10501 10502 const downloadElement = document.querySelector('[data-toggle="mobile-app-download"]'); 10503 if (downloadElement) { 10504 const MobileAppRedirect = element.querySelector('[name="MobileAppRedirect"]')?.value || '', 10505 AppMarketLink = element.querySelector('[name="AppMarketLink"]')?.value || '', 10506 OpenAppLink = element.querySelector('[name="OpenAppLink"]')?.value || ''; 10507 10508 let storeUrl; 10509 if (MobileAppRedirect == 'ios') { 10510 storeUrl = `https://itunes.apple.com/app/id${AppMarketLink}`; 10511 } else if (MobileAppRedirect == 'android') { 10512 storeUrl = `market://details?id=${AppMarketLink}`; 10513 } 10514 if (!storeUrl) { 10515 console.warn('appMobile warn: storeUrl undefined'); 10516 return; 10517 }; 10518 10519 const meta = document.createElement('meta'); 10520 meta.name = 'apple-itunes-app'; 10521 meta.content = `app-id=${AppMarketLink}, affiliate-data=myAffiliateData, app-argument=${OpenAppLink}://`; 10522 document.head.append(meta); 10523 10524 downloadElement.addEventListener('click', e => { 10525 document.location.href = storeUrl; 10526 }); 10527 } 10528 10529 const hiddenElement = document.querySelector('[data-toggle="mobile-app-hidden"]'); 10530 if (hiddenElement && element) { 10531 hiddenElement.addEventListener('click', e => { 10532 element.remove(); 10533 setCookie('mobileApp', 1); 10534 }); 10535 } 10536 }, 10537 assignOrders: { 10538 set: async function(form, load = false) { 10539 if (!form) return; 10540 10541 window['snippetMessage'] = ''; 10542 10543 popoverAlertHide(form); 10544 if (!checkValidity(form)) return; 10545 10546 const formData = new FormData(form); 10547 10548 const recaptchaResponse = formData.get('g-recaptcha-response'); 10549 if (recaptchaResponse != null && typeof grecaptcha !== 'undefined') formData.delete('security_code'); 10550 10551 loader(true); 10552 let response; 10553 try { 10554 response = await request('POST', getEndpoint('ASSIGN_ORDERS_TO_CUSTOMER'), formData); 10555 if (!response.status) { 10556 await getCaptcha(form, response); 10557 10558 const field = response.field || response.key || ''; 10559 let selector = form.querySelector(`[name="${field == 'g-recaptcha-response' ? 'security_code' : field}"]`); 10560 if (selector) { 10561 popoverAlert({ 10562 selector: selector, 10563 message: response.statusText || response.message || COMMON_LANG?.form_required, 10564 }); 10565 } else { 10566 if (SNIPPET_JS.CUSTOMER_ACTIVATION_POPUP) { 10567 SNIPPET_JS.CUSTOMER_ACTIVATION_POPUP.STATUS_TEXT = response.error ? response.message : ''; 10568 SNIPPET_JS.CUSTOMER_ACTIVATION_POPUP.STATUS = response.error || ''; 10569 } else { 10570 notify({ 10571 text: response.statusText || response.message || COMMON_LANG?.send_error, 10572 class: 'danger', 10573 }); 10574 } 10575 } 10576 10577 return; 10578 } 10579 10580 if (load) { 10581 loadSnippet({ 10582 snippet: '_customer-activation-popup', 10583 params: { 10584 include: 'customer-register', 10585 activation: 'assign-orders', 10586 status: response.error ? response.error : '' 10587 }, 10588 success: function(loadRes){ 10589 if (response.error) { 10590 window['snippetMessage'] = response.message; 10591 } else { 10592 window['snippetMessage'] = ''; 10593 } 10594 10595 modal({
10596 id: 'modal-customer-activation-popup', 10597 width: '480px', 10598 html: loadRes, 10599 close: false, 10600 disabledClose: true, 10601 }); 10602 } 10603 }); 10604 10605 return; 10606 } 10607 10608 if (!response.error && !response.can_resend && !response.expires_in && response.status) { 10609 modalClose(); 10610 modal({ 10611 html: response.message, 10612 alert: true, 10613 class: 'success', 10614 buttons: [ 10615 {text: COMMON_LANG?.continue, href: `${PAGE_LINK.CUSTOMER_ORDER_LIST}`, class: 'btn btn-primary'}, 10616 ], 10617 callbacks: { 10618 close: function() { 10619 window.location.href = `/${PAGE_LINK.CUSTOMER_ORDER_LIST}`; 10620 }, 10621 }, 10622 }); 10623 } 10624 } catch (error) { 10625 console.warn(`ASSIGN_ORDERS_TO_CUSTOMER error => ${error}`); 10626 } finally { 10627 await captchaReset(form); 10628 loader(); 10629 } 10630 }, 10631 init: function() { 10632 const assignOrders = document.querySelectorAll('[data-toggle="assign-orders"]'); 10633 Array.from(assignOrders).forEach(form => { 10634 if (form.tagName.toLowerCase() !== 'form') return; 10635 10636 if (form.componentAssignOrders == 1) return; 10637 10638 form.addEventListener('submit', async e => { 10639 e.stopPropagation(); 10640 e.preventDefault(); 10641 components.assignOrders.set(form, true); 10642 }); 10643 10644 form.componentAssignOrders = 1; 10645 }); 10646 } 10647 }, 10648 callbacks: [], 10649 init: function(callback) { 10650 setTimeout(() => { 10651 for (let i in components) { 10652 if (i == 'init') { 10653 if (callback && typeof callback == 'function') callback(); 10654 return; 10655 }; 10656 10657 if (typeof components[i] == 'function') { 10658 components[i](); 10659 } else if (typeof components[i].init == 'function') { 10660 components[i].init(); 10661 } else if (i == 'callbacks') { 10662 for (let j in components.callbacks) { 10663 if (typeof components.callbacks[j] == 'function') { 10664 components.callbacks[j](); 10665 } 10666 } 10667 } 10668 } 10669 }, 1); 10670 }, 10671} 10672 10673const onMessageEvents = () => { 10674 const isAllowedOrigin = (origin) => { 10675 const ALLOWED_BASE_DOMAINS = ['paneltsoft.com', 'tsoftpanel.com']; 10676 10677 try { 10678 const url = new URL(origin); 10679 return ALLOWED_BASE_DOMAINS.some(d =>url.hostname === d || url.hostname.endsWith(`.${d}`)); 10680 } catch { 10681 return false; 10682 } 10683 } 10684 10685 window.addEventListener('message', function(m) { 10686 if (!isAllowedOrigin(m.origin)) return; 10687 10688 try { 10689 let resp = JSON.parse(m.data); 10690 if (!resp.action || !resp.value) { 10691 return false 10692 } 10693 switch(resp.action) { 10694 case 'scroll': 10695 document.getElementById(resp.value).scrollIntoView({behaviour: "smooth",block : "start"}); 10696 break; 10697 default : 10698 break; 10699 } 10700 } catch(e) { 10701 return false; 10702 } 10703 }) 10704} 10705 10706function initAfterInteraction() { 10707 const interactionEvents = [ 10708 'pointerdown', 10709 'scroll', 10710 'keydown' 10711 ]; 10712 10713 let initialized = false; 10714 10715 const handleInteraction = () => { 10716 if (initialized) return; 10717 10718 initialized = true; 10719 10720 clearTimeout(timeout); 10721
10722 interactionEvents.forEach(event => { 10723 window.removeEventListener(event, handleInteraction); 10724 }); 10725 10726 getPluginsPopup(); 10727 notificationAccept.init(); 10728 }; 10729 10730 interactionEvents.forEach(event => { 10731 window.addEventListener(event, handleInteraction, { 10732 once: true, 10733 passive: true 10734 }); 10735 }); 10736 10737 const timeout = setTimeout(handleInteraction, 5000); 10738} 10739 10740document.addEventListener('lazybeforeunveil', e => { 10741 var img = e.target; 10742 if (img.tagName === 'IMG' && !img.getAttribute('data-src')) { 10743 if (img.dataset.toggle == 'product-image') img.setAttribute('data-src', DEFAULT_PRODUCT_IMAGE); 10744 } 10745}); 10746 10747document.addEventListener('lazyloaded', e => { 10748 const img = e.target; 10749 if (!img || img.tagName !== 'IMG') return; 10750 10751 const src = img.dataset.src; 10752 if (!src) return; 10753 10754 setTimeout(() => { 10755 components.setupImageTracking.trackImage(img, src); 10756 }, 100); 10757}); 10758 10759document.addEventListener('DOMContentLoaded', async function() { 10760 await components.init(); 10761 productLoader.init(); 10762 initAfterInteraction(); 10763 await messageCount(); 10764 await queryControl(); 10765 await memberKvkkControl(); 10766 await passwordStrengthControl(); 10767 await phonePromptControl(); 10768 onPageReady(); 10769 10770 window.addEventListener('scroll', lazyLoadedBg, false); 10771 refererStorage.addItemStorage(); 10772 10773 onMessageEvents(); 10774});
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.