PageSourceSearch

https://inkas.org/assets/design/logo-contrast.js

js inkas.org collected 2026-10-03 00:52:24 UTC 5,208 bytes, 117 lines download raw bytes

1// Read the already-loaded local images; no requests or page screenshots are needed.
2const imageSamples = new WeakMap();
3let activeBrand, frame;
4
5function imagePixel(image, x, y) {
6  if (!image.complete || !image.naturalWidth) return null;
7  let sample = imageSamples.get(image);
8  if (!sample || sample.src !== image.currentSrc) {
9    const canvas = document.createElement('canvas');
10    const scale = 96 / Math.max(image.naturalWidth, image.naturalHeight);
11    canvas.width = Math.max(1, Math.round(image.naturalWidth * scale));
12    canvas.height = Math.max(1, Math.round(image.naturalHeight * scale));
13    try {
14      const context = canvas.getContext('2d', {willReadFrequently: true});
15      context.drawImage(image, 0, 0, canvas.width, canvas.height);
16      sample = {src: image.currentSrc, pixels: context.getImageData(0, 0, canvas.width, canvas.height).data, width: canvas.width, height: canvas.height};
17    } catch {
18      // An image without canvas permission leaves the normal surface-color fallback.
19      sample = {src: image.currentSrc};
20    }
21    imageSamples.set(image, sample);
22  }
23  if (!sample.pixels) return null;
24  const rect = image.getBoundingClientRect(), style = getComputedStyle(image);
25  let width = rect.width, height = rect.height;
26  const scale = style.objectFit === 'cover'
27    ? Math.max(width / image.naturalWidth, height / image.naturalHeight)
28    : Math.min(width / image.naturalWidth, height / image.naturalHeight);
29  if (['cover', 'contain', 'scale-down', 'none'].includes(style.objectFit)) {
30    const fit = style.objectFit === 'none' ? 1 : style.objectFit === 'scale-down' ? Math.min(1, scale) : scale;
31    width = image.naturalWidth * fit;
32    height = image.naturalHeight * fit;
33  }
34  const position = style.objectPosition.split(' ');
35  const offset = (value, space) => value?.endsWith('%') ? space * parseFloat(value) / 100 : parseFloat(value) || 0;
36  const u = (x - rect.left - offset(position[0], rect.width - width)) / width;
37  const v = (y - rect.top - offset(position[1], rect.height - height)) / height;
38  if (u < 0 || u >= 1 || v < 0 || v >= 1) return null;
39  const index = (Math.floor(v * sample.height) * sample.width + Math.floor(u * sample.width)) * 4;
40  return [...sample.pixels.slice(index, index + 3), sample.pixels[index + 3] / 255];
41}
42
43function color(value) {
44  const parts = value.match(/[\d.]+/g)?.map(Number);
45  return parts?.length >= 3 ? [parts[0], parts[1], parts[2], parts[3] ?? 1] : [0, 0, 0, 0];
46}
47
48function over(front, back) {
49  return front.slice(0, 3).map((channel, i) => channel * front[3] + back[i] * (1 - front[3]));
50}
51
52function surfacePixel(element, x, y, cache) {
53  if (!element) return [250, 248, 239];
54  let surface = cache.get(element);
55  if (!surface) {
56    const style = getComputedStyle(element);
57    surface = color(style.backgroundColor);
58    // The site's gradient surfaces have a solid or light paper base.
59    if (surface[3] < 1 && style.backgroundImage.includes('gradient(')) {
60      const stops = style.backgroundImage.match(/rgba?\([^)]*\)/g)?.map(color) || [];
61      const opaque = stops.find(stop => stop[3] === 1);
62      if (opaque) surface = opaque;
63    }
64    cache.set(element, surface);
65  }
66  const base = surface[3] === 1 ? surface.slice(0, 3) : over(surface, surfacePixel(element.parentElement, x, y, cache));
67  const pixel = element instanceof HTMLImageElement ? imagePixel(element, x, y) : null;
68  return pixel ? over(pixel, base) : base;
69}
70
71function luminance(rgb) {
72  const linear = rgb.map(channel => {
73    const value = channel / 255;
74    return value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4;
75  });
76  return linear[0] * .2126 + linear[1] * .7152 + linear[2] * .0722;
77}
78
79function update() {
80  frame = null;
81  if (!activeBrand?.isConnected) return;
82  const header = activeBrand.closest('.site-header'), cache = new Map();
83  for (const part of activeBrand.querySelectorAll('.vector-logo,.brand-subheading')) {
84    const rect = part.getBoundingClientRect();
85    if (!rect.width || !rect.height) continue;
86    let sum = 0, count = 0;
87    for (let row = 0; row < 3; row++) {
88      for (let column = 0; column < 7; column++) {
89        const x = rect.left + rect.width * (column + .5) / 7;
90        const y = rect.top + rect.height * (row + .5) / 3;
91        if (x < 0 || y < 0 || x >= innerWidth || y >= innerHeight) continue;
92        const element = document.elementsFromPoint(x, y).find(item => !header.contains(item));
93        sum += luminance(surfacePixel(element, x, y, cache));
94        count++;
95      }
96    }
97    // Hysteresis prevents flicker around the light/dark crossover while scrolling.
98    const threshold = part.classList.contains('is-on-dark') ? .26 : .22;
99    part.classList.toggle('is-on-dark', count > 0 && sum / count < threshold);
100  }
101}
102
103function schedule() {
104  if (!frame) frame = requestAnimationFrame(update);
105}
106
107window.addEventListener('scroll', schedule, {passive: true});
108window.addEventListener('resize', schedule, {passive: true});
109document.addEventListener('load', event => {
110  if (event.target instanceof HTMLImageElement) schedule();
111}, true);
112document.fonts?.ready.then(schedule);
113
114export function bindLogoContrast(brand) {
115  activeBrand = brand;
116  schedule();
117}

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.