1 2let NODES=[],ORGANIGRAMA_DATA=[],ORGANIGRAMA_BY_CODE=new Map(); 3 4const stage=document.getElementById("orgStage"); 5const holder=document.getElementById("stageHolder"); 6const viewport=document.getElementById("viewport"); 7const modalElement=document.getElementById("organigramaModal"); 8const detailTitle=document.getElementById("detailTitle"); 9const detailBoss=document.getElementById("detailBoss"); 10const detailFunctions=document.getElementById("detailFunctions"); 11const functionsSection=document.getElementById("functionsSection"); 12const BASE_WIDTH=1448.24; 13const CONTENT_HEIGHT=1571.451; 14const TOP_TRIM=94.205; 15let fitScale=1,zoomMultiplier=1; 16 17function escapeHtml(value){ 18 return String(value??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'"); 19} 20 21function textToParagraphs(text){ 22 const clean=String(text||"").trim(); 23 if(!clean)return ""; 24 return clean.split(/\n+/).filter(Boolean).map(line=>`<p>${escapeHtml(line.trim())}</p>`).join(""); 25} 26 27async function cargarDatosOrganigrama(){ 28 const ORGANIGRAMA_JSON_URL="/documents/organiEnap.json"; 29 const response=await fetch(ORGANIGRAMA_JSON_URL,{cache:"no-store"}); 30 if(!response.ok)throw new Error(`No fue posible cargar el JSON (${response.status})`); 31 ORGANIGRAMA_DATA=await response.json(); 32 ORGANIGRAMA_BY_CODE=new Map(ORGANIGRAMA_DATA.map(item=>[String(item.externalCode||""),item])); 33 34 const cards=Array.from(document.querySelectorAll("#organigrama .org-node")); 35 NODES=cards.map(card=>{ 36 const code=String(card.dataset.code||""); 37 const item=ORGANIGRAMA_BY_CODE.get(code)||{}; 38 const esDirectorio=code==="G1HDIRECENAP"; 39 const title=String(item.name||"").trim(); 40 const jefatura=String(item.nombreJefatura||"").trim(); 41 const person=esDirectorio?"":(jefatura||"Vacante"); 42 43 const titleElement=card.querySelector(".node-title"); 44 const personElement=card.querySelector(".person"); 45 if(titleElement)titleElement.textContent=title; 46 if(personElement)personElement.textContent=person; 47 48 return{externalCode:code,title,jefatura:person,descripcion:String(item.descripcion||"").trim()}; 49 }); 50} 51 52function nodeHasModal(index){ 53 return NODES[index]&&NODES[index].externalCode!=="G1HDIRECENAP"; 54} 55 56function abrirModal(index){ 57 if(!nodeHasModal(index))return; 58 const node=NODES[index]; 59 detailTitle.textContent=node.title; 60 detailBoss.textContent=node.jefatura||"Vacante"; 61 if(node.descripcion){ 62 functionsSection.classList.remove("d-none"); 63 detailFunctions.innerHTML=textToParagraphs(node.descripcion); 64 }else{ 65 functionsSection.classList.add("d-none"); 66 detailFunctions.innerHTML=""; 67 } 68 if(window.bootstrap?.Modal){ 69 bootstrap.Modal.getOrCreateInstance(modalElement).show(); 70 } 71} 72 73function applyScale(){ 74 const scale=fitScale*zoomMultiplier; 75 stage.style.top=`${-TOP_TRIM*scale}px`; 76 stage.style.transform=`scale(${scale})`; 77 holder.style.width=`${BASE_WIDTH*scale}px`; 78 holder.style.height=`${CONTENT_HEIGHT*scale}px`; 79 viewport.style.height=`${Math.max(1,Math.min(CONTENT_HEIGHT*scale,window.innerHeight*.8))}px`; 80} 81 82function centerOrganigram(){ 83 viewport.scrollLeft=Math.max(0,viewport.scrollWidth-viewport.clientWidth)/2; 84} 85 86function clampViewportScroll(){ 87 const max=Math.max(0,holder.getBoundingClientRect().height-viewport.clientHeight); 88 if(viewport.scrollTop>max)viewport.scrollTop=max; 89} 90 91function fitToViewport(){ 92 fitScale=1;zoomMultiplier=1;applyScale(); 93 requestAnimationFrame(()=>{centerOrganigram();viewport.scrollTop=0;clampViewportScroll()}); 94} 95 96function zoomBy(delta){ 97 const ratio=(viewport.scrollLeft+viewport.clientWidth/2)/Math.max(1,viewport.scrollWidth); 98 zoomMultiplier=Math.min(2.5,Math.max(.45,zoomMultiplier+delta)); 99 applyScale(); 100 requestAnimationFrame(()=>{viewport.scrollLeft=Math.max(0,ratio*viewport.scrollWidth-viewport.clientWidth/2);clampViewportScroll()}); 101} 102 103function activarEventos(){ 104 document.querySelectorAll("#organigrama .info-button").forEach(button=>{ 105 button.addEventListener("click",event=>{event.stopPropagation();abrirModal(Number(button.dataset.node))}); 106 }); 107 document.querySelectorAll("#organigrama .org-node").forEach(card=>{ 108 card.addEventListener("dblclick",()=>abrirModal(Number(card.dataset.node))); 109 }); 110 document.getElementById("zoomIn").addEventListener("click",()=>zoomBy(.15)); 111 document.getElementById("zoomOut").addEventListener("click",()=>zoomBy(-.15)); 112 document.getElementById("zoomReset").addEventListener("click",fitToViewport); 113 viewport.addEventListener("scroll",clampViewportScroll,{passive:true}); 114 window.addEventListener("resize",()=>requestAnimationFrame(applyScale)); 115 116 const drag={active:false,startX:0,startY:0,left:0,top:0}; 117 viewport.addEventListener("mousedown",e=>{ 118 if(e.button!==0||e.target.closest("button,a,input,textarea,select"))return; 119 drag.active=true;drag.startX=e.clientX;drag.startY=e.clientY;drag.left=viewport.scrollLeft;drag.top=viewport.scrollTop; 120 e.preventDefault(); 121 }); 122 window.addEventListener("mousemove",e=>{ 123 if(!drag.active)return; 124 viewport.scrollLeft=drag.left-(e.clientX-drag.startX);
125 viewport.scrollTop=drag.top-(e.clientY-drag.startY); 126 }); 127 window.addEventListener("mouseup",()=>{drag.active=false}); 128} 129 130async function inicializarOrganigrama(){ 131 await cargarDatosOrganigrama(); 132 activarEventos(); 133 fitToViewport(); 134} 135 136inicializarOrganigrama().catch(error=>console.error("Error al inicializar el organigrama:",error));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.