PageSourceSearch

https://www.smallfarm-chiangmai.com/blackgoat/pd-buynow.js.download

js smallfarm-chiangmai.com collected 2026-10-10 00:32:06 UTC 14,768 bytes, 424 lines download raw bytes

1(function(){
2  
3  // CSRF Token Helper
4  function getCsrfToken() {
5    return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
6  }
7  
8  // Utility functions
9  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
10  
11  function getVariantSummaryText(){
12    const vs = document.getElementById('pd-variant-summary');
13    if (!vs) return '';
14    const items = Array.from(vs.querySelectorAll('.vs-item strong')).map(el => el.textContent.trim()).filter(Boolean);
15    return items.length ? items.join(' / ') : '';
16  }
17
18  function getCurrentProductData(){
19    const form = document.getElementById('pd-buy-form');
20    if (!form) return null;
21
22    const productId = parseInt(form.dataset.pid || '0', 10);
23    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
24    const variantId = parseInt(document.getElementById('pd-variant-id')?.value || '0', 10) || 0;
25    
26    if (!productId || productId <= 0) {
27      throw new Error('Product ID tidak valid');
28    }
29
30    if (qty <= 0) {
31      throw new Error('Quantity harus lebih dari 0');
32    }
33
34    return { productId, qty, variantId };
35  }
36
37  function createBuyNowPayload(productId, qty, variantId) {
38    // Get product metadata
39    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
40    
41    // Get correct price, SKU, and weight from selected variant data instead of DOM elements
42    let price = 0;
43    let sku = '';
44    let weight = 0;
45    let dimensions = null;
46    
47    if (variantId > 0 && typeof window.variants !== 'undefined' && Array.isArray(window.variants)) {
48      // Find the selected variant in the variants array
49      const selectedVariant = window.variants.find(v => v.id === variantId);
50      if (selectedVariant) {
51        price = selectedVariant.final || 0;
52        sku = selectedVariant.sku || '';
53        weight = selectedVariant.weight || 0;
54        
55        // Include dimensions if available
56        if (selectedVariant.length_cm || selectedVariant.width_cm || selectedVariant.height_cm) {
57          dimensions = {
58            length_cm: selectedVariant.length_cm || 0,
59            width_cm: selectedVariant.width_cm || 0,
60            height_cm: selectedVariant.height_cm || 0
61          };
62        }
63        
64        // Debug info to verify fix is working
65        if (window.console && window.console.debug) {
66          console.debug('BuyNow: Using variant data', {
67            variantId: variantId,
68            price: price,
69            sku: sku,
70            weight: weight,
71            dimensions: dimensions,
72            variantData: selectedVariant
73          });
74        }
75      }
76    }
77    
78    // Fallback to DOM elements if variant data not found
79    if (price === 0) {
80      const priceTxt = document.getElementById('pd-price-current')?.textContent || '';
81      price = parseInt(String(priceTxt).replace(/[^\d]/g,''), 10) || 0;
82      
83      if (window.console && window.console.debug) {
84        console.debug('BuyNow: Using DOM price fallback', { 
85          priceTxt: priceTxt, 
86          price: price,
87          variantId: variantId,
88          reason: variantId > 0 ? 'variant_data_not_found' : 'no_variant_selected'
89        });
90      }
91    }
92    
93    if (!sku) {
94      const skuEl = document.getElementById('pd-sku');
95      sku = skuEl ? skuEl.textContent.trim() : (window.PD_SKU || '');
96    }
97    
98    const imgEl = document.getElementById('pd-main-img');
99    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
100
101    const variantText = getVariantSummaryText();
102    
103    // Get pre-order data if product is pre-order
104    const preorderData = window.PD_PREORDER_DATA || null;
105    
106    return {
107      product_id: productId,
108      qty: qty,
109      variant_id: variantId,
110      meta: {
111        name,
112        sku,
113        price,
114        image_url,
115        variant_text: variantText,
116        weight: weight,
117        dimensions: dimensions
118      },
119      is_preorder: preorderData ? 'Y' : 'N',
120      preorder_estimated_arrival: preorderData ? preorderData.estimated_arrival : null,
121      preorder_note: preorderData ? preorderData.note : ''
122    };
123  }
124
125  /**
126   * Main Buy Now function
127   * Performs validation and redirects to checkout with buy_now mode
128   */
129  window.buyNow = async function(e) {
130    if (e) e.preventDefault();
131    
132    try {
133      // Check user login status
134      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
135                        document.body.classList.contains('logged-in') ||
136                        window.userLoggedIn === true ||
137                        document.body.dataset.userLoggedIn === 'true';
138      
139      if (!isLoggedIn) {
140        // Redirect to login with return URL to current page
141        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
142        window.location.href = '/login?return=' + currentUrl;
143        return;
144      }
145
146      // Get current product data
147      const { productId, qty, variantId } = getCurrentProductData();
148      
149      // Create payload for buy now
150      const payload = createBuyNowPayload(productId, qty, variantId);
151      
152      // Store buy now data in sessionStorage for checkout page
153      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
154      
155      // Redirect to checkout with buy_now mode
156      window.location.href = '/checkout?mode=buy_now';
157      
158    } catch (err) {
159      console.error('Buy Now error:', err);
160      
161      if (window.showToast) {
162        window.showToast({ 
163          type: 'warn', 
164          title: 'Gagal memproses', 
165          message: String(err?.message || 'Terjadi kesalahan saat memproses pembelian'), 
166          duration: 4500 
167        });
168      } else {
169        alert(err.message || 'Terjadi kesalahan saat memproses pembelian');
170      }
171    }
172  };
173
174  /**
175   * Alternative Buy Now with confirmation dialog
176   */
177  window.buyNowWithConfirm = async function(e) {
178    if (e) e.preventDefault();
179    
180    try {
181      const { productId, qty, variantId } = getCurrentProductData();
182      
183      const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
184      const variantText = getVariantSummaryText();
185      const priceTxt = document.getElementById('pd-price-current')?.textContent || 'Rp0';
186      
187      const confirmMessage = `Lanjut ke checkout?\n\nProduk: ${name}${variantText ? `\nVariant: ${variantText}` : ''}\nQty: ${qty}\nHarga: ${priceTxt}`;
188      
189      if (!confirm(confirmMessage)) {
190        return;
191      }
192      
193      // Proceed with buy now
194      await window.buyNow(e);
195      
196    } catch (err) {
197      console.error('Buy Now with confirm error:', err);
198      alert(err.message || 'Terjadi kesalahan');
199    }
200  };
201
202  /**
203   * Attach event listener for Buy with Voucher button
204   */
205  function setupBuyButtons() {
206    // Setup desktop buy button (regular buy now without voucher)
207    const buyBtn = document.getElementById('btn-buy');
208    if (buyBtn) {
209      buyBtn.addEventListener('click', function(e) {
210        e.preventDefault();
211        if (buyBtn.disabled) return;
212        if (window.buyNow) {
213          window.buyNow(e);
214        }
215      });
216    }
217    
218    // Setup desktop buy with voucher button
219    const voucherBtn = document.getElementById('btn-buy-voucher');
220    if (voucherBtn) {
221      voucherBtn.addEventListener('click', function(e) {
222        e.preventDefault();
223        
224        // Validate stock availability before proceeding
225        if (this.disabled || this.getAttribute('data-stock-blocked') === 'true') {
226          const stockReason = this.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
227          
228          if (window.showToast) {
229            window.showToast({
230              type: 'warn',
231              title: 'Stok Tidak Tersedia',
232              message: stockReason,
233              duration: 3500
234            });
235          } else {
236            alert(stockReason);
237          }
238          return;
239        }
240        
241        const voucherCode = this.dataset.voucherCode;
242        if (voucherCode) {
243          window.buyNowWithVoucher(e, voucherCode);
244        }
245      });
246    }
247    
248  }
249  
250  // CRITICAL: Attach mobile button handler at DOCUMENT level with CAPTURE phase
251  // This is the ONLY way to guarantee it fires regardless of other scripts
252  document.addEventListener('click', function(e) {
253    // Check if click is on mobile buy button or its children
254    const target = e.target.closest('#mf-buy');
255    if (!target) return; // Not our button
256    
257    e.preventDefault();
258    e.stopPropagation();
259    e.stopImmediatePropagation();
260    
261    if (target.disabled) return;
262    
263    // Get voucher code from parent FAB's data attribute
264    const mobileFab = document.getElementById('pd-mobile-fab');
265    const voucherCode = mobileFab ? (mobileFab.getAttribute('data-voucher-code') || '') : '';
266    
267    if (voucherCode && voucherCode.trim() !== '' && window.buyNowWithVoucher) {
268      window.buyNowWithVoucher(e, voucherCode);
269    } else if (window.buyNow) {
270      window.buyNow(e);
271    }
272  }, true); // CAPTURE PHASE - fires BEFORE any bubble phase handlers
273  
274  // Execute immediately if DOM already loaded, otherwise wait
275  if (document.readyState === 'loading') {
276    document.addEventListener('DOMContentLoaded', setupBuyButtons);
277  } else {
278    setupBuyButtons();
279  }
280
281  /**
282   * Buy Now with Auto-Applied Voucher
283   * Applies voucher to session before redirecting to checkout (like cart does)
284   * 
285   * @param {Event} e - Click event
286   * @param {string} voucherCode - Voucher code to apply
287   */
288  window.buyNowWithVoucher = async function(e, voucherCode) {
289    if (e) e.preventDefault();
290    
291    console.log('[buyNowWithVoucher] Called with voucher code:', voucherCode);
292    
293    try {
294      // CRITICAL: Check stock availability first
295      const voucherBtn = document.getElementById('btn-buy-voucher');
296      if (voucherBtn) {
297        if (voucherBtn.disabled || voucherBtn.getAttribute('data-stock-blocked') === 'true') {
298          const stockReason = voucherBtn.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
299          
300          if (window.showToast) {
301            window.showToast({
302              type: 'warn',
303              title: 'Stok Tidak Tersedia',
304              message: stockReason,
305              duration: 3500
306            });
307          } else {
308            alert(stockReason);
309          }
310          return;
311        }
312      }
313      
314      // Check login status first
315      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
316                        document.body.classList.contains('logged-in') ||
317                        window.userLoggedIn === true ||
318                        document.body.dataset.userLoggedIn === 'true';
319      
320      console.log('[buyNowWithVoucher] User logged in:', isLoggedIn);
321      
322      if (!isLoggedIn) {
323        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
324        window.location.href = '/login?return=' + currentUrl;
325        return;
326      }
327
328      // Get current product data
329      const { productId, qty, variantId } = getCurrentProductData();
330      const payload = createBuyNowPayload(productId, qty, variantId);
331      
332      console.log('[buyNowWithVoucher] Product data:', { productId, qty, variantId });
333      
334      // Get category path for voucher validation
335      const categoryPath = window.PD_CATEGORY_PATH || '';
336      
337      // Step 1: Clear any existing voucher first (important for clean state)
338      console.log('[buyNowWithVoucher] Step 1: Clearing existing voucher...');
339      try {
340        await fetch('/api/voucher/remove.php', {
341          method: 'POST',
342          headers: { 
343            'X-CSRF-Token': getCsrfToken()
344          },
345          body: JSON.stringify({
346            csrf_token: getCsrfToken()
347          }),
348          credentials: 'same-origin'
349        });
350      } catch (e) {
351        // Ignore error if no voucher exists
352        console.debug('No existing voucher to remove or removal failed');
353      }
354      
355      // Step 2: Apply voucher to session (like cart does)
356      console.log('[buyNowWithVoucher] Step 2: Applying voucher to session...');
357      const applyRes = await fetch('/api/voucher/apply.php', {
358        method: 'POST',
359        headers: { 
360          'Content-Type': 'application/json',
361          'X-CSRF-Token': getCsrfToken()
362        },
363        credentials: 'same-origin',
364        body: JSON.stringify({
365          csrf_token: getCsrfToken(),
366          code: voucherCode,
367          items: [{
368            pid: productId,
369            price: payload.meta.price,
370            qty: qty,
371            category_path: categoryPath
372          }],
373          shipping_cost: null // Will be calculated in checkout
374        })
375      });
376      
377      const applyData = await applyRes.json();
378      
379      console.log('[buyNowWithVoucher] Voucher apply response:', applyData);
380      
381      if (!applyData.success) {
382        // Voucher invalid - show error and fallback to regular buy now
383        console.warn('[buyNowWithVoucher] Voucher application failed:', applyData.message);
384        if (window.showToast) {
385          window.showToast({
386            type: 'error',
387            title: 'Voucher Tidak Valid',
388            message: applyData.message || 'Voucher tidak dapat diterapkan untuk produk ini',
389            duration: 4000
390          });
391        } else {
392          alert(applyData.message || 'Voucher tidak dapat diterapkan');
393        }
394        
395        // Fallback to regular buy now without voucher
396        console.log('Voucher failed, proceeding with regular buy now');
397        return window.buyNow(e);
398      }
399      
400      // Step 3: Store buy now data in sessionStorage
401      console.log('[buyNowWithVoucher] Step 3: Storing buy now data in sessionStorage');
402      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
403      
404      // Step 4: Redirect to checkout
405      // Voucher sudah di session, checkout akan auto-load via loadAppliedVoucher()
406      console.log('[buyNowWithVoucher] Step 4: Redirecting to checkout with voucher applied');
407      window.location.href = '/checkout?mode=buy_now';
408      
409    } catch (err) {
410      console.error('Buy Now with Voucher error:', err);
411      
412      if (window.showToast) {
413        window.showToast({
414          type: 'error',
415          title: 'Terjadi Kesalahan',
416          message: 'Gagal memproses pembelian dengan voucher. Silakan coba lagi.',
417          duration: 3000
418        });
419      } else {
420        alert('Gagal memproses pembelian. Silakan coba lagi.');
421      }
422    }
423  };
424})();

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.