Open Consciousness Courses
Menu

Theme

Choose your preferred appearance for the site.

Sign in

Access your saved courses and progress.

Create account

Start tracking learning goals and saved courses.

Tracked Courses

Manage favorites, move items to cart, and export your tracked list as JSON.

0 tracked

Export

Copy your tracked courses JSON.

Tip: click “Copy” to place JSON into clipboard.

Clear tracked courses?

This will remove all tracked courses from this browser.

Done

Action completed.

' : ''; }); } function initCommonDialogs(){ document.querySelectorAll('dialog').forEach(d=>{ d.addEventListener('click', e=>{ if(e.target===d) d.close(); }); }); document.addEventListener('keydown', e=>{ if(e.key==='Escape'){ const open=[...document.querySelectorAll('dialog')].filter(d=>d.open); if(open.length) open[open.length-1].close(); } }); } const state = { DATA: [], subset: [], query: '', sort: 'recent' }; const searchEl=document.getElementById('search'); const sortEl=document.getElementById('sort'); const exportBtn=document.getElementById('export'); const clearBtn=document.getElementById('clear-all'); function normalizeId(v){ if(v===null || v===undefined) return ''; return String(v); } function ensureMetaForFavIds(ids){ const meta=getFavMeta(); let changed=false; const now=Date.now(); ids.forEach(id=>{ if(!meta[id]){ meta[id]={trackedAt: now}; changed=true; } }); if(changed) setFavMeta(meta); return meta; } function buildSubset(){ const favIdsRaw=getFav().map(normalizeId).filter(Boolean); const favSet=new Set(favIdsRaw); const meta=ensureMetaForFavIds(favSet); let items=state.DATA.filter(x=>favSet.has(normalizeId(x.id))); const q=(state.query||'').trim().toLowerCase(); if(q){ items = items.filter(it=>{ const t = (it.title||'').toLowerCase(); const d = (it.shortDescription||'').toLowerCase(); const c = (it.category||'').toLowerCase(); const l = (it.level||'').toLowerCase(); return t.includes(q) || d.includes(q) || c.includes(q) || l.includes(q); }); } const sort=state.sort || 'recent'; items.sort((a,b)=>{ const aid=normalizeId(a.id), bid=normalizeId(b.id); const am=meta[aid]?.trackedAt || 0; const bm=meta[bid]?.trackedAt || 0; const at=(a.title||'').toLowerCase(); const bt=(b.title||'').toLowerCase(); const ap=Number(a.price||0); const bp=Number(b.price||0); const ar=Number(a.rating||0); const br=Number(b.rating||0); if(sort==='recent') return bm-am; if(sort==='title') return at.localeCompare(bt); if(sort==='price_asc') return ap-bp; if(sort==='price_desc') return bp-ap; if(sort==='rating_desc') return br-ar; return bm-am; }); state.subset=items; } function money(v){ const n=Number(v); if(Number.isFinite(n)) return '$'+n.toFixed(2); return '$0.00'; } function clampText(s, max){ const str=String(s||''); if(str.length<=max) return str; return str.slice(0, max-1)+'…'; } function render(){ buildSubset(); const L=document.getElementById('list'); const empty=document.getElementById('empty'); const count=document.getElementById('count'); L.innerHTML=''; const favTotal=new Set(getFav().map(normalizeId).filter(Boolean)).size; count.textContent=String(favTotal); empty.classList.toggle('hidden', state.subset.length>0 || favTotal>0 ? state.subset.length>0 : false); if(favTotal===0){ empty.classList.remove('hidden'); return; } if(state.subset.length===0){ empty.innerHTML='No matches. Clear your search or explore the catalog.'; empty.classList.remove('hidden'); return; } else { empty.innerHTML='No tracked courses yet. Explore the catalog.'; empty.classList.add('hidden'); } const cart=getCart(); state.subset.forEach(it=>{ const id=normalizeId(it.id); const qty=Number(cart[id]||0); const li=document.createElement('li'); li.className='border border-gray-200 rounded-xl p-4 sm:p-5 bg-white shadow-sm hover:shadow-md transition-shadow flex flex-col'; li.innerHTML=`

${escapeHtml(it.title||'Untitled course')}

Tracked

${escapeHtml(clampText(it.shortDescription||'', 180))}

