Skip to content
(function (window, document) {
"use strict";
const selectors = {
board: "[data-coupon-board]",
card: "[data-coupon-card]",
toggleBtn: ".coupon-toggle-btn",
copyBtn: ".coupon-copy-btn",
summarizeBtn: ".coupon-summarize-btn",
ideasBtn: ".coupon-ideas-btn",
summaryBox: ".coupon-summary-box",
ideasBox: ".coupon-ideas-box",
details: ".coupon-details",
feedback: ".coupon-feedback",
terms: ".coupon-desc-list li",
title: ".coupon-title",
dateEl: "#coupon-current-date"
};
const config = Object.assign(
{
geminiApiKey: "",
geminiModel: "gemini-2.5-flash-preview-09-2025"
},
window.couponsConfig || {}
);
document.addEventListener("DOMContentLoaded", init);
document.addEventListener("click", handleClicks);
function init() {
updateCurrentDate();
}
function updateCurrentDate() {
const el = document.querySelector(selectors.dateEl);
if (!el) return;
const now = new Date();
const formatted = now.toLocaleDateString("en-IN", {
day: "2-digit",
month: "long",
year: "numeric"
});
el.textContent = `As of ${formatted}`;
}
function handleClicks(event) {
const toggleBtn = event.target.closest(selectors.toggleBtn);
if (toggleBtn) {
event.preventDefault();
toggleDetails(toggleBtn);
return;
}
const copyBtn = event.target.closest(selectors.copyBtn);
if (copyBtn) {
event.preventDefault();
handleCopy(copyBtn);
return;
}
const summarizeBtn = event.target.closest(selectors.summarizeBtn);
if (summarizeBtn) {
event.preventDefault();
handleSummarize(summarizeBtn);
return;
}
const ideasBtn = event.target.closest(selectors.ideasBtn);
if (ideasBtn) {
event.preventDefault();
handleIdeas(ideasBtn);
}
}
function toggleDetails(btn) {
const card = btn.closest(selectors.card);
if (!card) return;
const details = card.querySelector(selectors.details);
if (!details) return;
const isCollapsed = details.dataset.collapsed !== "false";
details.dataset.collapsed = isCollapsed ? "false" : "true";
details.setAttribute("aria-hidden", String(!isCollapsed));
btn.setAttribute("aria-expanded", String(isCollapsed));
btn.textContent = isCollapsed ? "Hide Details" : "Show Details";
}
async function handleCopy(btn) {
const card = btn.closest(selectors.card);
if (!card) return;
const codeBox = btn.closest(".coupon-code-box");
if (!codeBox) return;
const code = codeBox.dataset.couponCode;
const autoOpen = codeBox.dataset.autoOpen === "true";
const dealUrl = codeBox.dataset.dealUrl || "";
const feedbackEl = codeBox.querySelector(selectors.feedback);
if (!code) return;
if (code === "NOCODE") {
openDealUrl(dealUrl);
showFeedback(feedbackEl, "Deal opened in a new tab.");
return;
}
try {
await copyToClipboard(code);
showFeedback(feedbackEl, `Copied ${code}!`);
if (autoOpen && dealUrl) {
window.setTimeout(() => openDealUrl(dealUrl), 250);
}
} catch (error) {
console.error("Copy failed:", error);
showFeedback(feedbackEl, "Please copy manually.", true);
}
}
function copyToClipboard(text) {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text);
}
return new Promise((resolve, reject) => {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
const successful = document.execCommand("copy");
document.body.removeChild(textarea);
successful ? resolve() : reject(new Error("execCommand failed"));
} catch (err) {
document.body.removeChild(textarea);
reject(err);
}
});
}
function showFeedback(el, message, isError = false) {
if (!el) return;
el.textContent = message;
el.classList.toggle("is-visible", true);
el.style.color = isError ? "#b93838" : "#1b8c32";
window.clearTimeout(el._timeout);
el._timeout = window.setTimeout(() => {
el.classList.remove("is-visible");
}, 1800);
}
async function handleSummarize(btn) {
const card = btn.closest(selectors.card);
if (!card) return;
const summaryBox = card.querySelector(selectors.summaryBox);
const details = card.querySelector(selectors.details);
if (!summaryBox || !details) return;
if (summaryBox.innerHTML.trim()) {
summaryBox.classList.add("is-visible");
btn.hidden = true;
details.dataset.collapsed = "false";
details.setAttribute("aria-hidden", "false");
card.querySelector(selectors.toggleBtn)?.setAttribute("aria-expanded", "true");
return;
}
const terms = collectTerms(card);
if (!terms.length) {
summaryBox.textContent = "No details available to summarize.";
summaryBox.classList.add("is-visible");
btn.hidden = true;
return;
}
const originalText = btn.textContent;
btn.textContent = "✨ Summarizing…";
btn.disabled = true;
try {
const systemPrompt =
"You are a helpful assistant. Summarize the coupon terms into one clear sentence. Start with 'In short:' and highlight the main benefit or restriction.";
const response = await callGemini(systemPrompt, terms.join("
"));
const cleaned = response.replace(/^In short:/i, "").trim();
summaryBox.innerHTML = `In short: ${cleaned}`;
summaryBox.classList.add("is-visible");
btn.hidden = true;
forceExpand(details, card);
} catch (error) {
console.error("Gemini summarize error:", error);
summaryBox.textContent = config.geminiApiKey
? "Sorry, we couldn’t load the summary right now."
: "Add your Gemini API key to enable this summary.";
summaryBox.classList.add("is-visible");
btn.textContent = originalText;
btn.disabled = !config.geminiApiKey;
}
}
async function handleIdeas(btn) {
const card = btn.closest(selectors.card);
if (!card) return;
const ideasBox = card.querySelector(selectors.ideasBox);
const details = card.querySelector(selectors.details);
if (!ideasBox || !details) return;
if (ideasBox.innerHTML.trim()) {
ideasBox.classList.add("is-visible");
btn.hidden = true;
forceExpand(details, card);
return;
}
const terms = collectTerms(card);
const title = (card.querySelector(selectors.title)?.textContent || "").trim();
if (!terms.length && !title) {
ideasBox.textContent = "Not enough info to suggest ideas.";
ideasBox.classList.add("is-visible");
btn.hidden = true;
return;
}
const originalText = btn.textContent;
btn.textContent = "✨ Gathering ideas…";
btn.disabled = true;
try {
const systemPrompt =
"You are a shopping assistant. Suggest 3-5 specific products or categories that match the coupon. Output as bullet points without extra text.";
const payload = `Coupon Title: ${title}
Terms:
${terms.join("
")}`;
const response = await callGemini(systemPrompt, payload);
const items = response
.split("
")
.map((line) => line.replace(/^[\-\*\u2022]\s*/, "").trim())
.filter(Boolean);
if (!items.length) {
throw new Error("No suggestions returned.");
}
ideasBox.innerHTML = `Shopping Ideas:
${items.map((item) => `- ${item}
`).join("")}
`;
ideasBox.classList.add("is-visible");
btn.hidden = true;
forceExpand(details, card);
} catch (error) {
console.error("Gemini ideas error:", error);
ideasBox.textContent = config.geminiApiKey
? "Sorry, we couldn’t get ideas right now."
: "Add your Gemini API key to enable ideas.";
ideasBox.classList.add("is-visible");
btn.textContent = originalText;
btn.disabled = !config.geminiApiKey;
}
}
function collectTerms(card) {
return Array.from(card.querySelectorAll(selectors.terms)).map((item) => `- ${item.textContent.trim()}`);
}
function forceExpand(details, card) {
details.dataset.collapsed = "false";
details.setAttribute("aria-hidden", "false");
const toggleBtn = card.querySelector(selectors.toggleBtn);
if (toggleBtn) {
toggleBtn.textContent = "Hide Details";
toggleBtn.setAttribute("aria-expanded", "true");
}
}
function openDealUrl(url) {
if (!url) return;
const newWindow = window.open(url, "_blank", "noopener");
if (newWindow) {
newWindow.opener = null;
}
}
async function callGemini(systemPrompt, userQuery, retries = 2, delay = 900) {
if (!config.geminiApiKey) {
throw new Error("Gemini API key missing.");
}
const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/${config.geminiModel}:generateContent?key=${encodeURIComponent(
config.geminiApiKey
)}`;
const payload = {
contents: [{ parts: [{ text: userQuery }] }],
systemInstruction: {
parts: [{ text: systemPrompt }]
}
};
try {
const response = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (!response.ok) {
if (response.status === 429 && retries > 0) {
await wait(delay);
return callGemini(systemPrompt, userQuery, retries - 1, delay * 2);
}
throw new Error(`Gemini API error: ${response.status} ${response.statusText}`);
}
const json = await response.json();
const text = json?.candidates?.[0]?.content?.parts?.[0]?.text;
if (!text) {
throw new Error("Gemini response missing text.");
}
return text;
} catch (error) {
if (retries > 0) {
await wait(delay);
return callGemini(systemPrompt, userQuery, retries - 1, delay * 2);
}
throw error;
}
}
function wait(ms) {
return new Promise((resolve) => window.setTimeout(resolve, ms));
}
})(window, document);