Pico y Placa Ibague

`; try { mount(root); } catch (err) { console.error(err); root.innerHTML = `
No se pudo cargar el módulo.
Revisa consola del navegador (F12).
Detalle: ${String(err && err.message ? err.message : err)}
`; } }); }; // 1) DOM ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => window.PZ_PYP_INIT_ALL()); } else { window.PZ_PYP_INIT_ALL(); } // 2) Elementor frontend hook (cuando el widget se inserta después) window.addEventListener('elementor/frontend/init', () => { // Espera un tick a que Elementor meta el HTML al DOM setTimeout(() => window.PZ_PYP_INIT_ALL(), 50); }); // 3) Reintentos (por si Elementor tarda o hay lazy render) let tries = 0; const t = setInterval(() => { tries++; window.PZ_PYP_INIT_ALL(); if (tries >= 10) clearInterval(t); }, 300); function getTZ(root){ return (root.getAttribute('data-tz') || 'America/Bogota').trim() || 'America/Bogota'; } function nowPartsInTZ(tz){ const dtf = new Intl.DateTimeFormat('en-CA', { timeZone: tz, year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', second:'2-digit', hour12:false }); const parts = dtf.formatToParts(new Date()); const m = {}; for (const p of parts) if (p.type !== 'literal') m[p.type] = p.value; return { year:+m.year, month:+m.month, day:+m.day, hour:+m.hour, minute:+m.minute, second:+m.second }; } function ymdFromParts(p){ return `${p.year}-${pad2(p.month)}-${pad2(p.day)}`; } function minutesOfDayFromParts(p){ return p.hour*60 + p.minute; } function dateUTCNoonFromYMD(ymd){ const [y,m,d] = ymd.split('-').map(Number); return new Date(Date.UTC(y, m-1, d, 12, 0, 0)); } function addDaysYMD(ymd, delta){ const dt = dateUTCNoonFromYMD(ymd); dt.setUTCDate(dt.getUTCDate() + delta); return `${dt.getUTCFullYear()}-${pad2(dt.getUTCMonth()+1)}-${pad2(dt.getUTCDate())}`; } function fmtTimeNoSeconds(tz){ return new Intl.DateTimeFormat('es-CO', { timeZone: tz, hour:'numeric', minute:'2-digit', hour12:true }).format(new Date()); } function weekdayUpperES(tz, ymd){ const dt = dateUTCNoonFromYMD(ymd); const w = new Intl.DateTimeFormat('es-CO', { timeZone: tz, weekday:'long' }).format(dt); return String(w).toUpperCase(); } function prettyDateES(ymd){ const [y,m,d] = ymd.split('-').map(Number); const meses = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic']; return `${d} de ${meses[m-1]} ${y}`; } function isHHMM(s){ return /^([01]\d|2[0-3]):([0-5]\d)$/.test(String(s||'').trim()); } function time24to12ES(hhmm){ if (!isHHMM(hhmm)) return String(hhmm||''); const [h,m] = hhmm.split(':').map(Number); const h12 = ((h + 11) % 12) + 1; const suf = h >= 12 ? 'p. m.' : 'a. m.'; return `${h12}:${pad2(m)} ${suf}`; } function rangeLabel(slot){ return `${time24to12ES(String(slot.start))} – ${time24to12ES(String(slot.end))}`; } function scheduleHtmlMultiline(schedule){ const slots = Array.isArray(schedule) ? schedule : []; if (slots.length === 1 && String(slots[0].start)==="00:00" && String(slots[0].end)==="23:59") return `
Todo el día
`; if (!slots.length) return `
Sin restricción
`; if (slots.length === 1) return `
${rangeLabel(slots[0])}
`; return `
    ${slots.map(s => `
  • ${rangeLabel(s)}
  • `).join("")}
