sektaolga-fodmapcommits2767f39
Analyzing diff...
index.html+121 −15
index a8ce1e3..152781b 100644 @@ -4,7 +4,7 @@ <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> <title>FODMAP-конструктор приёма пищи</title> <style> -:root{--bg:#f6f3ed;--card:#fff;--ink:#282723;--muted:#77736a;--green:#5c9b6a;--amber:#d8a23d;--red:#cf615a;--line:#e6e0d7;--accent:#7569a8}*{box-sizing:border-box}body{margin:0;font-family:Inter,system-ui,-apple-system,Segoe UI,Arial,sans-serif;background:var(--bg);color:var(--ink)}.wrap{max-width:1180px;margin:auto;padding:28px}.top{display:flex;justify-content:space-between;gap:18px;align-items:flex-end;margin-bottom:20px}h1{font-size:30px;margin:0 0 6px}.sub{color:var(--muted);max-width:760px}.grid{display:grid;grid-template-columns:1.35fr .65fr;gap:18px}.card{background:var(--card);border:1px solid var(--line);border-radius:20px;padding:20px;box-shadow:0 8px 30px #00000008}h2{font-size:18px;margin:0 0 14px}.add{display:grid;grid-template-columns:1fr 110px 105px 48px;gap:10px;margin-bottom:16px}select,input,textarea,button{font:inherit;border:1px solid var(--line);border-radius:12px;padding:11px 12px;background:white;color:var(--ink)}button{cursor:pointer;font-weight:650}.primary{background:var(--ink);color:white;border-color:var(--ink)}.meal-row{display:grid;grid-template-columns:1fr 105px 88px 38px;gap:9px;align-items:center;padding:10px 0;border-top:1px solid var(--line)}.tag{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:700}.dot{width:10px;height:10px;border-radius:50%}.g{background:var(--green)}.a{background:var(--amber)}.r{background:var(--red)}.u{background:#9a958c}.remove{border:0;background:transparent;font-size:20px;padding:4px}.meterbox{display:flex;gap:22px;align-items:center}.glass{width:105px;height:220px;border:4px solid #b9b3a9;border-top:0;border-radius:0 0 28px 28px;position:relative;overflow:hidden;background:#faf9f6}.fill{position:absolute;left:0;right:0;bottom:0;height:0%;background:var(--green);transition:.3s}.critical{position:absolute;left:0;right:0;bottom:75%;border-top:2px dashed #7e786e;z-index:2}.critical span{position:absolute;right:4px;top:-21px;font-size:10px;color:#6e685f}.score{font-size:42px;font-weight:800}.status{font-weight:750;margin-top:5px}.small{font-size:12px;color:var(--muted);line-height:1.45}.groups{display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:15px}.group{background:#f7f5f1;padding:8px 10px;border-radius:10px;font-size:12px}.advice{margin-top:14px;padding:13px;border-radius:14px;background:#f6f4fb;line-height:1.5}.advice b{display:block;margin-bottom:4px}.adapt{display:grid;gap:8px;margin-top:10px}.adapt button{text-align:left;background:white}.journal{margin-top:18px}.symptoms{display:grid;grid-template-columns:1fr 1fr;gap:10px}.history{margin-top:12px}.entry{padding:14px 0;border-top:1px solid var(--line);font-size:13px}.daychart{margin:16px 0;padding:14px;border:1px solid var(--line);border-radius:14px;background:#faf9f6}.chart-controls{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:12px}.chart-body{display:flex;gap:20px;flex-wrap:wrap;align-items:flex-start}.chart-legend{display:grid;gap:5px;font-size:12px;min-width:190px}.chart-legend div{display:flex;align-items:center;gap:7px;cursor:pointer;padding:3px 5px;border-radius:8px}.chart-legend div:hover{background:#efece5}.chart-legend i{width:11px;height:11px;border-radius:3px;display:inline-block;flex:none}.chart-bars{display:grid;gap:6px;font-size:12px;min-width:200px;flex:1}.bar-track{background:#ece8e0;border-radius:6px;height:12px;overflow:hidden}.bar-fill{height:100%;border-radius:6px}.chart-detail{margin-top:10px;line-height:1.5}.chart-slice{cursor:pointer}.chart-slice:hover{opacity:.82} +:root{--bg:#f6f3ed;--card:#fff;--ink:#282723;--muted:#77736a;--green:#5c9b6a;--amber:#d8a23d;--red:#cf615a;--line:#e6e0d7;--accent:#7569a8}*{box-sizing:border-box}body{margin:0;font-family:Inter,system-ui,-apple-system,Segoe UI,Arial,sans-serif;background:var(--bg);color:var(--ink)}.wrap{max-width:1180px;margin:auto;padding:28px}.top{display:flex;justify-content:space-between;gap:18px;align-items:flex-end;margin-bottom:20px}h1{font-size:30px;margin:0 0 6px}.sub{color:var(--muted);max-width:760px}.grid{display:grid;grid-template-columns:1.35fr .65fr;gap:18px}.card{background:var(--card);border:1px solid var(--line);border-radius:20px;padding:20px;box-shadow:0 8px 30px #00000008}h2{font-size:18px;margin:0 0 14px}.add{display:grid;grid-template-columns:1fr 110px 105px 48px;gap:10px;margin-bottom:16px}select,input,textarea,button{font:inherit;border:1px solid var(--line);border-radius:12px;padding:11px 12px;background:white;color:var(--ink)}button{cursor:pointer;font-weight:650}.primary{background:var(--ink);color:white;border-color:var(--ink)}.meal-row{display:grid;grid-template-columns:1fr 105px 88px 38px;gap:9px;align-items:center;padding:10px 0;border-top:1px solid var(--line)}.tag{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:700}.dot{width:10px;height:10px;border-radius:50%}.g{background:var(--green)}.a{background:var(--amber)}.r{background:var(--red)}.u{background:#9a958c}.remove{border:0;background:transparent;font-size:20px;padding:4px}.meterbox{display:flex;gap:22px;align-items:center}.glass{width:105px;height:220px;border:4px solid #b9b3a9;border-top:0;border-radius:0 0 28px 28px;position:relative;overflow:hidden;background:#faf9f6}.fill{position:absolute;left:0;right:0;bottom:0;height:0%;background:var(--green);transition:.3s}.critical{position:absolute;left:0;right:0;bottom:75%;border-top:2px dashed #7e786e;z-index:2}.critical span{position:absolute;right:4px;top:-21px;font-size:10px;color:#6e685f}.score{font-size:42px;font-weight:800}.status{font-weight:750;margin-top:5px}.small{font-size:12px;color:var(--muted);line-height:1.45}.groups{display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:15px}.group{background:#f7f5f1;padding:8px 10px;border-radius:10px;font-size:12px}.advice{margin-top:14px;padding:13px;border-radius:14px;background:#f6f4fb;line-height:1.5}.advice b{display:block;margin-bottom:4px}.adapt{display:grid;gap:8px;margin-top:10px}.adapt button{text-align:left;background:white}.journal{margin-top:18px}.symptoms{display:grid;grid-template-columns:1fr 1fr;gap:10px}.history{margin-top:12px}.entry{padding:14px 0;border-top:1px solid var(--line);font-size:13px}.typeahead{position:relative}.typeahead input{width:100%}.suggest{display:none;position:absolute;z-index:30;left:0;right:0;top:100%;margin-top:4px;background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 30px #0000001f;max-height:260px;overflow:auto}.suggest.open{display:block}.suggest div{padding:9px 12px;cursor:pointer;font-size:14px}.suggest div.sel,.suggest div:hover{background:#f2efe8}.suggest .hi{color:var(--muted);font-size:12px;margin-left:6px}.suggest .none{color:var(--muted);cursor:default}.daychart{margin:16px 0;padding:14px;border:1px solid var(--line);border-radius:14px;background:#faf9f6}.chart-controls{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:12px}.chart-body{display:flex;gap:20px;flex-wrap:wrap;align-items:flex-start}.chart-legend{display:grid;gap:5px;font-size:12px;min-width:190px}.chart-legend div{display:flex;align-items:center;gap:7px;cursor:pointer;padding:3px 5px;border-radius:8px}.chart-legend div:hover{background:#efece5}.chart-legend i{width:11px;height:11px;border-radius:3px;display:inline-block;flex:none}.chart-bars{display:grid;gap:6px;font-size:12px;min-width:200px;flex:1}.bar-track{background:#ece8e0;border-radius:6px;height:12px;overflow:hidden}.bar-fill{height:100%;border-radius:6px}.chart-detail{margin-top:10px;line-height:1.5}.chart-slice{cursor:pointer}.chart-slice:hover{opacity:.82} .entry-head{display:flex;justify-content:space-between;gap:10px;align-items:flex-start} .entry-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:9px} .entry-actions button{padding:7px 9px;font-size:12px} @@ -29,7 +29,7 @@ <div class="warn"><b>Важно:</b> это ориентировочный инструмент, а не медицинская диагностика. Публичные источники обычно дают пороговые порции, а не лабораторные граммы каждого FODMAP. Поэтому общий «стакан» — модель риска stacking, а не измерение реальных граммов FODMAP.</div> <div class="grid"><main> <div class="card"><h2>🍽 Приём пищи</h2> -<div class="add"><select id="food"></select><input id="grams" type="number" min="1" value="100" placeholder="г"><button class="primary" onclick="addFood()">Добавить</button><button title="Добавить новый продукт" onclick="openFoodModal()">+</button></div> +<div class="add"><div class="typeahead"><input id="food" type="text" autocomplete="off" placeholder="Начните набирать продукт" oninput="onFoodInput()" onfocus="onFoodInput()" onkeydown="onFoodKey(event)" aria-label="Продукт"><div id="foodSuggest" class="suggest" role="listbox"></div></div><input id="grams" type="number" min="1" value="100" placeholder="г"><button class="primary" onclick="addFood()">Добавить</button><button title="Добавить новый продукт" onclick="openFoodModal()">+</button></div> <div class="symptoms" style="margin-bottom:12px"> <div><div class="small">Дата приёма пищи</div><input id="mealDate" type="date" style="width:100%"></div> <div><div class="small">Время приёма пищи</div><input id="mealTime" type="time" style="width:100%"></div> @@ -128,7 +128,90 @@ const foods=[ {name:'Куриная печень',g:9999,a:9999,groups:[],load:0,n:'без лука/чеснока и высоко-FODMAP соусов'}, {name:'Сушёная хурма',g:10,a:20,groups:['фруктоза','полиолы'],load:.48,n:'нет надёжного публичного порога; консервативная оценка сухофрукта',uncertain:true}, {name:'Хурма',g:60,a:100,groups:['фруктоза','полиолы'],load:.3,n:'Sharon/persimmon: около 60 г low-порция'},{name:'Болгарский перец',g:43,a:75,groups:['фруктоза','полиолы'],load:.18,n:'консервативно для красного; зелёный имеет другой профиль'}, -{name:'Тыква запечённая',g:63,a:75,groups:['фруктоза','фруктаны'],load:.22,n:'консервативно как butternut; kabocha переносится в больших порциях'} +{name:'Тыква запечённая',g:63,a:75,groups:['фруктоза','фруктаны'],load:.22,n:'консервативно как butternut; kabocha переносится в больших порциях'}, +{name:'Персики',g:0,a:0,groups:['фруктоза','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Манго',g:0,a:0,groups:['фруктоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Консервированные фрукты в сиропе/соку',g:0,a:0,groups:['фруктоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Мёд',g:0,a:0,groups:['фруктоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сухофрукты (общее)',g:0,a:0,groups:['фруктоза','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Фруктовый сок',g:0,a:0,groups:['фруктоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Концентрированные фруктовые соусы',g:0,a:0,groups:['фруктоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Козье молоко',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Мороженое молочное',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Йогурт обычный',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сливки',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сыр мягкий',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сыр сливочный',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Моцарелла',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Адыгейский сыр',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сулугуни',g:0,a:0,groups:['лактоза'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Артишок',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Спаржа',g:0,a:0,groups:['фруктоза','фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Брокколи',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Брюссельская капуста',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Белокочанная капуста',g:0,a:0,groups:['фруктаны','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Укроп',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Бамия (окра)',g:0,a:0,groups:['GOS','фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Чеснок',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Лук репчатый',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Лук-порей',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Лук-шалот',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Рожь',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Пшеница',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Хлеб пшеничный',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Печенье пшеничное',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Макароны пшеничные',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Фасоль запечённая',g:0,a:0,groups:['GOS','фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Чечевица',g:0,a:0,groups:['GOS'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Красная фасоль',g:0,a:0,groups:['GOS','фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Горох',g:0,a:0,groups:['GOS','фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Рамбутан',g:0,a:0,groups:['фруктаны','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Персик белый',g:0,a:0,groups:['полиолы','фруктаны'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Абрикосы',g:0,a:0,groups:['полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Авокадо',g:0,a:0,groups:['сорбитол','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Вишня',g:0,a:0,groups:['фруктоза','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Личи',g:0,a:0,groups:['фруктоза','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Чернослив',g:0,a:0,groups:['сорбитол','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Горох стручковый',g:0,a:0,groups:['полиолы','GOS'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Грибы',g:0,a:0,groups:['маннитол','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Изомальтит',g:0,a:0,groups:['полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Мальтит',g:0,a:0,groups:['полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Маннит',g:0,a:0,groups:['маннитол','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сорбит',g:0,a:0,groups:['сорбитол','полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Ксилит',g:0,a:0,groups:['полиолы'],load:0,unknown:true,hint:'high',uncertain:true,n:'высокое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Грейпфрут',g:0,a:0,groups:['фруктоза'],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Канталупа',g:0,a:0,groups:['фруктаны'],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Карамбола',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Дуриан',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Киви',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Лимон',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Лайм',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Маракуйя',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Папайя',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Танжело',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Патока светлая',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Кленовый сироп',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Глюкоза',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сахар (сахароза)',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Молоко безлактозное',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Молоко соевое',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Йогурт безлактозный',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сыр твёрдый',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Шербет',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Замороженный сок',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Китайская капуста',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Сельдерей',g:0,a:0,groups:['полиолы'],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Зелёный лук (перья)',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Кукуруза',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Баклажаны',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Зелёная фасоль',g:0,a:0,groups:['сорбитол'],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Салат листовой',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Пастернак',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Мангольд',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Побеги бамбука',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Хлеб безглютеновый',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Хлеб из полбы',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'}, +{name:'Крупяная продукция безглютеновая',g:0,a:0,groups:[],load:0,unknown:true,hint:'low',uncertain:true,n:'низкое содержание FODMAP по таблице — таблица «Структура FODMAP-диеты» (клинический обзор); точный порог в граммах не указан'} ]; @@ -170,14 +253,33 @@ function downloadJournalBackup(){ let backup={metadata:{format:'olga-fodmap-journal-backup',version:1,exportedAt:new Date().toISOString()},entries:getEntries(),customFoods:JSON.parse(localStorage.getItem('fodmapCustomFoods')||'[]')},blob=new Blob([JSON.stringify(backup,null,2)],{type:'application/json;charset=utf-8'}),url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=`olga-fodmap-backup-${localDateString()}.json`;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),1000);exportMessage('Резервная копия скачана.') } -const foodSel=document.getElementById('food'); -function populateFoods(){ - let current=foodSel.value;foodSel.innerHTML=''; - foods.map((f,i)=>({f,i})).sort((a,b)=>a.f.name.localeCompare(b.f.name,'ru')).forEach(({f,i})=>{ - let o=document.createElement('option');o.value=i;o.textContent=f.name+(f.custom?' • свой':'');foodSel.appendChild(o) - }); - if([...foodSel.options].some(o=>o.value===current))foodSel.value=current; +const foodSel=document.getElementById('food');let selectedFoodIndex=-1,suggestIndex=-1,suggestList=[]; +function populateFoods(){if(selectedFoodIndex>=foods.length)selectedFoodIndex=-1} +function foodHint(f){return f.hint==='high'?'высокий FODMAP':f.hint==='low'?'низкий FODMAP':f.unknown?'нет данных':f.load===0?'FODMAP-free':'green ≤ '+f.g+' г'} +function matchFoods(q){ + q=q.trim().toLocaleLowerCase('ru'); + let list=foods.map((f,i)=>({f,i})); + if(q)list=list.filter(x=>x.f.name.toLocaleLowerCase('ru').includes(q)) + .sort((a,b)=>a.f.name.toLocaleLowerCase('ru').indexOf(q)-b.f.name.toLocaleLowerCase('ru').indexOf(q)||a.f.name.localeCompare(b.f.name,'ru')); + else list.sort((a,b)=>a.f.name.localeCompare(b.f.name,'ru')); + return list.slice(0,12); +} +function onFoodInput(){ + let box=document.getElementById('foodSuggest');suggestList=matchFoods(foodSel.value);suggestIndex=-1; + if(!suggestList.length){box.innerHTML='<div class="none">Ничего не найдено. Кнопка + добавит свой продукт.</div>';box.classList.add('open');return} + box.innerHTML=suggestList.map((x,k)=>`<div role="option" data-k="${k}" onmousedown="pickFood(${x.i})">${esc(x.f.name)}${x.f.custom?' • свой':''}<span class="hi">${esc(foodHint(x.f))}</span></div>`).join(''); + box.classList.add('open'); +} +function highlightSuggest(){[...document.querySelectorAll('#foodSuggest div')].forEach((el,k)=>el.classList.toggle('sel',k===suggestIndex))} +function onFoodKey(e){ + if(e.key==='ArrowDown'){e.preventDefault();suggestIndex=Math.min(suggestIndex+1,suggestList.length-1);highlightSuggest()} + else if(e.key==='ArrowUp'){e.preventDefault();suggestIndex=Math.max(suggestIndex-1,0);highlightSuggest()} + else if(e.key==='Enter'){e.preventDefault();if(suggestList.length)pickFood(suggestList[suggestIndex>=0?suggestIndex:0].i)} + else if(e.key==='Escape')closeSuggest(); } +function closeSuggest(){document.getElementById('foodSuggest').classList.remove('open')} +function pickFood(i){selectedFoodIndex=i;foodSel.value=foods[i].name;closeSuggest();document.getElementById('grams').focus()} +document.addEventListener('click',e=>{if(!e.target.closest('.typeahead'))closeSuggest()}); populateFoods(); document.getElementById('mealDate').value=localDateString(); document.getElementById('mealTime').value=localTimeString(); @@ -186,14 +288,18 @@ const severityText={0:'симптомов нет',1:'едва заметные', document.getElementById('severity').oninput=e=>document.getElementById('sevLabel').textContent=e.target.value+' / 10 — '+severityText[e.target.value]; function level(f,g){ - if(f.unknown)return 'u'; if(f.load===0)return 'g'; if(f.red&&g>=f.red)return'r'; + if(f.unknown)return f.hint==='high'?'r':f.hint==='low'?'g':'u'; if(f.load===0)return 'g'; if(f.red&&g>=f.red)return'r'; if(g<=f.g)return'g'; if(g<=f.a)return'a'; return'r' } function contribution(f,g){ if(f.unknown||f.load===0)return 0;let ratio=g/f.g; if(ratio<=1)return f.load*ratio;if(ratio<=2)return f.load*(1+(ratio-1)*1.3);return f.load*(2.3+(ratio-2)*1.7) } -function addFood(){let i=+foodSel.value,g=+document.getElementById('grams').value;if(!g)return;let ex=meal.find(x=>x.i===i);if(ex)ex.g+=g;else meal.push({i,g});render()} +function addFood(){let g=+document.getElementById('grams').value,i=selectedFoodIndex; + if(i<0||!foods[i]||foods[i].name!==foodSel.value){let m=foods.findIndex(f=>f.name.toLocaleLowerCase('ru')===foodSel.value.trim().toLocaleLowerCase('ru')); + if(m<0){let s=matchFoods(foodSel.value);if(foodSel.value.trim()&&s.length===1)m=s[0].i} + if(m<0)return alert('Выберите продукт из подсказок или добавьте свой кнопкой +.');i=m;selectedFoodIndex=i;foodSel.value=foods[i].name} + if(!g)return;let ex=meal.find(x=>x.i===i);if(ex)ex.g+=g;else meal.push({i,g});render()} function removeFood(k){meal.splice(k,1);render()} function updateGram(k,v){meal[k].g=Math.max(0,+v||0);render()} @@ -255,8 +361,8 @@ function render(){ let el=document.getElementById('meal'); if(!meal.length)el.innerHTML='<div class="small">Пока ничего не добавлено.</div>'; else el.innerHTML=meal.map((x,k)=>{ - let f=foods[x.i],l=level(f,x.g),label=l==='g'?'GREEN':l==='a'?'AMBER':l==='r'?'RED':'НЕТ ДАННЫХ'; - let general=f.unknown?'Общий порог: нет данных':f.load===0?'Общий диапазон: FODMAP практически отсутствуют':`Общий: green ≤ ${f.g} г${f.a?`, amber ≤ ${f.a} г`:''}`; + let f=foods[x.i],l=level(f,x.g),label=f.hint==='high'?'ВЫСОКИЙ FODMAP':f.hint==='low'?'НИЗКИЙ FODMAP':l==='g'?'GREEN':l==='a'?'AMBER':l==='r'?'RED':'НЕТ ДАННЫХ'; + let general=f.hint?(f.hint==='high'?'Высокий FODMAP по таблице; порог в граммах не задан':'Низкий FODMAP по таблице; порог в граммах не задан'):f.unknown?'Общий порог: нет данных':f.load===0?'Общий диапазон: FODMAP практически отсутствуют':`Общий: green ≤ ${f.g} г${f.a?`, amber ≤ ${f.a} г`:''}`; return `<div class="meal-row"><div><b>${esc(f.name)}</b>${f.custom?'<span class="custom-badge">свой</span>':''}<div class="small">${esc(general)}${f.uncertain?' • оценочно':''}</div>${personalHtml(f,x.g)}</div><input type="number" value="${x.g}" onchange="updateGram(${k},this.value)"><span class="tag"><i class="dot ${l}"></i>${label}</span><button class="remove" onclick="removeFood(${k})">×</button></div>` }).join(''); document.getElementById('editingMeal').textContent=editingMealId?'редактируется сохранённый приём пищи':''; @@ -327,7 +433,7 @@ function saveCustomFood(){ let name=document.getElementById('newFoodName').value.trim();if(!name)return alert('Введите название продукта.');if(foods.some(f=>foodKey(f)===name.toLocaleLowerCase('ru')))return alert('Такой продукт уже есть в базе.'); let g=+document.getElementById('newFoodGreen').value,a=+document.getElementById('newFoodAmber').value,groups=document.getElementById('newFoodGroups').value.split(',').map(x=>x.trim()).filter(Boolean),note=document.getElementById('newFoodNote').value.trim(),f; if(g>0){if(!a||a<g)a=g*2;f={name,g,a,groups,load:.25,n:note||'пользовательский порог',uncertain:true,custom:true}}else f={name,g:0,a:0,groups,load:0,n:note||'FODMAP-пороги не заданы',unknown:true,custom:true}; - customFoods.push(f);localStorage.setItem('fodmapCustomFoods',JSON.stringify(customFoods));foods.push(f);populateFoods();foodSel.value=foods.length-1;closeFoodModal();['newFoodName','newFoodGreen','newFoodAmber','newFoodGroups','newFoodNote'].forEach(id=>document.getElementById(id).value='') + customFoods.push(f);localStorage.setItem('fodmapCustomFoods',JSON.stringify(customFoods));foods.push(f);populateFoods();selectedFoodIndex=foods.length-1;foodSel.value=f.name;closeFoodModal();['newFoodName','newFoodGreen','newFoodAmber','newFoodGroups','newFoodNote'].forEach(id=>document.getElementById(id).value='') } const CHART_GROUPS=['фруктоза','лактоза','фруктаны','GOS','сорбитол','маннитол','полиолы'];