Календарь++
[!infobar]
// Интеграция планетарных часов в астрологический календарь const CSV_PATH = "2628.csv"; const LAT = 48.4510; const LON = 34.9833; // === КЛАСС ПЛАНЕТАРНЫХ ЧАСОВ === class PlanetaryHours { constructor(lat, lon) { this.lat = lat; this.lon = lon; this.chaldeanOrder = ["Сатурн", "Юпитер", "Марс", "Солнце", "Венера", "Меркурий", "Луна"]; this.dayRulers = ["Солнце", "Луна", "Марс", "Меркурий", "Юпитер", "Венера", "Сатурн"]; this.planetSymbols = { "Солнце": "☉", "Луна": "☽", "Марс": "♂", "Меркурий": "☿", "Юпитер": "♃", "Венера": "♀", "Сатурн": "♄" }; this.tattvas = ["Акаша", "Вайю", "Теджас", "Апас", "Притхиви"]; } calculateSunTimes(date) { const dayOfYear = Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 86400000); const gamma = 2 * Math.PI / 365 * (dayOfYear - 1); const TZ = -(date.getTimezoneOffset() / 60); const eqtime = 229.18 * (0.000075 + 0.001868 * Math.cos(gamma) - 0.032077 * Math.sin(gamma)); const decl = 0.006918 - 0.399912 * Math.cos(gamma) + 0.070257 * Math.sin(gamma); const ha = Math.acos(Math.cos(90.833 * Math.PI / 180) / (Math.cos(this.lat * Math.PI / 180) * Math.cos(decl)) - Math.tan(this.lat * Math.PI / 180) * Math.tan(decl)); const sunrise = ((720 - 4 * (this.lon + ha * 180 / Math.PI) - eqtime) / 60) + TZ; const sunset = ((720 - 4 * (this.lon - ha * 180 / Math.PI) - eqtime) / 60) + TZ; return { sunrise, sunset }; } getAllHours(date) { const sunTimes = this.calculateSunTimes(date); const dayLength = sunTimes.sunset - sunTimes.sunrise; const nightLength = 24 - dayLength; const dayHourLen = dayLength / 12; const nightHourLen = nightLength / 12; const dayOfWeek = date.getDay(); const dayRuler = this.dayRulers[dayOfWeek]; const startIdx = this.chaldeanOrder.indexOf(dayRuler); const hours = []; for (let i = 0; i < 12; i++) { const planetIdx = (startIdx + i) % 7; const planet = this.chaldeanOrder[planetIdx]; const start = sunTimes.sunrise + (i * dayHourLen); const tattvaLen = dayHourLen / 5; const tattvas = []; for (let t = 0; t < 5; t++) { tattvas.push({ name: this.tattvas[t], num: t + 1, start: start + (t * tattvaLen) }); } hours.push({ num: i + 1, planet, symbol: this.planetSymbols[planet], type: "Дневной", start, duration: dayHourLen * 60, tattvas }); } for (let i = 0; i < 12; i++) { const planetIdx = (startIdx + 12 + i) % 7; const planet = this.chaldeanOrder[planetIdx]; const start = sunTimes.sunset + (i * nightHourLen); const tattvaLen = nightHourLen / 5; const tattvas = []; for (let t = 0; t < 5; t++) { tattvas.push({ name: this.tattvas[t], num: t + 1, start: start + (t * tattvaLen) }); } hours.push({ num: i + 1, planet, symbol: this.planetSymbols[planet], type: "Ночной", start, duration: nightHourLen * 60, tattvas }); } return { dayRuler, sunrise: sunTimes.sunrise, sunset: sunTimes.sunset, hours }; } getCurrent(date) { if (!date) date = new Date(); const all = this.getAllHours(date); const time = date.getHours() + date.getMinutes() / 60 + date.getSeconds() / 3600; for (const hour of all.hours) { if (time >= hour.start && time < hour.start + hour.duration / 60) { let tattva = null; for (const t of hour.tattvas) { if (time >= t.start && time < t.start + hour.duration / 60 / 5) { tattva = t; break; } } return { ...hour, tattva, dayRuler: all.dayRuler }; } } return { num: 1, planet: "Солнце", symbol: "☉", type: "Дневной", tattva: { name: "Акаша", num: 1 } }; } formatTime(dec) { const h = Math.floor(dec); const m = Math.floor((dec - h) * 60); return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`; } } const phCalc = new PlanetaryHours(LAT, LON); const currentHour = phCalc.getCurrent(new Date()); function getTheme() { return document.body.classList.contains('theme-dark') ? 'dark' : 'light'; } const theme = getTheme(); const colors = theme === 'dark' ? { bg: 'rgba(59, 66, 82, 0.2)', text: '<a href="https://www.google.com/search?q=%23eceff4" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#eceff4</a>', muted: '<a href="https://www.google.com/search?q=%236c7086" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#6c7086</a>', accent: '<a href="https://www.google.com/search?q=%2388c0d0" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#88c0d0</a>', border: 'rgba(76, 86, 106, 0.3)' } : { bg: 'rgba(245, 247, 250, 0.2)', text: '<a href="https://www.google.com/search?q=%232e3440" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#2e3440</a>', muted: '<a href="https://www.google.com/search?q=%234c566a" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#4c566a</a>', accent: '<a href="https://www.google.com/search?q=%235e81ac" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#5e81ac</a>', border: 'rgba(216, 222, 233, 0.3)' }; function getMoon() { const now = new Date(); let year = now.getFullYear(), month = now.getMonth() + 1, day = now.getDate(); if (month < 3) { year--; month += 12; } const jd = (365.25 * year + 30.6 * (month + 1) + day - 694039.09) / 29.5305882; let phase = Math.round((jd - Math.floor(jd)) * 8); if (phase >= 8) phase = 0; const phases = ["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"]; const zodiacs = ["♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"]; const zodiacIdx = Math.floor((((now - new Date(now.getFullYear(), 0, 0)) / 86400000 / 27.322) % 1) * 12); return { emoji: phases[phase], zodiac: zodiacs[zodiacIdx], phase }; } class AstroCalendar { constructor() { this.data = []; } init(csv) { const lines = csv.trim().split('\n'); const headers = lines[0].split(';'); this.data = lines.slice(1).map(line => { const vals = line.split(';'); const entry = {}; headers.forEach((h, i) => entry[h.trim()] = vals[i] ? vals[i].trim() : ''); return entry; }); } getByDate(d) { return this.data.find(e => e['Дата'] === d) || null; } } let csvContent = ''; try { csvContent = await app.vault.adapter.read(CSV_PATH); } catch (e) { dv.paragraph(`❌ ${CSV_PATH} не найден`); return; } const cal = new AstroCalendar(); cal.init(csvContent); const moon = getMoon(); let userNotes = {}; try { const saved = localStorage.getItem('astroNotes'); if (saved) userNotes = JSON.parse(saved); } catch (e) {} let currentDate = new Date(); const uid = 'ac-' + Date.now(); let expanded = false; const months = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']; const days = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']; function fmt(d) { return `${String(d.getDate()).padStart(2, '0')}.${String(d.getMonth() + 1).padStart(2, '0')}.${d.getFullYear()}`; } const styles = `<style> .ac-widget { background: ${colors.bg}; backdrop-filter: blur(10px); border: 1px solid ${colors.border}; border-radius: 12px; overflow: hidden; margin: 1em 0; } .ac-header { background: linear-gradient(135deg, ${colors.accent}66, ${colors.accent}33); backdrop-filter: blur(10px); padding: 12px 16px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; } .ac-header:hover { opacity: 0.9; } .ac-title { display: flex; align-items: center; gap: 12px; color: ${colors.text}; } .ac-widgets { display: flex; gap: 8px; align-items: center; } .ac-planet, .ac-moon { width: 50px; height: 50px; border-radius: 50%; background: ${colors.bg}; backdrop-filter: blur(10px); border: 2px solid ${colors.border}; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; } .ac-planet::before { content: ''; position: absolute; width: 100%; height: 100%; border-radius: 50%; background: conic-gradient(${colors.accent} 0deg ${(currentHour.num / 12) * 360}deg, transparent ${(currentHour.num / 12) * 360}deg); opacity: 0.3; } .ac-moon::after { content: ''; position: absolute; top: 0; height: 100%; width: ${Math.abs((moon.phase - 4) / 4) * 100}%; ${moon.phase < 4 ? 'left: 0;' : 'right: 0;'} background: rgba(0,0,0,0.4); border-radius: 50%; } .ac-emoji { font-size: 20px; z-index: 1; } .ac-sub { font-size: 11px; color: ${colors.accent}; font-weight: 700; z-index: 1; } .ac-arrow { color: ${colors.text}; transition: transform 0.3s; } .ac-arrow.exp { transform: rotate(180deg); } .ac-body { max-height: 0; overflow: hidden; transition: max-height 0.3s; } .ac-body.exp { max-height: 2000px; } .ac-nav { display: flex; justify-content: space-between; padding: 12px; background: ${colors.bg}; backdrop-filter: blur(10px); border-bottom: 1px solid ${colors.border}; } .ac-btn { background: ${colors.bg}; backdrop-filter: blur(10px); border: 1px solid ${colors.border}; width: 32px; height: 32px; border-radius: 6px; color: ${colors.text}; cursor: pointer; font-weight: bold; } .ac-btn:hover { opacity: 0.8; } .ac-month { font-weight: 700; color: ${colors.accent}; min-width: 150px; text-align: center; } .ac-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; background: ${colors.border}; border: 1px solid ${colors.border}; } .ac-day-h { background: ${colors.bg}; backdrop-filter: blur(10px); text-align: center; padding: 8px 4px; font-weight: 700; font-size: 11px; color: ${colors.muted}; } .ac-cell { background: ${colors.bg}; backdrop-filter: blur(10px); min-height: 70px; padding: 6px; cursor: pointer; position: relative; } .ac-cell:hover { opacity: 0.9; } .ac-cell.other { opacity: 0.4; } .ac-cell.today { background: ${colors.accent}33; border: 2px solid ${colors.accent}; } .ac-num { font-size: 13px; font-weight: 700; color: ${colors.text}; } .ac-info { font-size: 18px; margin: 4px 0; } .ac-note-dot { position: absolute; top: 4px; right: 4px; width: 6px; height: 6px; background: <a href="https://www.google.com/search?q=%2351cf66" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#51cf66</a>; border-radius: 50%; } .ac-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.75); z-index: 9999; display: flex; justify-content: center; align-items: center; } .ac-modal { background: ${colors.bg}; backdrop-filter: blur(20px); border-radius: 12px; padding: 24px; max-width: 700px; width: 95%; max-height: 90vh; overflow-y: auto; border: 1px solid ${colors.border}; position: relative; } .ac-close { position: absolute; top: 12px; right: 12px; background: ${colors.bg}; border: 1px solid ${colors.border}; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: 20px; } .ac-close:hover { background: <a href="https://www.google.com/search?q=%23ff6b6b" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#ff6b6b</a>; color: white; } .ac-modal-h { font-size: 24px; font-weight: 700; color: ${colors.accent}; margin-bottom: 16px; padding-right: 40px; } .ac-detail { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid ${colors.border}; font-size: 13px; } .ac-label { font-weight: 600; color: ${colors.muted}; } .ac-value { color: ${colors.text}; } .ac-hours-table { width: 100%; border-collapse: collapse; margin-top: 16px; font-size: 12px; } .ac-hours-table th { background: ${colors.bg}; color: ${colors.accent}; padding: 8px 4px; text-align: left; border-bottom: 2px solid ${colors.border}; } .ac-hours-table td { padding: 6px 4px; border-bottom: 1px solid ${colors.border}; } .ac-hours-table tr:hover { background: ${colors.bg}; } .ac-current-hour { background: ${colors.accent}22 !important; font-weight: 700; } .ac-textarea { width: 100%; min-height: 80px; background: ${colors.bg}; border: 1px solid ${colors.border}; border-radius: 6px; padding: 10px; color: ${colors.text}; } .ac-save { background: <a href="https://www.google.com/search?q=%2351cf66" target="_blank" rel="noopener noreferrer" class="hashtag-link" onclick="event.stopPropagation()">#51cf66</a>; border: none; padding: 10px 18px; border-radius: 6px; color: white; font-weight: 600; cursor: pointer; margin-top: 10px; } .ac-save:hover { opacity: 0.85; } .ac-tabs { display: flex; gap: 8px; margin-bottom: 16px; } .ac-tab { padding: 8px 16px; background: ${colors.bg}; border: 1px solid ${colors.border}; border-radius: 6px; cursor: pointer; font-size: 13px; } .ac-tab.active { background: ${colors.accent}; color: white; border-color: ${colors.accent}; } .ac-tab:hover { opacity: 0.8; } </style>`; function create() { const c = document.getElementById(uid); const today = new Date(); c.innerHTML = ` <div class="ac-widget"> <div class="ac-header" id="${uid}-t"> <div class="ac-title"> <div style="font-size:24px;">📅</div> <div> <div style="font-weight:700;font-size:15px;">Дневник</div> <div style="font-size:12px;opacity:0.9;">${today.getDate()} ${months[today.getMonth()]}</div> </div> </div> <div class="ac-widgets"> <div class="ac-planet"> <div class="ac-emoji">${currentHour.symbol}</div> <div class="ac-sub">${currentHour.num}</div> </div> <div class="ac-moon"> <div class="ac-emoji">${moon.emoji}</div> <div class="ac-sub">${moon.zodiac}</div> </div> </div> <div class="ac-arrow" id="${uid}-a">▼</div> </div> <div class="ac-body" id="${uid}-b"> <div class="ac-nav"> <div style="display:flex;gap:8px;align-items:center;"> <button class="ac-btn" id="${uid}-p">←</button> <div class="ac-month" id="${uid}-m"></div> <button class="ac-btn" id="${uid}-n">→</button> </div> <button class="ac-btn" id="${uid}-td" style="width:auto;padding:0 12px;">Сегодня</button> </div> <div class="ac-grid" id="${uid}-g"></div> </div> </div>`; document.getElementById(`${uid}-t`).onclick = toggle; document.getElementById(`${uid}-p`).onclick = () => { currentDate.setMonth(currentDate.getMonth() - 1); render(); }; document.getElementById(`${uid}-n`).onclick = () => { currentDate.setMonth(currentDate.getMonth() + 1); render(); }; document.getElementById(`${uid}-td`).onclick = () => { currentDate = new Date(); render(); }; } function toggle() { expanded = !expanded; const b = document.getElementById(`${uid}-b`); const a = document.getElementById(`${uid}-a`); if (expanded) { b.classList.add('exp'); a.classList.add('exp'); render(); } else { b.classList.remove('exp'); a.classList.remove('exp'); } } function render() { const y = currentDate.getFullYear(); const m = currentDate.getMonth(); document.getElementById(`${uid}-m`).textContent = `${months[m]} ${y}`; const g = document.getElementById(`${uid}-g`); g.innerHTML = ''; days.forEach(d => { const h = document.createElement('div'); h.className = 'ac-day-h'; h.textContent = d; g.appendChild(h); }); const first = new Date(y, m, 1); const last = new Date(y, m + 1, 0); let dow = first.getDay(); dow = dow === 0 ? 6 : dow - 1; const prevLast = new Date(y, m, 0); for (let i = dow - 1; i >= 0; i--) { const day = prevLast.getDate() - i; g.appendChild(makeCell(day, new Date(y, m - 1, day), true)); } for (let day = 1; day <= last.getDate(); day++) { g.appendChild(makeCell(day, new Date(y, m, day), false)); } const total = dow + last.getDate(); const remain = total <= 35 ? 35 - total : 42 - total; for (let day = 1; day <= remain; day++) { g.appendChild(makeCell(day, new Date(y, m + 1, day), true)); } } function makeCell(day, date, other) { const dateStr = fmt(date); const info = cal.getByDate(dateStr); const today = fmt(new Date()); const isToday = dateStr === today && !other; const cell = document.createElement('div'); cell.className = 'ac-cell'; if (other) cell.classList.add('other'); if (isToday) cell.classList.add('today'); const num = document.createElement('div'); num.className = 'ac-num'; num.textContent = day; cell.appendChild(num); if (info && !other && info['Имя фазы']) { const phases = { 'Новолуние': '🌑', 'Первая четверть луны': '🌓', 'Полнолуние': '🌕', 'Последняя четверть луны': '🌗' }; const i = document.createElement('div'); i.className = 'ac-info'; i.textContent = phases[info['Имя фазы']] || ''; cell.appendChild(i); } if (userNotes[dateStr]) { const dot = document.createElement('div'); dot.className = 'ac-note-dot'; cell.appendChild(dot); } if (!other) cell.onclick = () => openModal(dateStr, date); return cell; } function openModal(dateStr, date) { const [d, m, y] = dateStr.split('.'); const info = cal.getByDate(dateStr); const hours = phCalc.getAllHours(date); const now = new Date(); const isToday = dateStr === fmt(now); const overlay = document.createElement('div'); overlay.className = 'ac-modal-overlay'; let html = ''; if (info) { const items = [ { l: '☀️ Восход', v: info['Восход солнца'] }, { l: '🌅 Заход', v: info['Заход солнца'] }, { l: '☀️ Солнце', v: info['Солнце в знаке'] }, { l: '🌙 Луна', v: info['Луна в знаке'] }, { l: '🌙 Лунные сутки', v: info['Лунные сутки'] }, { l: '🌙 Фаза', v: info['Имя фазы'] } ]; items.forEach(i => { if (i.v) html += `<div class="ac-detail"><span class="ac-label">${i.l}</span><span class="ac-value">${i.v}</span></div>`; }); } let hoursHTML = '<table class="ac-hours-table"><thead><tr><th>Время</th><th>Час</th><th>Тип</th><th>Планета</th><th>Таттва</th></tr></thead><tbody>'; hours.hours.forEach(h => { const isCurrent = isToday && now.getHours() + now.getMinutes() / 60 >= h.start && now.getHours() + now.getMinutes() / 60 < h.start + h.duration / 60; const rowClass = isCurrent ? 'ac-current-hour' : ''; hoursHTML += `<tr class="${rowClass}"> <td>${phCalc.formatTime(h.start)}</td> <td>${h.num}</td> <td>${h.type}</td> <td>${h.symbol} ${h.planet}</td> <td>${h.tattvas.map(t => t.name).join(', ')}</td> </tr>`; }); hoursHTML += '</tbody></table>'; overlay.innerHTML = ` <div class="ac-modal"> <button class="ac-close">×</button> <div class="ac-modal-h">${parseInt(d)} ${months[parseInt(m) - 1]} ${y}</div> <div class="ac-tabs"> <div class="ac-tab active" id="tab-info">📊 Информация</div> <div class="ac-tab" id="tab-hours">⏰ Планетарные часы</div> <div class="ac-tab" id="tab-notes">📝 Заметки</div> </div> <div id="content-info"> <div style="background:${colors.bg};padding:14px;border-radius:8px;">${html || '<p style="text-align:center;color:' + colors.muted + ';">Нет данных</p>'}</div> </div> <div id="content-hours" style="display:none;"> <div style="margin-bottom:12px;color:${colors.accent};font-weight:600;">Управитель дня: ${hours.dayRuler} ${phCalc.planetSymbols[hours.dayRuler]}</div> ${hoursHTML} </div> <div id="content-notes" style="display:none;"> <textarea class="ac-textarea" id="n-${dateStr}" placeholder="Ваши заметки...">${userNotes[dateStr] || ''}</textarea> <button class="ac-save" id="s-${dateStr}">💾 Сохранить</button> </div> </div>`; document.body.appendChild(overlay); overlay.querySelector('.ac-close').onclick = () => overlay.remove(); overlay.onclick = (e) => { if (e.target === overlay) overlay.remove(); }; const tabs = ['info', 'hours', 'notes']; tabs.forEach(tab => { document.getElementById(`tab-${tab}`).onclick = () => { tabs.forEach(t => { document.getElementById(`tab-${t}`).classList.remove('active'); document.getElementById(`content-${t}`).style.display = 'none'; }); document.getElementById(`tab-${tab}`).classList.add('active'); document.getElementById(`content-${tab}`).style.display = 'block'; }; }); document.getElementById(`s-${dateStr}`).onclick = () => { const note = document.getElementById(`n-${dateStr}`).value.trim(); if (note) userNotes[dateStr] = note; else delete userNotes[dateStr]; localStorage.setItem('astroNotes', JSON.stringify(userNotes)); render(); overlay.remove(); }; } dv.container.innerHTML = styles + `<div id="${uid}"></div>`; setTimeout(create, 100);Секс не состоялся…..и теперь непонятно когда будет …..
Ждем вечера