1(function () { 2 const MOBILE_BREAKPOINT = 900; 3 4 function esc(s) { 5 return String(s ?? "").replace(/[&<>"']/g, (m) => ( 6 { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[m] 7 )); 8 } 9 10 function value(row, key) { 11 return String(row?.[key] ?? ""); 12 } 13 14function buildCaptionHTML(row) { 15 return ` 16 <div class="ic-meta"> 17 <div class="ic-meta-row"> 18 <div class="ic-meta-label">Región:</div> 19 <div class="ic-meta-value">${esc(value(row, "Features"))}</div> 20 </div> 21 22 <div class="ic-meta-row"> 23 <div class="ic-meta-label">Lat:</div> 24 <div class="ic-meta-value">${esc(value(row, "Lat:"))}</div> 25 </div> 26 27 <div class="ic-meta-row"> 28 <div class="ic-meta-label">Lon:</div> 29 <div class="ic-meta-value">${esc(value(row, "Lon:"))}</div> 30 </div> 31 32 <div class="ic-meta-row"> 33 <div class="ic-meta-label">Orientación:</div> 34 <div class="ic-meta-value">${esc(value(row, "Orient"))}</div> 35 </div> 36 37 <div class="ic-meta-row"> 38 <div class="ic-meta-label">Descripción:</div> 39 <div class="ic-meta-value">${esc(value(row, "Desc"))}</div> 40 </div> 41 42 <div class="ic-meta-four-col"> 43 <div class="ic-meta-label">Investigador:</div> 44 <div class="ic-meta-value">${esc(value(row, "Investigador:"))}</div> 45 <div class="ic-meta-label">Año:</div> 46 <div class="ic-meta-value">${esc(value(row, "Año:"))}</div> 47 48 <div class="ic-meta-label">Autor:</div> 49 <div class="ic-meta-value">${esc(value(row, "Autor:"))}</div> 50 <div class="ic-meta-label">Año:</div> 51 <div class="ic-meta-value">${esc(value(row, "Año:_2"))}</div> 52 53 <div class="ic-meta-label">Foto original:</div> 54 <div class="ic-meta-value">${esc(value(row, "Foto original:"))}</div> 55 <div class="ic-meta-label">Año:</div> 56 <div class="ic-meta-value">${esc(value(row, "Año:_3"))}</div> 57 58 <div class="ic-meta-label">Foto nueva:</div> 59 <div class="ic-meta-value">${esc(value(row, "Foto nuevo:"))}</div> 60 <div class="ic-meta-label">Año:</div> 61 <div class="ic-meta-value">${esc(value(row, "Año:_4"))}</div> 62 </div> 63 </div> 64 `; 65} 66 67 function buildMarkup(row) { 68 const oldUrl = value(row, "Old"); 69 const newUrl = value(row, "New"); 70 71 return ` 72 <div class="ic-wrap"> 73 <div class="ic-intro"> 74 <div class="ic-title">${esc(value(row, "Sidetext") || value(row, "Features"))}</div> 75 </div> 76 77 <div class="img-comp-container ic-comp"> 78 <div class="img-comp-img"> 79 <img class="ic-base-img" src="${esc(oldUrl)}" alt=""> 80 </div> 81 <div class="img-comp-img img-comp-overlay"> 82 <img class="ic-overlay-img" src="${esc(newUrl)}" alt=""> 83 </div> 84 </div> 85 86 ${buildCaptionHTML(row)} 87 </div> 88 `; 89 } 90 91function compareImages(overlay) { 92 let slider, clicked = 0, w = overlay.offsetWidth, h = overlay.offsetHeight; 93 94 if (w === 0 || h === 0) { 95 setTimeout(() => compareImages(overlay), 100); 96 return; 97 } 98 99 overlay.style.width = (w / 2) + "px"; 100 101 let existingSlider = overlay.parentElement.querySelector(".img-comp-slider"); 102 if (existingSlider) existingSlider.remove(); 103 104 slider = document.createElement("DIV"); 105 slider.setAttribute("class", "img-comp-slider"); 106 overlay.parentElement.insertBefore(slider, overlay); 107 108 slider.style.top = (h / 2) - (slider.offsetHeight / 2) + "px"; 109 slider.style.left = (w / 2) - (slider.offsetWidth / 2) + "px"; 110 111 function slideReady(e) { 112 e.preventDefault(); 113 clicked = 1; 114 window.addEventListener("mousemove", slideMove, { passive: false }); 115 window.addEventListener("touchmove", slideMove, { passive: false }); 116 window.addEventListener("mouseup", slideFinish, { passive: true }); 117 window.addEventListener("touchend", slideFinish, { passive: true }); 118 } 119 120 function slideFinish() { 121 clicked = 0; 122 window.removeEventListener("mousemove", slideMove); 123 window.removeEventListener("touchmove", slideMove); 124 window.removeEventListener("mouseup", slideFinish); 125 window.removeEventListener("touchend", slideFinish); 126 } 127 128 function slideMove(e) { 129 if (!clicked) return; 130 e.preventDefault(); 131 132 let pos = getCursorPos(e); 133 if (pos < 0) pos = 0; 134 if (pos > w) pos = w; 135 slide(pos); 136 } 137 138 function getCursorPos(e) { 139 const rect = overlay.parentElement.getBoundingClientRect(); 140 let x = 0; 141 142 if (e.touches && e.touches.length) { 143 x = e.touches[0].pageX - rect.left - window.pageXOffset; 144 } else { 145 x = e.pageX - rect.left - window.pageXOffset; 146 } 147 148 return x; 149 } 150 151 function slide(x) { 152 overlay.style.width = x + "px"; 153 slider.style.left = overlay.offsetWidth - (slider.offsetWidth / 2) + "px"; 154 } 155 156 slider.addEventListener("mousedown", slideReady, { passive: false }); 157 slider.addEventListener("touchstart", slideReady, { passive: false }); 158} 159 160function initComparisons(root) { 161 const overlays = root.querySelectorAll(".img-comp-overlay"); 162 overlays.forEach((overlay) => { 163 if (!overlay.parentElement.querySelector(".img-comp-slider")) { 164 compareImages(overlay); 165 } 166 }); 167} 168 169 function normalizeRowKeys(row) { 170 // Because repeated "Año:" headers collide, vis.js should pass these normalized aliases. 171 // But this keeps us safe if it already has them. 172 return { 173 ...row, 174 "Año:_2": row["Año:_2"] ?? row["Año 2"] ?? row["Año__2"] ?? "", 175 "Año:_3": row["Año:_3"] ?? row["Año 3"] ?? row["Año__3"] ?? "", 176 "Año:_4": row["Año:_4"] ?? row["Año 4"] ?? row["Año__4"] ?? "" 177 }; 178 } 179 180 function renderImageComparison(row, opts = {}) { 181 const story = document.getElementById(opts.storyId || "vis-story"); 182 if (!story) return; 183 184 const normalized = normalizeRowKeys(row); 185 story.innerHTML = buildMarkup(normalized); 186 story.removeAttribute("hidden"); 187 188const baseImg = story.querySelector(".ic-base-img"); 189const overlayWrap = story.querySelector(".img-comp-overlay"); 190const overlayImg = story.querySelector(".ic-overlay-img"); 191 192function syncComparisonSize() { 193 if (!baseImg || !overlayWrap || !overlayImg) return; 194 195 const w = baseImg.offsetWidth; 196 const h = baseImg.offsetHeight; 197 if (!w || !h) { 198 requestAnimationFrame(syncComparisonSize); 199 return; 200 } 201 202 overlayWrap.style.height = `${h}px`; 203 overlayImg.style.width = `${w}px`; 204 overlayImg.style.height = `${h}px`; 205 overlayImg.style.maxWidth = "none"; 206 207 initComparisons(story); 208} 209 210if (baseImg && !baseImg.complete) { 211 baseImg.onload = syncComparisonSize; 212} else { 213 requestAnimationFrame(syncComparisonSize); 214} 215 216 if (window.innerWidth <= MOBILE_BREAKPOINT) { 217 requestAnimationFrame(() => { 218 story.scrollIntoView({ behavior: "smooth", block: "start" }); 219 }); 220 } 221 } 222 223 function renderImageCompIntro(html) { 224 const story = document.getElementById("vis-story"); 225 if (!story) return; 226 story.innerHTML = html; 227 story.removeAttribute("hidden"); 228 } 229 230 window.ImageComp = { 231 renderImageComparison, 232 renderImageCompIntro 233 }; 234})();
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.