`; } function toMinutes(hhmm){ if (!isHHMM(hhmm)) return null; const [h,m] = hhmm.split(':').map(Number); return h*60 + m; } function getHorarioEstado(schedule, nowMin){ const slots = (Array.isArray(schedule) ? schedule : []) .map(s => ({ s: toMinutes(String(s.start||'')), e: toMinutes(String(s.end||'')) })) .filter(x => x.s !== null && x.e !== null) .sort((a,b) => a.s - b.s); if (!slots.length) return { kind:'none', endMin:null }; for (const slot of slots){ if (nowMin >= slot.s && nowMin < slot.e) return { kind:'active', endMin: slot.e }; } return { kind:'inactive', endMin:null }; } function countdownHM(endMin, nowMin){ if (endMin === null) return null; const rem = Math.max(0, endMin - nowMin); return { h: Math.floor(rem/60), m: rem % 60 }; } function safeUrl(u){ const s = String(u ?? '').trim(); return /^https?:\/\//i.test(s) ? s : ''; } function dayKeyFromYMD(ymd){ const dt = dateUTCNoonFromYMD(ymd); return ['sun','mon','tue','wed','thu','fri','sat'][dt.getUTCDay()]; } function iconSvg(key){ const common = `width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true"`; const car = ``; const moto = ``; const bus = ``; const k = String(key||'').toLowerCase(); if (k.includes('moto')) return moto; if (k.includes('bus')) return bus; return car; } function resolveRestriction(city, cat, ymd){ const rule = cat.rule || {}; const dk = dayKeyFromYMD(ymd); if (rule.type === "none"){ return { value:"NO APLICA", kind:"na", schedule: [] }; } if (rule.type === "weekday_map"){ const v = rule.map && rule.map[dk] ? String(rule.map[dk]) : "SIN INFORMACIÓN"; if (v === "NO_APLICA") return { value:"NO APLICA", kind:"na", schedule: [] }; if (v === "SIN INFORMACIÓN") return { value:"SIN INFORMACIÓN", kind:"unknown", schedule: [] }; return { value: v, kind:"digits", schedule: [] }; } return { value:"SIN INFORMACIÓN", kind:"unknown", schedule: [] }; } function mount(root){ const tz = getTZ(root); const key = String(root.getAttribute("data-key") || "").trim(); const cfg = window.PZ_PYP_DATA && window.PZ_PYP_DATA[key] ? window.PZ_PYP_DATA[key] : null; if (!cfg) throw new Error(`No existe window.PZ_PYP_DATA["${key}"]. Revisa data-key.`); const city = cfg.city; const sw = city.switch || {}; const baseUrl = String(sw.baseUrl || "").trim(); const options = Array.isArray(sw.options) ? sw.options : []; root.innerHTML = `
PICO Y PLACA
Hora: --:--
--
--
Si vas a viajar, revisa el pico y placa para la fecha de tu viaje.
¿Tu placa es de Ibagué?
Esto solo cambia el horario.
`; const $ = (sel) => root.querySelector(sel); const elTitle = $('.pz-title'); const elTime = $('.pz-time'); const elCity = $('.pz-cityselect'); const elWeek = $('.pz-weekday'); const elDate = $('.pz-date'); const elDateInput = $('.pz-dateinput'); const elDateCenter = $('.pz-datecenter'); const elPrevDay = $('.pz-daybtn--left'); const elNextDay = $('.pz-daybtn--right'); const elGrid = $('.pz-grid'); const elFoot = $('.pz-foot'); const elVariantWrap = $('.pz-variant'); const elVariantBtns = root.querySelectorAll('.pz-variantBtn'); elCity.innerHTML = options .slice() .sort((a,b) => String(a.name||'').localeCompare(String(b.name||''), 'es')) .map(o => ``) .join(''); elCity.value = city.id; let todayTZ = ymdFromParts(nowPartsInTZ(tz)); let selectedYMD = todayTZ; let placaVariant = "ibague"; function updateHeader(){ elTitle.textContent = `PICO Y PLACA ${String(city.name || city.id).toUpperCase()}`.trim(); elTime.textContent = `Hora: ${fmtTimeNoSeconds(tz)}`; elWeek.textContent = weekdayUpperES(tz, selectedYMD); elDate.textContent = prettyDateES(selectedYMD); elDateInput.value = selectedYMD; } function updateFooter(){ const srcName = String(city.source?.name || '').trim(); const srcUrl = safeUrl(city.source?.url || ''); elFoot.innerHTML = srcUrl ? `Fuente: ${srcName || 'Fuente oficial'}` : (srcName ? `Fuente: ${srcName}` : ''); } function pickSchedule(cat){ if (cat.id === "particulares" && cat.schedules){ const v = (placaVariant === "fuera") ? "fuera" : "ibague"; return Array.isArray(cat.schedules[v]) ? cat.schedules[v] : []; } return Array.isArray(cat.schedule) ? cat.schedule : []; } function render(){ const part = (city.categories || []).find(c => c.id === "particulares" && c.schedules?.ibague && c.schedules?.fuera); elVariantWrap.style.display = part ? 'flex' : 'none'; const cats = (city.categories || []).slice().sort((a,b) => (a.priority||99)-(b.priority||99)); elGrid.innerHTML = cats.map(cat => `
${iconSvg(cat.icon || cat.id)}
${String(cat.name || cat.id).toUpperCase()}
Placas restringidas:
--
Horario hoy:
--
`).join(''); updateDynamic(); } function updateDynamic(){ const nowP = nowPartsInTZ(tz); todayTZ = ymdFromParts(nowP); const nowMin = minutesOfDayFromParts(nowP); const isToday = selectedYMD === todayTZ; elTime.textContent = `Hora: ${fmtTimeNoSeconds(tz)}`; (city.categories || []).forEach(cat => { const card = elGrid.querySelector(`[data-cat="${cat.id}"]`); if (!card) return; const elValue = card.querySelector('.pz-value'); const elLabel = card.querySelector('.pz-label'); const elBand = card.querySelector('.pz-band'); const elHours = card.querySelector('.pz-bandhours'); const elCd = card.querySelector('.pz-countdown'); const elCdText = card.querySelector('.pz-counttext'); const r = resolveRestriction(city, cat, selectedYMD); const sched = pickSchedule(cat); if (r.kind === 'na'){ elLabel.textContent = 'Estado:'; elValue.textContent = 'NO APLICA'; elBand.classList.remove('pz-band--yellow'); elBand.classList.add('pz-band--gray'); elHours.innerHTML = `
Sin restricción
`; elCd.style.display = 'none'; return; } if (r.kind === 'unknown'){ elLabel.textContent = 'Estado:'; elValue.textContent = 'SIN INFORMACIÓN'; elBand.classList.remove('pz-band--yellow'); elBand.classList.add('pz-band--gray'); elHours.innerHTML = `
Sin información
`; elCd.style.display = 'none'; return; } elLabel.textContent = 'Placas restringidas:'; elValue.textContent = String(r.value); elBand.classList.remove('pz-band--gray'); elBand.classList.add('pz-band--yellow'); elHours.innerHTML = scheduleHtmlMultiline(sched); // Countdown solo si hoy y dentro de una franja activa if (!isToday || !sched.length){ elCd.style.display = 'none'; return; } const st = getHorarioEstado(sched, nowMin); if (st.kind !== 'active'){ elCd.style.display = 'none'; return; } const hm = countdownHM(st.endMin, nowMin); elCd.style.display = 'flex'; elCdText.textContent = `Termina en: ${hm.h}h ${pad2(hm.m)}m`; }); } // Eventos elCity.addEventListener('change', () => { const nextId = String(elCity.value || '').trim(); const target = options.find(o => String(o.id) === nextId); const url = safeUrl(target?.id ? `${baseUrl}${target.id}/` : ''); if (url) window.location.href = url; }); elVariantBtns.forEach(btn => { btn.addEventListener('click', () => { placaVariant = (btn.getAttribute('data-variant') === 'fuera') ? 'fuera' : 'ibague'; elVariantBtns.forEach(b => { const active = b === btn; b.classList.toggle('is-active', active); b.setAttribute('aria-checked', active ? 'true' : 'false'); }); updateDynamic(); }); }); elPrevDay.addEventListener('click', () => { selectedYMD = addDaysYMD(selectedYMD, -1); updateHeader(); render(); }); elNextDay.addEventListener('click', () => { selectedYMD = addDaysYMD(selectedYMD, +1); updateHeader(); render(); }); function openPicker(){ if (typeof elDateInput.showPicker === 'function') elDateInput.showPicker(); else elDateInput.focus(); } elDateCenter.addEventListener('click', openPicker); elDateInput.addEventListener('change', () => { if (elDateInput.value){ selectedYMD = elDateInput.value; updateHeader(); render(); } }); updateHeader(); updateFooter(); render(); root._pzTimer && clearInterval(root._pzTimer); root._pzTimer = setInterval(() => { updateHeader(); updateDynamic(); }, 30 * 1000); } })();
Redacción Seguros Bolívar

¡Hola! Somos el equipo de redacción. A través de nuestros artículos también queremos brindar tranquilidad y enriquecer la vida con integridad. Por eso, aquí ofrecemos contenidos educativos, recomendaciones, noticias y mucho más. Bienvenidos todos los lectores y las empresas que desean aprender y actualizarse con información de calidad.

Recent Posts

¿Cuánto es el aumento del arriendo en el 2026?

De acuerdo con la ley 820 de 2003, el aumento del arriendo lo determina la inflación y el IPC del…

9 January, 2026

Guía de prevención general para empresas en el 2026

Prepare su empresa para el 2026 con esta guía de prevención, así protegerá su negocio frente a riesgos y garantizará…

9 January, 2026

Seguros Bolívar destaca entre las aseguradoras líderes en sostenibilidad a nivel mundial

Según el CSA 2025, Seguros Bolívar se ubica entre los 12 mejores puntajes en sostenibilidad a nivel mundial en el…

8 January, 2026

Cómo el Seguro para copropiedades le acompaña en los retos del año nuevo

Descubra cómo el Seguro para Copropiedades le ayuda a enfrentar los retos del año nuevo con confianza y protección.

8 January, 2026

Evite el sedentarismo en vacaciones y priorice su salud

Consejos prácticos para evitar el sedentarismo y cuidar su bienestar físico y mental durante las vacaciones.

6 January, 2026

Exámenes médicos para iniciar el 2026

¡Comience el año cuidando su salud! Estos son algunos exámenes de rigor que se debe realizar. ¡Tome lápiz y papel!

6 January, 2026