Category: ${escapeHtml(it.category||'—')} Level: ${escapeHtml(it.level||'—')} Duration: ${escapeHtml(String(it.durationHours ?? '—'))}h Rating: ⭐ ${escapeHtml(String(it.rating ?? '—'))}
${escapeHtml(money(it.price))}
In cart: ${qty}
`; L.appendChild(li); }); L.querySelectorAll('[data-untrack]').forEach(b=>b.addEventListener('click',()=>{ const id=normalizeId(b.getAttribute('data-untrack')); const favs=getFav().map(normalizeId).filter(Boolean); const next=favs.filter(x=>x!==id); setFav(next); const meta=getFavMeta(); if(meta[id]){ delete meta[id]; setFavMeta(meta); } render(); showToast('Removed', 'Course removed from tracked list.'); })); L.querySelectorAll('[data-add]').forEach(b=>b.addEventListener('click',()=>{ const id=normalizeId(b.getAttribute('data-add')); const cart=getCart(); cart[id]=Number(cart[id]||0)+1; setCart(cart); render(); showToast('Added to cart', 'The course was added to your cart.'); })); } function escapeHtml(str){ return String(str) .replaceAll('&','&') .replaceAll('<','<') .replaceAll('>','>') .replaceAll('"','"') .replaceAll("'","'"); } function showToast(title, text){ const dlg=document.getElementById('toast-modal'); const t=document.getElementById('toast-title'); const p=document.getElementById('toast-text'); t.textContent=title; p.textContent=text; try { dlg.showModal(); } catch { dlg.setAttribute('open',''); } } function exportSubset(){ const favs=new Set(getFav().map(normalizeId).filter(Boolean)); const subset=state.DATA.filter(x=>favs.has(normalizeId(x.id))); const dlg=document.getElementById('export-modal'); const area=document.getElementById('export-text'); const copyStatus=document.getElementById('copy-status'); copyStatus.classList.add('hidden'); area.value=JSON.stringify(subset, null, 2); try { dlg.showModal(); } catch { dlg.setAttribute('open',''); } setTimeout(()=>area.focus(), 50); setTimeout(()=>{ area.setSelectionRange(0, area.value.length); }, 80); } document.getElementById('copy-export').addEventListener('click', async ()=>{ const area=document.getElementById('export-text'); const copyStatus=document.getElementById('copy-status'); const txt=area.value||''; let ok=false; try{ await navigator.clipboard.writeText(txt); ok=true; }catch(e){ try{ area.focus(); area.select(); ok=document.execCommand('copy'); }catch(e2){ ok=false; } } copyStatus.textContent = ok ? 'Copied to clipboard.' : 'Copy failed. Select text and copy manually.'; copyStatus.classList.remove('hidden'); copyStatus.classList.toggle('text-emerald-700', ok); copyStatus.classList.toggle('text-gray-700', !ok); }); exportBtn.addEventListener('click', exportSubset); clearBtn.addEventListener('click', ()=>{ const favs=getFav().map(normalizeId).filter(Boolean); if(favs.length===0){ showToast('Nothing to clear', 'Your tracked list is already empty.'); return; } const dlg=document.getElementById('confirm-clear-modal'); try { dlg.showModal(); } catch { dlg.setAttribute('open',''); } }); document.getElementById('confirm-clear').addEventListener('click', ()=>{ setFav([]); setFavMeta({}); const dlg=document.getElementById('confirm-clear-modal'); dlg.close(); render(); showToast('Cleared', 'All tracked courses were removed.'); }); searchEl.addEventListener('input', ()=>{ state.query=searchEl.value||''; render(); }); sortEl.addEventListener('change', ()=>{ state.sort=sortEl.value||'recent'; render(); }); fetch('catalog.json', {cache:'no-store'}) .then(r=>{ if(!r.ok) throw new Error('Failed to load catalog.json'); return r.json(); }) .then(DATA=>{ if(!Array.isArray(DATA)) DATA=[]; state.DATA=DATA; const favIds=getFav().map(normalizeId).filter(Boolean); ensureMetaForFavIds(new Set(favIds)); render(); }) .catch(()=>{ state.DATA=[]; render(); showToast('Catalog unavailable', 'Could not load catalog.json. Please try again later.'); }); })();