/* =========================================================================
KUCHAŘKA KAMILY PEŠEK // PASCAL 7.0 WEB ENGINE (331 RECEPTŮ & 568 JÍDEL)
Kulinářská paměť Kamily Pešek & Software-salon ALTÁN (Helena Heroutová)
========================================================================= */
// Audio engine (Retro PC Speaker via Web Audio API)
let audioCtx = null;
let soundEnabled = true;
function playSpeaker(freq = 800, duration = 0.04, type = 'square') {
if (!soundEnabled) return;
try {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = type;
osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
gain.gain.setValueAtTime(0.08, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + duration);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + duration);
} catch (e) {}
}
// Data references loaded from data.js
const KNIHA_RECEPTU = window.KNIHA_RECEPTU || [];
const HISTORICKA_JIDLA = window.HISTORICKA_JIDLA || [];
// App State
let currentView = 'menu'; // 'menu', 'recept', 'recepty_list', 'hledat', 'suroviny', 'davno', 'oblibena', 'kronika', 'altan'
let currentRecipeId = 1;
let currentCategory = 'Vše';
let listPage = 0;
const PAGE_SIZE = 12;
const screenEl = document.getElementById('screen-content');
const winTitleEl = document.getElementById('win-title');
const timeEl = document.getElementById('time-display');
// Clock
setInterval(() => {
const d = new Date();
timeEl.textContent = d.toTimeString().split(' ')[0];
}, 1000);
// =========================================================================
// CZECH TEXT NORMALIZATION & FULLTEXT SEARCH ENGINE
// =========================================================================
// Normalizace: malá písmena, odstranění diakritiky
function norm(str) {
return (str || "").toLowerCase()
.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
// Český lehký stemmer (odstranění pádových a slovesných koncovek pro dokonalou flexi)
function stem(word) {
let w = norm(word).replace(/[^a-z0-9]/g, "");
if (w.length <= 3) return w;
// Dlouhé přídavné a slovesné koncovky
w = w.replace(/(ovy|ova|ove|ymi|ych|ich|ovi|ami|ach|ech|eho|emu|ajici|ejici)$/, "");
if (w.length > 3) {
// Slovesa a podstatná jména
w = w.replace(/(ime|ite|eme|ete|aji|eji|ala|alo|ali|ila|ilo|ili|ula|ulo|uli)$/, "");
}
if (w.length > 3) {
// Dvojpísmenné koncovky
w = w.replace(/(em|ou|am|um|ym|im|ky|ka|ku|ek|ce|ci|ho|mu|mi|es|as|at|et|it|ut|ti|al|il|ul|ne|na|ny)$/, "");
}
if (w.length > 3) {
// Krátké samohlásky
w = w.replace(/[aeiouy]$/, "");
}
return w;
}
// Rozdělení dotazu na čistá slova
function tokenize(str) {
return norm(str)
.split(/[\s,.;:()!?"'\/\\+*#%&<=>[\]{}|~_`^$]+/)
.filter(t => t.length > 0);
}
// Kontrola shody slova:
function wordMatches(targetWord, queryWord, queryStem) {
const tw = norm(targetWord);
if (tw === queryWord) return true;
if (tw.startsWith(queryWord) || queryWord.startsWith(tw)) return true;
const ts = stem(targetWord);
if (ts === queryStem) return true;
if (queryStem.length >= 3 && ts.startsWith(queryStem)) return true;
if (ts.length >= 3 && queryStem.startsWith(ts)) return true;
return false;
}
// Zvýraznění shod v textu (HTML mark)
function highlightMatches(text, query) {
if (!query || !text) return text;
const tokens = tokenize(query).filter(t => t.length > 1);
if (tokens.length === 0) return text;
const stems = tokens.map(stem);
const parts = text.split(/(\s+|[.,;()\-+*\/:"'!?<>])/);
return parts.map(part => {
if (!part.trim()) return part;
const np = norm(part);
const sp = stem(part);
const isMatch = tokens.some((tok, idx) => wordMatches(part, tok, stems[idx]));
if (isMatch) {
return `${part}`;
}
return part;
}).join("");
}
// Robustní vyhledávání v sešitu Kamily Pešek
function searchKniha(query, categoryFilter = 'Vše') {
if (!query || !query.trim()) return [];
const tokens = tokenize(query);
if (tokens.length === 0) return [];
const stems = tokens.map(stem);
const source = categoryFilter === 'Vše'
? KNIHA_RECEPTU
: KNIHA_RECEPTU.filter(r => r.kategorie === categoryFilter);
const results = [];
source.forEach(r => {
const titleWords = tokenize(r.nazev);
const surWords = tokenize((r.suroviny || []).join(" "));
const posWords = tokenize(r.postup || "");
const katWords = tokenize(r.kategorie || "");
let score = 0;
let matchedTokens = 0;
let snippet = "";
tokens.forEach((tok, idx) => {
const st = stems[idx];
let hit = false;
// 1. Shoda v názvu (priorita #1)
if (titleWords.some(tw => wordMatches(tw, tok, st))) {
score += 150;
hit = true;
}
// 2. Shoda v surovinách
if (surWords.some(sw => wordMatches(sw, tok, st))) {
score += 50;
hit = true;
if (!snippet) {
const found = (r.suroviny || []).find(s => tokenize(s).some(sw => wordMatches(sw, tok, st)));
if (found) snippet = "Surovina: " + found;
}
}
// 3. Shoda v kategorii
if (katWords.some(kw => wordMatches(kw, tok, st))) {
score += 25;
hit = true;
}
// 4. Shoda v postupu
if (posWords.some(pw => wordMatches(pw, tok, st))) {
score += 20;
hit = true;
if (!snippet && r.postup) {
snippet = "Postup: " + r.postup.substring(0, 110) + "...";
}
}
if (hit) matchedTokens++;
});
// Masivní bonus za pokrytí všech hledaných slov (AND logika)
if (matchedTokens === tokens.length) {
score += 500 * tokens.length;
}
if (score > 0) {
results.push({
recipe: r,
score: score,
matchedCount: matchedTokens,
snippet: snippet
});
}
});
// Seřadit: nejdřív ty, co splňují všechny tokeny, pak podle skóre
results.sort((a, b) => {
const fullA = a.matchedCount === tokens.length ? 1 : 0;
const fullB = b.matchedCount === tokens.length ? 1 : 0;
if (fullA !== fullB) return fullB - fullA;
return b.score - a.score || a.recipe.id - b.recipe.id;
});
return results;
}
// Categories list
const KATEGORIE = [
'Vše',
'Polévky',
'Masitá jídla',
'Vánoční cukroví',
'Moučníky a sladké',
'Přílohy a bezmasá',
'Omáčky',
'Saláty a zelenina',
'Ostatní'
];
// =========================================================================
// VIEWS & CONTROLLERS
// =========================================================================
// 1. HLAVNÍ NABÍDKA
function renderMainMenu() {
currentView = 'menu';
winTitleEl.textContent = "KUCHAŘKA KAMILY PEŠEK // HLAVNÍ NABÍDKA PASCAL 7.0";
let html = `
════════════════════════════════════════════════════════════════════════════════
KUCHAŘKA KAMILY PEŠEK // AUTENTICKÝ SEŠIT RECEPTŮ (1998–2026)
Software-salon ALTÁN (Ing. Helena Heroutová, Dejvice) & Konzdat
════════════════════════════════════════════════════════════════════════════════
Vítejte v rodinném gastronomickém archivu babičky Kamily Pešek.
Tento systém obsahuje ${KNIHA_RECEPTU.length} přepsaných receptů z jejího ručně psaného
sešitu (strany 1–43) a ${HISTORICKA_JIDLA.length} záznamů rodinného jídelníčku z let 1998–2021.
── RYCHLÉ VYHLEDÁVÁNÍ V KUCHAŘCE ───────────────────────────────────────────
HLEDAT:
── HLAVNÍ PROGRAMOVÉ MODULY ────────────────────────────────────────────────
[H / /]>> FULLTEXTOVÉ HLEDÁNÍ V CELÉM SEŠITU (${KNIHA_RECEPTU.length} receptů) <<
[R]Procházet receptář Kamily Pešek podle kategorií
[S]Vařit podle surovin ve špajzu a lednici (Algoritmus KUCHARKA.EXE)
[N]Náhodný recept pro dnešní den (Inspirace ze sešitu)
Zadejte hledaný název jídla, surovinu nebo kousek postupu (např. kuře, bábovka, kmínová, guláš, česnek, ořechy, brambory).
Vyhledávač automaticky ohýbá česká slova a prohledává všech ${KNIHA_RECEPTU.length} receptů ze sešitu.
Žádný recept v sešitu neodpovídá hledanému výrazu "${q}".
`;
}
container.innerHTML = mHtml;
};
searchInput.addEventListener('input', handleSearch);
if (initialQuery) {
handleSearch();
}
}
function clearRecipeSearch(category) {
openReceptyList(category, 0);
}
// 4. DETAIL RECEPTU
function openRecipe(id) {
const r = KNIHA_RECEPTU.find(x => x.id === id);
if (!r) return;
currentView = 'recept';
currentRecipeId = id;
playSpeaker(900, 0.05);
winTitleEl.textContent = `RECEPT [${r.id}/${KNIHA_RECEPTU.length}]: ${r.nazev.toUpperCase()}`;
let html = `
════════════════════════════════════════════════════════════════════════════════
RECEPT Č. ${r.id}: ${r.nazev}
Původní sešit Kamily Pešek: Strana ${r.strana} | Kategorie: ${r.kategorie}
════════════════════════════════════════════════════════════════════════════════
── POTŘEBNÉ SUROVINY ZE SEŠITU ─────────────────────────────────────────────
`;
if (r.suroviny && r.suroviny.length > 0) {
r.suroviny.forEach(s => {
html += `
• ${s}
`;
});
} else {
html += `
• Běžné základní suroviny dle chuti a zvyklostí
`;
}
html += `
── POSTUP PŘÍPRAVY ZE SEŠITU KAMILY PEŠEK ─────────────────────────────────
${r.postup || "Tradiční rodinný postup podle zápisu Kamily Pešek."}
`;
screenEl.innerHTML = html;
}
// 5. NÁHODNÝ RECEPT
function openRandomRecipe() {
if (KNIHA_RECEPTU.length === 0) return;
const randomId = Math.floor(Math.random() * KNIHA_RECEPTU.length) + 1;
openRecipe(randomId);
}
// 6. VAŘENÍ ZE SUROVIN VE ŠPAJZU (ALGORITMUS)
function openSuroviny(defaultQuery = "") {
currentView = 'suroviny';
playSpeaker(700, 0.06);
winTitleEl.textContent = "ALGORITMUS: VAŘENÍ PODLE SUROVIN VE ŠPAJZU A LEDNICI";
let html = `
════════════════════════════════════════════════════════════════════════════════
VAŘENÍ PODLE SUROVIN // ROZHODOVACÍ SYSTÉM PRO HOSPODYNI (KUCHARKA.EXE)
════════════════════════════════════════════════════════════════════════════════
Zadejte suroviny, které máte právě doma ve špajzu a lednici (oddělené čárkou či mezerou).
Algoritmus prohledá všech ${KNIHA_RECEPTU.length} receptů Kamily Pešek i ${HISTORICKA_JIDLA.length} jídel z kroniky.
MÁM DOMA:
Rychlé koše:
`;
screenEl.innerHTML = html;
const input = document.getElementById('suroviny-input');
input.focus();
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') doSearchSuroviny();
});
if (defaultQuery) {
doSearchSuroviny();
}
}
function fillPantry(str) {
const input = document.getElementById('suroviny-input');
if (input) {
input.value = str;
doSearchSuroviny();
}
}
function doSearchSuroviny() {
const query = document.getElementById('suroviny-input').value.trim();
const resEl = document.getElementById('vysledky-hledani');
if (!query) {
resEl.innerHTML = `
`;
});
}
if (scoredRecipes.length === 0 && scoredHist.length === 0) {
html += `
Pro tyto suroviny nebyla v databázi nalezena přímá shoda. Zkuste zadat základní ingredience jako: brambory, maso, vejce, smetana, kuře.
`;
}
resEl.innerHTML = html;
}
// 7. DÁVNO NEVAŘENO
function openDavno() {
currentView = 'davno';
playSpeaker(600, 0.05);
winTitleEl.textContent = "DOPORUČENÍ: DÁVNO NEVAŘENO // PREVENCE STEREOTYPU";
let html = `
════════════════════════════════════════════════════════════════════════════════
DOPORUČENÍ: DÁVNO NEVAŘENO // HISTORICKÝ CYKLUS RODINY (DoporucD)
════════════════════════════════════════════════════════════════════════════════
Tento modul dekompilovaný z původního Delphi programu prochází archiv datumu
a vyhledává pokrmy, které se na stole neobjevily nejdéle, aby se jídelníček netočil.
DOPORUČENÁ JÍDLA S NEJDELŠÍM ČASOVÝM ODSTUPEM:
`;
HISTORICKA_JIDLA.slice(0, 12).forEach((j, idx) => {
html += `
`;
screenEl.innerHTML = html;
}
// 10. ALTÁN / NÁPOVĚDA
function openAltan() {
currentView = 'altan';
playSpeaker(750, 0.05);
winTitleEl.textContent = "SOFTWARE-SALON ALTÁN // DEJVICE & BOHNICE";
let html = `
════════════════════════════════════════════════════════════════════════════════
SOFTWARE-SALON ALTÁN // VÁCLAVKOVA 2, PRAHA 6 (DEJVICE)
════════════════════════════════════════════════════════════════════════════════
PŮVODNÍ PROGRAMÁTORSKÁ STOPA Z JEDNOTKY TURBO PASCALU (POMUCKY.BAK):
const rec : array[1..3] of string = (
'S PŘIPOMÍNKAMI A DOTAZY SE OBRACEJTE NA ADRESU:',
' Ing. B. Mikulecký, CSc., Ing. H. Heroutová',
' Software-salon ALTÁN ',
' Václavkova 2, 160 00 Praha 6, tel. 02/311 13 25');
Ing. Helena Heroutová patřila k průkopnické generaci českých softwarových inženýrek,
které v 80. a 90. letech tvořily základní procedurální knihovny pro Turbo Pascal
a DOS v dejvickém ateliéru na Václavkově ulici.
Když se na přelomu tisíciletí objevilo vývojové prostředí Borland Delphi, koncept
rodinné kuchařky se přetavil do plnohodnotné 32bitové aplikace KUCHARKA.EXE.
Tato webová aplikace věrně propojuje kulinářské dědictví babičky Kamily Pešek
(kompletní rukopisný sešit čítající ${KNIHA_RECEPTU.length} receptů)
s procedurální logikou Turbo Pascalu pro budoucí generace.
`;
screenEl.innerHTML = html;
}
// =========================================================================
// GLOBAL KEYBOARD & TOUCH NAVIGATION
// =========================================================================
window.addEventListener('keydown', (e) => {
// If typing in input, only ESC blurs
if (document.activeElement && document.activeElement.tagName === 'INPUT') {
if (e.key === 'Escape') {
document.activeElement.blur();
renderMainMenu();
}
return;
}
playSpeaker(800, 0.03);
switch (e.key) {
case '/':
case 'h': case 'H':
e.preventDefault();
openSearch();
break;
case 'r': case 'R': case 'F2':
e.preventDefault();
openReceptyList('Vše');
break;
case 's': case 'S': case 'F3':
e.preventDefault();
openSuroviny();
break;
case 'n': case 'N':
openRandomRecipe();
break;
case 'd': case 'D': case 'F4':
e.preventDefault();
openDavno();
break;
case 'o': case 'O':
openOblibena();
break;
case 'k': case 'K': case 'F5':
e.preventDefault();
openKronika();
break;
case 'a': case 'A': case 'F1':
e.preventDefault();
openAltan();
break;
case 'Escape':
renderMainMenu();
break;
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8':
if (currentView === 'menu') {
const idx = parseInt(e.key);
if (KATEGORIE[idx]) openReceptyList(KATEGORIE[idx]);
}
break;
}
});
// Touch / Navigation controls handler
document.querySelectorAll('[data-act]').forEach(el => {
el.addEventListener('click', () => {
const act = el.getAttribute('data-act');
if (act === 'menu') renderMainMenu();
else if (act === 'hledat') openSearch();
else if (act === 'recepty') openReceptyList('Vše');
else if (act === 'suroviny') openSuroviny();
else if (act === 'davno') openDavno();
else if (act === 'oblibena') openOblibena();
else if (act === 'kronika') openKronika();
else if (act === 'altan') openAltan();
});
});
// Sound toggle
document.getElementById('sound-btn').addEventListener('click', () => {
soundEnabled = !soundEnabled;
document.getElementById('sound-btn').textContent = soundEnabled ? '🔊 ZVUK: ON' : '🔈 ZVUK: OFF';
if (soundEnabled) playSpeaker(1200, 0.08);
});
// Initial boot
renderMainMenu();