PageSourceSearch

https://terevaka.net/projects/rapanui/imgcmp/imagecomp.js

js terevaka.net collected 2026-10-02 12:19:29 UTC 7,129 bytes, 234 lines download raw bytes

1(function () {
2  const MOBILE_BREAKPOINT = 900;
3
4  function esc(s) {
5    return String(s ?? "").replace(/[&<>"']/g, (m) => (
6      { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[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.