' +
'' +
''; // end pageDetailScroll
mc.className = 'main-inner view-enter';
// Animate gauge after render
requestAnimationFrame(function() {
var gaugeCircle = document.getElementById('gaugeCircleAnim');
if (gaugeCircle) {
var r = 70, c = 2*Math.PI*r, target = c - (computedScore/100)*c;
gaugeCircle.style.strokeDashoffset = target;
}
// Setup section scroll observer
setupSectionObserver();
// Load images if HTML exists
if (p.htmlExists && p.htmlFile) {
loadPageImages(p);
}
});
}
// ===== HELPER: Large gauge =====
function renderLargeGauge(score) {
var r = 70, c = 2*Math.PI*r;
var color = scoreColor(score);
return '
';
}
function copyToClipboard(id, btn) {
var el = document.getElementById('copyText-' + id);
if (!el) return;
var text = el.textContent;
navigator.clipboard.writeText(text).then(function() {
btn.classList.add('copied');
btn.querySelector('.material-symbols-outlined').textContent = 'check';
setTimeout(function() {
btn.classList.remove('copied');
btn.querySelector('.material-symbols-outlined').textContent = 'content_copy';
}, 2000);
});
}
// ===== HELPER: Character count badge =====
function charCountBadge(text, min, max) {
if (!text) return 'Manquant';
var len = text.length;
var cls = len >= min && len <= max ? 'char-ok' : (len < min ? 'char-warn' : 'char-over');
return '' + len + '/' + max + ' car.';
}
// ===== HELPER: KPI card =====
function kpiCard(label, value, color) {
return '
' +
'' + label + '' +
'' + value + '' +
'
';
}
// ===== HELPER: Priority color =====
function priColorMap(pri) {
var m = { P1:'#dc2626', P2:'#ea580c', P3:'#ca8a04', P4:'#2563eb', P5:'#9ca3af', Blog:'#7c3aed' };
return m[pri] || '#45474c';
}
// ===== HELPER: Clean secondary keywords =====
function cleanSecondaryKws(kws) {
if (!kws) return [];
return kws.filter(function(k) {
return k.indexOf('**') < 0 && k.indexOf('H2 :') < 0 && k.indexOf('H3 :') < 0 && k.indexOf('*') !== 0 && k.length < 60 && k.length > 3;
});
}
// ===== HELPER: Get H3s for H2 index =====
function getH3sForH2(p, h2Index) {
if (!p.h3s || !p.h2s) return [];
// Heuristic: distribute H3s among H2s
var h3PerH2 = Math.ceil(p.h3s.length / p.h2s.length);
var start = h2Index * h3PerH2;
return p.h3s.slice(start, start + h3PerH2);
}
// ===== HELPER: Search from chip =====
function searchFromChip(kw) {
filters.search = kw;
navigateTo('#pages');
}
// ===== HELPER: Section scroll observer =====
function setupSectionObserver() {
var sections = document.querySelectorAll('section[id^="sec-"]');
var navLinks = document.querySelectorAll('#sectionNav a');
if (!sections.length || !navLinks.length) return;
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
navLinks.forEach(function(a) { a.classList.remove('active-section'); });
var link = document.querySelector('#sectionNav a[data-section="' + entry.target.id + '"]');
if (link) link.classList.add('active-section');
}
});
}, { rootMargin: '-100px 0px -70% 0px' });
sections.forEach(function(s) { observer.observe(s); });
// Smooth scroll for section nav clicks
navLinks.forEach(function(a) {
a.addEventListener('click', function(e) {
e.preventDefault();
var target = document.getElementById(a.dataset.section);
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
}
// ===== HELPER: Load images from HTML page =====
function loadPageImages(p) {
if (!p.htmlFile) return;
fetch('./' + p.htmlFile)
.then(function(r) { return r.text(); })
.then(function(html) {
var parser = new DOMParser();
var doc = parser.parseFromString(html, 'text/html');
var imgs = doc.querySelectorAll('img');
var grid = document.getElementById('mediaGrid');
if (!grid) return;
if (imgs.length === 0) {
grid.innerHTML = '
image_not_supported
Aucune image trouvee dans le HTML
';
return;
}
var cards = '';
imgs.forEach(function(img, i) {
var src = img.getAttribute('src') || '';
var alt = img.getAttribute('alt') || '';
var savedAlt = localStorage.getItem('alt-' + p.slug + '-' + i);
if (savedAlt !== null) alt = savedAlt;
cards += '
' +
'
' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'' + esc(src.split('/').pop()) + '' +
'
' +
'
' +
'
';
});
grid.innerHTML = cards;
})
.catch(function() {
var grid = document.getElementById('mediaGrid');
if (grid) grid.innerHTML = '
Impossible de charger le fichier HTML
';
});
}
// ========== SHARED HELPERS ==========
function scoreColor(score) {
if (score >= 80) return '#16a34a';
if (score >= 60) return '#ca8a04';
if (score >= 40) return '#ea580c';
return '#dc2626';
}
function posColor(pos) {
if (pos === null || pos === undefined) return '#9ca3af';
if (pos < 3) return '#16a34a';
if (pos < 10) return '#ca8a04';
if (pos < 20) return '#ea580c';
return '#dc2626';
}
function positionBadge(pos) {
if (pos === null || pos === undefined) return '\u2014';
var cls = 'pos-red';
if (pos < 3) cls = 'pos-green';
else if (pos < 10) cls = 'pos-yellow';
else if (pos < 20) cls = 'pos-orange';
return '' + pos + '';
}
function priorityBadge(pri) {
return '' + pri + '';
}
function scoreBar(score) {
var color = scoreColor(score);
return '
' +
'
' +
'' + score + '' +
'
';
}
function pipelineBadges(status) {
var steps = [
{ key: 'brief', label: 'B' },
{ key: 'content', label: 'C' },
{ key: 'html', label: 'H' },
{ key: 'published', label: 'P' },
];
return steps.map(function(s) {
var done = status[s.key];
return '' + (done ? 'check_circle' : 'radio_button_unchecked') + '';
}).join(' ');
}
function sortArrow(key) {
if (currentSort.key !== key) return '\u2195';
return currentSort.dir === 'asc' ? '\u2191' : '\u2193';
}
function esc(s) {
if (!s) return '';
var d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
// SEO API base URL
var SEO_API = 'https://mc.brunel-digital.fr/seo-api';
async function loadKeywordResearch(keyword) {
var container = document.getElementById('kwResearchContainer');
if (!container) return;
container.innerHTML = '
Analyse SERP en cours pour ' + esc(keyword) + '...
';
try {
var resp = await fetch(SEO_API + '/api/analysis?q=' + encodeURIComponent(keyword));
var data = await resp.json();
if (data.error) throw new Error(data.error);
renderKeywordResults(container, data, keyword);
} catch(e) {
container.innerHTML = '
error
' + esc(e.message) + '
';
}
}
function renderKeywordResults(container, data, keyword) {
var s = data.summary || {};
var serp = data.serp || {};
var gsc = data.gsc || {};
var gscRows = gsc.rows || [];
var html = '';
// ====== ROW 1: Position Overview (glassmorphism) ======
html += '
';
html += '';
html += '
';
// Search position
var orgPos = s.dermeliaOrganicPos;
html += '
';
html += '
languageSearch
';
html += orgPos ? '
#' + orgPos + '
' : '
Non classe
';
html += '
';
// Maps position
var locPos = s.dermeliaLocalPos;
html += '
';
html += '
pin_dropMaps
';
html += locPos ? '
#' + locPos + '
' : '
Absent
';
html += '
';
// GSC Clicks
var gscMain = gscRows.length > 0 ? gscRows[0] : null;
html += '