(function(){ var GRID = document.getElementById('mission-grid'); if (!GRID) return; if (GRID.dataset.dynamic !== '1') return; var API = 'https://dronesera.com/wp-json/wp/v2/posts'; var PER_PAGE = 12; var PALETTE = [ { bg:'linear-gradient(135deg,#2A2410 0%,#1A1810 50%,#0A0E1A 100%)', fg:'#FFD23F', icon:'\u00A7' }, { bg:'linear-gradient(135deg,#102A2C 0%,#0A1418 50%,#0A0E1A 100%)', fg:'#5EE7FF', icon:'\u25A3' }, { bg:'linear-gradient(135deg,#2A1810 0%,#1A1410 50%,#0A0E1A 100%)', fg:'#FF6B1A', icon:'\u25C9' }, { bg:'linear-gradient(135deg,#1A1A2A 0%,#0F0F1A 50%,#0A0E1A 100%)', fg:'#A78BFA', icon:'\u0024' }, { bg:'linear-gradient(135deg,#102A1A 0%,#0A1810 50%,#0A0E1A 100%)', fg:'#7CE3A0', icon:'\u25A6' } ]; function tagFromTitle(title){ var t = String(title).toLowerCase(); if (/\breview\b|compared|first impressions|vs\./.test(t)) return 'REVIEW'; if (/\bregulation|fcc|faa|compliance|part\s*\d+|bvlos|trust/.test(t)) return 'REGULATION'; if (/\bguide|how to|tips|beginner|step[- ]by[- ]step|best\b/.test(t)) return 'GUIDE'; return 'MISSION'; } function escAttr(s){ return String(s).replace(/&/g,'&').replace(/"/g,'"').replace(//g,'>'); } function buildCard(post, idx){ var title = (post.title && post.title.rendered) || '(untitled)'; var url = post.link; var rawExcerpt = (post.excerpt && post.excerpt.rendered || '').replace(/<[^>]+>/g,'').replace(/’/g,"'").replace(/–|–/g,'-').replace(/“|”|“|”/g,'"').replace(/&/g,'&').replace(/…/g,'...').replace(/&#\d+;/g,'').trim(); var excerpt = rawExcerpt.length > 220 ? rawExcerpt.slice(0,217)+'...' : rawExcerpt; var date = new Date(post.date_gmt ? post.date_gmt + 'Z' : post.date); var day = String(date.getUTCDate()).padStart(2,'0'); var month = date.toLocaleString('en-US',{month:'short',timeZone:'UTC'}).toUpperCase(); var year = date.getUTCFullYear(); var missionNo = '#' + String(900 - idx).padStart(3,'0'); var p = PALETTE[idx % PALETTE.length]; var tagClass = tagFromTitle(title).toLowerCase(); var altText = (post._embedded && post._embedded['wp:featuredmedia'] && post._embedded['wp:featuredmedia'][0] && post._embedded['wp:featuredmedia'][0].alt_text) || title; var featHTML; if (post._embedded && post._embedded['wp:featuredmedia'] && post._embedded['wp:featuredmedia'][0]) { var media = post._embedded['wp:featuredmedia'][0]; var sizes = media.media_details && media.media_details.sizes; var src = (sizes && (sizes['medium_large'] || sizes['large'] || sizes['full']) && (sizes['medium_large']||sizes['large']||sizes['full']).source_url) || media.source_url; featHTML = ''; } else { featHTML = ''; } var wordCount = rawExcerpt ? rawExcerpt.split(/\s+/).filter(Boolean).length : 30; var readMin = Math.max(2, Math.round(wordCount * 1.4 / 50)); var bar2 = (tagClass==='guide') ? ' on' : ''; var bar3 = (tagClass==='review') ? ' on' : ''; var card = document.createElement('a'); card.href = url; card.className = 'mission-card'; card.setAttribute('aria-label', title); card.innerHTML = featHTML + '
'+day+''+month+' '+year+'
' + '
' + '
' + ''+tagFromTitle(title)+'' + 'MISSION '+missionNo+'' + '
' + '

'+title+'

' + '

'+excerpt+'

' + '
' + '
' + '
READ '+readMin+' MIN
' + '
' + '
' + '
\u2192
'; return card; } function showError(msg){ GRID.innerHTML = '

'+msg+'

'; } // Placeholder while loading GRID.innerHTML = '

Loading latest missions\u2026

'; fetch(API + '?per_page=' + PER_PAGE + '&orderby=date&order=desc&_embed=1', { credentials: 'omit', headers: { 'Accept': 'application/json' } }) .then(function(r){ if (!r.ok) throw new Error('HTTP '+r.status); return r.json(); }) .then(function(posts){ if (!Array.isArray(posts) || !posts.length) throw new Error('No posts returned'); GRID.innerHTML = ''; posts.forEach(function(p,i){ GRID.appendChild(buildCard(p,i)); }); if (window.aimadeReveal && typeof window.aimadeReveal.observe === 'function') { GRID.querySelectorAll('.mission-card').forEach(function(c){ window.aimadeReveal.observe(c); }); } var meta = document.querySelector('.mission-section .section-head .meta'); if (meta) meta.textContent = posts.length + ' of 63 \u00B7 sorted by launch date'; }) .catch(function(err){ console.error('[mission-grid] dynamic load failed:', err); showError('Live feed unavailable (HTTP error). Please refresh in a moment.'); }); })();