@@ -541,6 +541,9 @@
< button type = "button" onclick = "openMealPlanImportModal()" class = "cb-nav-btn" title = "Zutaten aus Wochenplan holen" >
< span > 📅< / span > < span > Aus Wochenplan holen< / span >
< / button >
< button type = "button" onclick = "openCookbookImportModal()" class = "cb-nav-btn" title = "Zutaten aus einem Kochbuch-Rezept holen" >
< span > 📖< / span > < span > Aus Kochbuch holen< / span >
< / button >
< button type = "button" onclick = "deleteCheckedItems()" class = "cb-nav-btn" title = "Erledigte Artikel löschen" >
< span > 🧹< / span > < span > Erledigte löschen< / span >
< / button >
@@ -573,6 +576,9 @@
< button type = "button" onclick = "handleMobileMenuAction(openMealPlanImportModal)" class = "cb-mobile-item" >
< span > 📅< / span > < span > Aus Wochenplan holen< / span >
< / button >
< button type = "button" onclick = "handleMobileMenuAction(openCookbookImportModal)" class = "cb-mobile-item" >
< span > 📖< / span > < span > Aus Kochbuch holen< / span >
< / button >
< button type = "button" onclick = "handleMobileMenuAction(deleteCheckedItems)" class = "cb-mobile-item" >
< span > 🧹< / span > < span > Erledigte löschen< / span >
< / button >
@@ -616,10 +622,15 @@
< div class = "shop-empty-state" id = "empty-state" style = "display: none;" >
< div class = "shop-empty-icon" > 🎉< / div >
< h2 class = "shop-empty-title" > Alles eingekauft!< / h2 >
< p class = "shop-empty-desc" > Deine Einkaufsliste ist leer. Tippe unten rechts auf das < strong > +< / strong > , um einen neuen Eintrag zu notieren, oder übernimm Zutaten direkt aus dem Wochenplan.< / p >
< p class = "shop-empty-desc" > Deine Einkaufsliste ist leer. Tippe unten rechts auf das < strong > +< / strong > , um einen neuen Eintrag zu notieren, oder übernimm Zutaten direkt aus dem Wochenplan oder Kochbuch .< / p >
< div style = "display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;" >
< button type = "button" class = "cb-btn cb-btn-green" onclick = "openMealPlanImportModal()" >
< span > 📅< / span > < span > Aus Wochenplan importier en< / span >
< span > 📅< / span > < span > Aus Wochenplan hol en< / span >
< / button >
< button type = "button" class = "cb-btn cb-btn-creme" onclick = "openCookbookImportModal()" style = "border: 1.5px solid var(--shop-green); color: var(--shop-green); font-weight: 700;" >
< span > 📖< / span > < span > Aus Kochbuch holen< / span >
< / button >
< / div >
< / div >
< / main >
@@ -816,6 +827,31 @@
< / div >
< / div >
<!-- ========================================== -->
<!-- MODAL: ZUTATEN AUS KOCHBUCH HOLEN -->
<!-- ========================================== -->
< div id = "modal-import-cookbook" class = "cb-modal-backdrop no-print" onclick = "onBackdropClick(event, 'modal-import-cookbook')" >
< div class = "cb-modal-card" style = "max-width: 600px;" >
< div class = "cb-modal-header" >
< h3 class = "cb-modal-title" > < span > 📖< / span > Zutaten aus Kochbuch holen< / h3 >
< button type = "button" class = "cb-modal-close" onclick = "closeModal('modal-import-cookbook')" title = "Schließen" > × < / button >
< / div >
< div class = "cb-modal-body" >
< div id = "cookbook-import-body" style = "min-height: 120px;" >
<!-- Dynamischer Inhalt -->
< / div >
< div class = "modal-btn-row" >
< button type = "button" class = "cb-btn cb-btn-secondary" onclick = "closeModal('modal-import-cookbook')" >
Schließen
< / button >
< button type = "button" class = "cb-btn cb-btn-green" id = "btn-confirm-import-cookbook" onclick = "confirmImportCookbookIngredients()" disabled >
Ausgewählte übernehmen
< / button >
< / div >
< / div >
< / div >
< / div >
<!-- ========================================== -->
<!-- MODAL: SCHNELL - REGALWAHL (1 - TAP) -->
<!-- ========================================== -->
@@ -1813,6 +1849,398 @@
}
}
// ==========================================
// MODAL: ZUTATEN AUS KOCHBUCH HOLEN
// ==========================================
let allCookbookRecipes = null ;
let selectedCookbookRecipes = [ ] ;
const CHAPTER _NAMES = {
1 : "Suppen & Eintöpfe" ,
2 : "Traditionell & Basics" ,
3 : "Pasta & Nudelgerichte" ,
4 : "Kartoffeln" ,
5 : "Reis & Getreide" ,
6 : "Teig & Gebackenes" ,
7 : "Asiatische Küche" ,
8 : "Süßspeisen" ,
9 : "Desserts" ,
10 : "Kuchen & Gebäck"
} ;
function cleanIngredientName ( name ) {
if ( ! name ) return '' ;
let str = name . trim ( ) . replace ( /^[ \-\*\s]+/ , '' ) ;
str = str . replace ( /\s*\([^)]*\)/g , '' ) ;
if ( str . includes ( ',' ) ) {
const parts = str . split ( ',' ) ;
if ( parts [ 1 ] . trim ( ) . split ( /\s+/ ) . length <= 4 ) {
str = parts [ 0 ] ;
}
}
str = str . replace ( /[\s.,;:]+$/ , '' ) . replace ( /\s+/g , ' ' ) . trim ( ) ;
if ( str && str [ 0 ] === str [ 0 ] . toLowerCase ( ) ) {
str = str [ 0 ] . toUpperCase ( ) + str . slice ( 1 ) ;
}
return str ;
}
function getNormalizedIngredientKey ( name ) {
if ( ! name ) return "" ;
let str = name . toLowerCase ( ) . replace ( /^[ \-\*\s]+/ , "" ) . replace ( /\s*\([^)]*\)/g , "" ) ;
if ( str . includes ( "," ) ) str = str . split ( "," ) [ 0 ] ;
str = str . replace ( /[\s.,;:]+$/ , "" ) . replace ( /\s+/g , " " ) . trim ( ) ;
if ( str . endsWith ( "en" ) && str . length > 5 ) str = str . slice ( 0 , - 2 ) ;
else if ( str . endsWith ( "n" ) && str . length > 4 && ! str . endsWith ( "in" ) ) str = str . slice ( 0 , - 1 ) ;
return str ;
}
function findMatchingOpenShoppingItem ( name ) {
const k = getNormalizedIngredientKey ( name ) ;
if ( ! k ) return null ;
return allItems . find ( it => ! it . checked && getNormalizedIngredientKey ( it . item _name ) === k ) || null ;
}
async function openCookbookImportModal ( preselectedRecipeId = null ) {
openModal ( 'modal-import-cookbook' ) ;
const body = document . getElementById ( 'cookbook-import-body' ) ;
const confirmBtn = document . getElementById ( 'btn-confirm-import-cookbook' ) ;
if ( confirmBtn ) {
confirmBtn . disabled = true ;
confirmBtn . textContent = 'Ausgewählte übernehmen' ;
}
if ( ! allCookbookRecipes ) {
body . innerHTML = `
<div style="text-align: center; padding: 35px 20px; color: #555;">
<div class="shop-spinner shop-spinner-green" style="margin-bottom: 12px;"></div>
<div style="font-weight: 600; font-size: 0.95rem; color: var(--shop-green);">Rezepte werden geladen...</div>
</div>
` ;
try {
const res = await fetch ( '/api/recipes' ) ;
if ( ! res . ok ) throw new Error ( 'Fehler beim Laden der Rezepte' ) ;
allCookbookRecipes = await res . json ( ) ;
} catch ( err ) {
console . error ( 'Fehler beim Laden der Rezepte:' , err ) ;
body . innerHTML = `
<div style="text-align: center; padding: 25px; color: #b02a37;">
Fehler beim Laden der Rezepte. Bitte stelle sicher, dass du online bist.
</div>
` ;
return ;
}
}
if ( preselectedRecipeId ) {
selectedCookbookRecipes = [ ] ;
}
renderCookbookImportUI ( ) ;
if ( preselectedRecipeId ) {
await addCookbookRecipeToSelection ( preselectedRecipeId ) ;
}
}
function renderCookbookImportUI ( ) {
const body = document . getElementById ( 'cookbook-import-body' ) ;
if ( ! body ) return ;
const recipesByChapter = { } ;
allCookbookRecipes . forEach ( r => {
const ch = r . chapter _number || 1 ;
if ( ! recipesByChapter [ ch ] ) recipesByChapter [ ch ] = [ ] ;
recipesByChapter [ ch ] . push ( r ) ;
} ) ;
const sortedChapters = Object . keys ( recipesByChapter ) . map ( Number ) . sort ( ( a , b ) => a - b ) ;
let optionsHtml = '<option value="">-- Rezept auswählen... --</option>' ;
sortedChapters . forEach ( ch => {
const chName = CHAPTER _NAMES [ ch ] || ` Kapitel ${ ch } ` ;
optionsHtml += ` <optgroup label="Kapitel ${ ch } : ${ escapeAttr ( chName ) } "> ` ;
recipesByChapter [ ch ] . forEach ( r => {
const sub = r . subtitle ? ` ( ${ r . subtitle } ) ` : '' ;
optionsHtml += ` <option value=" ${ r . id } "> ${ escapeHtml ( r . title ) } ${ escapeHtml ( sub ) } </option> ` ;
} ) ;
optionsHtml += ` </optgroup> ` ;
} ) ;
body . innerHTML = `
<div style="margin-bottom: 14px; background: rgba(30, 77, 59, 0.04); border: 1.5px solid rgba(30, 77, 59, 0.15); border-radius: 8px; padding: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; flex-wrap: wrap; gap: 4px;">
<label style="font-family: var(--font-hand); font-size: 1.15rem; font-weight: 700; color: var(--shop-green); margin: 0;">
📖 Rezept aus dem Kochbuch wählen:
</label>
<span style="font-size: 0.8rem; color: #666;"> ${ allCookbookRecipes . length } Rezepte</span>
</div>
<div style="display: flex; flex-direction: column; gap: 8px; position: relative;">
<input type="text" id="cookbook-search-input" class="cb-input"
placeholder="🔍 Tippen zum Suchen (z. B. Chili, Strudel, Suppe...)"
oninput="onFilterCookbookSearch(this.value)"
autocomplete="off"
style="width: 100%; box-sizing: border-box; padding: 8px 12px; font-size: 0.92rem; border-radius: 6px;">
<div id="cookbook-search-dropdown-results" style="display: none; max-height: 180px; overflow-y: auto; background: #fff; border: 1.5px solid var(--shop-green); border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 50;"></div>
<select id="cookbook-recipe-select" class="cb-select" onchange="onSelectCookbookDropdown(this.value)" style="width: 100%; box-sizing: border-box; padding: 8px 12px; font-size: 0.92rem; border-radius: 6px;">
${ optionsHtml }
</select>
</div>
</div>
<div id="cookbook-selected-recipes-container" style="max-height: 48vh; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; padding-right: 4px;">
${ renderSelectedCookbookRecipeCards ( ) }
</div>
` ;
updateCookbookConfirmBtn ( ) ;
}
function renderSelectedCookbookRecipeCards ( ) {
if ( selectedCookbookRecipes . length === 0 ) {
return `
<div style="text-align: center; padding: 25px 15px; color: #666; background: rgba(30, 77, 59, 0.02); border: 1.5px dashed rgba(30, 77, 59, 0.2); border-radius: 8px;">
<span style="font-size: 2.2rem; display: block; margin-bottom: 6px;">📖</span>
<p style="font-size: 1.05rem; margin: 0 0 6px 0; font-weight: 700; color: var(--shop-green);">Noch kein Rezept ausgewählt</p>
<p style="font-size: 0.88rem; margin: 0; color: #777; max-width: 440px; margin-inline: auto;">
Wähle oben ein Rezept aus, um dessen Zutaten auf deine Einkaufsliste zu setzen. Mengen von bereits vorhandenen Zutaten werden automatisch addiert – ohne Dopplungen!
</p>
</div>
` ;
}
return selectedCookbookRecipes . map ( r => {
const ingredients = r . parsed _ingredients || [ ] ;
const chName = CHAPTER _NAMES [ r . chapter _number ] || ` Kapitel ${ r . chapter _number || 1 } ` ;
let itemsHtml = '' ;
if ( ingredients . length === 0 ) {
itemsHtml = ` <div style="color: #888; font-size: 0.88rem; font-style: italic; padding: 6px 0;">Keine Zutaten für dieses Rezept hinterlegt.</div> ` ;
} else {
itemsHtml = ingredients . map ( ing => {
if ( ing . is _header ) {
return ` <div style="font-weight: 700; color: var(--shop-green); font-size: 0.85rem; margin-top: 6px; font-family: var(--font-hand);"> ${ escapeHtml ( ing . raw ) } </div> ` ;
}
const cleanName = cleanIngredientName ( ing . name || ing . raw || '' ) ;
if ( ! cleanName ) return '' ;
const amtStr = formatAmount ( ing . amount _formatted || ing . amount _str || ing . amount , ing . unit ) ;
const match = findMatchingOpenShoppingItem ( cleanName ) ;
return `
<label style="display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 0.92rem; cursor: pointer; padding: 4px 6px; border-radius: 4px; background: ${ match ? 'rgba(46, 125, 50, 0.06)' : 'transparent' } ;">
<div style="display: flex; align-items: center; gap: 8px;">
<input type="checkbox" class="cookbook-ing-checkbox" checked
data-recipe-id=" ${ r . id } "
data-name=" ${ escapeAttr ( cleanName ) } "
data-amount=" ${ escapeAttr ( ing . amount _formatted || ing . amount _str || ( ing . amount !== null && ing . amount !== undefined ? String ( ing . amount ) : '' ) ) } "
data-unit=" ${ escapeAttr ( ing . unit || '' ) } "
data-source=" ${ escapeAttr ( r . title ) } "
onchange="updateCookbookConfirmBtn()">
<span> ${ amtStr ? ` <strong> ${ escapeHtml ( amtStr ) } </strong> ` : '' } ${ escapeHtml ( cleanName ) } </span>
</div>
${ match ? `
<span style="font-size: 0.72rem; background: #e8f5e9; color: #2e7d32; border: 1px solid #c8e6c9; border-radius: 10px; padding: 1px 7px; font-weight: 600; white-space: nowrap;" title="Bereits auf der Einkaufsliste – Menge wird addiert!">
➕ wird addiert ( ${ escapeHtml ( match . amount ? match . amount + ' ' + ( match . unit || '' ) : 'auf Liste' ) } )
</span>
` : '' }
</label>
` ;
} ) . join ( '' ) ;
}
return `
<div class="cookbook-recipe-card" data-recipe-id=" ${ r . id } " style="background: rgba(30, 77, 59, 0.05); border: 1.5px solid rgba(30, 77, 59, 0.18); border-radius: 8px; padding: 12px;">
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px; gap: 8px;">
<div>
<div style="font-family: var(--font-hand); font-size: 1.25rem; font-weight: 700; color: var(--shop-green);">
🍳 ${ escapeHtml ( r . title ) }
</div>
${ r . subtitle ? ` <div style="font-size: 0.8rem; color: #666; font-style: italic;"> ${ escapeHtml ( r . subtitle ) } </div> ` : '' }
</div>
<div style="display: flex; gap: 6px; align-items: center;">
<span style="font-size: 0.72rem; background: rgba(30, 77, 59, 0.12); color: var(--shop-green); padding: 2px 7px; border-radius: 6px; font-weight: 600;"> ${ escapeHtml ( chName ) } </span>
<button type="button" onclick="removeCookbookRecipeFromSelection( ${ r . id } )" title="Dieses Rezept entfernen" style="background: none; border: none; color: #b02a37; font-size: 1.15rem; cursor: pointer; padding: 0 4px; line-height: 1;">✕</button>
</div>
</div>
<div style="display: flex; justify-content: flex-end; gap: 10px; margin-bottom: 8px; font-size: 0.82rem;">
<button type="button" onclick="toggleCookbookRecipeChecks( ${ r . id } , true)" style="background: none; border: none; color: var(--shop-green); font-weight: 700; cursor: pointer; text-decoration: underline;">Alle</button>
<span style="color: #ccc;">|</span>
<button type="button" onclick="toggleCookbookRecipeChecks( ${ r . id } , false)" style="background: none; border: none; color: #888; font-weight: 600; cursor: pointer; text-decoration: underline;">Keine</button>
</div>
<div style="display: flex; flex-direction: column; gap: 4px;">
${ itemsHtml }
</div>
</div>
` ;
} ) . join ( '' ) ;
}
async function addCookbookRecipeToSelection ( recipeId ) {
recipeId = parseInt ( recipeId , 10 ) ;
if ( ! recipeId ) return ;
if ( selectedCookbookRecipes . some ( r => r . id === recipeId ) ) {
showToast ( 'Rezept bereits in der Auswahl' ) ;
return ;
}
try {
const res = await fetch ( ` /api/recipes/ ${ recipeId } ` ) ;
if ( ! res . ok ) throw new Error ( 'Rezept nicht gefunden' ) ;
const recipeData = await res . json ( ) ;
selectedCookbookRecipes . push ( recipeData ) ;
const container = document . getElementById ( 'cookbook-selected-recipes-container' ) ;
if ( container ) {
container . innerHTML = renderSelectedCookbookRecipeCards ( ) ;
}
updateCookbookConfirmBtn ( ) ;
} catch ( err ) {
console . error ( 'Fehler beim Laden des Rezepts:' , err ) ;
showToast ( 'Fehler beim Laden des Rezepts' ) ;
}
}
function removeCookbookRecipeFromSelection ( recipeId ) {
selectedCookbookRecipes = selectedCookbookRecipes . filter ( r => r . id !== recipeId ) ;
const container = document . getElementById ( 'cookbook-selected-recipes-container' ) ;
if ( container ) {
container . innerHTML = renderSelectedCookbookRecipeCards ( ) ;
}
updateCookbookConfirmBtn ( ) ;
}
function toggleCookbookRecipeChecks ( recipeId , val ) {
const cbs = document . querySelectorAll ( ` .cookbook-ing-checkbox[data-recipe-id=" ${ recipeId } "] ` ) ;
cbs . forEach ( cb => cb . checked = val ) ;
updateCookbookConfirmBtn ( ) ;
}
function onSelectCookbookDropdown ( recipeId ) {
if ( ! recipeId ) return ;
addCookbookRecipeToSelection ( recipeId ) ;
const sel = document . getElementById ( 'cookbook-recipe-select' ) ;
if ( sel ) sel . value = '' ;
}
function onFilterCookbookSearch ( query ) {
const resultsDiv = document . getElementById ( 'cookbook-search-dropdown-results' ) ;
if ( ! resultsDiv || ! allCookbookRecipes ) return ;
const q = ( query || '' ) . toLowerCase ( ) . trim ( ) ;
if ( ! q ) {
resultsDiv . style . display = 'none' ;
resultsDiv . innerHTML = '' ;
return ;
}
const matches = allCookbookRecipes . filter ( r => {
const titleMatch = ( r . title || '' ) . toLowerCase ( ) . includes ( q ) ;
const subMatch = ( r . subtitle || '' ) . toLowerCase ( ) . includes ( q ) ;
const ingMatch = ( r . ingredients || '' ) . toLowerCase ( ) . includes ( q ) ;
return titleMatch || subMatch || ingMatch ;
} ) . slice ( 0 , 10 ) ;
if ( matches . length === 0 ) {
resultsDiv . innerHTML = ` <div style="padding: 10px; font-size: 0.88rem; color: #888; text-align: center;">Kein passendes Rezept gefunden</div> ` ;
resultsDiv . style . display = 'block' ;
return ;
}
resultsDiv . innerHTML = matches . map ( r => {
const chName = CHAPTER _NAMES [ r . chapter _number ] || ` Kapitel ${ r . chapter _number || 1 } ` ;
return `
<div onclick="selectSearchRecipeMatch( ${ r . id } )" style="padding: 8px 12px; cursor: pointer; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; transition: background 0.15s ease;" onmouseover="this.style.background='rgba(30,77,59,0.08)'" onmouseout="this.style.background='transparent'">
<div>
<strong style="color: var(--shop-green);"> ${ escapeHtml ( r . title ) } </strong>
${ r . subtitle ? ` <span style="font-size: 0.8rem; color: #666; margin-left: 6px;"> ${ escapeHtml ( r . subtitle ) } </span> ` : '' }
</div>
<span style="font-size: 0.72rem; color: #888;"> ${ escapeHtml ( chName ) } </span>
</div>
` ;
} ) . join ( '' ) ;
resultsDiv . style . display = 'block' ;
}
function selectSearchRecipeMatch ( recipeId ) {
addCookbookRecipeToSelection ( recipeId ) ;
const searchInput = document . getElementById ( 'cookbook-search-input' ) ;
if ( searchInput ) searchInput . value = '' ;
const resultsDiv = document . getElementById ( 'cookbook-search-dropdown-results' ) ;
if ( resultsDiv ) {
resultsDiv . style . display = 'none' ;
resultsDiv . innerHTML = '' ;
}
}
function updateCookbookConfirmBtn ( ) {
const checkedBoxes = Array . from ( document . querySelectorAll ( '.cookbook-ing-checkbox:checked' ) ) ;
const btn = document . getElementById ( 'btn-confirm-import-cookbook' ) ;
if ( btn ) {
btn . disabled = checkedBoxes . length === 0 ;
btn . textContent = ` Ausgewählte übernehmen ( ${ checkedBoxes . length } ) ` ;
}
}
async function confirmImportCookbookIngredients ( ) {
const cbs = Array . from ( document . querySelectorAll ( '.cookbook-ing-checkbox:checked' ) ) ;
if ( cbs . length === 0 ) return ;
const confirmBtn = document . getElementById ( 'btn-confirm-import-cookbook' ) ;
if ( confirmBtn ) {
confirmBtn . disabled = true ;
confirmBtn . innerHTML = '<span class="shop-spinner" style="margin-right: 6px;"></span> <span>Wird übernommen...</span>' ;
}
const itemsToAdd = cbs . map ( cb => ( {
item _name : cb . dataset . name ,
amount : cb . dataset . amount || '' ,
unit : cb . dataset . unit || '' ,
source : cb . dataset . source || '' ,
category : null ,
note : ''
} ) ) ;
try {
const res = await fetch ( '/api/shopping-list/bulk' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' } ,
body : JSON . stringify ( { items : itemsToAdd } )
} ) ;
if ( res . ok ) {
const d = await res . json ( ) ;
if ( d . items && Array . isArray ( d . items ) ) {
allItems = d . items ;
} else {
await refreshDataFromServer ( ) ;
}
saveLocalCache ( allItems ) ;
closeModal ( 'modal-import-cookbook' ) ;
renderCategoryFilterChips ( ) ;
renderShoppingList ( ) ;
showToast ( ` ✓ ${ itemsToAdd . length } Zutaten übernommen (Mengen zusammengeführt) ` ) ;
} else {
showToast ( 'Fehler beim Übernehmen' ) ;
if ( confirmBtn ) {
confirmBtn . disabled = false ;
confirmBtn . textContent = 'Ausgewählte übernehmen' ;
}
}
} catch ( err ) {
console . error ( 'Fehler beim Importieren aus dem Kochbuch:' , err ) ;
showToast ( 'Fehler beim Übernehmen' ) ;
if ( confirmBtn ) {
confirmBtn . disabled = false ;
confirmBtn . textContent = 'Ausgewählte übernehmen' ;
}
}
}
// ==========================================
// ==========================================
// MODAL: SUPERMARKT-BEREICHE VERWALTUNG
@@ -2108,6 +2536,16 @@
renderCategoryFilterChips ( ) ;
renderShoppingList ( ) ;
initWebSocket ( ) ;
// Prüfen, ob ein Rezept per URL-Parameter übergeben wurde
const urlParams = new URLSearchParams ( window . location . search ) ;
const impRecipeId = urlParams . get ( 'import_recipe_id' ) ;
if ( impRecipeId ) {
window . history . replaceState ( { } , document . title , window . location . pathname ) ;
setTimeout ( ( ) => {
openCookbookImportModal ( parseInt ( impRecipeId , 10 ) ) ;
} , 120 ) ;
}
} ) ;
< / script >
< / body >