const stores = {
billa: {
name: "Billa",
budgetBrands: ["clever"],
color: "rgb(255 255 225)",
},
spar: {
name: "Spar",
budgetBrands: ["s-budget"],
color: "rgb(225 244 225)",
},
hofer: {
name: "Hofer",
budgetBrands: ["milfina"],
color: "rgb(230 230 255)",
},
lidl: {
name: "Lidl",
budgetBrands: ["milbona"],
color: "rgb(255 225 225)",
},
mpreis: {
name: "MPREIS",
budgetBrands: [],
color: "rgb(255 230 230)",
},
dm: {
name: "DM",
budgetBrands: [],
color: "rgb(255 240 230)",
},
unimarkt: {
name: "Unimarkt",
budgetBrands: ["jeden tag", "unipur"],
color: "rgb(179, 217, 255)",
},
};
const STORE_KEYS = Object.keys(stores);
const BUDGET_BRANDS = [].concat(
...Object.values(stores).map((store) => store.budgetBrands)
);
/**
* @description Returns the current date in ISO format
* @returns {string} ISO date string in format YYYY-MM-DD
*/
function currentDate() {
return new Date().toISOString().slice(0, 10);
}
/**
* @description Gets the query parameter from the URL
* @param {string} name Name of the query parameter
* @returns {string | null} Value of the query parameter or null if not found
*/
function getQueryParameter(name) {
const url = new URL(window.location.href);
return url.searchParams.get(name);
}
/**
* @description Converts a string to a number
* @param {string} value String to convert
* @param {number} defaultValue Default value if conversion fails
* @returns {number} Converted number or default value
*/
function toNumber(value, defaultValue) {
try {
return Number.parseFloat(value);
} catch (e) {
return defaultValue;
}
}
/**
* @description Create dom element from html string and add inner html via string template
* @param {string} el Element type
* @param {string} html Inner html
* @returns {HTMLElement} DOM element
*/
function dom(el, html) {
const element = document.createElement(el);
element.innerHTML = html;
return element;
}
async function loadItems() {
const response = await fetch("latest-canonical.json");
const items = await response.json();
for (const item of items) {
item.search = item.name + " " + item.unit;
item.search = item.search.toLowerCase().replace(",", ".");
item.numPrices = item.priceHistory.length;
item.priceOldest =
item.priceHistory[item.priceHistory.length - 1].price;
item.dateOldest = item.priceHistory[item.priceHistory.length - 1].date;
item.date = item.priceHistory[0].date;
let highestPriceBefore = -1;
for (let i = 1; i < item.priceHistory.length; i++) {
const price = item.priceHistory[i];
highestPriceBefore = Math.max(highestPriceBefore, price.price);
}
if (highestPriceBefore == -1) highestPriceBefore = item.price;
item.highestBefore = highestPriceBefore;
}
return items;
}
/**
* @description Class for managing the shopping carts, which are stored in local storage
*/
class ShoppingCarts {
constructor() {
this.carts = [];
this.load();
}
/**
* @description Load the shopping carts from local storage into carts array
*/
load() {
const val = localStorage.getItem("carts");
this.carts = val ? JSON.parse(val) : [];
}
/**
* @description Save the shopping carts to local storage, with key "carts"
*/
save() {
localStorage.setItem("carts", JSON.stringify(this.carts, null, 2));
}
/**
* @description Check if the shopping carts contains a cart with the given name
* @param {string} name Name of the shopping cart to check
*/
has(name) {
for (const cart of this.carts) {
if (cart.name === name) return true;
}
return false;
}
/**
* @description Add new shopping card to array and save new carts array to local storage
* @param {string} name Name of the shopping cart to add
*/
add(name) {
this.carts.push({
name: name,
items: [],
});
this.save();
}
/**
* @description Remove shopping cart from carts array based on name and save updated array to local storage
* @param {string} name Name of the shopping cart to remove
*/
remove(name) {
this.carts = this.carts.filter((cart) => cart.name !== name);
this.save();
}
}
const shoppingCarts = new ShoppingCarts();
shoppingCarts.load();
function itemToStoreLink(item) {
if (STORE_KEYS.includes(item.store)) {
return `${item.name}`
}
return `${item.name}`;
}
function itemToDOM(item) {
let storeDom = dom("td", item.store);
storeDom.setAttribute("data-label", "Kette");
let nameDom = dom("td", `${itemToStoreLink(item)}`);
nameDom.setAttribute("data-label", "Name");
let quantity = item.quantity || ""
let unit = item.unit || "";
if(quantity >= 1000 && (unit == 'g' || unit == 'ml')) {
quantity = parseFloat((0.001 * quantity).toFixed(2));
unit = unit == 'ml' ? 'l' : 'kg';
}
let unitDom = dom("td", (item.isWeighted ? "⚖ " : "") + `${quantity} ${unit}`);
unitDom.setAttribute("data-label", "Menge");
let increase = "";
if (item.priceHistory.length > 1) {
let percentageChange = Math.round((item.priceHistory[0].price - item.priceHistory[1].price) / item.priceHistory[1].price * 100);
increase = `${(percentageChange > 0 ? "+" + percentageChange : percentageChange)}%`;
}
let priceDomText = `${Number(item.price).toFixed(2)} ${increase} ${item.priceHistory.length > 1 ? "(" + (item.priceHistory.length - 1) + ")" : ""}`;
let pricesText = "";
for (let i = 0; i < item.priceHistory.length; i++) {
const date = item.priceHistory[i].date;
const currPrice = item.priceHistory[i].price;
const lastPrice = item.priceHistory[i + 1] ? item.priceHistory[i + 1].price : currPrice;
const increase = Math.round((currPrice - lastPrice) / lastPrice * 100);
let priceColor = "black";
if (increase > 0) priceColor = "red";
if (increase < 0) priceColor = "green";
pricesText += `${date} ${currPrice} ${increase > 0 ? "+" + increase : increase}%`;
if (i != item.priceHistory.length - 1) pricesText += "
";
}
let priceDom = dom("td", `${priceDomText}