UNPKG

swisseph-wasm

Version:

High-precision Swiss Ephemeris WebAssembly library for astronomical calculations in JavaScript

435 lines (388 loc) 19.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Swiss Ephemeris Playground</title> <script src="https://cdn.tailwindcss.com"></script> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> </head> <body class="bg-gray-50 min-h-screen"> <div id="app"> <!-- Header --> <header class="bg-white border-b border-gray-200"> <div class="container mx-auto px-4 py-6"> <div class="flex items-center justify-between"> <div> <h1 class="text-2xl font-bold text-gray-900"> Swiss Ephemeris Playground </h1> <p class="text-gray-600 mt-1">Write and execute custom astronomical programs</p> </div> <div class="text-right"> <div class="flex gap-3 mb-2"> <a href="api-explorer.html" class="text-sm text-blue-600 hover:text-blue-800">API Explorer</a> <a href="tests.html" class="text-sm text-blue-600 hover:text-blue-800">Tests</a> </div> <div class="text-sm" :class="status.color">{{ status.text }}</div> <div class="text-xs text-gray-400 mt-1">{{ version }}</div> </div> </div> </div> </header> <div class="container mx-auto px-4 py-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-4"> <!-- Left: Code Editor --> <div class="space-y-4"> <div class="bg-white rounded border border-gray-200 p-4"> <div class="flex items-center justify-between mb-3"> <h2 class="text-lg font-semibold text-gray-900">Code Editor</h2> <button @click="runCode" class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 transition-all font-medium text-sm"> Run Code </button> </div> <textarea v-model="code" class="w-full h-[500px] bg-gray-50 border border-gray-300 rounded p-3 font-mono text-sm text-gray-900 focus:outline-none focus:border-blue-500 resize-none" placeholder="Write your code here..."></textarea> <div class="mt-2 text-xs text-gray-500"> Tip: Use <code class="bg-gray-100 px-2 py-0.5 rounded">swisseph</code> to access all methods </div> </div> <!-- Quick Reference --> <div class="bg-white rounded border border-gray-200 p-3"> <h3 class="text-sm font-semibold text-gray-900 mb-2">Quick Reference</h3> <div class="space-y-1 text-xs"> <div><code class="text-blue-600">swisseph.julday(2000, 1, 1, 12.0)</code> - Convert date to JD</div> <div><code class="text-blue-600">swisseph.calc(jd, swisseph.SE_SUN, ...)</code> - Planet position</div> <div><code class="text-blue-600">swisseph.houses(jd, lat, lon, 'P')</code> - House cusps</div> </div> </div> </div> <!-- Right: Templates + Output + Saved --> <div class="space-y-4"> <!-- Program Templates - Compact Batch --> <div class="bg-white rounded border border-gray-200 p-3"> <h2 class="text-sm font-semibold text-gray-900 mb-2">Program Templates</h2> <div class="grid grid-cols-3 gap-2"> <button v-for="template in templates" :key="template.name" @click="loadTemplate(template)" class="p-2 bg-gray-50 rounded border border-gray-200 hover:border-blue-400 hover:bg-blue-50 transition-all text-center"> <div class="font-medium text-gray-900 text-xs">{{ template.name }}</div> </button> </div> </div> <!-- Output --> <div class="bg-white rounded border border-gray-200 p-4"> <div class="flex items-center justify-between mb-3"> <h2 class="text-lg font-semibold text-gray-900">Output</h2> <button @click="clearOutput" class="px-3 py-1 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 transition-all text-sm"> Clear </button> </div> <div class="bg-gray-50 rounded border border-gray-200 p-3 h-80 overflow-y-auto font-mono text-sm"> <div v-if="output.length === 0" class="text-gray-400 italic"> No output yet. Run some code to see results. </div> <div v-for="(item, idx) in output" :key="idx" class="mb-2"> <div v-if="item.type === 'log'" class="text-blue-600"> <span class="text-gray-400"></span> {{ item.value }} </div> <div v-else-if="item.type === 'result'" class="text-green-600"> <span class="text-gray-400"></span> {{ item.value }} </div> <div v-else-if="item.type === 'error'" class="text-red-600"> <span class="text-gray-400"></span> {{ item.value }} </div> <div v-else-if="item.type === 'object'" class="text-gray-700"> <pre class="whitespace-pre-wrap">{{ item.value }}</pre> </div> </div> </div> </div> <!-- Saved Programs --> <div class="bg-white rounded border border-gray-200 p-3"> <div class="flex items-center justify-between mb-2"> <h2 class="text-sm font-semibold text-gray-900">Saved Programs</h2> <button @click="saveProgram" class="px-2 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 transition-all text-xs"> + Save </button> </div> <div v-if="savedPrograms.length === 0" class="text-gray-400 text-xs italic"> No saved programs yet </div> <div v-else class="space-y-2 max-h-40 overflow-y-auto"> <div v-for="(program, idx) in savedPrograms" :key="idx" class="bg-gray-50 rounded border border-gray-200 p-2 hover:bg-gray-100 transition-all"> <div class="flex items-center justify-between"> <div class="flex-1 min-w-0"> <div class="font-medium text-gray-900 text-xs truncate">{{ program.name }}</div> <div class="text-xs text-gray-500">{{ program.date }}</div> </div> <div class="flex gap-1 ml-2"> <button @click="loadSavedProgram(program)" class="px-2 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200 text-xs"> Load </button> <button @click="deleteSavedProgram(idx)" class="px-2 py-1 bg-red-100 text-red-700 rounded hover:bg-red-200 text-xs"> Del </button> </div> </div> </div> </div> </div> </div> </div> </div> </div> <script type="module"> import SwissEph from '../src/swisseph.js'; const { createApp } = Vue; createApp({ data() { return { swisseph: null, status: { text: 'Loading...', color: 'text-yellow-600' }, version: '', code: '', output: [], savedPrograms: [], templates: [ { name: 'Birth Chart', description: 'Calculate natal chart positions', code: `// Birth Chart Calculator const birthDate = { year: 1990, month: 6, day: 15, hour: 14.5 }; const birthPlace = { lat: 40.7128, lon: -74.0060 }; // New York // Convert to Julian Day const jd = swisseph.julday(birthDate.year, birthDate.month, birthDate.day, birthDate.hour); console.log('Julian Day:', jd); // Calculate Sun position const sun = swisseph.calc(jd, swisseph.SE_SUN, swisseph.SEFLG_SWIEPH); console.log('Sun:', sun.longitude.toFixed(2) + '°'); // Calculate Moon position const moon = swisseph.calc(jd, swisseph.SE_MOON, swisseph.SEFLG_SWIEPH); console.log('Moon:', moon.longitude.toFixed(2) + '°'); // Calculate Ascendant const houses = swisseph.houses(jd, birthPlace.lat, birthPlace.lon, 'P'); console.log('Ascendant:', houses.ascmc[0].toFixed(2) + '°'); return { sun: sun.longitude, moon: moon.longitude, ascendant: houses.ascmc[0] };` }, { name: 'Planet Positions', description: 'Get all planet positions', code: `// All Planet Positions const jd = swisseph.julday(2024, 1, 1, 12.0); const planets = [ { id: swisseph.SE_SUN, name: 'Sun' }, { id: swisseph.SE_MOON, name: 'Moon' }, { id: swisseph.SE_MERCURY, name: 'Mercury' }, { id: swisseph.SE_VENUS, name: 'Venus' }, { id: swisseph.SE_MARS, name: 'Mars' }, { id: swisseph.SE_JUPITER, name: 'Jupiter' }, { id: swisseph.SE_SATURN, name: 'Saturn' } ]; const positions = {}; planets.forEach(planet => { const pos = swisseph.calc(jd, planet.id, swisseph.SEFLG_SWIEPH); positions[planet.name] = pos.longitude.toFixed(2) + '°'; console.log(planet.name + ':', positions[planet.name]); }); return positions;` }, { name: 'House System', description: 'Calculate house cusps', code: `// House System Calculator const jd = swisseph.julday(2024, 1, 1, 12.0); const lat = 51.5074; // London const lon = -0.1278; // Calculate houses (Placidus) const houses = swisseph.houses(jd, lat, lon, 'P'); console.log('House Cusps (Placidus):'); for (let i = 1; i <= 12; i++) { console.log('House ' + i + ':', houses.cusps[i].toFixed(2) + '°'); } console.log('\\nAngles:'); console.log('Ascendant:', houses.ascmc[0].toFixed(2) + '°'); console.log('MC:', houses.ascmc[1].toFixed(2) + '°'); return { cusps: houses.cusps.slice(1, 13), ascendant: houses.ascmc[0], mc: houses.ascmc[1] };` }, { name: 'Sidereal Time', description: 'Calculate sidereal time', code: `// Sidereal Time Calculator const now = new Date(); const jd = swisseph.julday( now.getFullYear(), now.getMonth() + 1, now.getDate(), now.getHours() + now.getMinutes() / 60 ); const sidtime = swisseph.sidtime(jd); const hours = Math.floor(sidtime); const minutes = Math.floor((sidtime - hours) * 60); const seconds = Math.floor(((sidtime - hours) * 60 - minutes) * 60); console.log('Current Date:', now.toLocaleDateString()); console.log('Julian Day:', jd.toFixed(4)); console.log('Sidereal Time:', hours + 'h ' + minutes + 'm ' + seconds + 's'); return { julianDay: jd, siderealTime: sidtime, formatted: hours + ':' + minutes + ':' + seconds };` }, { name: 'Ayanamsa', description: 'Calculate ayanamsa values', code: `// Ayanamsa Calculator const jd = swisseph.julday(2024, 1, 1, 0.0); // Set Lahiri ayanamsa swisseph.set_sid_mode(swisseph.SE_SIDM_LAHIRI, 0, 0); const ayanamsa = swisseph.get_ayanamsa(jd); const name = swisseph.get_ayanamsa_name(swisseph.SE_SIDM_LAHIRI); console.log('Ayanamsa System:', name); console.log('Ayanamsa Value:', ayanamsa.toFixed(6) + '°'); // Calculate tropical vs sidereal Sun const tropicalSun = swisseph.calc(jd, swisseph.SE_SUN, swisseph.SEFLG_SWIEPH); const siderealSun = tropicalSun.longitude - ayanamsa; console.log('\\nSun Position:'); console.log('Tropical:', tropicalSun.longitude.toFixed(2) + '°'); console.log('Sidereal:', siderealSun.toFixed(2) + '°'); return { ayanamsa: ayanamsa, tropical: tropicalSun.longitude, sidereal: siderealSun };` }, { name: 'Date Conversion', description: 'Convert between date formats', code: `// Date Conversion Examples const date = { year: 2024, month: 6, day: 21, hour: 12.0 }; // Convert to Julian Day const jd = swisseph.julday(date.year, date.month, date.day, date.hour); console.log('Julian Day:', jd); // Convert back to calendar const revDate = swisseph.revjul(jd, 1); console.log('Reverse:', revDate.year + '-' + revDate.month + '-' + revDate.day + ' ' + revDate.hour + 'h'); // Calculate day of week const dow = swisseph.day_of_week(jd); const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; console.log('Day of Week:', days[dow]); // Delta T const deltaT = swisseph.deltat(jd); console.log('Delta T:', (deltaT * 86400).toFixed(2) + ' seconds'); return { julianDay: jd, dayOfWeek: days[dow], deltaT: deltaT };` } ] }; }, async mounted() { await this.init(); this.loadSavedPrograms(); }, methods: { async init() { try { this.status = { text: 'Initializing...', color: 'text-yellow-600' }; this.swisseph = new SwissEph(); await this.swisseph.initSwissEph(); this.version = 'Swiss Ephemeris v' + this.swisseph.version(); this.status = { text: 'Ready', color: 'text-green-600' }; // Load first template by default this.loadTemplate(this.templates[0]); } catch (error) { this.status = { text: 'Error: ' + error.message, color: 'text-red-600' }; } }, loadTemplate(template) { this.code = template.code; this.output = []; }, runCode() { this.output = []; // Create a custom console const customConsole = { log: (...args) => { args.forEach(arg => { if (typeof arg === 'object') { this.output.push({ type: 'object', value: JSON.stringify(arg, null, 2) }); } else { this.output.push({ type: 'log', value: String(arg) }); } }); } }; try { // Create function with swisseph and console in scope const func = new Function('swisseph', 'console', this.code); const result = func(this.swisseph, customConsole); if (result !== undefined) { if (typeof result === 'object') { this.output.push({ type: 'object', value: JSON.stringify(result, null, 2) }); } else { this.output.push({ type: 'result', value: String(result) }); } } } catch (error) { this.output.push({ type: 'error', value: error.message }); } }, clearOutput() { this.output = []; }, saveProgram() { const name = prompt('Enter a name for this program:'); if (!name) return; const program = { name: name, code: this.code, date: new Date().toLocaleString() }; this.savedPrograms.push(program); localStorage.setItem('swisseph_programs', JSON.stringify(this.savedPrograms)); }, loadSavedProgram(program) { this.code = program.code; this.output = []; }, deleteSavedProgram(idx) { if (confirm('Delete this program?')) { this.savedPrograms.splice(idx, 1); localStorage.setItem('swisseph_programs', JSON.stringify(this.savedPrograms)); } }, loadSavedPrograms() { const saved = localStorage.getItem('swisseph_programs'); if (saved) { this.savedPrograms = JSON.parse(saved); } } } }).mount('#app'); </script> </body> </html>