PageSourceSearch

https://www.sarfmarket.com.tr/resources/assets/js/common.js?v=1791357061

js sarfmarket.com.tr collected 2026-10-08 01:26:30 UTC 430,742 bytes, 10,774 lines download raw bytes

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.