/* AMIIN · Ranking Halal/Muslim-Friendly (JS externo v1.2)
- CSV principal: https://amiin.es/wp-content/uploads/2025/09/Ranking.csv
- Columnas flexibles (case-insensitive):
Estrellas: "Estrellas_%" | "Estrellas"
Lunas: "Lunas_%" | "Lunas"
Nota final: "notafinal" | "Nota_final" | "Nota final" | "Nota_final_/10"
- Fallbacks de datos: CSV local -> Google CSV -> Google GViz JSON
*/
(function(){
"use strict";
var SOURCES = {
localCSV: "https://amiin.es/wp-content/uploads/2025/09/Ranking.csv",
googleCSV:"https://docs.google.com/spreadsheets/d/e/2PACX-1vReAdUJ-QqL81u8NoOFmoSGMN0MBTbOjmUW2qL3z3OGeDvlb_PlxTeV5Fntj7XAmg/pub?output=csv",
googleGViz:"https://docs.google.com/spreadsheets/d/e/2PACX-1vReAdUJ-QqL81u8NoOFmoSGMN0MBTbOjmUW2qL3z3OGeDvlb_PlxTeV5Fntj7XAmg/gviz/tq?tqx=out:json"
};
var WEIGHT_STAR=0.45, WEIGHT_MOON=0.55, FLOOR=3.0;
var MACRO_COLS=["Renta_disponible","PIB_per_capita","Tasa_empleo","Densidad_empresarial","Accesibilidad_turismo","Asequibilidad_vivienda","Nivel_educativo","Coste_vida_relativo","Seguridad_general"];
var HALAL_NONBIN=["Poblacion_musulmana","Porcentaje_musulmana","Oratorios_asociaciones","Restaurantes_tiendas_halal","Empresas_certificadas_halal","Eventos_halal","Plan_municipal_diversidad","Delitos_odio_invertido"];
var HALAL_BIN=["Cementerio_islamico","Docentes_religion_islamica","Reconocimiento_Eid"];
var ROOT = document.getElementById("amiin-sheet-ranking");
if(!ROOT){ return; }
// ===== CSS inyectado (desde script externo) =====
var CSS = `
:root{--blue:#0e00ff;--ink:#0b1220;--muted:#5b657a;--bg:#f5f7fb;--card:#fff;--line:#e7eaf1;--chip:#f2f4fb;--star-empty:#cfd6e6;--star-full:#ffd54f;--moon-empty:#cfd6e6;--moon-full:#3aa6ff}
.amiin-wrap{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Arial,sans-serif;color:var(--ink);background:var(--bg);border:1px solid var(--line);border-radius:16px;padding:18px}
.amiin-header h2{margin:0 0 4px 0;font-size:22px;font-weight:800}
.sub{color:var(--muted);font-size:12px;margin:0 0 8px 0}
.status{margin:8px 0;font-size:12px;color:var(--muted)}
.amiin-controls{display:grid;gap:10px;margin:10px 0 14px}
.amiin-controls .row{display:flex;flex-wrap:wrap;gap:10px}
.amiin-controls label{display:grid;gap:4px;font-size:12px}
.amiin-controls input,.amiin-controls select{padding:8px;border:1px solid var(--line);border-radius:10px;background:#fff}
.btn{background:var(--blue);color:#fff;border:1px solid var(--blue);border-radius:999px;padding:8px 12px;cursor:pointer;font-size:12px}
.btn.secondary{background:#eef2ff;color:#1f2a5a;border:1px solid #d7dcff}
.view{display:none}.view.is-active{display:block}
.pager{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.pager button{background:#eef2ff;color:#1f2a5a;border:1px solid #d7dcff;border-radius:999px;padding:6px 10px;cursor:pointer;font-size:12px}
.pager button.is-active{background:var(--blue);border-color:var(--blue);color:#fff}
.rank-list{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.card{display:grid;grid-template-columns:56px 1fr auto;gap:12px;align-items:center;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:12px}
.rank{width:48px;height:48px;border-radius:12px;background:#fff;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:14px;color:var(--blue);border:2px solid var(--blue)}
.meta{display:grid;gap:2px}.city{font-weight:700}.ccaa{font-size:12px;color:var(--muted)}.notes{font-size:12px;color:#30384a}
.scores{display:grid;gap:6px;text-align:right}
.row{display:flex;align-items:center;gap:8px;justify-content:flex-end}
.label{font-size:11px;color:var(--muted)}
.chip{display:inline-flex;align-items:center;gap:8px;background:var(--chip);border:1px solid var(--line);border-radius:999px;padding:6px 10px}
.stars,.moons{position:relative;display:inline-block;line-height:1;font-size:14px}
.stars>span:first-child{color:var(--star-empty)}
.stars>span:last-child{color:var(--star-full);position:absolute;inset:0;width:0%;overflow:hidden;white-space:nowrap}
.moons>span:first-child{color:var(--moon-empty)}
.moons>span:last-child{color:var(--moon-full);position:absolute;inset:0;width:0%;overflow:hidden;white-space:nowrap}
.total{margin-top:2px}
.total-chip{display:inline-flex;align-items:center;gap:6px;background:#eef2ff;color:#1f2a5a;border:1px solid #d7dcff;border-radius:999px;padding:6px 10px;font-variant-numeric:tabular-nums}
.table-wrap{background:#fff;border:1px solid var(--line);border-radius:14px;padding:10px}
table{width:100%;border-collapse:collapse;font-size:14px}
thead th{background:#f0f3ff;border-bottom:1px solid var(--line);padding:8px;text-align:left;cursor:pointer}
tbody td{padding:8px;border-bottom:1px solid var(--line)}
.table-pager{display:flex;justify-content:space-between;align-items:center;margin-top:8px}
@media (max-width:640px){.card{grid-template-columns:44px 1fr}.scores{text-align:left}}
`;
var style=document.createElement("style"); style.textContent=CSS; document.head.appendChild(style);
// ===== HTML mínimo dentro del contenedor =====
ROOT.innerHTML = ''
+ ''
+ '
Cargando datos…
'
+ ''
+ ''
+ ''
+ '| # | Ciudad | CCAA | Estrellas % | Lunas % | Nota /10 | '
+ '
';
// ===== Refs & estado =====
var statusBox=ROOT.querySelector("#amiin-status");
var rankList=ROOT.querySelector("#rank-list");
var pager=ROOT.querySelector(".pager");
var fSearch=ROOT.querySelector("#f-search");
var fCCAA=ROOT.querySelector("#f-ccaa");
var fMin=ROOT.querySelector("#f-min");
var fMax=ROOT.querySelector("#f-max");
var btnClear=ROOT.querySelector("#btn-clear");
var btnCSV=ROOT.querySelector("#btn-csv");
var btnViewRanking=ROOT.querySelector("#btn-view-ranking");
var btnViewTable=ROOT.querySelector("#btn-view-table");
var viewRanking=ROOT.querySelector("#view-ranking");
var viewTable=ROOT.querySelector("#view-table");
var tBody=ROOT.querySelector("#summary-table tbody");
var tPrev=ROOT.querySelector("#t-prev");
var tNext=ROOT.querySelector("#t-next");
var tInfo=ROOT.querySelector("#t-info");
var DATA=[], CURRENT=[];
var page=1, PER_PAGE=10, tPage=1, T_PER=10, tSortKey="rank", tSortDir="asc";
// ===== Utils =====
var clamp=function(n,min,max){ return Math.max(min,Math.min(max,n)); };
var num=function(v){ if(v===null||v===undefined) return null; var x=parseFloat(String(v).replace(",",".").trim()); return isFinite(x)?x:null; };
function detectDelimiter(line){ var commas=(line.match(/,/g)||[]).length; var semis=(line.match(/;/g)||[]).length; return semis>commas?';':','; }
function parseCSV(text){
var first = text.split(/\r?\n/,1)[0] || '';
var DELIM = detectDelimiter(first);
var rows=[], i=0, f="", r=[], inQ=false;
while(i0 || r.length>0){ r.push(f); rows.push(r); }
return rows;
}
// header-map: genera un objeto {lowercasedHeader: originalHeader}
function mapHeaders(headers){
var m={};
headers.forEach(function(h){ var key=String(h||'').trim(); if(key) m[key.toLowerCase()]=key; });
return m;
}
// toma por alias (case-insensitive)
function pickByAliases(obj, headerMap, aliases){
for(var i=0;i'+c+'';}).join('');
}
function renderRanking(){
var pages=Math.ceil(CURRENT.length/10)||1;
page=Math.min(page,pages);
var start=(page-1)*10, end=start+10;
var slice=CURRENT.slice(start,end);
rankList.innerHTML='';
slice.forEach(function(d){
var li=document.createElement('li'); li.className='card';
var r=document.createElement('div'); r.className='rank'; r.textContent=d.rank;
var meta=document.createElement('div'); meta.className='meta';
meta.innerHTML=''+d.city+'
'+d.ccaa+'
'+(d.notes||'')+'
';
var scores=document.createElement('div'); scores.className='scores';
var s=document.createElement('span'); s.className='stars'; s.innerHTML='★★★★★★★★★★'; s.querySelector('span:last-child').style.width=Math.max(0,Math.min(100,d.star))+'%';
var m=document.createElement('span'); m.className='moons'; m.innerHTML='☾☾☾☾☾☾☾☾☾☾'; m.querySelector('span:last-child').style.width=Math.max(0,Math.min(100,d.moon))+'%';
var row1=document.createElement('div'); row1.className='row'; row1.innerHTML='Estrellas'; var chip1=document.createElement('span'); chip1.className='chip'; chip1.appendChild(s); row1.appendChild(chip1);
var row2=document.createElement('div'); row2.className='row'; row2.innerHTML='Medias lunas'; var chip2=document.createElement('span'); chip2.className='chip'; chip2.appendChild(m); row2.appendChild(chip2);
var total=document.createElement('div'); total.className='total'; var pill=document.createElement('span'); pill.className='total-chip'; pill.innerHTML=''+d.score.toFixed(1)+' / 10'; total.appendChild(pill);
scores.appendChild(row1); scores.appendChild(row2); scores.appendChild(total);
li.append(r,meta,scores);
rankList.appendChild(li);
});
pager.innerHTML='';
for(var i=1;i<=pages;i++){
var b=document.createElement('button'); b.textContent=((i-1)*10+1)+'–'+Math.min(i*10,CURRENT.length);
if(i===page) b.classList.add('is-active');
(function(pi){ b.addEventListener('click', function(){ page=pi; renderRanking(); window.scrollTo({top:ROOT.offsetTop,behavior:'smooth'}); }); })(i);
pager.appendChild(b);
}
}
function renderTable(){
var rows=CURRENT.slice().sort(function(a,b){
var k=tSortKey, m=(tSortDir==='asc')?1:-1;
if(k==='city'||k==='ccaa') return m*String(a[k]).localeCompare(String(b[k]));
return m*((a[k]||0)-(b[k]||0));
});
var pages=Math.ceil(CURRENT.length/T_PER)||1;
tPage=Math.min(tPage,pages);
var start=(tPage-1)*T_PER, end=start+T_PER;
var slice=rows.slice(start,end);
tBody.innerHTML=slice.map(function(d){
return '| '+d.rank+' | '+d.city+' | '+d.ccaa+' | '+d.star.toFixed(0)+'% | '+d.moon.toFixed(0)+'% | '+d.score.toFixed(1)+' |
';
}).join('');
tInfo.textContent='Mostrando '+(slice.length?start+1:0)+'–'+Math.min(end,CURRENT.length)+' de '+CURRENT.length;
tPrev.disabled=(tPage<=1); tNext.disabled=(tPage>=pages);
}
function applyFilters(){
var q=(fSearch.value||'').trim().toLowerCase();
var ccaa=fCCAA.value||'';
var min=clamp(parseFloat((fMin.value||'0').replace(',','.'))||0,0,10);
var max=clamp(parseFloat((fMax.value||'10').replace(',','.'))||10,0,10);
CURRENT = DATA.filter(function(d){
var okQ=!q || d.city.toLowerCase().includes(q);
var okC=!ccaa || d.ccaa===ccaa;
var okS=d.score>=min && d.score<=max;
return okQ && okC && okS;
});
page=1; tPage=1;
}
function renderAll(){ renderRanking(); if(viewTable.classList.contains('is-active')) renderTable(); }
[fSearch,fCCAA,fMin,fMax].forEach(function(inp){ inp.addEventListener('input', function(){ applyFilters(); renderAll(); }); });
btnClear.addEventListener('click', function(){ fSearch.value=''; fCCAA.value=''; fMin.value='0'; fMax.value='10'; applyFilters(); renderAll(); });
btnCSV.addEventListener('click', function(){
var head=['rank','city','ccaa','Estrellas_%','Lunas_%','Nota_/10'], lines=[head.join(',')];
CURRENT.forEach(function(d){
lines.push([d.rank,'"'+d.city.replace(/"/g,'""')+'"','"'+d.ccaa.replace(/"/g,'""')+'"',d.star.toFixed(0),d.moon.toFixed(0),d.score.toFixed(1)].join(','));
});
var blob=new Blob([lines.join('\n')],{type:'text/csv;charset=utf-8;'});
var url=URL.createObjectURL(blob); var a=document.createElement('a'); a.href=url; a.download='amiin_ranking_vista.csv';
document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
});
ROOT.querySelectorAll('#summary-table thead th').forEach(function(th){
th.addEventListener('click', function(){
var k=th.getAttribute('data-k'); if(!k) return;
if(tSortKey===k){ tSortDir=(tSortDir==='asc'?'desc':'asc'); } else { tSortKey=k; tSortDir='asc'; }
renderTable();
});
});
tPrev.addEventListener('click', function(){ tPage=Math.max(1,tPage-1); renderTable(); });
tNext.addEventListener('click', function(){ tPage=tPage+1; renderTable(); });
btnViewRanking.addEventListener('click', function(){ viewRanking.classList.add('is-active'); viewTable.classList.remove('is-active'); renderRanking(); });
btnViewTable.addEventListener('click', function(){ viewTable.classList.add('is-active'); viewRanking.classList.remove('is-active'); renderTable(); });
// ===== Carga con fallbacks =====
function tryLoad(){
setStatus("Cargando datos…");
// 1) CSV local
fetchWithTimeout(SOURCES.localCSV+"?nocache="+Date.now(),15000)
.then(function(res){ if(!res.ok) throw new Error("HTTP "+res.status); return res.text(); })
.then(function(txt){
var rows=parseCSV(txt);
if(!rows.length || rows[0].length<2) throw new Error("CSV local vacío/dañado");
processRows(rows); setStatus(""); renderAll();
})
.catch(function(err1){
// 2) CSV Google
fetchWithTimeout(SOURCES.googleCSV+"&_="+Date.now(),15000)
.then(function(res){ if(!res.ok) throw new Error("HTTP "+res.status); return res.text(); })
.then(function(txt){
var rows=parseCSV(txt);
if(!rows.length || rows[0].length<2) throw new Error("CSV Google vacío/dañado");
processRows(rows); setStatus(""); renderAll();
})
.catch(function(err2){
// 3) GViz JSON
fetchWithTimeout(SOURCES.googleGViz+"&_="+Date.now(),15000)
.then(function(res){ if(!res.ok) throw new Error("HTTP "+res.status); return res.text(); })
.then(function(text){
var rows=(function(t){var m=t.match(/setResponse\(([\s\S]*?)\)\s*;?$/);if(!m) throw new Error("GViz: formato inesperado");var j=JSON.parse(m[1]);if(!j.table||!j.table.cols||!j.table.rows) throw new Error("GViz: tabla vacía");var h=j.table.cols.map(function(c){return (c.label||c.id||'').trim();});var rw=j.table.rows.map(function(r){return r.c.map(function(cell){return (cell&&cell.v!=null)?String(cell.v):'';});});return [h].concat(rw);})(text);
processRows(rows); setStatus(""); renderAll();
})
.catch(function(err3){
setStatus("Error al cargar datos. Detalles:
"
+ "Local: "+(err1.message||err1)+"
"
+ "Google CSV: "+(err2.message||err2)+"
"
+ "GViz: "+(err3.message||err3)+"");
});
});
});
}
tryLoad();
})();