/**
* IndiaMeters Purchase Panel - v4 (Production Quality)
*
* Improvements:
* ✅ Promise-based Ecwid waiting (no polling)
* ✅ Listener registered BEFORE Add to Cart, removed AFTER firing
* ✅ Dynamic price detection (updates on variant changes)
* ✅ Debug logging (toggle DEBUG flag)
* ✅ Better DOM selectors (not just text)
* ✅ Proper error handling
*/
(function() {
'use strict';
// ===== CONFIG =====
var DEBUG = false; // Set to true for console logging during development
var config = {
selectors: {
price: '.details-product-price__value',
priceAlt: '.ec-price-item',
addBtn: 'button, a[role=button]'
},
timeouts: {
cartChangeListener: 3500,
ecwidWait: 50 // attempts
}
};
// ===== LOGGING =====
function log(label, data) {
if (DEBUG) {
console.log('[IndiaMeters] ' + label, data || '');
}
}
// ===== STATE =====
var state = {
productId: null,
buyNowBtn: null,
stickyBar: null,
priceObserver: null,
currentPrice: null
};
// ===== PROMISES & WAITING =====
/**
* Wait for Ecwid to be ready
* Returns a Promise that resolves when Ecwid.OnPageLoaded exists
*/
function waitForEcwid() {
return new Promise(function(resolve) {
var attempts = 0;
var maxAttempts = config.timeouts.ecwidWait;
var check = function() {
attempts++;
if (typeof Ecwid !== 'undefined' && Ecwid.OnPageLoaded) {
log('Ecwid ready after ' + attempts + ' attempts');
resolve(Ecwid);
} else if (attempts < maxAttempts) {
setTimeout(check, 200);
} else {
log('Ecwid not ready after ' + maxAttempts + ' attempts');
resolve(null);
}
};
check();
});
}
// ===== HELPERS =====
function findPurchaseButton() {
var buttons = document.querySelectorAll(config.selectors.addBtn);
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var text = (btn.textContent || btn.value || '').toLowerCase();
// Match: "add", "buy" (but not "buy now", "continue", "checkout")
var hasAddAction = text.indexOf('add') > -1 ||
(text.indexOf('buy') > -1 && text.indexOf('buy now') === -1);
var isNotExcluded = text.indexOf('continue') === -1 &&
text.indexOf('checkout') === -1;
if (hasAddAction && isNotExcluded) {
log('Found purchase button: ' + text);
return btn;
}
}
log('No purchase button found');
return null;
}
function getPriceText() {
try {
// Try primary selector first
var el = document.querySelector(config.selectors.price);
if (el) {
var match = el.textContent.match(/[₹$]\s?[\d,]+(?:\.\d{2})?/);
if (match) return match[0].trim();
}
// Try alternative
var elAlt = document.querySelector(config.selectors.priceAlt);
if (elAlt) {
var m = elAlt.textContent.match(/[₹$]\s?[\d,]+(?:\.\d{2})?/);
if (m) return m[0].trim();
}
return '₹0';
} catch (e) {
log('Price extraction error: ' + e.message);
return '₹0';
}
}
function clickAddToCart() {
var btn = findPurchaseButton();
if (btn) {
log('Clicking Add to Cart');
btn.click();
}
}
function gotoCheckout() {
if (typeof Ecwid !== 'undefined' && Ecwid.Cart && typeof Ecwid.Cart.gotoCheckout === 'function') {
log('Going to checkout');
Ecwid.Cart.gotoCheckout();
}
}
// ===== LISTENER MANAGEMENT =====
/**
* Trigger Add to Cart, then checkout after cart updates
* Listener is created, added BEFORE clicking, and removed AFTER firing
*/
function handleBuyNow(uiElement) {
if (!uiElement) return;
uiElement.disabled = true;
var originalText = uiElement.textContent;
uiElement.textContent = 'Adding...';
log('Buy Now triggered');
// Create listener BEFORE clicking
var onCartChanged = function() {
log('Cart changed, removing listener and going to checkout');
// Remove listener immediately after firing
if (typeof Ecwid !== 'undefined' && Ecwid.OnCartChanged &&
typeof Ecwid.OnCartChanged.remove === 'function') {
Ecwid.OnCartChanged.remove(onCartChanged);
}
uiElement.textContent = 'Redirecting...';
setTimeout(gotoCheckout, 300);
};
// Register listener BEFORE clicking
if (typeof Ecwid !== 'undefined' && Ecwid.OnCartChanged &&
typeof Ecwid.OnCartChanged.add === 'function') {
Ecwid.OnCartChanged.add(onCartChanged);
log('OnCartChanged listener registered');
}
// Click Add to Cart
clickAddToCart();
// Safety timeout: remove listener if cart doesn't update
setTimeout(function() {
if (typeof Ecwid !== 'undefined' && Ecwid.OnCartChanged &&
typeof Ecwid.OnCartChanged.remove === 'function') {
Ecwid.OnCartChanged.remove(onCartChanged);
log('Listener cleanup timeout fired');
}
uiElement.disabled = false;
uiElement.textContent = originalText;
}, config.timeouts.cartChangeListener);
}
// ===== UI: INLINE BUTTON =====
function createInlineButton() {
if (document.getElementById('im-btn')) {
log('Inline button already exists');
return;
}
var purchaseBtn = findPurchaseButton();
if (!purchaseBtn) {
log('Cannot create inline button: purchase button not found');
return;
}
var buyNow = document.createElement('button');
buyNow.id = 'im-btn';
buyNow.className = 'im-btn';
buyNow.textContent = 'Buy Now';
buyNow.type = 'button';
buyNow.addEventListener('click', function(e) {
e.preventDefault();
handleBuyNow(buyNow);
});
purchaseBtn.parentNode.insertBefore(buyNow, purchaseBtn.nextSibling);
state.buyNowBtn = buyNow;
log('Inline Buy Now button created');
}
// ===== UI: STICK