{"id":70825,"date":"2026-05-27T08:18:56","date_gmt":"2026-05-27T06:18:56","guid":{"rendered":"https:\/\/tickelia.com\/demo_2023\/?p=70825"},"modified":"2026-05-27T08:19:00","modified_gmt":"2026-05-27T06:19:00","slug":"prueba-calculadora-comunidades","status":"publish","type":"post","link":"https:\/\/tickelia.com\/demo_2023\/blog\/actualidad\/prueba-calculadora-comunidades\/","title":{"rendered":"Prueba calculadora comunidades"},"content":{"rendered":"<div class=\"calc-laborables\">\r\n  <style>\r\n    \/* \u00c1mbito aislado para evitar conflictos de estilos *\/\r\n    .calc-laborables {\r\n      --card: #ffffff;\r\n      --text: #172033;\r\n      --muted: #64748b;\r\n      --primary: #2563eb;\r\n      --primary-dark: #1d4ed8;\r\n      --border: #dbe3ef;\r\n      --soft: #eef4ff;\r\n      --danger: #fee2e2;\r\n      --danger-text: #991b1b;\r\n      --weekend: #f1f5f9;\r\n      --holiday: #fff1f2;\r\n      --workday: #ecfdf5;\r\n      --shadow: 0 18px 45px rgba(15, 23, 42, .08);\r\n      --radius: 18px;\r\n      \r\n      max-width: 1180px; \r\n      margin: 0 auto;\r\n      box-sizing: border-box;\r\n    }\r\n\r\n    .calc-laborables * { \r\n      box-sizing: border-box; \r\n    }\r\n\r\n    .calc-laborables .calc-hero, \r\n    .calc-laborables .calc-panel {\r\n      background: rgba(255, 255, 255, .9);\r\n      border: 1px solid rgba(219, 227, 239, .95);\r\n      border-radius: var(--radius);\r\n      box-shadow: var(--shadow);\r\n      padding: 24px;\r\n      margin-bottom: 20px;\r\n    }\r\n\r\n    .calc-laborables .calc-hero {\r\n      backdrop-filter: blur(10px);\r\n    }\r\n\r\n    .calc-laborables h1.calc-title {\r\n      margin: 0 0 8px;\r\n      font-size: clamp(1.8rem, 4vw, 3rem);\r\n      letter-spacing: -0.04em;\r\n      color: var(--text);\r\n    }\r\n\r\n    .calc-laborables h2.calc-section-title {\r\n      margin: 0 0 16px;\r\n      font-size: 1.25rem;\r\n      letter-spacing: -0.02em;\r\n      color: var(--text);\r\n    }\r\n\r\n    .calc-laborables .subtitle, \r\n    .calc-laborables .note {\r\n      color: var(--muted);\r\n      margin: 0;\r\n      line-height: 1.55;\r\n    }\r\n\r\n    .calc-laborables .grid {\r\n      display: grid;\r\n      grid-template-columns: repeat(12, 1fr);\r\n      gap: 16px;\r\n      align-items: end;\r\n    }\r\n\r\n    .calc-laborables .field { display: flex; flex-direction: column; gap: 7px; }\r\n    .calc-laborables .span-2 { grid-column: span 2; }\r\n    .calc-laborables .span-3 { grid-column: span 3; }\r\n    .calc-laborables .span-4 { grid-column: span 4; }\r\n    .calc-laborables .span-6 { grid-column: span 6; }\r\n    .calc-laborables .span-12 { grid-column: span 12; }\r\n\r\n    .calc-laborables label {\r\n      font-size: .88rem;\r\n      font-weight: 700;\r\n      color: #334155;\r\n    }\r\n\r\n    .calc-laborables select, \r\n    .calc-laborables input {\r\n      width: 100%;\r\n      height: 44px;\r\n      border: 1px solid var(--border);\r\n      border-radius: 12px;\r\n      padding: 0 12px;\r\n      background: #fff;\r\n      color: var(--text);\r\n      font-size: .98rem;\r\n      outline: none;\r\n      transition: border-color .2s, box-shadow .2s;\r\n    }\r\n\r\n    .calc-laborables select:focus, \r\n    .calc-laborables input:focus {\r\n      border-color: var(--primary);\r\n      box-shadow: 0 0 0 4px rgba(37, 99, 235, .12);\r\n    }\r\n\r\n    .calc-laborables button.calc-btn {\r\n      height: 44px;\r\n      border: 0;\r\n      border-radius: 12px;\r\n      background: var(--primary);\r\n      color: white;\r\n      font-weight: 800;\r\n      padding: 0 18px;\r\n      cursor: pointer;\r\n      transition: background .2s, transform .2s;\r\n    }\r\n\r\n    .calc-laborables button.calc-btn:hover { background: var(--primary-dark); transform: translateY(-1px); }\r\n    .calc-laborables button.calc-btn:disabled { background: #94a3b8; cursor: not-allowed; transform: none; }\r\n\r\n    .calc-laborables .status {\r\n      display: none;\r\n      margin-top: 14px;\r\n      border-radius: 12px;\r\n      padding: 12px 14px;\r\n      font-size: .95rem;\r\n      line-height: 1.45;\r\n    }\r\n\r\n    .calc-laborables .status.show { display: block; }\r\n    .calc-laborables .status.info { background: var(--soft); color: #1e40af; }\r\n    .calc-laborables .status.error { background: var(--danger); color: var(--danger-text); }\r\n\r\n    .calc-laborables .results {\r\n      display: grid;\r\n      grid-template-columns: repeat(4, 1fr);\r\n      gap: 14px;\r\n    }\r\n\r\n    .calc-laborables .metric {\r\n      border: 1px solid var(--border);\r\n      border-radius: 16px;\r\n      padding: 16px;\r\n      background: linear-gradient(180deg, #fff 0%, #f8fafc 100%);\r\n    }\r\n\r\n    .calc-laborables .metric small {\r\n      display: block;\r\n      color: var(--muted);\r\n      font-weight: 700;\r\n      margin-bottom: 6px;\r\n    }\r\n\r\n    .calc-laborables .metric strong {\r\n      display: block;\r\n      font-size: 2rem;\r\n      letter-spacing: -0.04em;\r\n      color: var(--text);\r\n    }\r\n\r\n    .calc-laborables .months {\r\n      display: grid;\r\n      grid-template-columns: repeat(auto-fit, minmax(285px, 1fr));\r\n      gap: 16px;\r\n    }\r\n\r\n    .calc-laborables .month {\r\n      border: 1px solid var(--border);\r\n      border-radius: 16px;\r\n      overflow: hidden;\r\n      background: #fff;\r\n    }\r\n\r\n    .calc-laborables .month-title {\r\n      padding: 12px 14px;\r\n      background: #f8fafc;\r\n      border-bottom: 1px solid var(--border);\r\n      font-weight: 900;\r\n      text-transform: capitalize;\r\n      color: var(--text);\r\n    }\r\n\r\n    .calc-laborables .weekdays, \r\n    .calc-laborables .days {\r\n      display: grid;\r\n      grid-template-columns: repeat(7, 1fr);\r\n    }\r\n\r\n    .calc-laborables .weekdays div {\r\n      padding: 8px 4px;\r\n      text-align: center;\r\n      font-size: .72rem;\r\n      color: var(--muted);\r\n      font-weight: 800;\r\n      background: #fbfdff;\r\n      border-bottom: 1px solid var(--border);\r\n    }\r\n\r\n    .calc-laborables .day {\r\n      min-height: 42px;\r\n      border-right: 1px solid #eef2f7;\r\n      border-bottom: 1px solid #eef2f7;\r\n      padding: 5px;\r\n      font-size: .8rem;\r\n      position: relative;\r\n      color: var(--text);\r\n    }\r\n\r\n    .calc-laborables .day:nth-child(7n) { border-right: 0; }\r\n    .calc-laborables .day.empty { background: #fbfdff; }\r\n    .calc-laborables .day.workday { background: var(--workday); }\r\n    .calc-laborables .day.weekend { background: var(--weekend); color: #64748b; }\r\n    .calc-laborables .day.holiday { background: var(--holiday); color: #9f1239; font-weight: 800; }\r\n    .calc-laborables .day.out { opacity: .36; }\r\n\r\n    .calc-laborables .dot {\r\n      position: absolute;\r\n      right: 5px;\r\n      bottom: 5px;\r\n      width: 6px;\r\n      height: 6px;\r\n      border-radius: 999px;\r\n      background: currentColor;\r\n    }\r\n\r\n    .calc-laborables .holiday-list {\r\n      margin: 0;\r\n      padding: 0;\r\n      list-style: none;\r\n      display: grid;\r\n      gap: 10px;\r\n    }\r\n\r\n    .calc-laborables .holiday-list li {\r\n      border: 1px solid var(--border);\r\n      border-radius: 14px;\r\n      padding: 11px 13px;\r\n      background: #fff;\r\n      display: flex;\r\n      justify-content: space-between;\r\n      gap: 14px;\r\n      align-items: center;\r\n    }\r\n\r\n    .calc-laborables .badge {\r\n      display: inline-flex;\r\n      align-items: center;\r\n      border-radius: 999px;\r\n      padding: 4px 9px;\r\n      font-size: .75rem;\r\n      font-weight: 800;\r\n      background: #e0f2fe;\r\n      color: #075985;\r\n      white-space: nowrap;\r\n    }\r\n\r\n    .calc-laborables .legend {\r\n      display: flex;\r\n      flex-wrap: wrap;\r\n      gap: 10px;\r\n      margin-bottom: 14px;\r\n      color: var(--muted);\r\n      font-size: .86rem;\r\n    }\r\n\r\n    .calc-laborables .legend span {\r\n      display: inline-flex;\r\n      align-items: center;\r\n      gap: 6px;\r\n    }\r\n\r\n    .calc-laborables .square {\r\n      width: 13px;\r\n      height: 13px;\r\n      border-radius: 4px;\r\n      border: 1px solid var(--border);\r\n    }\r\n\r\n    .calc-laborables .sq-work { background: var(--workday); }\r\n    .calc-laborables .sq-weekend { background: var(--weekend); }\r\n    .calc-laborables .sq-holiday { background: var(--holiday); }\r\n\r\n    .calc-laborables .note {\r\n      font-size: .86rem;\r\n      margin-top: 12px;\r\n    }\r\n\r\n    .calc-laborables .note a {\r\n      color: var(--primary);\r\n      font-weight: 800;\r\n    }\r\n\r\n    @media (max-width: 860px) {\r\n      .calc-laborables .span-3, \r\n      .calc-laborables .span-4, \r\n      .calc-laborables .span-6 { grid-column: span 12; }\r\n      .calc-laborables .results { grid-template-columns: repeat(2, 1fr); }\r\n    }\r\n\r\n    @media (max-width: 560px) {\r\n      .calc-laborables .hero, \r\n      .calc-laborables .panel { padding: 18px; }\r\n      .calc-laborables .results { grid-template-columns: 1fr; }\r\n    }\r\n  <\/style>\r\n\r\n  <section class=\"calc-hero\">\r\n    <h1 class=\"calc-title\">Calculadora de d\u00edas laborables por comunidad aut\u00f3noma<\/h1>\r\n    <p class=\"subtitle\">\r\n      Calcula d\u00edas laborables entre dos fechas descontando s\u00e1bados, domingos y festivos nacionales\/auton\u00f3micos.\r\n      Los datos se cargan desde la API p\u00fablica Nager.Date.\r\n    <\/p>\r\n  <\/section>\r\n\r\n  <section class=\"calc-panel\">\r\n    <div class=\"grid\">\r\n      <div class=\"field span-3\">\r\n        <label for=\"startDate\">Fecha de inicio<\/label>\r\n        <input type=\"date\" id=\"startDate\" min=\"1975-01-01\" max=\"2075-12-31\" value=\"2026-01-01\">\r\n      <\/div>\r\n\r\n      <div class=\"field span-3\">\r\n        <label for=\"endDate\">Fecha final<\/label>\r\n        <input type=\"date\" id=\"endDate\" min=\"1975-01-01\" max=\"2075-12-31\" value=\"2026-12-31\">\r\n      <\/div>\r\n\r\n      <div class=\"field span-4\">\r\n        <label for=\"region\">Comunidad aut\u00f3noma<\/label>\r\n        <select id=\"region\"><\/select>\r\n      <\/div>\r\n\r\n      <div class=\"field span-2\">\r\n        <label for=\"hours\">Horas\/d\u00eda<\/label>\r\n        <input type=\"number\" id=\"hours\" min=\"0\" step=\"0.5\" value=\"8\">\r\n      <\/div>\r\n\r\n      <div class=\"field span-12\">\r\n        <button id=\"calculateBtn\" class=\"calc-btn\">Calcular d\u00edas laborables<\/button>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <div id=\"status\" class=\"status\"><\/div>\r\n\r\n    <p class=\"note\">\r\n      Fuente: <a href=\"https:\/\/date.nager.at\/api\" target=\"_blank\" rel=\"noopener\">Nager.Date API<\/a>.\r\n      La API indica que no tiene l\u00edmites de consulta y que permite CORS para uso en navegador.\r\n      Esta versi\u00f3n no incluye festivos municipales\/locales.\r\n    <\/p>\r\n  <\/section>\r\n\r\n  <section class=\"calc-panel\" id=\"summaryPanel\" style=\"display:none;\">\r\n    <div class=\"results\">\r\n      <div class=\"metric\"><small>D\u00edas naturales<\/small><strong id=\"totalDays\">0<\/strong><\/div>\r\n      <div class=\"metric\"><small>D\u00edas laborables<\/small><strong id=\"workDays\">0<\/strong><\/div>\r\n      <div class=\"metric\"><small>Festivos aplicados<\/small><strong id=\"holidayDays\">0<\/strong><\/div>\r\n      <div class=\"metric\"><small>Horas laborables<\/small><strong id=\"workHours\">0<\/strong><\/div>\r\n    <\/div>\r\n  <\/section>\r\n\r\n  <section class=\"calc-panel\" id=\"holidaysPanel\" style=\"display:none;\">\r\n    <h2 class=\"calc-section-title\">Festivos dentro del rango<\/h2>\r\n    <ul id=\"holidayList\" class=\"holiday-list\"><\/ul>\r\n  <\/section>\r\n\r\n  <section class=\"calc-panel\" id=\"calendarPanel\" style=\"display:none;\">\r\n    <h2 class=\"calc-section-title\">Calendario visual<\/h2>\r\n    <div class=\"legend\">\r\n      <span><i class=\"square sq-work\"><\/i> Laborable<\/span>\r\n      <span><i class=\"square sq-weekend\"><\/i> Fin de semana<\/span>\r\n      <span><i class=\"square sq-holiday\"><\/i> Festivo<\/span>\r\n    <\/div>\r\n    <div id=\"months\" class=\"months\"><\/div>\r\n  <\/section>\r\n\r\n  <script>\r\n    (function() {\r\n      const API_BASE = \"https:\/\/date.nager.at\/api\/v3\/PublicHolidays\";\r\n\r\n      const REGIONS = [\r\n        { code: \"ALL\", name: \"Toda Espa\u00f1a \/ solo festivos nacionales\" },\r\n        { code: \"ES-AN\", name: \"Andaluc\u00eda\" },\r\n        { code: \"ES-AR\", name: \"Arag\u00f3n\" },\r\n        { code: \"ES-AS\", name: \"Principado de Asturias\" },\r\n        { code: \"ES-CB\", name: \"Cantabria\" },\r\n        { code: \"ES-CE\", name: \"Ceuta\" },\r\n        { code: \"ES-CL\", name: \"Castilla y Le\u00f3n\" },\r\n        { code: \"ES-CM\", name: \"Castilla-La Mancha\" },\r\n        { code: \"ES-CN\", name: \"Canarias\" },\r\n        { code: \"ES-CT\", name: \"Catalu\u00f1a\" },\r\n        { code: \"ES-EX\", name: \"Extremadura\" },\r\n        { code: \"ES-GA\", name: \"Galicia\" },\r\n        { code: \"ES-IB\", name: \"Illes Balears\" },\r\n        { code: \"ES-MC\", name: \"Regi\u00f3n de Murcia\" },\r\n        { code: \"ES-MD\", name: \"Comunidad de Madrid\" },\r\n        { code: \"ES-ML\", name: \"Melilla\" },\r\n        { code: \"ES-NC\", name: \"Comunidad Foral de Navarra\" },\r\n        { code: \"ES-PV\", name: \"Pa\u00eds Vasco\" },\r\n        { code: \"ES-RI\", name: \"La Rioja\" },\r\n        { code: \"ES-VC\", name: \"Comunitat Valenciana\" }\r\n      ];\r\n\r\n      const els = {\r\n        startDate: document.getElementById(\"startDate\"),\r\n        endDate: document.getElementById(\"endDate\"),\r\n        region: document.getElementById(\"region\"),\r\n        hours: document.getElementById(\"hours\"),\r\n        calculateBtn: document.getElementById(\"calculateBtn\"),\r\n        status: document.getElementById(\"status\"),\r\n        summaryPanel: document.getElementById(\"summaryPanel\"),\r\n        holidaysPanel: document.getElementById(\"holidaysPanel\"),\r\n        calendarPanel: document.getElementById(\"calendarPanel\"),\r\n        totalDays: document.getElementById(\"totalDays\"),\r\n        workDays: document.getElementById(\"workDays\"),\r\n        holidayDays: document.getElementById(\"holidayDays\"),\r\n        workHours: document.getElementById(\"workHours\"),\r\n        holidayList: document.getElementById(\"holidayList\"),\r\n        months: document.getElementById(\"months\")\r\n      };\r\n\r\n      const cache = new Map();\r\n\r\n      function initRegions() {\r\n        els.region.innerHTML = \"\";\r\n        REGIONS.forEach(region => {\r\n          const opt = document.createElement(\"option\");\r\n          opt.value = region.code;\r\n          opt.textContent = region.name;\r\n          els.region.appendChild(opt);\r\n        });\r\n        els.region.value = \"ES-CT\";\r\n      }\r\n\r\n      function showStatus(message, type = \"info\") {\r\n        els.status.textContent = message;\r\n        els.status.className = `status show ${type}`;\r\n      }\r\n\r\n      function clearStatus() {\r\n        els.status.textContent = \"\";\r\n        els.status.className = \"status\";\r\n      }\r\n\r\n      async function fetchHolidays(year) {\r\n        if (cache.has(year)) return cache.get(year);\r\n        const url = `${API_BASE}\/${year}\/ES`;\r\n        const res = await fetch(url, { cache: \"force-cache\" });\r\n        if (!res.ok) throw new Error(`No se pudieron cargar los festivos de ${year}. C\u00f3digo: ${res.status}`);\r\n        const data = await res.json();\r\n        cache.set(year, data);\r\n        return data;\r\n      }\r\n\r\n      function yearsInRange(start, end) {\r\n        const startYear = new Date(start + \"T00:00:00\").getFullYear();\r\n        const endYear = new Date(end + \"T00:00:00\").getFullYear();\r\n        const years = [];\r\n        for (let y = startYear; y <= endYear; y++) years.push(y);\r\n        return years;\r\n      }\r\n\r\n      function validateDates(start, end) {\r\n        if (!start || !end) throw new Error(\"Selecciona fecha de inicio y fecha final.\");\r\n        if (new Date(start) > new Date(end)) throw new Error(\"La fecha final debe ser igual o posterior a la fecha de inicio.\");\r\n        const years = yearsInRange(start, end);\r\n        if (years.length > 8) throw new Error(\"Para evitar demasiadas llamadas a la API, usa un rango m\u00e1ximo de 8 a\u00f1os.\");\r\n      }\r\n\r\n      function appliesToRegion(holiday, regionCode) {\r\n        const counties = holiday.counties;\r\n        if (regionCode === \"ALL\") return !counties || counties.length === 0;\r\n        return !counties || counties.length === 0 || counties.includes(regionCode);\r\n      }\r\n\r\n      function normalizeHoliday(h) {\r\n        return {\r\n          date: h.date,\r\n          name: h.localName || h.name || \"Festivo\",\r\n          globalName: h.name || h.localName || \"Festivo\",\r\n          regionCodes: h.counties || [],\r\n          type: Array.isArray(h.types) ? h.types.join(\", \") : \"Public\"\r\n        };\r\n      }\r\n\r\n      async function loadHolidaysForRange(start, end, regionCode) {\r\n        const all = [];\r\n        for (const year of yearsInRange(start, end)) {\r\n          const data = await fetchHolidays(year);\r\n          all.push(...data.filter(h => appliesToRegion(h, regionCode)).map(normalizeHoliday));\r\n        }\r\n\r\n        const deduped = new Map();\r\n        all.forEach(h => deduped.set(`${h.date}|${h.name}`, h));\r\n\r\n        return [...deduped.values()]\r\n          .filter(h => h.date >= start && h.date <= end)\r\n          .sort((a, b) => a.date.localeCompare(b.date));\r\n      }\r\n\r\n      function eachDate(start, end, cb) {\r\n        const d = new Date(start + \"T00:00:00\");\r\n        const last = new Date(end + \"T00:00:00\");\r\n        while (d <= last) {\r\n          cb(new Date(d));\r\n          d.setDate(d.getDate() + 1);\r\n        }\r\n      }\r\n\r\n      function iso(d) {\r\n        return d.toISOString().slice(0, 10);\r\n      }\r\n\r\n      function isWeekend(d) {\r\n        const day = d.getDay();\r\n        return day === 0 || day === 6;\r\n      }\r\n\r\n      function formatDate(date) {\r\n        return new Date(date + \"T00:00:00\").toLocaleDateString(\"es-ES\", {\r\n          weekday: \"short\",\r\n          day: \"2-digit\",\r\n          month: \"short\",\r\n          year: \"numeric\"\r\n        });\r\n      }\r\n\r\n      function regionName(code) {\r\n        return REGIONS.find(r => r.code === code)?.name || code;\r\n      }\r\n\r\n      async function handleCalculate() {\r\n        const start = els.startDate.value;\r\n        const end = els.endDate.value;\r\n        const regionCode = els.region.value;\r\n\r\n        try {\r\n          validateDates(start, end);\r\n          els.calculateBtn.disabled = true;\r\n          showStatus(`Cargando festivos para ${regionName(regionCode)}...`);\r\n\r\n          const holidays = await loadHolidaysForRange(start, end, regionCode);\r\n          const holidayByDate = new Map();\r\n\r\n          holidays.forEach(h => {\r\n            if (!holidayByDate.has(h.date)) holidayByDate.set(h.date, []);\r\n            holidayByDate.get(h.date).push(h);\r\n          });\r\n\r\n          let total = 0;\r\n          let holidayCount = 0;\r\n          let workCount = 0;\r\n\r\n          eachDate(start, end, d => {\r\n            total++;\r\n            const date = iso(d);\r\n            const weekend = isWeekend(d);\r\n            const holiday = holidayByDate.has(date);\r\n\r\n            if (holiday && !weekend) holidayCount++;\r\n            if (!weekend && !holiday) workCount++;\r\n          });\r\n\r\n          els.totalDays.textContent = total;\r\n          els.workDays.textContent = workCount;\r\n          els.holidayDays.textContent = holidayCount;\r\n          els.workHours.textContent = String(workCount * Number(els.hours.value || 0)).replace(\".\", \",\");\r\n\r\n          renderHolidayList(holidays);\r\n          renderCalendar(start, end, holidayByDate);\r\n\r\n          els.summaryPanel.style.display = \"block\";\r\n          els.holidaysPanel.style.display = \"block\";\r\n          els.calendarPanel.style.display = \"block\";\r\n\r\n          clearStatus();\r\n        } catch (err) {\r\n          showStatus(err.message || \"Ha ocurrido un error al calcular.\", \"error\");\r\n        } finally {\r\n          els.calculateBtn.disabled = false;\r\n        }\r\n      }\r\n\r\n      function renderHolidayList(holidays) {\r\n        els.holidayList.innerHTML = \"\";\r\n\r\n        if (!holidays.length) {\r\n          const li = document.createElement(\"li\");\r\n          li.textContent = \"No hay festivos en el rango seleccionado.\";\r\n          els.holidayList.appendChild(li);\r\n          return;\r\n        }\r\n\r\n        holidays.forEach(h => {\r\n          const li = document.createElement(\"li\");\r\n\r\n          const left = document.createElement(\"div\");\r\n          left.innerHTML = `\r\n            <strong>${formatDate(h.date)}<\/strong><br>\r\n            <span>${escapeHtml(h.name)}<\/span>\r\n          `;\r\n\r\n          const badge = document.createElement(\"span\");\r\n          badge.className = \"badge\";\r\n          badge.textContent = h.regionCodes.length ? \"Auton\u00f3mico\" : \"Nacional\";\r\n\r\n          li.appendChild(left);\r\n          li.appendChild(badge);\r\n          els.holidayList.appendChild(li);\r\n        });\r\n      }\r\n\r\n      function renderCalendar(start, end, holidayByDate) {\r\n        els.months.innerHTML = \"\";\r\n\r\n        const startD = new Date(start + \"T00:00:00\");\r\n        const endD = new Date(end + \"T00:00:00\");\r\n        const cursor = new Date(startD.getFullYear(), startD.getMonth(), 1);\r\n\r\n        while (cursor <= endD) {\r\n          const monthEl = document.createElement(\"article\");\r\n          monthEl.className = \"month\";\r\n\r\n          const title = document.createElement(\"div\");\r\n          title.className = \"month-title\";\r\n          title.textContent = cursor.toLocaleDateString(\"es-ES\", { month: \"long\", year: \"numeric\" });\r\n          monthEl.appendChild(title);\r\n\r\n          const weekdays = document.createElement(\"div\");\r\n          weekdays.className = \"weekdays\";\r\n          [\"L\", \"M\", \"X\", \"J\", \"V\", \"S\", \"D\"].forEach(d => {\r\n            const el = document.createElement(\"div\");\r\n            el.textContent = d;\r\n            weekdays.appendChild(el);\r\n          });\r\n          monthEl.appendChild(weekdays);\r\n\r\n          const days = document.createElement(\"div\");\r\n          days.className = \"days\";\r\n\r\n          const firstDay = new Date(cursor.getFullYear(), cursor.getMonth(), 1);\r\n          const startOffset = (firstDay.getDay() + 6) % 7;\r\n\r\n          for (let i = 0; i < startOffset; i++) {\r\n            const empty = document.createElement(\"div\");\r\n            empty.className = \"day empty\";\r\n            days.appendChild(empty);\r\n          }\r\n\r\n          const daysInMonth = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0).getDate();\r\n\r\n          for (let day = 1; day <= daysInMonth; day++) {\r\n            const d = new Date(cursor.getFullYear(), cursor.getMonth(), day);\r\n            const date = iso(d);\r\n            const cell = document.createElement(\"div\");\r\n            const out = d < startD || d > endD;\r\n            const holiday = holidayByDate.has(date);\r\n            const weekend = isWeekend(d);\r\n\r\n            cell.className = `day ${holiday ? \"holiday\" : weekend ? \"weekend\" : \"workday\"} ${out ? \"out\" : \"\"}`;\r\n            cell.textContent = day;\r\n\r\n            if (holiday) {\r\n              cell.title = holidayByDate.get(date).map(h => h.name).join(\" \/ \");\r\n              const dot = document.createElement(\"span\");\r\n              dot.className = \"dot\";\r\n              cell.appendChild(dot);\r\n            }\r\n\r\n            days.appendChild(cell);\r\n          }\r\n\r\n          monthEl.appendChild(days);\r\n          els.months.appendChild(monthEl);\r\n          cursor.setMonth(cursor.getMonth() + 1);\r\n        }\r\n      }\r\n\r\n      function escapeHtml(str) {\r\n        return String(str).replace(\/[&<>\"']\/g, s => ({\r\n          \"&\": \"&amp;\",\r\n          \"<\": \"&lt;\",\r\n          \">\": \"&gt;\",\r\n          '\"': \"&quot;\",\r\n          \"'\": \"&#039;\"\r\n        }[s]));\r\n      }\r\n\r\n      els.calculateBtn.addEventListener(\"click\", handleCalculate);\r\n      initRegions();\r\n    })();\r\n  <\/script>\r\n<\/div>\r\n\r\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":18,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[533],"tags":[],"class_list":["post-70825","post","type-post","status-publish","format-standard","hentry","category-actualidad"],"acf":[],"lang":"es","translations":{"es":70825},"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/posts\/70825","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/users\/18"}],"replies":[{"embeddable":true,"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/comments?post=70825"}],"version-history":[{"count":1,"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/posts\/70825\/revisions"}],"predecessor-version":[{"id":70827,"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/posts\/70825\/revisions\/70827"}],"wp:attachment":[{"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/media?parent=70825"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/categories?post=70825"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tickelia.com\/demo_2023\/wp-json\/wp\/v2\/tags?post=70825"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}