i18n = ({
es: {
locale: "es-MX",
period: "Período",
group: "Grupo de droga",
all: "Todos",
events: "eventos",
event: "evento",
year: "Año",
pctEvents:"% de eventos",
drugType: "Tipo de droga",
term: "Administración presidencial",
ops: "Operaciones militares",
download: "⬇ Descargar datos (CSV)",
grupos: {
"Mariguana": "Mariguana",
"Cocaina": "Cocaína",
"Opioides tradicionales": "Opioides tradicionales",
"Metanfetamina": "Metanfetamina",
"Fentanilo": "Fentanilo"
},
labs: {COCAINA: "Cocaína", HEROINA: "Heroína", METANFETAMINA: "Metanfetamina"},
drogas: {
mariguana: "Mariguana", semilla_de_mariguana: "Semilla de mariguana",
cocaina: "Cocaína", goma_de_opio: "Goma de opio", heroina: "Heroína",
semilla_de_amapola: "Semilla de amapola", metanfetamina: "Metanfetamina",
fentanilo: "Fentanilo", pastillas_de_fentanilo: "Pastillas de fentanilo",
ampolletas_de_fentanilo: "Ampolletas de fentanilo"
},
unidades: {kg: "kg", pastillas: "pastillas", ampolletas: "ampolletas"}
},
en: {
locale: "en-US",
period: "Period",
group: "Drug group",
all: "All",
events: "events",
event: "event",
year: "Year",
pctEvents:"% of events",
drugType: "Drug type",
term: "Presidential term",
ops: "Military operations",
download: "⬇ Download data (CSV)",
grupos: {
"Mariguana": "Marijuana",
"Cocaina": "Cocaine",
"Opioides tradicionales": "Traditional opioids",
"Metanfetamina": "Methamphetamine",
"Fentanilo": "Fentanyl"
},
labs: {COCAINA: "Cocaine", HEROINA: "Heroin", METANFETAMINA: "Methamphetamine"},
drogas: {
mariguana: "Marijuana", semilla_de_mariguana: "Marijuana seed",
cocaina: "Cocaine", goma_de_opio: "Opium gum", heroina: "Heroin",
semilla_de_amapola: "Poppy seed", metanfetamina: "Methamphetamine",
fentanilo: "Fentanyl", pastillas_de_fentanilo: "Fentanyl pills",
ampolletas_de_fentanilo: "Fentanyl ampoules"
},
unidades: {kg: "kg", pastillas: "pills", ampolletas: "ampoules"}
}
})[lang]
// Números: miles separados con espacio indivisible ("10 000") y punto decimal,
// igual en español e inglés. Sin coma de miles (estilo de la casa).
fmtNum = (v, maxDec = 2) => {
const s = Number(v).toLocaleString("en-US", {maximumFractionDigits: maxDec, useGrouping: false})
const [int, dec] = s.split(".")
const grouped = int.replace(/\B(?=(\d{3})+(?!\d))/g, "\u00A0")
return dec ? `${grouped}.${dec}` : grouped
}
// Rangos con guion corto ("1990-2000") se muestran con raya ("1990–2000")
fmtRange = s => String(s).replace("-", "–")
// Ancho de las gráficas: llena el contenedor, con tope para pantallas grandes.
// Se mide directamente el ancho de la columna de contenido y se vuelve a avisar cada vez
// que cambia (p. ej. cuando el iframe se acomoda tarde en celular), para que las gráficas
// se redibujen al ancho real y no se queden con el ancho provisional del primer dibujo.
vizWidth = Generators.observe(notify => {
const main = document.querySelector("main.content") || document.body
let last = -1
const send = () => {
const w = Math.min(Math.floor(main.clientWidth || width), 1000)
if (w > 0 && w !== last) { last = w; notify(w) }
}
send()
const ro = new ResizeObserver(send)
ro.observe(main)
window.addEventListener("resize", send)
return () => { ro.disconnect(); window.removeEventListener("resize", send) }
})