PageSourceSearch

https://esimcenter.com/cdn/assets/js/staff.js?v=v1.3.3-compact20260915

js esimcenter.com collected 2026-10-02 01:28:33 UTC 26,010 bytes, 626 lines download raw bytes

1/* defines */
2
3tableIsRendered = "false";
4ordery = "desc";
5orderCol = 0;
6
7/* Stripe kart hatası mesajları — TEK KAYNAK: PHP l() (window.lang.stripe,
8 * header.php'de aktif dilde basılır). window.lang yoksa İngilizce fallback. */
9stripeErrors = (window.lang && window.lang.stripe) ? window.lang.stripe : {
10    "invalid_number": "Your card number is not a valid credit card number",
11    "invalid_expiry_month": "Your card's expiration month is invalid",
12    "invalid_expiry_year": "Your card's expiration year is invalid",
13    "invalid_cvc": "Your card's security code is invalid",
14    "incorrect_number": "Your card number is incorrect",
15    "incomplete_number": "Your card number is incomplete",
16    "incomplete_cvc": "Your card's security code is incomplete",
17    "incomplete_expiry": "Your card's expiration date is incomplete",
18    "expired_card": "Your card has expired",
19    "incorrect_cvc": "Your card's security code is incorrect",
20    "incorrect_zip": "Your card's ZIP code could not be verified",
21    "invalid_expiry_year_past": "Your card's expiration year is in the past",
22    "card_declined": "Your card was declined",
23    "missing": "There is no card on file for the customer",
24    "processing_error": "An error occurred while processing your card",
25    "authentication_required": "Your bank needs to verify this payment. Please complete the verification and try again.",
26    "insufficient_funds": "Your card has insufficient funds. Please use a different card.",
27    "do_not_honor": "Your bank declined the payment. Please contact your bank or try another card.",
28    "generic_decline": "Your card was declined. Please contact your bank or try another card.",
29    "fraudulent": "This payment couldn't be processed. Please try another card or contact your bank.",
30    "lost_card": "Your card was declined. Please use a different card.",
31    "stolen_card": "Your card was declined. Please use a different card.",
32    "pickup_card": "Your card was declined. Please use a different card.",
33    "try_again_later": "The payment couldn't be processed right now. Please try again in a moment.",
34    "card_velocity_exceeded": "This card has reached its limit. Please use a different card.",
35    "currency_not_supported": "Your card doesn't support this currency. Please use a different card."
36};
37
38/* Ağ hatası mesajları — TEK KAYNAK: window.lang.errors (PHP l(), header.php'de aktif dilde
39 * basılır, stripeErrors ile aynı desen). window.lang.errors yoksa İngilizce fallback. Amaç:
40 * transient network hatalarında (request.js -> {'result':'error'}) bare alert(undefined) veya
41 * hardcoded İngilizce blocking alert yerine, dismissible + retry sunan tek kaynaklı mesaj. */
42netErrors = (window.lang && window.lang.errors) ? window.lang.errors : {
43    "network": "We couldn't reach the server. Please check your connection and try again.",
44    "retry": "Retry"
45};
46
47$.wait = function (ms) {
48    var defer = $.Deferred();
49    setTimeout(function () {
50        defer.resolve();
51    }, ms);
52    return defer;
53};
54
55function isset(keyword) {
56    keyword = $.trim(keyword);
57    if (typeof keyword === "undefined" || keyword.length == 0 || keyword === null || keyword === "" || keyword == "false" || keyword == false) {
58        return false;
59    } else {
60        return keyword;
61    }
62}
63
64function urlEncode(deger) {
65    return encodeURIComponent(deger);
66}
67
68function goUrl(url) {
69    window.open(url);
70}
71
72function urlDecode(deger) {
73    return unescape(decodeURIComponent((escape(deger))));
74}
75
76$.fn.justNumerics =
77    function (type) {
78        return this.each(function () {
79            $(this).keydown(function (e) {
80                var key = e.charCode || e.keyCode || 0;
81
82                if (type == "float") {
83                    return (
84                        key == 8 ||
85                        key == 9 ||
86                        key == 13 ||
87                        key == 46 ||
88                        key == 110 ||
89                        key == 190 ||
90                        (key >= 35 && key <= 40) ||
91                        (key >= 48 && key <= 57) ||
92                        (key >= 96 && key <= 105));
93                } else {
94                    return (
95                        key == 8 ||
96                        key == 9 ||
97                        key == 13 ||
98                        key == 46 ||
99                        key == 110 ||
100                        (key >= 35 && key <= 40) ||
101                        (key >= 48 && key <= 57) ||
102                        (key >= 96 && key <= 105));
103                }
104
105            });
106        });
107    };
108
109
110$.fn.removeSlowly = function () {
111    $(this).animate({
112        height: 'hide',
113        opacity: 'hide'
114    }, 400, function () {
115        $(this).addClass("none");
116        $(this).remove();
117    });
118};
119
120
121function currencyFormat(price, currency) {
122
123    if (typeof currency == "undefined") {
124        currency = "";
125    }
126
127    /* SITE_CURRENCY_v1 — DİL, PARA BİRİMİ DEĞİLDİR. Buradaki eski eşleme 'tr' dilini
128       'tl' para birimine çeviriyordu; mağaza USD tahsil ettiği için (Stripe 'usd',
129       PayPal 'USD') Türkçe arayüzde $0,84'lük bir tutar "0,84 TL" olarak gösteriliyordu
130       — ~40 kat yanlış. es/pt/ar/it/nl dillerinde ise hiç sembol basılmıyordu.
131       Çağıranlar hâlâ dil kodu geçtiği için burada normalize ediyoruz; gerçek çoklu para
132       birimi geldiğinde sembol SUNUCUDAN gelmeli (PHP tarafı: SITE_CURRENCY_SYMBOL). */
133    if (currency == "" || currency == "tr" || currency == "en" || currency == "es"
134        || currency == "pt" || currency == "ar" || currency == "it" || currency == "nl") {
135        currency = "$";
136    }
137
138    /* MONEY_DECIMALS_v1 — biçim PHP tarafındaki currencyFormat() ile BİREBİR aynı olmalı.
139       Eskiden değer olduğu gibi yapıştırılıyordu: sunucudan gelen "9.00" dizesi "$9.00"
140       basıyor, ama istemcide hesaplanan (fiyat × adet) sayısı "$13.5" / "$9" basıyordu —
141       aynı ekranda iki farklı para yazımı. Ondalık her zaman 2 hane:
142         TL  -> ondalık virgül, binlik nokta   (moneyFormat varsayılanı, PHP ile aynı)
143         $   -> ondalık nokta,  binlik virgül  (PHP: number_format($p, 2, '.', ',')) */
144    if (currency == "tl" || currency == "TL" || currency == "TRY" || currency == "try") {
145        money = moneyFormat(price) + " TL";
146    } else {
147        money = currency + moneyFormat(price, 2, ".", ",");
148    }
149
150    return money;
151}
152
153function moneyFormat(number, decimals, decPoint, thousandsSep) {
154
155    if (typeof decimals == "undefined") {
156        decimals = 2;
157    }
158
159    if (typeof decPoint == "undefined" || typeof decPoint == undefined) {
160        decPoint = ",";
161    }
162
163    if (typeof thousandsSep == "undefined" || typeof thousandsSep == undefined) {
164        thousandsSep = ".";
165    }
166
167    number = parseFloat(number);
168    number = number.toFixed(decimals);
169
170    var nstr = number.toString();
171    nstr += '';
172    x = nstr.split('.');
173    x1 = x[0];
174    x2 = x.length > 1 ? decPoint + x[1] : '';
175    var rgx = /(\d+)(\d{3})/;
176
177    while (rgx.test(x1))
178        x1 = x1.replace(rgx, '$1' + thousandsSep + '$2');
179
180    return x1 + x2;
181}
182
183/* EXPRESS_CHECKOUT_v1 — Stripe Express Checkout Element (Apple Pay / Google Pay / Link).
184 *
185 * NEDEN AYRI BİR ELEMENT: PaymentIntent'te `automatic_payment_methods` açık olması
186 * cüzdanları yalnız "uygun" yapar; Payment Element bunları kart formunun İÇİNDE, en
187 * altta ve çoğu zaman hiç göstermez. Tek dokunuşluk Apple/Google Pay düğmeleri ancak
188 * ayrı bir 'expressCheckout' element'i mount edilirse çıkar — projede bu element
189 * HİÇ yoktu, bu yüzden hiçbir yerde cüzdan düğmesi görünmüyordu.
190 *
191 * TEK KAYNAK: hem quick-buy modal'ı (footer.php) hem ödeme sihirbazı (payment.php) bu
192 * fonksiyonu çağırır. İki yüzey aynı sayfada birlikte render edildiği için (bkz.
193 * startWizardCardPayment'taki isim-çakışması notu) mount hedefi ÇAĞIRANDAN gelir.
194 *
195 * ÖNEMLİ — cüzdan ödemesinde `payment_method_data.billing_details` GÖNDERİLMEZ: fatura
196 * bilgisini Apple/Google cüzdanın kendisi verir; bizim ön-doldurduğumuz değerleri
197 * göndermek cüzdanın verdiğiyle çakışır ve Stripe ödemeyi reddeder. Kart yolundaki
198 * confirm çağrısı bu yüzden AYNEN korunur, burada ayrı bir çağrı kurulur.
199 *
200 * @param {object} o
201 *   o.stripe     Stripe örneği (clientSecret ile kurulmuş elements'in sahibi)
202 *   o.elements   stripe.elements({clientSecret}) — kart element'iyle AYNI örnek olmalı
203 *   o.container  mount edilecek CSS seçici (ör. '#quickbuy-express-checkout')
204 *   o.surface    analitik etiketi ('quick_buy' | 'wizard')
205 *   o.returnUrl  3DS/redirect dönüş adresi
206 *   o.onDone     function(errorMessage, paymentIntent) — ödeme denemesi bittiğinde
207 * @return {object|null} element örneği, kurulamazsa null
208 */
209function esimMountExpressCheckout(o) {
210
211    if (!o || !o.stripe || !o.elements || !o.container) { return null; }
212
213    var host = document.querySelector(o.container);
214    if (!host) { return null; }
215
216    var ece;
217    try {
218        ece = o.elements.create('expressCheckout', {
219            /* QUICKBUY_COMPACT_v1 — 40 Stripe'ın izin verdiği en küçük yükseklik. Düzen:
220               en çok 2 sütun × 1 satır; eskiden (otomatik düzen) 400 px'lik modalda tek
221               sütun seçiliyor, ikinci cüzdan (ör. Link + Google Pay) "Daha fazla göster"
222               bağlantısının arkasına saklanıyor ve kutu iki kat yer kaplıyordu. Tek cüzdan
223               varsa düğme satırı tek başına doldurur; 3+ cüzdan (nadir) yine taşma
224               bağlantısına düşer, hiçbiri gizlenmez. */
225            buttonHeight: 40,
226            layout: { maxColumns: 2, maxRows: 1, overflow: 'auto' },
227            /* YALNIZ üç cüzdan açık. Diğerleri BİLEREK kapalı:
228               - paypal: kendi PayPal entegrasyonumuz var (createPaypalOrder/capturePaypal);
229                 Stripe üzerinden ikinci bir PayPal yolu iki farklı sipariş kaydı demek olurdu.
230               - klarna / amazonPay: yönlendirme (redirect) gerektirir, oysa PaymentIntent
231                 `allow_redirects: 'never'` ile kuruluyor (paymentStripeHelpers) — düğme
232                 görünür ama ödeme confirm aşamasında reddedilirdi.
233               Kart burada YOK: zaten ayrı Payment Element'te, iki kart formu olurdu. */
234            paymentMethods: {
235                link: 'auto', applePay: 'auto', googlePay: 'auto',
236                paypal: 'never', klarna: 'never', amazonPay: 'never'
237            }
238        });
239    } catch (e) {
240        /* Eski Stripe.js sürümü veya desteklenmeyen ortam — sessizce vazgeç; kart
241           yolu bozulmasın (cüzdan bir hızlandırıcıdır, tek ödeme yolu değil). */
242        return null;
243    }
244
245    /* Kutu VARSAYILAN OLARAK GİZLİ durur ve yalnız gerçekten kullanılabilir bir cüzdan
246       varsa açılır. Aksi hâlde Apple Pay'i olmayan bir masaüstü kullanıcı boş bir
247       kutu + gereksiz "veya" ayıracı görürdü. */
248    host.style.display = 'none';
249
250    ece.on('ready', function (ev) {
251        var avail = (ev && ev.availablePaymentMethods) || null;
252        var any = !!avail && (avail.applePay || avail.googlePay || avail.link || avail.amazonPay || avail.paypal);
253
254        if (!any) { return; }
255
256        host.style.display = '';
257
258        /* EXPRESS_SEP_GUARD_v1 — "veya" ayıracı yalnız cüzdan kutusu GERÇEKTEN çizildiyse
259           gösterilir. Stripe 'ready' olayında bir cüzdanı "uygun" bildirse bile düğme kimi
260           ortamlarda hiç boyanmıyor (kutu 0 yükseklikte kalıyor) — o durumda ekranın en
261           üstünde, hiçbir şeyi ayırmayan tek başına bir "veya" kalıyordu ve ödeme adımı
262           özensiz görünüyordu. Ölçüm hemen yapılamaz: display:'' aynı karede layout
263           üretmez ve Stripe iframe'i yüksekliğini sonradan bildirir. Bu yüzden kısa
264           aralıklarla yoklanır; süre dolduğunda kutu da ayıraç da gizlenir. */
265        var sep = document.querySelector(o.container + '-sep');
266        var waited = 0;
267        (function settle() {
268            if (host.offsetHeight > 8) {
269                /* 'flex' ŞART: ayıracın iki yanındaki ince çizgiler flex çocuklarıdır.
270                   style.display='' yazıldığında element CSS varsayılanı olan 'block'a
271                   dönüyor, çizgiler yassılıyor ve geriye sola yapışmış tek bir "veya"
272                   kelimesi kalıyordu (canlıda görüldü). */
273                if (sep) { sep.style.display = 'flex'; }
274                return;
275            }
276            waited += 100;
277            if (waited > 3000) {   /* cömert tavan: çalışan bir cüzdan düğmesini asla gizlemesin */
278                host.style.display = 'none';
279                if (sep) { sep.style.display = 'none'; }
280                return;
281            }
282            setTimeout(settle, 100);
283        })();
284
285        try {
286            if (window.GA4) {
287                GA4.event('express_checkout_shown', {
288                    surface: o.surface || '',
289                    apple_pay: !!avail.applePay,
290                    google_pay: !!avail.googlePay,
291                    link: !!avail.link
292                });
293            }
294        } catch (e) {}
295    });
296
297    /* Stripe, click olayında resolve() çağrılmasını bekler; kayıtlı bir handler yoksa
298       varsayılanlarla devam eder. Handler'ı kaydedip resolve()'u çağırmamak akışı
299       KİLİTLER — bu yüzden try/catch içinde ve her koşulda resolve edilir. */
300    ece.on('click', function (event) {
301        try { if (event && typeof event.resolve === 'function') { event.resolve(); } } catch (e) {}
302    });
303
304    ece.on('confirm', function () {
305        try {
306            if (window.GA4) {
307                GA4.event('add_payment_info', { currency: 'USD', payment_type: 'wallet', surface: o.surface || '' });
308            }
309        } catch (e) {}
310
311        o.stripe.confirmPayment({
312            elements: o.elements,
313            confirmParams: { return_url: o.returnUrl },
314            redirect: 'if_required'
315        }).then(function (result) {
316
317            var msg = "";
318            if (result && result.error) {
319                var code = result.error.code;
320                if (typeof stripeErrors !== "undefined" && stripeErrors[code]) {
321                    msg = stripeErrors[code];
322                } else {
323                    msg = result.error.message || "";
324                }
325            }
326
327            if (typeof o.onDone === 'function') {
328                o.onDone(msg, result ? result.paymentIntent : null);
329            }
330        });
331    });
332
333    /* Kullanıcı cüzdan sayfasını kapattı — hata DEĞİL, sessizce kart formuna dönülür. */
334    ece.on('cancel', function () {
335        try {
336            if (window.GA4) { GA4.event('express_checkout_cancelled', { surface: o.surface || '' }); }
337        } catch (e) {}
338    });
339
340    try {
341        ece.mount(o.container);
342    } catch (e) {
343        return null;
344    }
345
346    return ece;
347}
348
349/* DELIVERABLE_MAIL_v1 — "eSIM teslim edilebilir bir e-posta" duvarı (TEK KAYNAK).
350 *
351 * Sunucu tarafındaki karşılığı Payment::deliverableMailBlock(): ödemeyi BAŞLATAN uçlar
352 * (web Stripe /payment/add, mobil /payment/createIntent, /payment/createPaypalOrder,
353 * /payment/createTikoOrder) hesabın maili boş/bozuk/sentetik ise 403 + mail_required=1
354 * döner. Sebebi: eSIM'in tek teslim kanalı e-postadır; böyle bir hesaptan ödeme alınırsa
355 * para çekilir ama eSIM eline geçmez.
356 *
357 * Bu fonksiyon kutuyu açar, /profile/mail'e yazar ve onSaved() ile ÇAĞIRANIN isteğini
358 * tekrarlatır. request.js bunu merkezden çağırdığı için hiçbir ödeme yüzeyine ayrı
359 * kod eklemek gerekmez — yeni bir ödeme yöntemi eklendiğinde de kendiliğinden çalışır.
360 *
361 * @param {function} onSaved  Mail başarıyla kaydedildiğinde çağrılır (isteği tekrarlar).
362 */
363function esimRequireMail(onSaved) {
364
365    var T = (window.lang && window.lang.mailWall) ? window.lang.mailWall : {};
366    var BASE = window.esimBase || '';
367
368    function esc(v) {
369        return String(v == null ? '' : v)
370            .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
371            .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
372    }
373
374    var html =
375        '<p class="mb-2">' + esc(T.desc || '') + '</p>' +
376        '<input type="email" id="esimMailWallInput" class="form-control" autocomplete="email" ' +
377        'inputmode="email" placeholder="' + esc(T.mail || '') + '" aria-label="' + esc(T.mail || '') + '">' +
378        '<div id="esimMailWallErr" class="text-danger mt-2" style="display:none;" role="alert" aria-live="polite"></div>';
379
380    boxup(html, {
381        'confirm': true,
382        'title': T.title || '',
383        'textOk': T.save || 'Ok',
384        'textCancel': T.cancel || 'Cancel'
385    }, function (ok) {
386
387        if (ok !== true) {
388            return;
389        }
390
391        var mail = (jQuery('#esimMailWallInput').val() || '').trim();
392
393        /* Format kontrolü sunucuda da var (UserProcess::updateMail SSoT); buradaki
394           yalnız kullanıcıya anında geri bildirim içindir. */
395        if (mail === '' || mail.indexOf('@') < 1 || mail.lastIndexOf('.') < mail.indexOf('@') + 2) {
396            esimRequireMail(onSaved);
397            jQuery('#esimMailWallErr').text(T.errMail || '').show(0);
398            return;
399        }
400
401        jQuery.ajax({
402            url: BASE + '/profile/mail?format=json',
403            method: 'POST',
404            data: {mail: mail},
405            dataType: 'json'
406        }).done(function (r) {
407
408            if (r && r.result === 'ok') {
409                jQuery('#boxUpModal').fadeOut(100).remove();
410                if (typeof onSaved === 'function') {
411                    onSaved(mail);
412                }
413                return;
414            }
415
416            /* Hata (ör. e-posta başka hesapta kayıtlı) → kutuyu sunucunun mesajıyla
417               tekrar aç; kullanıcı sepetini kaybetmeden başka adres deneyebilsin. */
418            esimRequireMail(onSaved);
419            jQuery('#esimMailWallInput').val(mail);
420            jQuery('#esimMailWallErr')
421                .text((r && (r.error || r.description)) || T.errGeneric || '')
422                .show(0);
423
424        }).fail(function () {
425            esimRequireMail(onSaved);
426            jQuery('#esimMailWallInput').val(mail);
427            jQuery('#esimMailWallErr').text(T.errGeneric || '').show(0);
428        });
429
430    });
431
432    /* boxup içeriği DOM'a eklendikten sonra odak: kullanıcı doğrudan yazmaya başlasın. */
433    setTimeout(function () {
434        jQuery('#esimMailWallInput').trigger('focus');
435    }, 320);
436}
437
438function boxup(string, args, callback) {
439
440    var defaultArray =
441        {
442            'confirm': false,
443            'textOk': 'Ok',
444            'textCancel': 'Cancel',
445            'classs': "",
446            'close': 0,
447            'title': ""
448
449        }
450
451    if (args) {
452        for (var index in defaultArray) {
453            if (typeof args[index] == "undefined") {
454                args[index] = defaultArray[index];
455            }
456        }
457    } else {
458        args = defaultArray;
459    }
460
461    jQuery('#boxUpModal').hide(200).remove();
462
463    newHtml = '<div class="modal modal-new fade show ' + args['classs'] + '" id="boxUpModal" tabindex="-1" role="dialog" aria-modal="true"><div class="modal-dialog" style="display:none;" role="document">        <div class="modal-content">  <div class="modal-header">  <h5 class="modal-title" id="staticBackdropLabel">' + args['title'] + '</h5>    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div>';
464    newHtml = newHtml + '<div class="modal-body"><div class="text">' + string + '</div></div>';
465    newHtml = newHtml + '<div class="boxupButtons modal-footer"></div>';
466    newHtml = newHtml + '</div></div></div>';
467
468    jQuery('body').append(newHtml);
469
470    if (args['confirm']) {
471        jQuery('.boxupButtons').append('<button class="btn btn-primary" value="ok">' + args['textOk'] + '</button>');
472        jQuery('.boxupButtons').append('<button class="btn btn-danger" value="cancel">' + args['textCancel'] + '</button>');
473    } else {
474        if (args['textOk']) {
475            jQuery('.boxupButtons').append('<button class="btn btn-primary" value="ok">' + args['textOk'] + '</button>');
476        } else {
477            jQuery('.boxupButtons').append('<button class="btn btn-primary" value="ok">Tamam</button>');
478        }
479    }
480
481    jQuery('#boxUpModal').show(0);
482    jQuery('#boxUpModal .modal-dialog').fadeIn(300);
483
484    jQuery(document).on('click', '#boxUpModal .btn-close', function () {
485        jQuery('#boxUpModal').fadeOut(100).remove();
486        callback(false);
487    });
488
489    $(document).keydown(function (e) {
490        if ($('#boxUpModal').is(':visible')) {
491            if (e.keyCode == 13) {
492                $('#boxUpModal .boxupButtons  button[value="ok"]').click();
493            }
494            if (e.keyCode == 27) {
495                $('#boxUpModal .boxupButtons  button[value="cancel"]').click();
496            }
497        }
498    });
499
500    jQuery(document).on('click', '#boxUpModal .boxupButtons button', function () {
501
502        if (callback) {
503            var wButton = jQuery(this).attr("value");
504            if (wButton == 'ok') {
505                if (args) {
506                    callback(true);
507                } else {
508                    callback(true);
509                }
510            } else if (wButton == 'cancel') {
511                callback(false);
512            }
513        }
514
515        jQuery('#boxUpModal').fadeOut(100, function () {
516            //    jQuery('#boxUpModal').remove();
517        });
518
519        callback = "";
520    });
521
522    if (args['close'] > 0) {
523        setTimeout(function () {
524            jQuery('#boxUpModal').fadeOut(100).remove();
525        }, args['close']);
526    }
527
528}
529
530
531/* BUY_CONTEXT_v1 (2026-08-26) — "bu eSIM hangi ülke araması / hangi sayfa üzerinden
532 * alındı?" sorusunun TEK yakalama noktası. Bu bilgi sunucuda hiçbir yerde yoktu:
533 * sepete ekleme bir AJAX çağrısıdır, sunucu yalnız /basket/add/<id> yolunu görür,
534 * kullanıcının o an hangi sayfada olduğunu GÖREMEZ. Bu yüzden bağlam istemcide
535 * üretilir ve sepet satırına yazılır; oradan satışa (_sale.context) kopyalanır.
536 *
537 * Biçim (admin tarafındaki okuyucu: buyContextFacts() / buyContextBadge()):
538 *   q=<kullanıcının yazdığı arama>|s=<seçtiği ülke slug'ı>|p=</sayfa/yolu>
539 * Alanlar opsiyoneldir. Arama terimi sessionStorage'da yaşar (arama kutusu
540 * multi-select.js'te; kullanıcı arayıp ülke sayfasına gidiyor, alım orada oluyor).
541 */
542function esimBuyContext() {
543    var parts = [];
544    try {
545        var term = (sessionStorage.getItem('esimSearchTerm') || '').trim();
546        var slug = (sessionStorage.getItem('esimSearchSlug') || '').trim();
547        if (term) { parts.push('q=' + term.slice(0, 60)); }
548        if (slug) { parts.push('s=' + slug.slice(0, 40)); }
549    } catch (e) { /* private mode / storage kapalı — bağlam kaybı ödemeyi ETKİLEMEZ */ }
550
551    try {
552        var path = (location.pathname || '/').replace(/\/+$/, '') || '/';
553        parts.push('p=' + path.slice(0, 80));
554    } catch (e) {}
555
556    return parts.join('|');
557}
558
559/* Basket Process */
560/* QUICKBUY_COUPON_FIELD_v1 — kupon doğrulama isteğinin TEK evi. İki yüzey kullanır:
561 * /payment sihirbazı (payment.php checkCouponCode) ve hızlı satın al modalı (footer.php).
562 * Uç (/payment/checkCoupon) kuponu SUNUCUDA doğrular ve sepet oturumuna yazar; toplamlar
563 * yanıttan ve sonraki /payment/add turundan gelir — burada para hesabı YOK. Kod ham gelir,
564 * URL kaçışı burada tek yerde. validator:false — sayfadaki başka bir formun boş alanı
565 * kupon denemesini engellememeli (istek kendi verisini taşımaz). */
566function couponCheck(code, callback) {
567    $(document).request({
568        'url': sa + "/payment/checkCoupon/" + encodeURIComponent(code),
569        'returnAlert': false,
570        'method': 'GET',
571        'validator': false
572    }, callback);
573}
574
575function addBasket(id, returnAlert, callback) {
576
577    if (typeof returnAlert == "undefined") {
578        returnAlert = false;
579    }
580
581    // GA4: select_item + add_to_cart (item data resolved from the page's product cards).
582    try { if (window.GA4) { GA4.addToCart(id); } } catch (e) {}
583
584    /* ctx boş olabilir (storage kapalı, bilinmeyen sayfa) — sunucu boşu sorunsuz kabul
585       eder, ödeme akışı bağlamdan BAĞIMSIZDIR. */
586    var __ctx = "";
587    try { __ctx = esimBuyContext(); } catch (e) {}
588
589    $(this).request({
590        "url": sa + "/basket/add/" + id + (__ctx ? "?ctx=" + encodeURIComponent(__ctx) : ""),
591        'returnAlert': returnAlert,
592        "method": "get"
593    }, function (response) {
594
595        if (response.result === "ok") {
596            if (callback) {
597                callback(response);
598            }
599
600            initPayment();
601
602        } else if (response.result === "error") {
603            /* Transient network failure (request.js error-handler -> {'result':'error'}, no
604             * .error/.title) — NOT a real server response. Do not fire the old hardcoded
605             * blocking alert('Could not add to cart.'); show a graceful, localized, dismissible
606             * message with a retry option instead (real server 'fail' messages still fall
607             * through to the branch below, untouched). */
608            boxup(netErrors.network, {
609                'confirm': true,
610                'textOk': netErrors.retry
611            }, function (retryConfirmed) {
612                if (retryConfirmed) {
613                    addBasket(id, returnAlert, callback);
614                }
615            });
616        } else {
617            if (response.error && response.error.indexOf("User") >= 0) {
618                window.location = sa + '/register';
619            } else {
620                alert(response.error ? response.error : (response.title ? response.title : 'Could not add to cart.'));
621            }
622        }
623    });
624
625}
626

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.