1
0
Files
VR-Web-Player/src/vr180player/bootstrap.ts
2026-06-10 12:48:36 +10:00

109 lines
3.1 KiB
TypeScript

import {
DEFAULT_PROJECTION,
PLAYER_SELECTOR,
type ProjectionMode,
VALID_PROJECTIONS
} from './config.js';
import { create2DControlPanel, createPlayButton, injectPlayerStyles } from './dom/dom.js';
import { createMediaAdapter, type SupportedMediaAdapter } from './media/media-adapter.js';
export type BootstrapContext = {
mediaAdapter: SupportedMediaAdapter;
playButton: HTMLButtonElement;
playerContainer: HTMLElement;
projectionMode: ProjectionMode;
};
export function bootstrapPlayer(playerBase: string, onReady: (context: BootstrapContext) => void): void {
injectPlayerStyles(playerBase);
onDocumentReady(() => {
const containers = document.querySelectorAll<HTMLElement>(PLAYER_SELECTOR);
if (containers.length === 0) {
console.error(`VR_WEB_PLAYER_DOM: Expected exactly one ${PLAYER_SELECTOR} container, found none.`);
return;
}
if (containers.length > 1) {
console.warn(`VR_WEB_PLAYER_DOM: This version supports exactly one ${PLAYER_SELECTOR} container per page. Found ${containers.length}; no player was initialized.`);
return;
}
const playerContainer = containers[0];
playerContainer.classList.add('vrwp');
const configuredProjection = (playerContainer.dataset.projection || DEFAULT_PROJECTION).trim().toLowerCase();
if (!VALID_PROJECTIONS.has(configuredProjection as ProjectionMode)) {
console.error(`VR_WEB_PLAYER_CONFIG: Unsupported data-projection="${configuredProjection}". Use "vr180" or "plane".`);
return;
}
const mediaAdapter = createMediaAdapter(playerContainer);
if (!mediaAdapter) {
console.error(`VR_WEB_PLAYER_DOM: ${PLAYER_SELECTOR} must contain exactly one supported media element: video or img.`);
return;
}
const playButton = createPlayButton();
playerContainer.appendChild(playButton);
playerContainer.appendChild(create2DControlPanel());
playButton.disabled = true;
mediaAdapter.bindLoadState({
onError: (event) => {
console.error(`VR_WEB_PLAYER_MEDIA: Failed to load ${mediaAdapter.kind} media.`, event);
playButton.disabled = true;
},
onReady: () => {
playButton.disabled = false;
}
});
mediaAdapter.load();
completeXrSupportCheck(playButton, () => {
onReady({
mediaAdapter,
playButton,
playerContainer,
projectionMode: configuredProjection as ProjectionMode
});
});
});
}
function onDocumentReady(callback: () => void): void {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', callback, { once: true });
return;
}
callback();
}
function completeXrSupportCheck(playButton: HTMLButtonElement, onComplete: () => void): void {
if (!navigator.xr) {
markXrUnsupported(playButton);
onComplete();
return;
}
navigator.xr.isSessionSupported('immersive-vr').then((supported) => {
if (supported) {
playButton.dataset.xrSupported = 'true';
} else {
markXrUnsupported(playButton);
}
onComplete();
}).catch((err) => {
console.error('XR Support Check Error:', err);
markXrUnsupported(playButton);
onComplete();
});
}
function markXrUnsupported(playButton: HTMLButtonElement): void {
playButton.dataset.xrSupported = 'false';
playButton.disabled = false;
}