mirror of
git://f0xx.org/android_cast
synced 2026-07-29 05:17:39 +03:00
1
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Hub landing globe — orthographic Canvas2D (globe3d=1).
|
* Hub landing globe — orthographic Canvas2D (globe3d=1).
|
||||||
* Matches globe3d=0 stack: globe (ocean+base grid) → grid overlay → continents.
|
* Bakes globe3d=0 stack to equirect maps once; runtime always samples by lon/lat
|
||||||
* Grid/land sampled from hub SVG disks in body-fixed frame (Mercator ortho, not geodesic lines).
|
* after inverse Y-rotation (rotation-invariant, full sphere wrap).
|
||||||
*/
|
*/
|
||||||
export const GLOBE_VIEW = { imgW: 1920, imgH: 1080, cx: 720, cy: 560, r: 322, rim: 330 };
|
export const GLOBE_VIEW = { imgW: 1920, imgH: 1080, cx: 720, cy: 560, r: 322, rim: 330 };
|
||||||
|
|
||||||
@@ -10,12 +10,11 @@ const DRAG_ROTATION_PER_PX = 0.004;
|
|||||||
const OCEAN_FALLBACK = { r: 22, g: 38, b: 60 };
|
const OCEAN_FALLBACK = { r: 22, g: 38, b: 60 };
|
||||||
const FRONT_Z = 0.02;
|
const FRONT_Z = 0.02;
|
||||||
const RASTER_SCALE = 2;
|
const RASTER_SCALE = 2;
|
||||||
const EQ_W = 1024;
|
const EQ_W = 2048;
|
||||||
const EQ_H = 512;
|
const EQ_H = 1024;
|
||||||
/** Hub ortho disk parallels ≈ sin(lat) offset from SVG pole ellipses. */
|
|
||||||
const GRID_PARALLELS = [0, 0.521, -0.521];
|
const GRID_PARALLELS = [0, 0.521, -0.521];
|
||||||
const GRID_MERIDIAN_SPACING = Math.PI / 6;
|
const GRID_MERIDIAN_SPACING = Math.PI / 6;
|
||||||
const GRID_LINE_RAD = 0.012;
|
const GRID_LINE_RAD = 0.022;
|
||||||
|
|
||||||
function hubAsset(rel, build) {
|
function hubAsset(rel, build) {
|
||||||
const link = document.querySelector('link[href*="hub.css"]');
|
const link = document.querySelector('link[href*="hub.css"]');
|
||||||
@@ -106,47 +105,58 @@ function inverseRotateY(nx, ny, nz, rotY) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hub SVG disk is N-up; negate Y for texture lookup only. */
|
function sphereTexFromLonLat(lon, lat) {
|
||||||
function toTextureBody(body) {
|
const cosLat = Math.cos(lat);
|
||||||
return { x: body.x, y: -body.y, z: body.z };
|
|
||||||
}
|
|
||||||
|
|
||||||
function hubPixelF(tex) {
|
|
||||||
return {
|
return {
|
||||||
x: GLOBE_VIEW.cx + tex.x * GLOBE_VIEW.r,
|
x: cosLat * Math.sin(lon),
|
||||||
y: GLOBE_VIEW.cy - tex.y * GLOBE_VIEW.r,
|
y: -Math.sin(lat),
|
||||||
|
z: cosLat * Math.cos(lon),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function mercatorFlatUV(lon, lat, w, h) {
|
function bodyLonLat(body) {
|
||||||
return {
|
const tex = { x: body.x, y: -body.y, z: body.z };
|
||||||
x: ((lon + Math.PI) / (2 * Math.PI)) * (w - 1),
|
|
||||||
y: (0.5 - lat / Math.PI) * (h - 1),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLandPixel(r, g, b, a) {
|
|
||||||
return a > 12 && r > 100 && g < 140 && b < 140;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isGridPixel(r, g, b, a) {
|
|
||||||
return a > 10 && r + g + b > 280;
|
|
||||||
}
|
|
||||||
|
|
||||||
function bodyLonLat(tex) {
|
|
||||||
return {
|
return {
|
||||||
lon: Math.atan2(tex.x, tex.z),
|
lon: Math.atan2(tex.x, tex.z),
|
||||||
lat: Math.asin(Math.max(-1, Math.min(1, tex.y))),
|
lat: Math.asin(Math.max(-1, Math.min(1, tex.y))),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function sampleEquator(map, lon, lat) {
|
function eqUV(lon, lat, w, h) {
|
||||||
const u = (lon + Math.PI) / (2 * Math.PI);
|
return {
|
||||||
const v = 0.5 - lat / Math.PI;
|
x: ((lon + Math.PI) / (2 * Math.PI)) * (w - 1),
|
||||||
return sampleImageDataBilinear(map.data, map.w, map.h, u * (map.w - 1), v * (map.h - 1));
|
y: (0.5 - lat / Math.PI) * (h - 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleEq(map, lon, lat) {
|
||||||
|
const uv = eqUV(lon, lat, map.w, map.h);
|
||||||
|
return sampleImageDataBilinear(map.data, map.w, map.h, uv.x, uv.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleHub(layer, tex) {
|
||||||
|
const hp = {
|
||||||
|
x: GLOBE_VIEW.cx + tex.x * GLOBE_VIEW.r,
|
||||||
|
y: GLOBE_VIEW.cy - tex.y * GLOBE_VIEW.r,
|
||||||
|
};
|
||||||
|
const sx = (hp.x / GLOBE_VIEW.imgW) * (layer.w - 1);
|
||||||
|
const sy = (hp.y / GLOBE_VIEW.imgH) * (layer.h - 1);
|
||||||
|
return sampleImageDataBilinear(layer.data, layer.w, layer.h, sx, sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleMercFlat(mercFlat, lon, lat) {
|
||||||
|
const uv = eqUV(lon, lat, mercFlat.w, mercFlat.h);
|
||||||
|
return sampleImageDataBilinear(mercFlat.data, mercFlat.w, mercFlat.h, uv.x, uv.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLandPixel(r, g, b, a) {
|
||||||
|
return a > 10 && r > 100 && g < 140 && b < 140;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGridPixel(r, g, b, a) {
|
||||||
|
return a > 8 && r + g + b > 260;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Procedural Mercator grid on wrap-around hemisphere (matches hub 30° meridians + pole ellipses). */
|
|
||||||
function analyticGridRgba(lon, lat) {
|
function analyticGridRgba(lon, lat) {
|
||||||
const wrapped = ((lon + Math.PI) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
const wrapped = ((lon + Math.PI) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
||||||
const cell = wrapped % GRID_MERIDIAN_SPACING;
|
const cell = wrapped % GRID_MERIDIAN_SPACING;
|
||||||
@@ -165,45 +175,27 @@ function analyticGridRgba(lon, lat) {
|
|||||||
return [0, 0, 0, 0];
|
return [0, 0, 0, 0];
|
||||||
}
|
}
|
||||||
if (onMeridian) {
|
if (onMeridian) {
|
||||||
const prime = merDist < GRID_LINE_RAD && (Math.abs(lon) < GRID_LINE_RAD || Math.abs(Math.abs(lon) - Math.PI) < GRID_LINE_RAD);
|
const prime = Math.abs(lon) < GRID_LINE_RAD * 1.5 || Math.abs(Math.abs(lon) - Math.PI) < GRID_LINE_RAD * 1.5;
|
||||||
return prime ? [238, 245, 255, 220] : [238, 245, 255, 170];
|
return prime ? [238, 245, 255, 235] : [238, 245, 255, 185];
|
||||||
}
|
}
|
||||||
return bright ? [219, 231, 246, 190] : [231, 236, 243, 130];
|
return bright ? [219, 231, 246, 210] : [231, 236, 243, 155];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function bakeOceanEquator(globe) {
|
||||||
* Equirect grid: ortho hub artwork on front body face + analytic lines on back wrap.
|
|
||||||
*/
|
|
||||||
function bakeGridEquator(gridOverlay, globe) {
|
|
||||||
const out = new Uint8ClampedArray(EQ_W * EQ_H * 4);
|
const out = new Uint8ClampedArray(EQ_W * EQ_H * 4);
|
||||||
for (let j = 0; j < EQ_H; j++) {
|
for (let j = 0; j < EQ_H; j++) {
|
||||||
for (let i = 0; i < EQ_W; i++) {
|
for (let i = 0; i < EQ_W; i++) {
|
||||||
const lon = (i / EQ_W) * 2 * Math.PI - Math.PI;
|
const lon = (i / EQ_W) * 2 * Math.PI - Math.PI;
|
||||||
const lat = Math.PI * 0.5 - (j / EQ_H) * Math.PI;
|
const lat = Math.PI * 0.5 - (j / EQ_H) * Math.PI;
|
||||||
const cosLat = Math.cos(lat);
|
const tex = sphereTexFromLonLat(lon, lat);
|
||||||
const tex = toTextureBody({
|
|
||||||
x: cosLat * Math.sin(lon),
|
|
||||||
y: Math.sin(lat),
|
|
||||||
z: cosLat * Math.cos(lon),
|
|
||||||
});
|
|
||||||
const p = (j * EQ_W + i) * 4;
|
const p = (j * EQ_W + i) * 4;
|
||||||
let rgba = [0, 0, 0, 0];
|
let rgba = [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
|
||||||
|
|
||||||
if (tex.z > FRONT_Z) {
|
if (tex.z > FRONT_Z) {
|
||||||
const overlay = sampleOverlayRgba(gridOverlay, tex);
|
const s = sampleHub(globe, tex);
|
||||||
if (isGridPixel(overlay[0], overlay[1], overlay[2], overlay[3])) {
|
if (!isGridPixel(s[0], s[1], s[2], s[3]) && s[3] > 8) {
|
||||||
rgba = overlay;
|
rgba = [s[0], s[1], s[2], 255];
|
||||||
} else {
|
|
||||||
const hp = hubPixelF(tex);
|
|
||||||
const sx = (hp.x / GLOBE_VIEW.imgW) * (globe.w - 1);
|
|
||||||
const sy = (hp.y / GLOBE_VIEW.imgH) * (globe.h - 1);
|
|
||||||
const base = sampleImageDataBilinear(globe.data, globe.w, globe.h, sx, sy);
|
|
||||||
if (isGridPixel(base[0], base[1], base[2], base[3])) {
|
|
||||||
rgba = base;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
rgba = analyticGridRgba(lon, lat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out[p] = rgba[0];
|
out[p] = rgba[0];
|
||||||
@@ -215,69 +207,68 @@ function bakeGridEquator(gridOverlay, globe) {
|
|||||||
return { data: out, w: EQ_W, h: EQ_H };
|
return { data: out, w: EQ_W, h: EQ_H };
|
||||||
}
|
}
|
||||||
|
|
||||||
function sampleOceanRgba(globe, tex) {
|
function bakeGridEquator(globe, gridOverlay) {
|
||||||
const hp = hubPixelF(tex);
|
const out = new Uint8ClampedArray(EQ_W * EQ_H * 4);
|
||||||
const sx = (hp.x / GLOBE_VIEW.imgW) * (globe.w - 1);
|
for (let j = 0; j < EQ_H; j++) {
|
||||||
const sy = (hp.y / GLOBE_VIEW.imgH) * (globe.h - 1);
|
for (let i = 0; i < EQ_W; i++) {
|
||||||
const ocean = sampleImageDataBilinear(globe.data, globe.w, globe.h, sx, sy);
|
const lon = (i / EQ_W) * 2 * Math.PI - Math.PI;
|
||||||
if (ocean[3] < 8) {
|
const lat = Math.PI * 0.5 - (j / EQ_H) * Math.PI;
|
||||||
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
|
const tex = sphereTexFromLonLat(lon, lat);
|
||||||
}
|
const p = (j * EQ_W + i) * 4;
|
||||||
return ocean;
|
let rgba = analyticGridRgba(lon, lat);
|
||||||
}
|
|
||||||
|
|
||||||
function sampleOverlayRgba(layer, tex) {
|
if (tex.z > FRONT_Z) {
|
||||||
const hp = hubPixelF(tex);
|
const overlay = sampleHub(gridOverlay, tex);
|
||||||
const sx = (hp.x / GLOBE_VIEW.imgW) * (layer.w - 1);
|
if (isGridPixel(overlay[0], overlay[1], overlay[2], overlay[3])) {
|
||||||
const sy = (hp.y / GLOBE_VIEW.imgH) * (layer.h - 1);
|
rgba = overlay;
|
||||||
return sampleImageDataBilinear(layer.data, layer.w, layer.h, sx, sy);
|
} else {
|
||||||
}
|
const base = sampleHub(globe, tex);
|
||||||
|
if (isGridPixel(base[0], base[1], base[2], base[3])) {
|
||||||
|
rgba = base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function sampleLandRgba(continents, mercFlat, body) {
|
out[p] = rgba[0];
|
||||||
const tex = toTextureBody(body);
|
out[p + 1] = rgba[1];
|
||||||
if (tex.z > FRONT_Z) {
|
out[p + 2] = rgba[2];
|
||||||
const hp = hubPixelF(tex);
|
out[p + 3] = rgba[3];
|
||||||
const sx = (hp.x / GLOBE_VIEW.imgW) * (continents.w - 1);
|
|
||||||
const sy = (hp.y / GLOBE_VIEW.imgH) * (continents.h - 1);
|
|
||||||
const ortho = sampleImageDataBilinear(continents.data, continents.w, continents.h, sx, sy);
|
|
||||||
if (ortho[3] > 6) {
|
|
||||||
return ortho;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return { data: out, w: EQ_W, h: EQ_H };
|
||||||
const ll = bodyLonLat(tex);
|
|
||||||
const uv = mercatorFlatUV(ll.lon, ll.lat, mercFlat.w, mercFlat.h);
|
|
||||||
const flat = sampleImageDataBilinear(mercFlat.data, mercFlat.w, mercFlat.h, uv.x, uv.y);
|
|
||||||
if (isLandPixel(flat[0], flat[1], flat[2], flat[3])) {
|
|
||||||
return [flat[0], flat[1], flat[2], Math.min(255, flat[3])];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sampleGridRgba(body, gridEq, gridOverlay, globe) {
|
function bakeLandEquator(continents, mercFlat) {
|
||||||
const tex = toTextureBody(body);
|
const out = new Uint8ClampedArray(EQ_W * EQ_H * 4);
|
||||||
if (tex.z > FRONT_Z) {
|
for (let j = 0; j < EQ_H; j++) {
|
||||||
const overlay = sampleOverlayRgba(gridOverlay, tex);
|
for (let i = 0; i < EQ_W; i++) {
|
||||||
if (overlay[3] >= 10) {
|
const lon = (i / EQ_W) * 2 * Math.PI - Math.PI;
|
||||||
return overlay;
|
const lat = Math.PI * 0.5 - (j / EQ_H) * Math.PI;
|
||||||
}
|
const tex = sphereTexFromLonLat(lon, lat);
|
||||||
const hp = hubPixelF(tex);
|
const p = (j * EQ_W + i) * 4;
|
||||||
const sx = (hp.x / GLOBE_VIEW.imgW) * (globe.w - 1);
|
let rgba = [0, 0, 0, 0];
|
||||||
const sy = (hp.y / GLOBE_VIEW.imgH) * (globe.h - 1);
|
|
||||||
const base = sampleImageDataBilinear(globe.data, globe.w, globe.h, sx, sy);
|
|
||||||
if (isGridPixel(base[0], base[1], base[2], base[3])) {
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
return [0, 0, 0, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const ll = bodyLonLat(tex);
|
if (tex.z > FRONT_Z) {
|
||||||
const eq = sampleEquator(gridEq, ll.lon, ll.lat);
|
const ortho = sampleHub(continents, tex);
|
||||||
if (eq[3] < 8) {
|
if (ortho[3] > 6) {
|
||||||
return [0, 0, 0, 0];
|
rgba = [ortho[0], ortho[1], ortho[2], ortho[3]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rgba[3] < 12) {
|
||||||
|
const flat = sampleMercFlat(mercFlat, lon, lat);
|
||||||
|
if (isLandPixel(flat[0], flat[1], flat[2], flat[3])) {
|
||||||
|
rgba = [flat[0], flat[1], flat[2], Math.min(255, flat[3])];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out[p] = rgba[0];
|
||||||
|
out[p + 1] = rgba[1];
|
||||||
|
out[p + 2] = rgba[2];
|
||||||
|
out[p + 3] = rgba[3];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return eq;
|
return { data: out, w: EQ_W, h: EQ_H };
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillDiskRegion(ctx, cx, cy, r, rotY, fn) {
|
function fillDiskRegion(ctx, cx, cy, r, rotY, fn) {
|
||||||
@@ -330,35 +321,34 @@ function blitLayer(ctx, cx, cy, r, layer) {
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawOceanLayer(ctx, cx, cy, r, rotY, globe) {
|
function drawOceanLayer(ctx, cx, cy, r, rotY, oceanEq) {
|
||||||
const layer = fillDiskRegion(ctx, cx, cy, r, rotY, function (body) {
|
const layer = fillDiskRegion(ctx, cx, cy, r, rotY, function (body) {
|
||||||
const tex = toTextureBody(body);
|
const ll = bodyLonLat(body);
|
||||||
if (tex.z > FRONT_Z) {
|
return sampleEq(oceanEq, ll.lon, ll.lat);
|
||||||
return sampleOceanRgba(globe, tex);
|
|
||||||
}
|
|
||||||
return [OCEAN_FALLBACK.r, OCEAN_FALLBACK.g, OCEAN_FALLBACK.b, 255];
|
|
||||||
});
|
});
|
||||||
blitLayer(ctx, cx, cy, r, layer);
|
blitLayer(ctx, cx, cy, r, layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawGridLayer(ctx, cx, cy, r, rotY, gridEq, gridOverlay, globe) {
|
function drawGridLayer(ctx, cx, cy, r, rotY, gridEq) {
|
||||||
const layer = fillDiskRegion(ctx, cx, cy, r, rotY, function (body) {
|
const layer = fillDiskRegion(ctx, cx, cy, r, rotY, function (body) {
|
||||||
const px = sampleGridRgba(body, gridEq, gridOverlay, globe);
|
const ll = bodyLonLat(body);
|
||||||
if (px[3] < 8) {
|
const g = sampleEq(gridEq, ll.lon, ll.lat);
|
||||||
|
if (g[3] < 8) {
|
||||||
return [0, 0, 0, 0];
|
return [0, 0, 0, 0];
|
||||||
}
|
}
|
||||||
return px;
|
return g;
|
||||||
});
|
});
|
||||||
blitLayer(ctx, cx, cy, r, layer);
|
blitLayer(ctx, cx, cy, r, layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawLandLayer(ctx, cx, cy, r, rotY, continents, mercFlat) {
|
function drawLandLayer(ctx, cx, cy, r, rotY, landEq) {
|
||||||
const layer = fillDiskRegion(ctx, cx, cy, r, rotY, function (body) {
|
const layer = fillDiskRegion(ctx, cx, cy, r, rotY, function (body) {
|
||||||
const rgba = sampleLandRgba(continents, mercFlat, body);
|
const ll = bodyLonLat(body);
|
||||||
if (!rgba) {
|
const l = sampleEq(landEq, ll.lon, ll.lat);
|
||||||
|
if (l[3] < 8) {
|
||||||
return [0, 0, 0, 0];
|
return [0, 0, 0, 0];
|
||||||
}
|
}
|
||||||
return rgba;
|
return l;
|
||||||
});
|
});
|
||||||
blitLayer(ctx, cx, cy, r, layer);
|
blitLayer(ctx, cx, cy, r, layer);
|
||||||
}
|
}
|
||||||
@@ -417,7 +407,10 @@ export async function mountHubGlobe(stage, opts) {
|
|||||||
const mercW = (mercatorFlatImg.naturalWidth || 800) * 2;
|
const mercW = (mercatorFlatImg.naturalWidth || 800) * 2;
|
||||||
const mercH = (mercatorFlatImg.naturalHeight || 519) * 2;
|
const mercH = (mercatorFlatImg.naturalHeight || 519) * 2;
|
||||||
const mercFlat = rasterizeImage(mercatorFlatImg, mercW, mercH, 1);
|
const mercFlat = rasterizeImage(mercatorFlatImg, mercW, mercH, 1);
|
||||||
const gridEq = bakeGridEquator(gridOverlay, globe);
|
|
||||||
|
const oceanEq = bakeOceanEquator(globe);
|
||||||
|
const gridEq = bakeGridEquator(globe, gridOverlay);
|
||||||
|
const landEq = bakeLandEquator(continents, mercFlat);
|
||||||
|
|
||||||
let rotation = typeof opts.initialRotationY === 'number' ? opts.initialRotationY : 0;
|
let rotation = typeof opts.initialRotationY === 'number' ? opts.initialRotationY : 0;
|
||||||
let angularVelocity = reducedMotion ? 0 : AUTO_SPIN_RAD_S;
|
let angularVelocity = reducedMotion ? 0 : AUTO_SPIN_RAD_S;
|
||||||
@@ -455,9 +448,9 @@ export async function mountHubGlobe(stage, opts) {
|
|||||||
const rim = rect.rim * dpr;
|
const rim = rect.rim * dpr;
|
||||||
|
|
||||||
ctx.clearRect(0, 0, px, px);
|
ctx.clearRect(0, 0, px, px);
|
||||||
drawOceanLayer(ctx, cx, cy, r, rotation, globe);
|
drawOceanLayer(ctx, cx, cy, r, rotation, oceanEq);
|
||||||
drawGridLayer(ctx, cx, cy, r, rotation, gridEq, gridOverlay, globe);
|
drawGridLayer(ctx, cx, cy, r, rotation, gridEq);
|
||||||
drawLandLayer(ctx, cx, cy, r, rotation, continents, mercFlat);
|
drawLandLayer(ctx, cx, cy, r, rotation, landEq);
|
||||||
drawRim(ctx, globeImg, cx, cy, r, rim, rect.scale * dpr);
|
drawRim(ctx, globeImg, cx, cy, r, rim, rect.scale * dpr);
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ unset($__platformDir);
|
|||||||
var supportsLayout = !!(window.CSS && CSS.supports && CSS.supports('object-fit', 'cover'));
|
var supportsLayout = !!(window.CSS && CSS.supports && CSS.supports('object-fit', 'cover'));
|
||||||
var logoEnabled = !!(stage && supportsSvg && supportsLayout);
|
var logoEnabled = !!(stage && supportsSvg && supportsLayout);
|
||||||
|
|
||||||
var logoBuild = '20260606globe7';
|
var logoBuild = '20260606globe8';
|
||||||
|
|
||||||
function hubAsset(rel) {
|
function hubAsset(rel) {
|
||||||
var link = document.querySelector('link[href*="hub.css"]');
|
var link = document.querySelector('link[href*="hub.css"]');
|
||||||
|
|||||||
Reference in New Issue
Block a user