{"id":6377,"date":"2026-04-09T14:27:42","date_gmt":"2026-04-09T14:27:42","guid":{"rendered":"https:\/\/spacecampvalencia.es\/?page_id=6377"},"modified":"2026-04-22T15:37:56","modified_gmt":"2026-04-22T15:37:56","slug":"envio-documentacion","status":"publish","type":"page","link":"https:\/\/spacecampvalencia.es\/en\/envio-documentacion","title":{"rendered":"Env\u00edo de Documentaci\u00f3n Firmada"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"6377\" class=\"elementor elementor-6377\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1d00ed32 e-flex e-con-boxed e-con e-parent\" data-id=\"1d00ed32\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-78d8de91 elementor-widget__width-initial elementor-absolute elementor-widget elementor-widget-image\" data-id=\"78d8de91\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"395\" height=\"401\" src=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet.png\" class=\"attachment-full size-full wp-image-2411\" alt=\"\" srcset=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet.png 395w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet-296x300.png 296w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet-12x12.png 12w\" sizes=\"(max-width: 395px) 100vw, 395px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-125d8529 elementor-widget__width-initial elementor-absolute elementor-widget elementor-widget-image\" data-id=\"125d8529\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"671\" height=\"479\" src=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet2.png\" class=\"attachment-full size-full wp-image-2412\" alt=\"\" srcset=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet2.png 671w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet2-300x214.png 300w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet2-18x12.png 18w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Planet2-600x428.png 600w\" sizes=\"(max-width: 671px) 100vw, 671px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-11130258 elementor-widget__width-initial elementor-absolute e-transform elementor-widget elementor-widget-image\" data-id=\"11130258\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;,&quot;_transform_rotateZ_effect&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:119,&quot;sizes&quot;:[]},&quot;_transform_rotateZ_effect_tablet&quot;:{&quot;unit&quot;:&quot;deg&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_rotateZ_effect_mobile&quot;:{&quot;unit&quot;:&quot;deg&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1046\" height=\"1015\" src=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars2.png\" class=\"attachment-full size-full wp-image-2414\" alt=\"\" srcset=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars2.png 1046w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars2-300x291.png 300w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars2-1024x994.png 1024w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars2-768x745.png 768w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars2-12x12.png 12w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars2-600x582.png 600w\" sizes=\"(max-width: 1046px) 100vw, 1046px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4fcc7293 elementor-widget__width-initial elementor-absolute e-transform elementor-widget elementor-widget-image\" data-id=\"4fcc7293\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;,&quot;_transform_rotateZ_effect&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:-24,&quot;sizes&quot;:[]},&quot;_transform_rotateZ_effect_tablet&quot;:{&quot;unit&quot;:&quot;deg&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_rotateZ_effect_mobile&quot;:{&quot;unit&quot;:&quot;deg&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"227\" height=\"230\" src=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars.png\" class=\"attachment-full size-full wp-image-2413\" alt=\"\" srcset=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars.png 227w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars-12x12.png 12w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars-100x100.png 100w\" sizes=\"(max-width: 227px) 100vw, 227px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-23292157 elementor-widget__width-initial elementor-absolute e-transform elementor-widget elementor-widget-image\" data-id=\"23292157\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;,&quot;_transform_rotateZ_effect&quot;:{&quot;unit&quot;:&quot;px&quot;,&quot;size&quot;:123,&quot;sizes&quot;:[]},&quot;_transform_rotateZ_effect_tablet&quot;:{&quot;unit&quot;:&quot;deg&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]},&quot;_transform_rotateZ_effect_mobile&quot;:{&quot;unit&quot;:&quot;deg&quot;,&quot;size&quot;:&quot;&quot;,&quot;sizes&quot;:[]}}\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"227\" height=\"230\" src=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars.png\" class=\"attachment-full size-full wp-image-2413\" alt=\"\" srcset=\"https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars.png 227w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars-12x12.png 12w, https:\/\/spacecampvalencia.es\/wp-content\/uploads\/2024\/03\/Stars-100x100.png 100w\" sizes=\"(max-width: 227px) 100vw, 227px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-10405aec elementor-widget elementor-widget-spacer\" data-id=\"10405aec\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"spacer.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-spacer\">\n\t\t\t<div class=\"elementor-spacer-inner\"><\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2a412371 elementor-widget elementor-widget-heading\" data-id=\"2a412371\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h1 class=\"elementor-heading-title elementor-size-default\">COMPROBACI\u00d3N DE FIRMAS\n<\/h1>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-66e63449 elementor-widget elementor-widget-heading\" data-id=\"66e63449\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h1 class=\"elementor-heading-title elementor-size-default\">CURSO DE PILOTO DE ULM<\/h1>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-6cee537 e-con-full e-flex e-con e-child\" data-id=\"6cee537\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1ffe1b4 elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"1ffe1b4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!-- ============================================================\r\n     VALIDADOR DE FIRMAS DIGITALES PDF - Elementor Forms\r\n     Bloquea el env\u00edo si el PDF no tiene firma v\u00e1lida\r\n     ============================================================ -->\r\n\r\n<div id=\"vfp-wrap\" style=\"max-width:100%;margin:1.5em auto;font-family:system-ui,-apple-system,sans-serif;\">\r\n  <div style=\"padding:1.5em 1.5em 0.5em;\">\r\n    <h3 style=\"margin:0 0 0.3em;\">Validaci\u00f3n de autorizaci\u00f3n firmada<\/h3>\r\n    <p style=\"color:#555;font-size:0.9em;margin:0;\">\r\n      Sube el PDF de la autorizaci\u00f3n. Comprobaremos sus firmas digitales antes de permitir el env\u00edo.\r\n    <\/p>\r\n    <div id=\"vfp-status\" style=\"margin-top:1em;color:#666;font-weight:500;\"><\/div>\r\n  <\/div>\r\n  <div id=\"vfp-result\" style=\"margin-top:0.5em;padding:0 1.5em;\"><\/div>\r\n<\/div>\r\n\r\n<script type=\"importmap\">\r\n{\r\n  \"imports\": {\r\n    \"asn1js\": \"https:\/\/esm.sh\/asn1js@3.0.5\",\r\n    \"pkijs\":  \"https:\/\/esm.sh\/pkijs@3.2.4\",\r\n    \"pvutils\":\"https:\/\/esm.sh\/pvutils@1.1.3\"\r\n  }\r\n}\r\n<\/script>\r\n\r\n<script type=\"module\">\r\nimport * as asn1js from 'asn1js';\r\nimport * as pkijs  from 'pkijs';\r\n\r\n\/\/ === CONFIGURACI\u00d3N ========================================================\r\nconst CAMPO_AUTORIZACION = 'autorizacion';\r\nconst CAMPO_PATRIA       = 'patriapotestad';\r\nconst CAMPO_FIRMANTES    = 'firmantes';\r\nconst CAMPO_SOURCE_ID    = 'sourceid';\r\nconst URL_PARAM_ID       = 'id';\r\n\/\/ ==========================================================================\r\n\r\nconst status = document.getElementById('vfp-status');\r\nconst result = document.getElementById('vfp-result');\r\n\r\n\/\/ Estado\r\nlet autorizacionOK  = false;\r\nlet firmantesCount  = 0;\r\nlet patriaFile      = null;\r\n\r\nconst OID_NAMES = {\r\n  '2.5.4.3':'Nombre com\u00fan (CN)','2.5.4.6':'Pa\u00eds','2.5.4.7':'Localidad',\r\n  '2.5.4.8':'Provincia','2.5.4.10':'Organizaci\u00f3n','2.5.4.11':'Departamento',\r\n  '2.5.4.4':'Apellidos','2.5.4.42':'Nombre','2.5.4.5':'N\u00famero de serie',\r\n  '1.2.840.113549.1.9.1':'Email','2.5.4.97':'Identificador de organizaci\u00f3n'\r\n};\r\n\r\n\/\/ --- Localizadores --------------------------------------------------------\r\nfunction getInputPorId(id) {\r\n  return document.querySelector(`[name=\"form_fields[${id}]\"]`);\r\n}\r\nfunction getInputArchivo() {\r\n  return document.querySelector(`input[type=\"file\"][name=\"form_fields[${CAMPO_AUTORIZACION}]\"]`)\r\n      || document.querySelector(`input[type=\"file\"][name*=\"${CAMPO_AUTORIZACION}\"]`);\r\n}\r\nfunction getInputPatria() {\r\n  return document.querySelector(`input[type=\"file\"][name=\"form_fields[${CAMPO_PATRIA}]\"]`);\r\n}\r\nfunction getWrapperPatria() {\r\n  const input = getInputPatria();\r\n  if (!input) return null;\r\n  return input.closest('.elementor-field-group') || input.closest('.elementor-field-type-upload');\r\n}\r\nfunction getFormulario() {\r\n  const input = getInputArchivo();\r\n  return input ? input.closest('form') : null;\r\n}\r\nfunction getBotonSubmit() {\r\n  const form = getFormulario();\r\n  if (!form) return null;\r\n  return form.querySelector('button[type=\"submit\"], input[type=\"submit\"]');\r\n}\r\n\r\n\/\/ --- Source ID desde la URL ----------------------------------------------\r\nfunction aplicarSourceId() {\r\n  let id = '';\r\n  try {\r\n    id = new URLSearchParams(window.location.search).get(URL_PARAM_ID) || '';\r\n  } catch (e) {}\r\n  if (!id) return;\r\n  const input = getInputPorId(CAMPO_SOURCE_ID);\r\n  if (input && input.value !== id) input.value = id;\r\n}\r\n\r\n\/\/ --- Campo firmantes ------------------------------------------------------\r\nfunction setFirmantes(n) {\r\n  const input = getInputPorId(CAMPO_FIRMANTES);\r\n  if (input) input.value = n > 0 ? String(n) : '';\r\n}\r\n\r\nfunction setNombresFirmantes(nombres) {\r\n  const slots = ['firmante1', 'firmante2'];\r\n  slots.forEach((id, i) => {\r\n    const input = getInputPorId(id);\r\n    if (input) input.value = nombres[i] || '';\r\n  });\r\n}\r\n\r\n\/\/ --- Chip de archivo validado --------------------------------------------\r\n\/\/ Cuando un archivo queda validado\/adjuntado ocultamos el input file y\r\n\/\/ mostramos un chip verde con el estado + bot\u00f3n \"Cambiar\". El input sigue\r\n\/\/ en el DOM con su archivo, as\u00ed que se enviar\u00e1 con el formulario.\r\nfunction setIndicador(input, texto) {\r\n  if (!input) return;\r\n  const padre = input.parentNode;\r\n  if (!padre) return;\r\n  const grupo = input.closest('.elementor-field-group');\r\n  const label = grupo ? grupo.querySelector('.elementor-field-label') : null;\r\n  let chip = padre.querySelector(':scope > .vfp-chip');\r\n\r\n  if (!texto) {\r\n    if (chip) chip.remove();\r\n    input.style.display = '';\r\n    if (label) label.style.display = '';\r\n    return;\r\n  }\r\n\r\n  input.style.display = 'none';\r\n  if (label) label.style.display = 'none';\r\n  if (!chip) {\r\n    chip = document.createElement('span');\r\n    chip.className = 'vfp-chip';\r\n    chip.style.cssText = [\r\n      'display:inline-flex', 'align-items:center', 'gap:0.6em',\r\n      'padding:0.5em 0.85em',\r\n      'background:#e8f5e9', 'border:1px solid #a5d6a7', 'border-radius:6px',\r\n      'font-size:0.9em', 'color:#2e7d32', 'max-width:100%', 'box-sizing:border-box'\r\n    ].join(';');\r\n    padre.appendChild(chip);\r\n  }\r\n  chip.innerHTML = '';\r\n  const txt = document.createElement('span');\r\n  txt.style.cssText = 'font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';\r\n  txt.textContent = '\u2705 ' + texto;\r\n  const btn = document.createElement('button');\r\n  btn.type = 'button';\r\n  btn.textContent = 'Cambiar';\r\n  btn.style.cssText = 'background:none;border:none;color:#2e7d32;text-decoration:underline;cursor:pointer;font-size:inherit;padding:0;';\r\n  btn.addEventListener('click', (e) => {\r\n    e.preventDefault();\r\n    e.stopPropagation();\r\n    try { input.value = ''; } catch (err) {}\r\n    input.dispatchEvent(new Event('change', { bubbles: true }));\r\n    input.click();\r\n  });\r\n  chip.appendChild(txt);\r\n  chip.appendChild(btn);\r\n}\r\n\r\n\/\/ --- Visibilidad patria potestad -----------------------------------------\r\nfunction mostrarPatria(show) {\r\n  const wrapper = getWrapperPatria();\r\n  const input   = getInputPatria();\r\n  if (wrapper) wrapper.style.display = show ? '' : 'none';\r\n  if (input) {\r\n    input.required = !!show;\r\n    if (!show) {\r\n      try { input.value = ''; } catch (e) {}\r\n      patriaFile = null;\r\n      setIndicador(input, '');\r\n    }\r\n  }\r\n}\r\n\r\n\/\/ --- Estado del bot\u00f3n de env\u00edo -------------------------------------------\r\nfunction puedeEnviar() {\r\n  if (!autorizacionOK) return false;\r\n  if (firmantesCount === 1) return !!patriaFile;\r\n  return true;\r\n}\r\n\r\nfunction motivoBloqueo() {\r\n  if (!autorizacionOK) return 'Sube un PDF de autorizaci\u00f3n firmado v\u00e1lido';\r\n  if (firmantesCount === 1 && !patriaFile) return 'Adjunta el documento de patria potestad';\r\n  return '';\r\n}\r\n\r\nfunction actualizarBoton() {\r\n  const btn = getBotonSubmit();\r\n  if (!btn) return;\r\n  if (puedeEnviar()) {\r\n    btn.disabled = false;\r\n    btn.style.opacity = '';\r\n    btn.style.cursor = '';\r\n    btn.title = '';\r\n  } else {\r\n    btn.disabled = true;\r\n    btn.style.opacity = '0.5';\r\n    btn.style.cursor = 'not-allowed';\r\n    btn.title = motivoBloqueo();\r\n  }\r\n}\r\n\r\n\/\/ --- Enganche del input de autorizaci\u00f3n ----------------------------------\r\nfunction engancharAutorizacion() {\r\n  const input = getInputArchivo();\r\n  if (!input) return false;\r\n  if (input.dataset.vfpBound === '1') return true;\r\n  input.dataset.vfpBound = '1';\r\n\r\n  input.addEventListener('change', async (e) => {\r\n    const file = e.target.files[0];\r\n    autorizacionOK = false;\r\n    firmantesCount = 0;\r\n    result.innerHTML = '';\r\n    setFirmantes(0);\r\n    setNombresFirmantes([]);\r\n    mostrarPatria(false);\r\n    setIndicador(input, '');\r\n    actualizarBoton();\r\n\r\n    if (!file) {\r\n      status.textContent = '';\r\n      return;\r\n    }\r\n\r\n    if (file.type !== 'application\/pdf' && !file.name.toLowerCase().endsWith('.pdf')) {\r\n      status.innerHTML = '<span style=\"color:#c00;\">\u26a0 El archivo debe ser un PDF.<\/span>';\r\n      limpiarAutorizacion(input);\r\n      return;\r\n    }\r\n\r\n    status.innerHTML = '\u23f3 Validando firmas\u2026';\r\n\r\n    try {\r\n      const buffer = await file.arrayBuffer();\r\n      const firmas = extraerFirmas(buffer);\r\n\r\n      if (firmas.length === 0) {\r\n        status.innerHTML = '<span style=\"color:#c00;\">\u274c El PDF no contiene firmas digitales.<\/span>';\r\n        limpiarAutorizacion(input);\r\n        return;\r\n      }\r\n\r\n      if (firmas.length > 2) {\r\n        status.innerHTML = `<span style=\"color:#c00;\">\u274c El PDF tiene ${firmas.length} firmas. El m\u00e1ximo admitido es 2.<\/span>`;\r\n        limpiarAutorizacion(input);\r\n        return;\r\n      }\r\n\r\n      const informes = firmas.map((f, i) => analizarFirma(f, i + 1));\r\n      const todasOK  = informes.every(inf => inf.ok && inf.firmante);\r\n\r\n      if (!todasOK) {\r\n        status.innerHTML = '<span style=\"color:#c00;\">\u274c Una o m\u00e1s firmas no son v\u00e1lidas.<\/span>';\r\n        limpiarAutorizacion(input);\r\n        return;\r\n      }\r\n\r\n      autorizacionOK = true;\r\n      firmantesCount = firmas.length;\r\n      setFirmantes(firmantesCount);\r\n      const nombres = informes\r\n        .filter(inf => inf.ok && inf.firmante)\r\n        .map(inf => nombreFirmante(inf.firmante.sujeto));\r\n      setNombresFirmantes(nombres);\r\n      pintarResumen(informes);\r\n      setIndicador(input, `Firma${firmantesCount > 1 ? 's' : ''} verificada${firmantesCount > 1 ? 's' : ''} \u00b7 ${file.name}`);\r\n\r\n      if (firmantesCount === 1) {\r\n        status.innerHTML = '<span style=\"color:#080;\">\u2705 1 firma v\u00e1lida. Adjunta el documento de patria potestad para poder enviar.<\/span>';\r\n        mostrarPatria(true);\r\n      } else {\r\n        status.innerHTML = '<span style=\"color:#080;\">\u2705 2 firmas v\u00e1lidas. Ya puedes enviar.<\/span>';\r\n        mostrarPatria(false);\r\n      }\r\n      actualizarBoton();\r\n\r\n    } catch (err) {\r\n      console.error(err);\r\n      status.innerHTML = '<span style=\"color:#c00;\">\u274c Error al procesar el PDF.<\/span>';\r\n      limpiarAutorizacion(input);\r\n    }\r\n  });\r\n\r\n  return true;\r\n}\r\n\r\nfunction limpiarAutorizacion(input) {\r\n  try { input.value = ''; } catch (e) {}\r\n  autorizacionOK  = false;\r\n  firmantesCount  = 0;\r\n  setFirmantes(0);\r\n  setNombresFirmantes([]);\r\n  mostrarPatria(false);\r\n  setIndicador(input, '');\r\n  actualizarBoton();\r\n}\r\n\r\n\/\/ --- Enganche del input de patria potestad -------------------------------\r\nfunction engancharPatria() {\r\n  const input = getInputPatria();\r\n  if (!input || input.dataset.vfpBound === '1') return;\r\n  input.dataset.vfpBound = '1';\r\n  input.addEventListener('change', (e) => {\r\n    const f = e.target.files && e.target.files[0];\r\n    patriaFile = f || null;\r\n    setIndicador(input, f ? `Documento adjuntado \u00b7 ${f.name}` : '');\r\n    actualizarBoton();\r\n  });\r\n}\r\n\r\n\/\/ --- Bloqueo del env\u00edo ---------------------------------------------------\r\nfunction engancharFormulario() {\r\n  const form = getFormulario();\r\n  if (!form || form.dataset.vfpBound === '1') return;\r\n  form.dataset.vfpBound = '1';\r\n\r\n  const btn = getBotonSubmit();\r\n  if (btn) {\r\n    btn.addEventListener('click', (e) => {\r\n      if (!puedeEnviar()) {\r\n        e.preventDefault();\r\n        e.stopPropagation();\r\n        e.stopImmediatePropagation();\r\n        status.innerHTML = '<span style=\"color:#c00;\">\u274c ' + escapar(motivoBloqueo()) + '.<\/span>';\r\n        status.scrollIntoView({ behavior:'smooth', block:'center' });\r\n        return false;\r\n      }\r\n    }, true);\r\n  }\r\n\r\n  form.addEventListener('submit', (e) => {\r\n    if (!puedeEnviar()) {\r\n      e.preventDefault();\r\n      e.stopPropagation();\r\n      e.stopImmediatePropagation();\r\n      return false;\r\n    }\r\n  }, true);\r\n}\r\n\r\n\/\/ --- Inicializaci\u00f3n ------------------------------------------------------\r\nfunction iniciar() {\r\n  aplicarSourceId();\r\n  const ok = engancharAutorizacion();\r\n  engancharPatria();\r\n  engancharFormulario();\r\n  mostrarPatria(false);      \/\/ oculta hasta saber cu\u00e1ntas firmas hay\r\n  actualizarBoton();\r\n  if (!ok) setTimeout(iniciar, 400);\r\n}\r\ndocument.addEventListener('DOMContentLoaded', iniciar);\r\niniciar();\r\n\r\nconst observer = new MutationObserver(() => {\r\n  aplicarSourceId();\r\n  engancharAutorizacion();\r\n  engancharPatria();\r\n  engancharFormulario();\r\n  if (firmantesCount !== 1) mostrarPatria(false);\r\n});\r\nobserver.observe(document.body, { childList: true, subtree: true });\r\n\r\n\/\/ ==========================================================================\r\n\/\/ An\u00e1lisis PDF\r\n\/\/ ==========================================================================\r\n\r\nfunction extraerFirmas(buffer) {\r\n  const bytes = new Uint8Array(buffer);\r\n  let texto = '';\r\n  for (let i = 0; i < bytes.length; i++) texto += String.fromCharCode(bytes[i]);\r\n  const firmas = [];\r\n  const regex = \/\\\/Contents\\s*<([0-9a-fA-F\\s]+)>\/g;\r\n  let match;\r\n  while ((match = regex.exec(texto)) !== null) {\r\n    const hex = match[1].replace(\/\\s\/g, '');\r\n    if (hex.length < 100) continue;\r\n    const sigBytes = new Uint8Array(hex.length \/ 2);\r\n    for (let i = 0; i < hex.length; i += 2) sigBytes[i \/ 2] = parseInt(hex.substr(i, 2), 16);\r\n    let fin = sigBytes.length;\r\n    while (fin > 0 && sigBytes[fin - 1] === 0) fin--;\r\n    if (fin > 0) firmas.push(sigBytes.slice(0, fin));\r\n  }\r\n  return firmas;\r\n}\r\n\r\nfunction analizarFirma(sigBytes, numero) {\r\n  try {\r\n    const ab = sigBytes.buffer.slice(sigBytes.byteOffset, sigBytes.byteOffset + sigBytes.byteLength);\r\n    const asn1 = asn1js.fromBER(ab);\r\n    if (asn1.offset === -1) throw new Error('Estructura ASN.1 no v\u00e1lida');\r\n    const contentInfo = new pkijs.ContentInfo({ schema: asn1.result });\r\n    const signedData  = new pkijs.SignedData({ schema: contentInfo.content });\r\n    const cert = encontrarCertFirmante(signedData);\r\n    if (!cert) return { numero, ok: false, error: 'No se localiz\u00f3 el certificado del firmante' };\r\n    const firmante = {\r\n      sujeto: extraerNombre(cert.subject),\r\n      emisor: extraerNombre(cert.issuer),\r\n      validoDesde: cert.notBefore.value,\r\n      validoHasta: cert.notAfter.value\r\n    };\r\n    return { numero, ok: true, firmante };\r\n  } catch (err) {\r\n    return { numero, ok: false, error: err.message };\r\n  }\r\n}\r\n\r\n\/\/ El PKCS#7 incluye firmante + CA intermedia + ra\u00edz. Hay que localizar el cert\r\n\/\/ del firmante; si no, acabar\u00edamos mostrando el nombre de la autoridad (AC).\r\nfunction encontrarCertFirmante(signedData) {\r\n  const certs = (signedData.certificates || []).filter(c => c instanceof pkijs.Certificate);\r\n  if (certs.length === 0) return null;\r\n  if (certs.length === 1) return certs[0];\r\n\r\n  \/\/ 1) Emparejar por n\u00famero de serie del SignerInfo (issuerAndSerialNumber).\r\n  const signerInfo = (signedData.signerInfos || [])[0];\r\n  const sidSerial  = signerInfo && signerInfo.sid && signerInfo.sid.serialNumber\r\n    ? bufToHex(signerInfo.sid.serialNumber.valueBlock.valueHexView)\r\n    : null;\r\n  if (sidSerial) {\r\n    const m = certs.find(c => bufToHex(c.serialNumber.valueBlock.valueHexView) === sidSerial);\r\n    if (m) return m;\r\n  }\r\n\r\n  \/\/ 2) Fallback: la hoja de la cadena (su subject no es emisor de ning\u00fan otro cert).\r\n  const dn = rdn => !rdn || !rdn.typesAndValues ? ''\r\n    : rdn.typesAndValues.map(tv => `${tv.type}=${tv.value.valueBlock.value}`).join(',');\r\n  const leaf = certs.find(x =>\r\n    !certs.some(y => y !== x && dn(y.issuer) === dn(x.subject))\r\n  );\r\n  return leaf || certs[0];\r\n}\r\n\r\nfunction bufToHex(buf) {\r\n  return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2,'0')).join('').toUpperCase();\r\n}\r\n\r\nfunction extraerNombre(rdn) {\r\n  const out = {};\r\n  if (!rdn || !rdn.typesAndValues) return out;\r\n  for (const tv of rdn.typesAndValues) {\r\n    const nombre = OID_NAMES[tv.type] || tv.type;\r\n    out[nombre] = tv.value.valueBlock.value;\r\n  }\r\n  return out;\r\n}\r\n\r\nfunction nombreFirmante(sujeto) {\r\n  if (!sujeto) return 'Firmante desconocido';\r\n  const nombre   = sujeto['Nombre'];\r\n  const apellido = sujeto['Apellidos'];\r\n  if (nombre && apellido) return `${nombre} ${apellido}`;\r\n  return sujeto['Nombre com\u00fan (CN)'] || nombre || apellido || 'Firmante desconocido';\r\n}\r\n\r\nfunction escapar(s) {\r\n  if (s == null) return '';\r\n  return String(s).replace(\/[&<>\"']\/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\r\n}\r\n\r\nfunction pintarResumen(informes) {\r\n  const nombres = informes\r\n    .filter(inf => inf.ok && inf.firmante)\r\n    .map(inf => escapar(nombreFirmante(inf.firmante.sujeto)));\r\n  if (nombres.length === 0) { result.innerHTML = ''; return; }\r\n  const items = nombres.map(n => `<li>${n}<\/li>`).join('');\r\n  result.innerHTML =\r\n    '<div style=\"background:#f0f7ff;border:1px solid #d6e4ff;border-radius:6px;padding:0.6em 0.9em;font-size:0.9em;\">' +\r\n      '<strong>Firmante' + (nombres.length > 1 ? 's' : '') + ':<\/strong>' +\r\n      '<ul style=\"margin:0.3em 0 0;padding-left:1.2em;\">' + items + '<\/ul>' +\r\n    '<\/div>';\r\n}\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1f46041 elementor-button-align-stretch elementor-widget elementor-widget-form\" data-id=\"1f46041\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;step_next_label&quot;:&quot;Next&quot;,&quot;step_previous_label&quot;:&quot;Previous&quot;,&quot;button_width&quot;:&quot;100&quot;,&quot;step_type&quot;:&quot;number_text&quot;,&quot;step_icon_shape&quot;:&quot;circle&quot;}\" data-widget_type=\"form.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<form class=\"elementor-form\" method=\"post\" name=\"New Form\" aria-label=\"New Form\">\r\n\t\t\t<input type=\"hidden\" name=\"post_id\" value=\"6377\"\/>\r\n\t\t\t<input type=\"hidden\" name=\"form_id\" value=\"1f46041\"\/>\r\n\t\t\t<input type=\"hidden\" name=\"referer_title\" value=\"Env\u00edo de Documentaci\u00f3n Firmada\" \/>\r\n\r\n\t\t\t\t\t\t\t<input type=\"hidden\" name=\"queried_id\" value=\"6377\"\/>\r\n\t\t\t\r\n\t\t\t<div class=\"elementor-form-fields-wrapper elementor-labels-above\">\r\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-type-hidden elementor-field-group elementor-column elementor-field-group-sourceid elementor-col-100\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<input size=\"1\" type=\"hidden\" name=\"form_fields[sourceid]\" id=\"form-field-sourceid\" class=\"elementor-field elementor-size-sm  elementor-field-textual\">\r\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-type-hidden elementor-field-group elementor-column elementor-field-group-firmantes elementor-col-100\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<input size=\"1\" type=\"hidden\" name=\"form_fields[firmantes]\" id=\"form-field-firmantes\" class=\"elementor-field elementor-size-sm  elementor-field-textual\">\r\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-type-hidden elementor-field-group elementor-column elementor-field-group-firmante1 elementor-col-100\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<input size=\"1\" type=\"hidden\" name=\"form_fields[firmante1]\" id=\"form-field-firmante1\" class=\"elementor-field elementor-size-sm  elementor-field-textual\">\r\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-type-hidden elementor-field-group elementor-column elementor-field-group-firmante2 elementor-col-100\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<input size=\"1\" type=\"hidden\" name=\"form_fields[firmante2]\" id=\"form-field-firmante2\" class=\"elementor-field elementor-size-sm  elementor-field-textual\">\r\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-type-upload elementor-field-group elementor-column elementor-field-group-autorizacion elementor-col-100\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t<label for=\"form-field-autorizacion\" class=\"elementor-field-label\">\r\n\t\t\t\t\t\t\t\tSube el PDF firmado por los progenitores\t\t\t\t\t\t\t<\/label>\r\n\t\t\t\t\t\t\t\t<input type=\"file\" name=\"form_fields[autorizacion]\" id=\"form-field-autorizacion\" class=\"elementor-field elementor-size-sm  elementor-upload-field\">\r\n\r\n\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-type-upload elementor-field-group elementor-column elementor-field-group-patriapotestad elementor-col-100\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t<label for=\"form-field-patriapotestad\" class=\"elementor-field-label\">\r\n\t\t\t\t\t\t\t\tDocumento que acredite que tienes la patria potestad\t\t\t\t\t\t\t<\/label>\r\n\t\t\t\t\t\t\t\t<input type=\"file\" name=\"form_fields[patriapotestad]\" id=\"form-field-patriapotestad\" class=\"elementor-field elementor-size-sm  elementor-upload-field\">\r\n\r\n\t\t\t\t\t\t<\/div>\r\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-group elementor-column elementor-field-type-submit elementor-col-100 e-form__buttons\">\r\n\t\t\t\t\t<button class=\"elementor-button elementor-size-sm\" type=\"submit\">\r\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-button-text\">Enviar<\/span>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<\/span>\r\n\t\t\t\t\t<\/button>\r\n\t\t\t\t<\/div>\r\n\t\t\t<\/div>\r\n\t\t<\/form>\r\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ae3c48c elementor-widget elementor-widget-spacer\" data-id=\"ae3c48c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"spacer.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-spacer\">\n\t\t\t<div class=\"elementor-spacer-inner\"><\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2fb1d31d elementor-widget elementor-widget-spacer\" data-id=\"2fb1d31d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"spacer.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-spacer\">\n\t\t\t<div class=\"elementor-spacer-inner\"><\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>COMPROBACI\u00d3N DE FIRMAS CURSO DE PILOTO DE ULM Validaci\u00f3n de autorizaci\u00f3n firmada Sube el PDF de la autorizaci\u00f3n. Comprobaremos sus firmas digitales antes de permitir el env\u00edo.<\/p>","protected":false},"author":1,"featured_media":1055,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"","_seopress_titles_desc":"","_seopress_robots_index":"","footnotes":""},"class_list":["post-6377","page","type-page","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/pages\/6377","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/comments?post=6377"}],"version-history":[{"count":80,"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/pages\/6377\/revisions"}],"predecessor-version":[{"id":6521,"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/pages\/6377\/revisions\/6521"}],"wp:attachment":[{"href":"https:\/\/spacecampvalencia.es\/en\/wp-json\/wp\/v2\/media?parent=6377"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}