PageSourceSearch

https://www.vacuumliften.com/misibg/cart.js.download

js vacuumliften.com collected 2026-10-09 21:55:10 UTC 6,762 bytes, 179 lines download raw bytes

1(function(){
2  function $(s,ctx=document){ return ctx.querySelector(s); }
3  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
4  
5  // CSRF token helper
6  function getCsrfToken() {
7    const metaTag = document.querySelector('meta[name="csrf-token"]');
8    return metaTag ? metaTag.getAttribute('content') : '';
9  }
10  
11  function guessPdMeta(){
12    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : ($('#pd-title')?.textContent || '');
13    const sku  = (function(){ const el=$('#pd-sku'); return el ? el.textContent.trim() : (window.PD_SKU||''); })();
14    // SECURITY FIX: Remove price from client-side metadata (will be validated server-side)
15    const imgEl = $('#pd-main-img');
16    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
17    return { name, sku, image_url }; // price removed for security
18  }
19
20  async function addToCart(productId, qty=1, variantId=0, metaOpt){
21    const meta = Object.assign(guessPdMeta(), metaOpt||{});
22    const payload = {
23      product_id: parseInt(productId||0,10) || 0,
24      qty: parseInt(qty||1,10) || 1,
25      variant_id: parseInt(variantId||0,10) || 0,
26      meta
27    };
28    
29    // Check if user is logged in
30    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
31                      document.body.classList.contains('logged-in') ||
32                      window.userLoggedIn === true ||
33                      document.body.dataset.userLoggedIn === 'true';
34    
35    // Skip localStorage untuk logged user - mereka pakai database
36    if (!isLoggedIn && window.guestCart) {
37      try {
38        window.guestCart.add(payload.product_id, payload.qty, payload.variant_id, meta);
39      } catch (e) {
40        console.warn('Failed to add to localStorage cart:', e);
41      }
42    }
43    
44    const res = await fetch('/api/cart/add.php', {
45      method: 'POST',
46      headers: { 
47        'Content-Type':'application/json',
48        'X-CSRF-Token': getCsrfToken()
49      },
50      credentials: 'same-origin',
51      body: JSON.stringify({
52        ...payload,
53        csrf_token: getCsrfToken()
54      }),
55    });
56    const data = await res.json().catch(()=>({ ok:false, error:'Gagal menambahkan ke keranjang' }));
57    if (!res.ok || !data.ok) {
58      const msg = data && data.error ? data.error : 'Gagal menambahkan ke keranjang';
59      throw new Error(msg);
60    }
61
62    // Update badge di navbar dengan data dari response
63    try {
64      const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
65      if (el && data.cart && typeof data.cart.items_count === 'number') {
66        el.textContent = String(data.cart.items_count);
67        el.classList.toggle('is-empty', data.cart.items_count <= 0);
68      }
69    } catch(_){}
70
71    // Siarkan event global (dipakai cart-badge.js dan halaman lain)
72    try {
73      document.dispatchEvent(new CustomEvent('cart:updated', { detail: data.cart || {} }));
74    } catch(_){}
75
76    // Update CartBadge dengan data dari response (lebih reliable)
77    try { 
78      if (window.CartBadge && window.CartBadge.set) {
79        window.CartBadge.set(data.cart.items_count||0);
80      }
81    } catch(_){}
82
83    // Tambahan: refresh cart counter setelah delay kecil untuk memastikan data tersinkronisasi
84    setTimeout(() => {
85      if (window.updateCartCounter) {
86        window.updateCartCounter();
87      }
88    }, 100);
89
90    return data;
91  }
92
93  // Function to update cart counter from server
94  async function updateCartCounter(){
95    try {
96      const res = await fetch('/api/cart/get.php', { credentials:'same-origin' });
97      const data = await res.json().catch(()=>({ok:false}));
98      if (data.ok && typeof data.items_count === 'number') {
99        const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
100        if (el) {
101          el.textContent = String(data.items_count);
102          el.classList.toggle('is-empty', data.items_count <= 0);
103        }
104        // Dispatch event
105        try {
106          document.dispatchEvent(new CustomEvent('cart:updated', { detail: { items_count: data.items_count } }));
107        } catch(_){}
108      }
109    } catch (err) {
110      console.log('Could not update cart counter:', err);
111    }
112  }
113
114  // Function to manually merge guest cart (if needed)
115  async function mergeGuestCart(){
116    try {
117      const res = await fetch('/api/cart/merge-guest.php', {
118        method: 'POST',
119        headers: { 
120          'Content-Type': 'application/json',
121          'X-CSRF-Token': getCsrfToken()
122        },
123        credentials: 'same-origin',
124        body: JSON.stringify({ csrf_token: getCsrfToken() })
125      });
126      const data = await res.json().catch(()=>({ok:false}));
127      
128      if (data.ok && data.merged_items > 0) {
129        console.log(`Guest cart merged: ${data.merged_items} items`);
130        // Update cart counter after merge
131        await updateCartCounter();
132        return data;
133      }
134      return data;
135    } catch (err) {
136      console.log('Could not merge guest cart:', err);
137      return { ok: false, error: err.message };
138    }
139  }
140
141  window.addToCart = addToCart;
142  window.updateCartCounter = updateCartCounter;
143  window.mergeGuestCart = mergeGuestCart;
144
145  // Optional: auto-bind untuk tombol apapun yang punya data-addtocart
146  document.addEventListener('click', async (e)=>{
147    const btn = e.target.closest('[data-addtocart]');
148    if (!btn) return;
149    e.preventDefault();
150    if (btn.disabled) return;
151
152    const pid = parseInt(btn.getAttribute('data-pid')||'0',10);
153    const vid = parseInt(btn.getAttribute('data-vid')||'0',10);
154    const qty = parseInt(btn.getAttribute('data-qty')||'1',10) || 1;
155
156    try {
157      const data = await addToCart(pid, qty, vid);
158      if (window.showToast) {
159        const meta = guessPdMeta();
160        window.showToast({
161          type:'success',
162          title:'Berhasil ditambahkan',
163          message:`<strong>${esc(meta.name)}</strong>${meta.sku?` · <em>SKU: ${esc(meta.sku)}</em>`:''}<br>Qty: ${qty}`,
164          duration: 3800,
165          actions: [
166            { label:'Lihat Keranjang', onClick: ()=> { window.location.href='/keranjang'; } },
167            { label:'Lanjut Belanja', variant:'alt', onClick: ()=>{} }
168          ]
169        });
170      }
171    } catch (err) {
172      if (window.showToast) {
173        window.showToast({ type:'warn', title:'Gagal', message: String(err?.message||'Gagal menambahkan'), duration: 4200 });
174      } else {
175        console.error(err);
176      }
177    }
178  });
179})();

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.