/** * 3D Foundation Project * Copyright 2025 Smithsonian Institution * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import Subscriber from "@ff/core/Subscriber"; import "@ff/ui/ButtonGroup"; import CVToolProvider from "../../components/CVToolProvider"; import CVDocument from "../../components/CVDocument"; import "../Logo"; import "./MainMenu"; import "./ToolBar"; import "./TourNavigator"; import "./TourMenu"; import "./LanguageMenu"; import "./TagCloud"; import HelpMain from "./HelpMain"; import { ITourMenuSelectEvent } from "./TourMenu"; import DocumentView, { customElement, html } from "./DocumentView"; import LanguageMenu from "./LanguageMenu"; import { EUIElements } from "client/components/CVInterface"; import CVAssetReader from "client/components/CVAssetReader"; import SplashScreen from "./SplashScreen"; //////////////////////////////////////////////////////////////////////////////// @customElement("sv-chrome-view") export default class ChromeView extends DocumentView { protected documentProps = new Subscriber("value", this.onUpdate, this); protected titleElement: HTMLDivElement; protected assetPath: string = ""; protected needsSplash: boolean = true; protected get toolProvider() { return this.system.getMainComponent(CVToolProvider); } protected get assetReader() { return this.system.getMainComponent(CVAssetReader); } protected firstConnected() { this.style.pointerEvents = "none"; this.setAttribute("pointer-events", "none"); this.classList.add("sv-chrome-view"); } protected connected() { super.connected(); this.toolProvider.ins.visible.on("value", this.onUpdate, this); this.activeDocument.setup.language.outs.activeLanguage.on("value", this.onUpdate, this); this.activeDocument.setup.audio.outs.isPlaying.on("value", this.onUpdate, this); this.activeDocument.setup.audio.outs.narrationPlaying.on("value", this.onUpdate, this); this.activeDocument.setup.audio.ins.captionsEnabled.on("value", this.onUpdate, this); this.titleElement = this.createElement("div", null); this.titleElement.classList.add("ff-ellipsis"); this.assetPath = this.assetReader.getSystemAssetUrl(""); } protected disconnected() { this.activeDocument.setup.audio.ins.captionsEnabled.off("value", this.onUpdate, this); this.activeDocument.setup.audio.outs.narrationPlaying.off("value", this.onUpdate, this); this.activeDocument.setup.audio.outs.isPlaying.off("value", this.onUpdate, this); this.activeDocument.setup.language.outs.activeLanguage.off("value", this.onUpdate, this); this.toolProvider.ins.visible.off("value", this.onUpdate, this); super.disconnected(); } protected render() { const document = this.activeDocument; const titleElement = this.titleElement; if (!document) { return html``; } const setup = document.setup; const interfaceVisible = setup.interface.ins.visible.value; const logoVisible = setup.interface.ins.logo.value && setup.interface.isShowing(EUIElements.logo); const menuVisible = setup.interface.ins.menu.value && setup.interface.isShowing(EUIElements.menu); const titleVisible = setup.interface.ins.visibleElements.value && setup.interface.isShowing(EUIElements.title); const helpVisible = setup.interface.ins.visibleElements.value && setup.interface.isShowing(EUIElements.help); const resetVisible = setup.interface.ins.visibleElements.value && setup.interface.isShowing(EUIElements.reset); const readerVisible = setup.reader.ins.enabled.value; const tours = setup.tours.tours; const toursEnabled = setup.tours.ins.enabled.value; const tourActive = setup.tours.outs.tourIndex.value >= 0; const languageManager = setup.language; const languages = languageManager.sceneLanguages; const activeLanguage = languageManager.outs.activeLanguage.value; const languagesVisible = languages.length > 1 && setup.interface.isShowing(EUIElements.language); const captionsVisible = setup.audio.outs.isPlaying.value && setup.audio.getClipCaptionUri(setup.audio.activeId); const captionsEnabled = setup.audio.ins.captionsEnabled.value; const audioVisible = setup.audio.outs.narrationPlaying.value; const isEditing = !!this.system.getComponent("CVStoryApplication", true); const toolBarAllowed = isEditing || !toursEnabled; // tag cloud is visible if annotations are enabled and there is at least one tag in the cloud const tagCloudVisible = setup.viewer.ins.annotationsVisible.value && setup.viewer.outs.tagCloud.value; const toolsVisible = !readerVisible && this.toolProvider.ins.visible.value; const showTourEndMsg = this.activeDocument.setup.tours.outs.ending.value; this.activeDocument.setup.tours.outs.ending.setValue(false); const introText = this.activeDocument.outs.intro.value; if(introText) { // TODO: Stop intro pop-up from spawning when setting property in Story if(this.needsSplash && introText.length > 0) { SplashScreen.show(this, this.activeDocument.setup.language, introText).then(() => { (this.getRootNode() as ShadowRoot).getElementById("sv-scene").focus(); }); } this.needsSplash = false; } if (!interfaceVisible) { return html``; } let title; if (toursEnabled) { if (!tourActive) { title = languageManager.getLocalizedString("Interactive Tours"); } else { title = languageManager.getLocalizedString("Tour") + ": " + setup.tours.outs.tourTitle.value; } } else { title = document.outs.title.value || "Missing Title" || document.name; } titleElement.innerHTML = title; return html`${showTourEndMsg ? html`` : null} ${audioVisible ? html`
${setup.audio.getPlayerById(setup.audio.narrationId)}
` : null}
${menuVisible ? html`` : null}
${titleVisible ? html`
${titleElement}
` : null} ${logoVisible ? html`` : null}
${toursEnabled && tourActive ? html`` : null} ${toursEnabled && !tourActive ? html`` : null} ${tagCloudVisible && toolBarAllowed ? html`` : null} ${toolsVisible && toolBarAllowed ? html`
` : null} `; } protected onSelectTour(event: ITourMenuSelectEvent) { const tours = this.activeDocument.setup.tours; tours.ins.tourIndex.setValue(event.detail.index); } protected closeTours() { const tours = this.activeDocument.setup.tours; tours.ins.enabled.setValue(false); tours.ins.closed.set(); } protected updateCaptions() { const captionIns = this.activeDocument.setup.audio.ins; captionIns.captionsEnabled.setValue(!captionIns.captionsEnabled.value); } protected openLanguageMenu() { const language = this.activeDocument.setup.language; if (!language.ins.enabled.value) { language.ins.enabled.setValue(true); LanguageMenu.show(this, this.activeDocument.setup.language).then(() => { language.ins.enabled.setValue(false); (this.querySelector("#language") as HTMLElement).focus(); }); } } protected openHelp() { const language = this.activeDocument.setup.language; const navMode = this.activeDocument.setup.navigation.ins.mode.value; HelpMain.show(this, language, navMode).then(() => { (this.querySelector("#main-help") as HTMLElement).focus(); }); } protected resetViewer() { this.activeDocument.setup.resetViewer(); } protected closeTools() { const toolIns = this.toolProvider.ins; toolIns.visible.setValue(false); toolIns.closed.set(); } protected onActiveDocument(previous: CVDocument, next: CVDocument) { if (previous) { this.documentProps.off(); } if (next) { const setup = next.setup; this.documentProps.on( next.outs.title, next.outs.assetPath, setup.interface.ins.visible, setup.interface.ins.logo, setup.interface.ins.menu, setup.viewer.ins.annotationsVisible, setup.reader.ins.enabled, setup.tours.ins.enabled, setup.tours.outs.tourIndex, ); } this.requestUpdate(); } }